Repository navigation
405 lines (359 loc) · 16.9 KB
/
Copy pathcompilation.yml
File metadata and controls
405 lines (359 loc) · 16.9 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
name: CI
permissions:
contents: write
on:
workflow_dispatch:
inputs:
skip_toolchain:
description: "Development mode: skip toolchain build and start directly from ps2sdk (uncheck for full build)"
type: boolean
default: false
arch_arm64_v8a:
description: "Android arm64-v8a (64-bit ARM, most modern phones)"
type: boolean
default: true
arch_armeabi_v7a:
description: "Android armeabi-v7a (32-bit ARM, older phones)"
type: boolean
default: true
arch_x86_64:
description: "Android x86_64 (64-bit Intel/AMD: emulators, Chromebooks)"
type: boolean
default: true
arch_x86:
description: "Android x86 (32-bit Intel/AMD)"
type: boolean
default: true
skip_runtime_test:
description: "Skip the post-build runtime test (Android userland under QEMU). Normally leave unchecked."
type: boolean
default: false
dev_steps:
description: "Steps to run (work in both modes): 1=toolchain 2=ps2sdk 3=ports 4=packer 5=ps2client. Step 5 is not built for Android/iOS. Example: '1 2 3 4' or '3'."
type: string
default: "1 2 3 4"
env:
DEBIAN_FRONTEND: noninteractive
TZ: Japan
ANDROID_API: 21
jobs:
# Turns the "archs" input into a build matrix. One table holds everything that differs
# between the Android ABIs, so adding an architecture never means editing the build steps.
prepare:
runs-on: ubuntu-latest
outputs:
matrix: ${{ steps.m.outputs.matrix }}
steps:
- id: m
env:
SEL_ARM64: ${{ inputs.arch_arm64_v8a }}
SEL_ARMV7: ${{ inputs.arch_armeabi_v7a }}
SEL_X8664: ${{ inputs.arch_x86_64 }}
SEL_X86: ${{ inputs.arch_x86 }}
run: |
set -e
# An architecture is built unless its box is explicitly unticked ("false").
# (If the workflow is ever started without inputs, all four are built.)
ARCHS=""
[ "$SEL_ARM64" != "false" ] && ARCHS="$ARCHS arm64-v8a"
[ "$SEL_ARMV7" != "false" ] && ARCHS="$ARCHS armeabi-v7a"
[ "$SEL_X8664" != "false" ] && ARCHS="$ARCHS x86_64"
[ "$SEL_X86" != "false" ] && ARCHS="$ARCHS x86"
if [ -z "$ARCHS" ]; then echo "ERROR: no architecture is ticked: tick at least one."; exit 1; fi
echo "Architectures to build:$ARCHS"
# arch triple (clang prefix) configure --host ELF machine readelf "Machine:" Termux image docker platform
declare -A TRIPLE=( [arm64-v8a]=aarch64-linux-android [armeabi-v7a]=armv7a-linux-androideabi [x86_64]=x86_64-linux-android [x86]=i686-linux-android )
declare -A HOST=( [arm64-v8a]=aarch64-linux-android [armeabi-v7a]=arm-linux-androideabi [x86_64]=x86_64-linux-android [x86]=i686-linux-android )
declare -A MACHEX=( [arm64-v8a]=b7 [armeabi-v7a]=28 [x86_64]=3e [x86]=03 )
declare -A RDMACH=( [arm64-v8a]=AArch64 [armeabi-v7a]=ARM [x86_64]=X86-64 [x86]=80386 )
declare -A IMAGE=( [arm64-v8a]=aarch64 [armeabi-v7a]=arm [x86_64]=x86_64 [x86]=i686 )
# API level: 32-bit ABIs need 24 (bionic declares fseeko/ftello for 32-bit only from API 24;
# at 21 binutils fails with "call to undeclared function fseeko"). 64-bit ABIs keep 21.
declare -A API=( [arm64-v8a]=21 [armeabi-v7a]=24 [x86_64]=21 [x86]=24 )
declare -A PLAT=( [arm64-v8a]=linux/arm64 [armeabi-v7a]=linux/arm/v7 [x86_64]=linux/amd64 [x86]=linux/386 )
items=()
for a in $ARCHS; do
if [ -z "${TRIPLE[$a]}" ]; then
echo "ERROR: unknown architecture '$a' (allowed: arm64-v8a armeabi-v7a x86_64 x86)"; exit 1
fi
items+=("{\"arch\":\"$a\",\"triple\":\"${TRIPLE[$a]}\",\"host\":\"${HOST[$a]}\",\"machex\":\"${MACHEX[$a]}\",\"rdmach\":\"${RDMACH[$a]}\",\"image\":\"${IMAGE[$a]}\",\"plat\":\"${PLAT[$a]}\",\"api\":\"${API[$a]}\"}")
done
matrix="{\"include\":[$(IFS=,; echo "${items[*]}")]}"
echo "matrix=$matrix"
echo "matrix=$matrix" >> "$GITHUB_OUTPUT"
build:
name: Build Android ${{ matrix.arch }}
needs: prepare
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.prepare.outputs.matrix) }}
env:
ANDROID_ARCH_TARGET: ${{ matrix.triple }}
ANDROID_API: ${{ matrix.api }}
runs-on: ubuntu-latest
container:
image: "ubuntu:20.04"
options: "--user 0 --privileged"
timeout-minutes: 240
defaults:
run:
shell: bash
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Enable QEMU emulation for Android ARM64 binaries
run: |
apt-get -y update
apt-get -y install qemu-user-static binfmt-support
- name: Install Ubuntu host packages
run: |
apt-get -y update
apt-get -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' install \
texinfo bison flex gettext autopoint autoconf automake libtool pkg-config \
libgmp3-dev libmpfr-dev libmpc-dev g++ gcc git libgsl-dev make patch \
zlib1g-dev wget curl ca-certificates gpg unzip
curl -LsSf https://apt.kitware.com/kitware-archive.sh | sh
apt-get -y install cmake
- name: Set up Android NDK
id: setup-ndk
uses: nttld/setup-ndk@v1
with:
ndk-version: r27c
- name: Export Android cross-compiler environment
run: |
NDK_TOOLCHAIN="${{ steps.setup-ndk.outputs.ndk-path }}/toolchains/llvm/prebuilt/linux-x86_64"
NATIVE_PS2DEV="$HOME/native-ps2dev"
mkdir -p "$NATIVE_PS2DEV"
printf 'ANDROID_NDK_HOME=%s\n' "${{ steps.setup-ndk.outputs.ndk-path }}" >> "$GITHUB_ENV"
printf 'NATIVE_PS2DEV=%s\n' "$NATIVE_PS2DEV" >> "$GITHUB_ENV"
printf 'CC=%s\n' "$NDK_TOOLCHAIN/bin/${ANDROID_ARCH_TARGET}${ANDROID_API}-clang" >> "$GITHUB_ENV"
printf 'CXX=%s\n' "$NDK_TOOLCHAIN/bin/${ANDROID_ARCH_TARGET}${ANDROID_API}-clang++" >> "$GITHUB_ENV"
printf 'AR=%s\n' "$NDK_TOOLCHAIN/bin/llvm-ar" >> "$GITHUB_ENV"
printf 'LD=%s\n' "$NDK_TOOLCHAIN/bin/ld" >> "$GITHUB_ENV"
printf 'RANLIB=%s\n' "$NDK_TOOLCHAIN/bin/llvm-ranlib" >> "$GITHUB_ENV"
printf 'NM=%s\n' "$NDK_TOOLCHAIN/bin/llvm-nm" >> "$GITHUB_ENV"
printf 'STRIP=%s\n' "$NDK_TOOLCHAIN/bin/llvm-strip" >> "$GITHUB_ENV"
printf 'CONFIGURE_HOST=%s\n' "${{ matrix.host }}" >> "$GITHUB_ENV"
printf 'PATH=%s:%s:%s:%s\n' "$NATIVE_PS2DEV/ee/bin" "$NATIVE_PS2DEV/iop/bin" "$NDK_TOOLCHAIN/bin" "$PATH" >> "$GITHUB_ENV"
- name: Build GMP/MPFR/MPC for Android host
if: ${{ !inputs.skip_toolchain }}
run: |
set -e
mkdir -p "$HOME/android-deps"
printf 'ANDROID_DEPS_PREFIX=%s\n' "$HOME/android-deps" >> "$GITHUB_ENV"
export ANDROID_DEPS_PREFIX="$HOME/android-deps"
cd /tmp
wget -q --tries=3 --timeout=30 -O gmp.tar.xz https://mirrors.kernel.org/gnu/gmp/gmp-6.3.0.tar.xz || wget -q --tries=3 --timeout=30 -O gmp.tar.xz https://ftp.gnu.org/gnu/gmp/gmp-6.3.0.tar.xz || curl -LsSf --retry 3 --connect-timeout 30 -o gmp.tar.xz https://gmplib.org/download/gmp/gmp-6.3.0.tar.xz
tar -xf gmp.tar.xz
cd gmp-6.3.0
./configure --host="$CONFIGURE_HOST" --prefix="$ANDROID_DEPS_PREFIX" \
--disable-shared --enable-static --with-pic CC_FOR_BUILD=cc
make -j "$(nproc)"
make install
cd /tmp
wget -q --tries=3 --timeout=30 -O mpfr.tar.xz https://mirrors.kernel.org/gnu/mpfr/mpfr-4.2.1.tar.xz || wget -q --tries=3 --timeout=30 -O mpfr.tar.xz https://ftp.gnu.org/gnu/mpfr/mpfr-4.2.1.tar.xz || curl -LsSf --retry 3 --connect-timeout 30 -o mpfr.tar.xz https://www.mpfr.org/mpfr-4.2.1/mpfr-4.2.1.tar.xz
tar -xf mpfr.tar.xz
cd mpfr-4.2.1
./configure --host="$CONFIGURE_HOST" --prefix="$ANDROID_DEPS_PREFIX" \
--with-gmp="$ANDROID_DEPS_PREFIX" --disable-shared --enable-static --with-pic
make -j "$(nproc)"
make install
cd /tmp
wget -q --tries=3 --timeout=30 -O mpc.tar.gz https://mirrors.kernel.org/gnu/mpc/mpc-1.3.1.tar.gz || wget -q --tries=3 --timeout=30 -O mpc.tar.gz https://ftp.gnu.org/gnu/mpc/mpc-1.3.1.tar.gz || curl -LsSf --retry 3 --connect-timeout 30 -o mpc.tar.gz https://ftpmirror.gnu.org/mpc/mpc-1.3.1.tar.gz
tar -xf mpc.tar.gz
cd mpc-1.3.1
./configure --host="$CONFIGURE_HOST" --prefix="$ANDROID_DEPS_PREFIX" \
--with-gmp="$ANDROID_DEPS_PREFIX" --with-mpfr="$ANDROID_DEPS_PREFIX" \
--disable-shared --enable-static --with-pic
make -j "$(nproc)"
make install
- name: Restore dev cache (ps2dev folder from the last successful dev run)
if: ${{ inputs.skip_toolchain }}
uses: actions/cache/restore@v4
with:
path: |
ps2dev
.ps2dev-state
key: ps2dev-dev-${{ matrix.arch }}-${{ github.run_id }}
restore-keys: |
ps2dev-dev-${{ matrix.arch }}-
- name: Runs all the stages in the shell
env:
SKIP_TOOLCHAIN: ${{ inputs.skip_toolchain }}
ANDROID_ELF_MACHINE_HEX: ${{ matrix.machex }}
DEV_STEPS: ${{ inputs.dev_steps }}
LZMA_PTHREAD: "0"
run: |
set -e
. ./config/ci-env.sh
STEPS="${DEV_STEPS:-1 2 3 4}"
echo "=== Steps to run: $STEPS (skip_toolchain=$SKIP_TOOLCHAIN) ==="
if [ "$SKIP_TOOLCHAIN" = "true" ]; then
case " $STEPS " in
*" 1 "*) echo "ERROR: step 1 (toolchain) is listed in dev_steps but skip_toolchain is enabled. Remove it from one of them."; exit 1;;
esac
echo "DEV MODE: toolchain step skipped"
mkdir -p "$PS2DEV/ee/mips64r5900el-ps2-elf/lib" "$PS2DEV/iop" "$PS2DEV/dvp" "$PS2DEV/bin"
fi
./build-all.sh $STEPS
- name: Save dev cache (so the next dev run can start from the later steps)
if: ${{ inputs.skip_toolchain && !cancelled() }}
uses: actions/cache/save@v4
with:
path: |
ps2dev
.ps2dev-state
key: ps2dev-dev-${{ matrix.arch }}-${{ github.run_id }}
- name: Print version of executables
run: |
if test -f ./config/ci-print-version.sh; then ./config/ci-print-version.sh; fi
- name: Get short SHA
id: slug
run: printf '%s\n' "sha8=$(printf '%s\n' ${GITHUB_SHA} | cut -c1-8)" >> $GITHUB_OUTPUT
- name: Strip debug information from the Android binaries
run: |
set -e
# Like the official "make install-strip". Without it the compilers keep full debug info
# (several hundred MB each) and the package is far larger than the official ones.
CHECK_MACHINE="${{ matrix.rdmach }}" bash ./config/ci-strip-package.sh ps2dev
- name: Verify package (PIE + dynamic dependencies of every Android binary)
run: |
set -e
# Fails the build BEFORE anything is archived/published if a binary is non-PIE or
# needs a library that a clean Android/Termux does not have (e.g. libc++_shared.so).
CHECK_MACHINE="${{ matrix.rdmach }}" bash ./config/ci-check-package.sh ps2dev
- name: Compress ps2dev folder (Android ${{ matrix.arch }})
run: |
set -e
echo "Creating archive without hard links..."
XZ_OPT=-9 tar \
--hard-dereference \
-cJf ps2dev-Android-${{ matrix.arch }}.tar.xz \
ps2dev
echo "Verifying archive..."
HL="$(tar -tvJf ps2dev-Android-${{ matrix.arch }}.tar.xz | \
awk '$1 ~ /^h/ { count++ } END { print count+0 }')"
echo "Hard links inside archive: $HL"
if [ "$HL" -ne 0 ]; then
echo "ERROR: archive contains hard links"
tar -tvJf ps2dev-Android-${{ matrix.arch }}.tar.xz | \
awk '$1 ~ /^h/ { print }'
exit 1
fi
echo "OK: archive contains 0 hard links"
echo "Archive created successfully:"
ls -lh ps2dev-Android-${{ matrix.arch }}.tar.xz
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: ${{ github.event.repository.name }}-Android-${{ matrix.arch }}-${{ steps.slug.outputs.sha8 }}
path: ps2dev-Android-${{ matrix.arch }}.tar.xz
# ---------------------------------------------------------------------------
# Runtime test: run the finished package inside a real Android (Bionic/Termux) userland.
# The build machine cannot execute Android programs natively, so a Termux container of
# the same ABI is used (emulated by QEMU for ARM, native for x86/x86_64).
# Each architecture is tested and published independently of the others.
# ---------------------------------------------------------------------------
test-android:
name: Runtime test Android ${{ matrix.arch }}
needs: [prepare, build]
if: ${{ always() && !inputs.skip_toolchain && !inputs.skip_runtime_test && needs.prepare.result == 'success' }}
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.prepare.outputs.matrix) }}
runs-on: ubuntu-latest
timeout-minutes: 90
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Get short SHA
id: slug
run: printf '%s\n' "sha8=$(printf '%s\n' ${GITHUB_SHA} | cut -c1-8)" >> $GITHUB_OUTPUT
# If this architecture's build failed there is no package: nothing to test.
- name: Download the package built by the build job
id: dl
continue-on-error: true
uses: actions/download-artifact@v4
with:
name: ${{ github.event.repository.name }}-Android-${{ matrix.arch }}-${{ steps.slug.outputs.sha8 }}
- name: Extract package and check its layout
if: steps.dl.outcome == 'success'
run: |
set -e
mkdir -p ext
tar -xJf ps2dev-Android-${{ matrix.arch }}.tar.xz -C ext
echo "--- top level of the package ---"
ls -A ext/ps2dev
# Files that belong to the build process must never be inside the package.
for stray in .ps2sdk-step-ok .ps2dev-state bin.android-backup; do
if [ -e "ext/ps2dev/$stray" ]; then echo "ERROR: stray build file inside the package: $stray"; exit 1; fi
done
- name: Set up QEMU
if: steps.dl.outcome == 'success'
uses: docker/setup-qemu-action@v3
with:
platforms: arm64,arm
- name: Run the self-test inside a real Android userland (Termux image)
if: steps.dl.outcome == 'success'
run: |
docker run --rm --platform ${{ matrix.plat }} \
-v "$PWD/ext/ps2dev:/opt/ps2dev:ro" \
-v "$PWD/tests:/opt/tests:ro" \
termux/termux-docker:${{ matrix.image }} \
bash /opt/tests/ps2dev-android-selftest.sh /opt/ps2dev ${{ matrix.machex }} 300
- name: Remember that this architecture passed
if: steps.dl.outcome == 'success'
run: echo ok > tested-ok.txt
- name: Upload test-passed marker
if: steps.dl.outcome == 'success'
uses: actions/upload-artifact@v4
with:
name: tested-${{ matrix.arch }}-${{ steps.slug.outputs.sha8 }}
path: tested-ok.txt
release:
name: Publish Android ${{ matrix.arch }}
needs: [prepare, build, test-android]
# Full builds only. Each architecture is published only if ITS package exists and ITS runtime
# test passed (or the test was deliberately skipped); other architectures are not affected.
if: ${{ always() && !inputs.skip_toolchain && needs.prepare.result == 'success' }}
strategy:
fail-fast: false
matrix: ${{ fromJSON(needs.prepare.outputs.matrix) }}
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Get short SHA
id: slug
run: printf '%s\n' "sha8=$(printf '%s\n' ${GITHUB_SHA} | cut -c1-8)" >> $GITHUB_OUTPUT
- name: Download the package
id: dl_pkg
continue-on-error: true
uses: actions/download-artifact@v4
with:
name: ${{ github.event.repository.name }}-Android-${{ matrix.arch }}-${{ steps.slug.outputs.sha8 }}
- name: Download the test-passed marker
id: dl_mark
continue-on-error: true
uses: actions/download-artifact@v4
with:
name: tested-${{ matrix.arch }}-${{ steps.slug.outputs.sha8 }}
- name: Decide whether this architecture may be published
id: gate
run: |
ok=true
if [ "${{ steps.dl_pkg.outcome }}" != "success" ]; then
echo "No package for ${{ matrix.arch }} (its build failed): nothing to publish."; ok=false
elif [ "${{ inputs.skip_runtime_test }}" != "true" ] && [ "${{ steps.dl_mark.outcome }}" != "success" ]; then
echo "Runtime test of ${{ matrix.arch }} did not pass: NOT publishing."; ok=false
fi
echo "ok=$ok" >> "$GITHUB_OUTPUT"
echo "publish ${{ matrix.arch }}: $ok"
- name: Upload package to v1.0.0 release
if: steps.gate.outputs.ok == 'true'
uses: softprops/action-gh-release@v2
with:
tag_name: v1.0.0
files: ps2dev-Android-${{ matrix.arch }}.tar.xz
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}