@@ -310,85 +310,71 @@ else
310310 ba_log " Codename One stub activity already present at $STUB_SRC_FILE "
311311fi
312312
313+ FQCN=" ${PACKAGE_NAME} .${MAIN_NAME} Stub"
313314MANIFEST_FILE=" $APP_MODULE_DIR /src/main/AndroidManifest.xml"
314- if [ ! -f " $MANIFEST_FILE " ]; then
315+
316+ normalize_stub_manifest () {
315317 mkdir -p " $( dirname " $MANIFEST_FILE " ) "
316- cat > " $MANIFEST_FILE " << EOF
317- <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="$PACKAGE_NAME ">
318- <application/>
318+ if [ ! -f " $MANIFEST_FILE " ]; then
319+ cat > " $MANIFEST_FILE " << 'EOF '
320+ <manifest xmlns:android="http://schemas.android.com/apk/res/android">
321+ <application/>
319322</manifest>
320323EOF
321- ba_log " Created minimal Android manifest at $MANIFEST_FILE "
322- fi
324+ ba_log " Created minimal Android manifest at $MANIFEST_FILE "
325+ fi
323326
324- ensure_manifest_stub () {
325- python3 - " $MANIFEST_FILE " " $MAIN_NAME " " $PACKAGE_NAME " << 'PY '
326- import sys
327- from pathlib import Path
328- import xml.etree.ElementTree as ET
329-
330- manifest_path = Path(sys.argv[1])
331- main_name = sys.argv[2]
332- package_name = sys.argv[3]
333- ns_android = "http://schemas.android.com/apk/res/android"
334-
335- ET.register_namespace('android', ns_android)
336- tree = ET.parse(manifest_path)
337- root = tree.getroot()
338- changed = False
339-
340- # Ensure manifest package aligns with the Codename One package
341- if root.get('package') != package_name:
342- root.set('package', package_name)
343- changed = True
344-
345- app = root.find('application')
346- if app is None:
347- app = ET.SubElement(root, 'application')
348- changed = True
349-
350- target_fqcn = f"{package_name}.{main_name}Stub"
351- target_relative = f".{main_name}Stub"
352- android_name = f"{{{ns_android}}}name"
353- android_exported = f"{{{ns_android}}}exported"
354-
355- for activity in app.findall('activity'):
356- name = activity.get(android_name)
357- if name == target_relative:
358- activity.set(android_name, target_fqcn)
359- changed = True
360- break
361- if name == target_fqcn:
362- break
363- else:
364- activity = ET.Element('activity')
365- activity.set(android_name, target_fqcn)
366- activity.set(android_exported, 'false')
367- app.append(activity)
368- changed = True
369-
370- if changed:
371- tree.write(manifest_path, encoding='utf-8', xml_declaration=True)
372- PY
373- }
327+ # Ensure the main manifest has an <application> element
328+ grep -q ' <application' " $MANIFEST_FILE " || sed -i ' s#</manifest># <application/>\n</manifest>#' " $MANIFEST_FILE "
374329
375- if ! ensure_manifest_stub; then
376- ba_log " Failed to ensure stub activity declaration in $MANIFEST_FILE " >&2
377- exit 1
378- fi
330+ # Remove deprecated package attribute and any inline <uses-sdk/> declarations
331+ perl -0777 -pe ' s/\s+package="[^"]*"//; s#<uses-sdk\b[^>]*/>\s*##g' -i " $MANIFEST_FILE "
379332
380- FQCN=" ${PACKAGE_NAME} .${MAIN_NAME} Stub"
333+ remove_stub_declaration () {
334+ local manifest_path=" $1 "
335+ [ -f " $manifest_path " ] || return 0
336+ local escaped tmp
337+ escaped=$( printf ' %s' " $FQCN " | sed ' s/[\\&/]/\\&/g' )
338+ tmp=$( mktemp)
339+ perl -0777 -pe " s{<activity\\ b[^>]*android:name=\\\" $escaped \\\" [^>]*/>\\ s*}{}g; s{<activity\\ b[^>]*android:name=\\\" $escaped \\\" [^>]*>.*?<\\ /activity>\\ s*}{}gs" " $manifest_path " > " $tmp "
340+ mv " $tmp " " $manifest_path "
341+ }
381342
382- if ! grep -Eq " android:name=\" ${PACKAGE_NAME// ./ \\ .} \\ .${MAIN_NAME} Stub\" " " $MANIFEST_FILE " ; then
383- ba_log " Manifest does not declare ${MAIN_NAME} Stub activity" >&2
384- exit 1
385- fi
343+ for SS in main debug release; do
344+ remove_stub_declaration " $APP_MODULE_DIR /src/$SS /AndroidManifest.xml"
345+ done
346+
347+ # Ensure tools namespace is available for merge directives
348+ if ! grep -q ' xmlns:tools=' " $MANIFEST_FILE " ; then
349+ perl -0777 -pe ' s/<manifest\b([^>]*)>/<manifest\1 xmlns:tools="http:\/\/schemas.android.com\/tools">/' -i " $MANIFEST_FILE "
350+ fi
351+
352+ # Insert a single canonical stub declaration
353+ awk -v fqcn=" $FQCN " '
354+ BEGIN{inserted=0}
355+ /<\/application>/ && !inserted {
356+ print " <activity android:name=\"" fqcn "\" android:exported=\"false\" tools:node=\"replace\" />"
357+ inserted=1
358+ }
359+ {print}
360+ ' " $MANIFEST_FILE " > " $MANIFEST_FILE .tmp"
361+ mv " $MANIFEST_FILE .tmp" " $MANIFEST_FILE "
362+
363+ ba_log " Canonicalized stub activity declaration in $MANIFEST_FILE "
364+ }
365+
366+ normalize_stub_manifest
386367
387368if [ ! -f " $STUB_SRC_FILE " ]; then
388369 ba_log " Missing stub activity source at $STUB_SRC_FILE " >&2
389370 exit 1
390371fi
391372
373+ if ! grep -Fq " android:name=\" $FQCN \" " " $MANIFEST_FILE " ; then
374+ ba_log " Manifest does not declare ${MAIN_NAME} Stub activity" >&2
375+ exit 1
376+ fi
377+
392378APP_BUILD_GRADLE=" $APP_MODULE_DIR /build.gradle"
393379if [ ! -f " $APP_BUILD_GRADLE " ]; then
394380 ba_log " Expected Gradle build file not found at $APP_BUILD_GRADLE " >&2
@@ -494,6 +480,16 @@ ba_log "Dependencies block after instrumentation update:"
494480awk ' /^\s*dependencies\s*\{/{flag=1} flag{print} /^\s*\}/{if(flag){exit}}' " $APP_BUILD_GRADLE " \
495481 | sed ' s/^/[build-android-app] | /'
496482
483+ ba_log " Validating manifest merge before assemble"
484+ if ! (
485+ cd " $GRADLE_PROJECT_DIR " &&
486+ JAVA_HOME=" $JAVA17_HOME " PATH=" $JAVA17_HOME /bin:$PATH " ./gradlew --no-daemon :app:processDebugMainManifest
487+ ); then
488+ ba_log " :app:processDebugMainManifest failed during preflight" >&2
489+ dump_manifest_merger_reports
490+ exit 1
491+ fi
492+
497493FINAL_ARTIFACT_DIR=" ${CN1_TEST_SCREENSHOT_EXPORT_DIR:- $REPO_ROOT / build-artifacts} "
498494mkdir -p " $FINAL_ARTIFACT_DIR "
499495if [ -n " ${GITHUB_ENV:- } " ]; then
@@ -1066,114 +1062,14 @@ fi
10661062APP_ID=" $( printf ' %s\n' " $APP_PROPERTIES_RAW " | awk -F' : ' ' /^applicationId:/{print $2; exit}' ) "
10671063NS_VALUE=" $( printf ' %s\n' " $APP_PROPERTIES_RAW " | awk -F' : ' ' /^namespace:/{print $2; exit}' ) "
10681064
1069- if [ -f " $APP_BUILD_GRADLE " ]; then
1070- if [ -z " $APP_ID " ] || [ " $APP_ID " != " $PACKAGE_NAME " ]; then
1071- ba_log " Patching applicationId -> $PACKAGE_NAME "
1072- awk -v appid=" $PACKAGE_NAME " '
1073- BEGIN{inDc=0;had=0}
1074- /^\s*defaultConfig\s*\{/ {inDc=1}
1075- inDc && /^\s*applicationId\s+/ {printf " applicationId \"%s\"\n", appid; had=1; next}
1076- inDc && /^\s*}/ {
1077- if(!had){printf " applicationId \"%s\"\n", appid; had=1}
1078- inDc=0
1079- }
1080- {print}
1081- ' " $APP_BUILD_GRADLE " > " $APP_BUILD_GRADLE .tmp" && mv " $APP_BUILD_GRADLE .tmp" " $APP_BUILD_GRADLE "
1082- fi
1083-
1084- if [ -z " $NS_VALUE " ] || [ " $NS_VALUE " != " $PACKAGE_NAME " ]; then
1085- ba_log " Patching namespace -> $PACKAGE_NAME "
1086- awk -v ns=" $PACKAGE_NAME " '
1087- BEGIN{inAndroid=0;had=0}
1088- /^android\s*\{/ {inAndroid=1}
1089- inAndroid && /^\s*namespace\s+/ {printf " namespace \"%s\"\n", ns; had=1; next}
1090- inAndroid && /^\s*}/ {
1091- if(inAndroid && !had){printf " namespace \"%s\"\n", ns; had=1}
1092- inAndroid=0
1093- }
1094- {print}
1095- ' " $APP_BUILD_GRADLE " > " $APP_BUILD_GRADLE .tmp" && mv " $APP_BUILD_GRADLE .tmp" " $APP_BUILD_GRADLE "
1096- fi
1097- fi
1098-
1099- FQCN=" ${PACKAGE_NAME} .${MAIN_NAME} Stub"
1100- remove_stub_declaration () {
1101- local manifest_path=" $1 "
1102- [ -f " $manifest_path " ] || return 0
1103- local tmp escaped
1104- tmp=$( mktemp)
1105- escaped=$( printf ' %s' " $FQCN " | sed ' s/[\\&/]/\\&/g' )
1106- perl -0777 -pe " s{<activity\\ b[^>]*android:name=\\\" $escaped \\\" [^>]*/>\\ s*}{}g; s{<activity\\ b[^>]*android:name=\\\" $escaped \\\" [^>]*>.*?<\\ /activity>\\ s*}{}gs" " $manifest_path " > " $tmp "
1107- if ! cmp -s " $manifest_path " " $tmp " ; then
1108- mv " $tmp " " $manifest_path "
1109- ba_log " Removed existing ${FQCN} declarations from $manifest_path "
1110- else
1111- rm -f " $tmp "
1112- fi
1113- }
1114-
1115- SRC_SETS=(main debug release)
1116- for SS in " ${SRC_SETS[@]} " ; do
1117- MANIFEST_PATH=" $APP_MODULE_DIR /src/$SS /AndroidManifest.xml"
1118- remove_stub_declaration " $MANIFEST_PATH "
1119- done
1120-
1121- MAIN_MANIFEST=" $APP_MODULE_DIR /src/main/AndroidManifest.xml"
1122- if [ ! -f " $MAIN_MANIFEST " ]; then
1123- mkdir -p " $( dirname " $MAIN_MANIFEST " ) "
1124- cat > " $MAIN_MANIFEST " << EOF
1125- <manifest xmlns:android="http://schemas.android.com/apk/res/android">
1126- <application/>
1127- </manifest>
1128- EOF
1129- ba_log " Created $MAIN_MANIFEST "
1130- fi
1131-
1132- grep -q ' <application' " $MAIN_MANIFEST " || sed -i ' s#</manifest># <application/>\n</manifest>#' " $MAIN_MANIFEST "
1133-
1134- perl -0777 -pe ' s/\s+package="[^"]*"//; s#<uses-sdk\b[^>]*/>\s*##g' -i " $MAIN_MANIFEST "
1135-
1136- # Ensure no stale stub declarations remain before inserting the canonical entry.
1137- remove_stub_declaration " $MAIN_MANIFEST "
1138-
1139- awk -v fqcn=" $FQCN " '
1140- BEGIN{inserted=0}
1141- /<\/application>/ && !inserted {
1142- print " <activity android:name=\"" fqcn "\" android:exported=\"false\" />"
1143- inserted=1
1144- }
1145- {print}
1146- ' " $MAIN_MANIFEST " > " $MAIN_MANIFEST .tmp"
1147- mv " $MAIN_MANIFEST .tmp" " $MAIN_MANIFEST "
1148-
1149- ba_log " Canonicalized stub activity declaration in $MAIN_MANIFEST "
1150-
1151- ba_log " Validating manifest merge after stub declaration"
1152- set +e
1153- (
1154- cd " $GRADLE_PROJECT_DIR "
1155- ./gradlew --no-daemon :app:processDebugMainManifest
1156- )
1157- PROCESS_EXIT=$?
1158- set -e
1159- if [ " $PROCESS_EXIT " -ne 0 ]; then
1160- ba_log " :app:processDebugMainManifest failed after manifest updates"
1161- dump_manifest_merger_reports
1065+ if [ -z " $APP_ID " ] || [ " $APP_ID " != " $PACKAGE_NAME " ]; then
1066+ ba_log " ERROR: applicationId=$APP_ID does not match Codename One package $PACKAGE_NAME " >&2
11621067 stop_emulator
11631068 exit 1
11641069fi
11651070
1166- ba_log " Rebuilding :app after identifier and manifest patches"
1167- set +e
1168- (
1169- cd " $GRADLE_PROJECT_DIR "
1170- ./gradlew --no-daemon :app:assembleDebug :app:assembleDebugAndroidTest -x lint -x test
1171- )
1172- PATCH_EXIT=$?
1173- set -e
1174- if [ " $PATCH_EXIT " -ne 0 ]; then
1175- ba_log " Gradle assemble failed after identifier patching"
1176- dump_manifest_merger_reports
1071+ if [ -z " $NS_VALUE " ] || [ " $NS_VALUE " != " $PACKAGE_NAME " ]; then
1072+ ba_log " ERROR: namespace=$NS_VALUE does not match Codename One package $PACKAGE_NAME " >&2
11771073 stop_emulator
11781074 exit 1
11791075fi
@@ -1192,23 +1088,8 @@ else
11921088 ba_log " WARN: merged manifest not found at $MERGED_MANIFEST "
11931089fi
11941090
1195- APP_PROPERTIES_RAW=$( cd " $GRADLE_PROJECT_DIR " && ./gradlew -q :app:properties 2> /dev/null || true)
1196- if [ -n " $APP_PROPERTIES_RAW " ]; then
1197- printf ' %s\n' " $APP_PROPERTIES_RAW " | grep -E ' ^(applicationId|testApplicationId|namespace):' | sed ' s/^/[build-android-app] props (post-patch): /'
1198- APP_ID=" $( printf ' %s\n' " $APP_PROPERTIES_RAW " | awk -F' : ' ' /^applicationId:/{print $2; exit}' ) "
1199- NS_VALUE=" $( printf ' %s\n' " $APP_PROPERTIES_RAW " | awk -F' : ' ' /^namespace:/{print $2; exit}' ) "
1200- if [ -n " $APP_ID " ] && [ " $APP_ID " != " $PACKAGE_NAME " ]; then
1201- ba_log " ERROR: applicationId=$APP_ID does not match Codename One package $PACKAGE_NAME " >&2
1202- stop_emulator
1203- exit 1
1204- fi
1205- if [ -n " $NS_VALUE " ] && [ " $NS_VALUE " != " $PACKAGE_NAME " ]; then
1206- ba_log " ERROR: namespace=$NS_VALUE does not match Codename One package $PACKAGE_NAME " >&2
1207- stop_emulator
1208- exit 1
1209- fi
1210- else
1211- ba_log " Warning: unable to query :app:properties after patching" >&2
1091+ if [ -z " $APP_PROPERTIES_RAW " ]; then
1092+ ba_log " Warning: unable to query :app:properties" >&2
12121093fi
12131094
12141095APP_APK=" $( find " $GRADLE_PROJECT_DIR /app/build/outputs/apk/debug" -maxdepth 1 -name ' *-debug.apk' | head -n1 || true) "
0 commit comments