Skip to content

Commit f1dedcb

Browse files
committed
Normalize manifest before first assemble
1 parent ab7bc93 commit f1dedcb

File tree

1 file changed

+67
-186
lines changed

1 file changed

+67
-186
lines changed

scripts/build-android-app.sh

Lines changed: 67 additions & 186 deletions
Original file line numberDiff line numberDiff line change
@@ -310,85 +310,71 @@ else
310310
ba_log "Codename One stub activity already present at $STUB_SRC_FILE"
311311
fi
312312

313+
FQCN="${PACKAGE_NAME}.${MAIN_NAME}Stub"
313314
MANIFEST_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>
320323
EOF
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

387368
if [ ! -f "$STUB_SRC_FILE" ]; then
388369
ba_log "Missing stub activity source at $STUB_SRC_FILE" >&2
389370
exit 1
390371
fi
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+
392378
APP_BUILD_GRADLE="$APP_MODULE_DIR/build.gradle"
393379
if [ ! -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:"
494480
awk '/^\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+
497493
FINAL_ARTIFACT_DIR="${CN1_TEST_SCREENSHOT_EXPORT_DIR:-$REPO_ROOT/build-artifacts}"
498494
mkdir -p "$FINAL_ARTIFACT_DIR"
499495
if [ -n "${GITHUB_ENV:-}" ]; then
@@ -1066,114 +1062,14 @@ fi
10661062
APP_ID="$(printf '%s\n' "$APP_PROPERTIES_RAW" | awk -F': ' '/^applicationId:/{print $2; exit}')"
10671063
NS_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
11641069
fi
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
11791075
fi
@@ -1192,23 +1088,8 @@ else
11921088
ba_log "WARN: merged manifest not found at $MERGED_MANIFEST"
11931089
fi
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
12121093
fi
12131094

12141095
APP_APK="$(find "$GRADLE_PROJECT_DIR/app/build/outputs/apk/debug" -maxdepth 1 -name '*-debug.apk' | head -n1 || true)"

0 commit comments

Comments
 (0)