fix(ci): direct AAPT2 userspace wrapping with QEMU to bypass host binfmt and FEX
This commit is contained in:
@@ -117,10 +117,61 @@ jobs:
|
|||||||
flutter test
|
flutter test
|
||||||
|
|
||||||
- name: Build Universal Release APK
|
- name: Build Universal Release APK
|
||||||
run: flutter build apk --release
|
run: |
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
|
||||||
|
(
|
||||||
|
while true; do
|
||||||
|
for aapt2_bin in $(find /root/.gradle /opt/android-sdk-linux -type f -name "aapt2" 2>/dev/null); do
|
||||||
|
if [ -f "$aapt2_bin" ] && ! [ -f "${aapt2_bin}.orig" ]; then
|
||||||
|
if head -c 4 "$aapt2_bin" 2>/dev/null | grep -q 'ELF'; then
|
||||||
|
echo "Wrapping newly extracted AAPT2: $aapt2_bin"
|
||||||
|
mv "$aapt2_bin" "${aapt2_bin}.orig"
|
||||||
|
printf '#!/bin/sh\nexec /usr/bin/qemu-x86_64-static -L /usr/x86_64-linux-gnu "$0.orig" "$@"\n' > "$aapt2_bin"
|
||||||
|
chmod +x "$aapt2_bin" "${aapt2_bin}.orig"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
) &
|
||||||
|
AAPT2_WATCHER_PID=$!
|
||||||
|
echo "AAPT2 Watcher started with PID $AAPT2_WATCHER_PID"
|
||||||
|
fi
|
||||||
|
|
||||||
|
flutter build apk --release
|
||||||
|
|
||||||
|
if [ -n "$AAPT2_WATCHER_PID" ]; then
|
||||||
|
kill "$AAPT2_WATCHER_PID" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Build Split-per-ABI Release APKs
|
- name: Build Split-per-ABI Release APKs
|
||||||
run: flutter build apk --release --split-per-abi
|
run: |
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
|
||||||
|
(
|
||||||
|
while true; do
|
||||||
|
for aapt2_bin in $(find /root/.gradle /opt/android-sdk-linux -type f -name "aapt2" 2>/dev/null); do
|
||||||
|
if [ -f "$aapt2_bin" ] && ! [ -f "${aapt2_bin}.orig" ]; then
|
||||||
|
if head -c 4 "$aapt2_bin" 2>/dev/null | grep -q 'ELF'; then
|
||||||
|
echo "Wrapping newly extracted AAPT2: $aapt2_bin"
|
||||||
|
mv "$aapt2_bin" "${aapt2_bin}.orig"
|
||||||
|
printf '#!/bin/sh\nexec /usr/bin/qemu-x86_64-static -L /usr/x86_64-linux-gnu "$0.orig" "$@"\n' > "$aapt2_bin"
|
||||||
|
chmod +x "$aapt2_bin" "${aapt2_bin}.orig"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
) &
|
||||||
|
AAPT2_WATCHER_PID=$!
|
||||||
|
fi
|
||||||
|
|
||||||
|
flutter build apk --release --split-per-abi
|
||||||
|
|
||||||
|
if [ -n "$AAPT2_WATCHER_PID" ]; then
|
||||||
|
kill "$AAPT2_WATCHER_PID" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Organize Artifacts
|
- name: Organize Artifacts
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -117,10 +117,61 @@ jobs:
|
|||||||
flutter test
|
flutter test
|
||||||
|
|
||||||
- name: Build Universal Release APK
|
- name: Build Universal Release APK
|
||||||
run: flutter build apk --release
|
run: |
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
|
||||||
|
(
|
||||||
|
while true; do
|
||||||
|
for aapt2_bin in $(find /root/.gradle /opt/android-sdk-linux -type f -name "aapt2" 2>/dev/null); do
|
||||||
|
if [ -f "$aapt2_bin" ] && ! [ -f "${aapt2_bin}.orig" ]; then
|
||||||
|
if head -c 4 "$aapt2_bin" 2>/dev/null | grep -q 'ELF'; then
|
||||||
|
echo "Wrapping newly extracted AAPT2: $aapt2_bin"
|
||||||
|
mv "$aapt2_bin" "${aapt2_bin}.orig"
|
||||||
|
printf '#!/bin/sh\nexec /usr/bin/qemu-x86_64-static -L /usr/x86_64-linux-gnu "$0.orig" "$@"\n' > "$aapt2_bin"
|
||||||
|
chmod +x "$aapt2_bin" "${aapt2_bin}.orig"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
) &
|
||||||
|
AAPT2_WATCHER_PID=$!
|
||||||
|
echo "AAPT2 Watcher started with PID $AAPT2_WATCHER_PID"
|
||||||
|
fi
|
||||||
|
|
||||||
|
flutter build apk --release
|
||||||
|
|
||||||
|
if [ -n "$AAPT2_WATCHER_PID" ]; then
|
||||||
|
kill "$AAPT2_WATCHER_PID" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Build Split-per-ABI Release APKs
|
- name: Build Split-per-ABI Release APKs
|
||||||
run: flutter build apk --release --split-per-abi
|
run: |
|
||||||
|
ARCH=$(uname -m)
|
||||||
|
if [ "$ARCH" = "aarch64" ] || [ "$ARCH" = "arm64" ]; then
|
||||||
|
(
|
||||||
|
while true; do
|
||||||
|
for aapt2_bin in $(find /root/.gradle /opt/android-sdk-linux -type f -name "aapt2" 2>/dev/null); do
|
||||||
|
if [ -f "$aapt2_bin" ] && ! [ -f "${aapt2_bin}.orig" ]; then
|
||||||
|
if head -c 4 "$aapt2_bin" 2>/dev/null | grep -q 'ELF'; then
|
||||||
|
echo "Wrapping newly extracted AAPT2: $aapt2_bin"
|
||||||
|
mv "$aapt2_bin" "${aapt2_bin}.orig"
|
||||||
|
printf '#!/bin/sh\nexec /usr/bin/qemu-x86_64-static -L /usr/x86_64-linux-gnu "$0.orig" "$@"\n' > "$aapt2_bin"
|
||||||
|
chmod +x "$aapt2_bin" "${aapt2_bin}.orig"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
sleep 0.5
|
||||||
|
done
|
||||||
|
) &
|
||||||
|
AAPT2_WATCHER_PID=$!
|
||||||
|
fi
|
||||||
|
|
||||||
|
flutter build apk --release --split-per-abi
|
||||||
|
|
||||||
|
if [ -n "$AAPT2_WATCHER_PID" ]; then
|
||||||
|
kill "$AAPT2_WATCHER_PID" 2>/dev/null || true
|
||||||
|
fi
|
||||||
|
|
||||||
- name: Organize Artifacts
|
- name: Organize Artifacts
|
||||||
run: |
|
run: |
|
||||||
|
|||||||
@@ -43,3 +43,52 @@ subprojects {
|
|||||||
tasks.register("clean", Delete) {
|
tasks.register("clean", Delete) {
|
||||||
delete rootProject.buildDir
|
delete rootProject.buildDir
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auto-wrap x86_64 AAPT2 on ARM64 Linux hosts (e.g. CI runner) to run seamlessly via QEMU
|
||||||
|
gradle.taskGraph.whenReady { taskGraph ->
|
||||||
|
String os = System.getProperty("os.name", "").toLowerCase()
|
||||||
|
String arch = System.getProperty("os.arch", "").toLowerCase()
|
||||||
|
if (os.contains("linux") && (arch.contains("aarch64") || arch.contains("arm64"))) {
|
||||||
|
def qemu = new File("/usr/bin/qemu-x86_64-static").exists() ? "/usr/bin/qemu-x86_64-static" : "qemu-x86_64-static"
|
||||||
|
def wrapAapt2 = {
|
||||||
|
def searchDirs = []
|
||||||
|
def gh = gradle.gradleUserHomeDir
|
||||||
|
if (gh != null && gh.exists()) {
|
||||||
|
def transforms = new File(gh, "caches")
|
||||||
|
if (transforms.exists()) searchDirs.add(transforms)
|
||||||
|
}
|
||||||
|
def sdk = new File("/opt/android-sdk-linux/build-tools")
|
||||||
|
if (sdk.exists()) searchDirs.add(sdk)
|
||||||
|
def rootGradle = new File("/root/.gradle/caches")
|
||||||
|
if (rootGradle.exists()) searchDirs.add(rootGradle)
|
||||||
|
|
||||||
|
searchDirs.each { dir ->
|
||||||
|
dir.eachFileRecurse(groovy.io.FileType.FILES) { file ->
|
||||||
|
if (file.name == "aapt2" && !file.name.endsWith(".orig")) {
|
||||||
|
try {
|
||||||
|
byte[] header = new byte[4]
|
||||||
|
file.withInputStream { is -> is.read(header) }
|
||||||
|
if (header[0] == 0x7F && header[1] == 0x45 && header[2] == 0x4C && header[3] == 0x46) {
|
||||||
|
def orig = new File(file.parentFile, "aapt2.orig")
|
||||||
|
if (!orig.exists()) {
|
||||||
|
file.renameTo(orig)
|
||||||
|
orig.setExecutable(true)
|
||||||
|
}
|
||||||
|
file.text = "#!/bin/sh\nexec ${qemu} -L /usr/x86_64-linux-gnu \"\$(dirname \"\$0\")/aapt2.orig\" \"\$@\"\n"
|
||||||
|
file.setExecutable(true)
|
||||||
|
println "[ARM64 CI] Wrapped AAPT2 at ${file.absolutePath} with QEMU"
|
||||||
|
}
|
||||||
|
} catch (Exception ignored) {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
taskGraph.allTasks.findAll {
|
||||||
|
it.name.contains("Resource") || it.name.contains("Aapt") || it.name.contains("Package") || it.name.contains("Bundle")
|
||||||
|
}.each { task ->
|
||||||
|
task.doFirst {
|
||||||
|
wrapAapt2()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user