diff options
Diffstat (limited to '.github/workflows/ghci.yml')
-rw-r--r-- | .github/workflows/ghci.yml | 401 |
1 files changed, 306 insertions, 95 deletions
diff --git a/.github/workflows/ghci.yml b/.github/workflows/ghci.yml index 236d60e..6b0802b 100644 --- a/.github/workflows/ghci.yml +++ b/.github/workflows/ghci.yml @@ -1,4 +1,4 @@ -name: GitHub CI tests +name: GitHub CI on: push: @@ -8,52 +8,261 @@ on: jobs: build: runs-on: ${{ matrix.os }} + name: build(${{ matrix.msystem || matrix.docker_image || matrix.os }}, + ${{ matrix.cc }}${{ matrix.api && ', ' }}${{ matrix.api }}) strategy: + fail-fast: false matrix: - os: [ubuntu-18.04, macos-10.15] - cc: [gcc, clang] - exclude: - - os: macos-10.15 - cc: gcc include: # Enable distcheck for one build - - os: ubuntu-18.04 + - os: ubuntu-latest cc: gcc do_distc: yes # Run Coverity on a clang build; Coverity's gcc causes issues - - os: ubuntu-18.04 + - os: ubuntu-latest cc: clang do_coverity: yes + # Add a tcc build + - os: ubuntu-latest + cc: tcc + ld: tcc + # Add docker-build on Alpine + - os: ubuntu-latest + cc: gcc + docker_image: alpine:latest + shell: '/tmp/docker_shell {0}' + art_reg_skip: 'font_nonunicode' + # Add docker-build with minimum version of dependencies + - os: ubuntu-latest + cc: gcc + docker_image: oldlibs + docker_pullprefix: 'ghcr.io/theoneric/libass-containers/' + shell: '/tmp/docker_shell {0}' + # Crash Tests detect (false?) leaks in Fontconfig, and + # various regression test fail, assumed due to older deps + skip_tests: yes + # MacOS Intel + - os: macos-13 + cc: clang + # MacOS arm64 + - os: macos-14 + cc: clang + # Add a Windows build (MinGW-gcc via MSYS2) with no extras + - os: windows-2019 + msystem: MINGW32 + cc: gcc + api: desktop + shell: 'msys2 {0}' + # Add a best-effort build for UWP apps for Microsoft Store + - os: windows-2019 + msystem: UCRT64 + cc: gcc + api: app + extra_cflags: -DWINAPI_FAMILY=WINAPI_FAMILY_APP -specs=/tmp/windowsapp.specs + shell: 'msys2 {0}' + + defaults: + run: + shell: ${{ matrix.shell || 'bash' }} + + env: + ART_SAMPLES: ext_art-samples steps: + # Available core count changes on runner type and over time, but GHA + # doesn’t easily expose this, so we’ll have to query this ourselves. Based on: + # https://github.com/orgs/community/discussions/25057#discussioncomment-5957241 + - name: determine logical cpu core count + id: cpus + shell: python + run: | + import os + import multiprocessing + + try: + num_cpus = len(os.sched_getaffinity(0)) + except AttributeError: + num_cpus = multiprocessing.cpu_count() + print(f"num_cpus={num_cpus}") + + output_file = os.environ["GITHUB_OUTPUT"] + with open(output_file, "a", encoding="utf-8") as output_stream: + output_stream.write(f"count={num_cpus}\n") + - name: checkout code - uses: actions/checkout@v2 + uses: actions/checkout@v4 + + - name: download test samples + uses: actions/checkout@v4 + with: + repository: libass/libass-tests + path: ${{ env.ART_SAMPLES }} + + - name: Start Docker + if: matrix.docker_image + shell: bash + run: | + # Note: Many containers default to the root user + docker pull "${{ matrix.docker_pullprefix }}${{ matrix.docker_image }}" + docker create --name dockerciimage \ + -v "/home/runner/work:/home/runner/work" --workdir "$PWD" \ + --entrypoint "tail" \ + "${{ matrix.docker_pullprefix }}${{ matrix.docker_image }}" \ + "-f" "/dev/null" + docker start dockerciimage + + # Create a proxy-shell for Docker containers + # Scripts for each step and the output file are inside the mounted + # directories, but some environment variable must be forwarded. + echo '#!/bin/sh + set -eu + if [ "$#" -ne 1 ] ; then + echo "Usage: $0 <script file>" + exit 1 + fi + exec /usr/bin/docker exec \ + --env GITHUB_OUTPUT --env GITHUB_ENV --env GITHUB_PATH --env GITHUB_STATE \ + dockerciimage sh -e "$1" + ' > /tmp/docker_shell + chmod a+x /tmp/docker_shell + + - name: Setup MSys2 + uses: msys2/setup-msys2@v2 + if: matrix.msystem + with: + msystem: ${{ matrix.msystem }} + update: false - name: install deps run: | - if echo "${{ matrix.os }}" | grep -qE '^macos-' ; then - #brew update - # fontconfig, freetype, autoconf and libtool are preinstalled - # and `brew install` fails if a non-uptodate version is already installed - #brew upgrade fontconfig freetype autoconf libtool - brew install automake fribidi harfbuzz nasm - else - sudo apt-get update #&& sudo apt-get upgrade - sudo apt-get install -y \ - libfontconfig1-dev libfreetype6-dev libfribidi-dev \ - libharfbuzz-dev nasm ${{ matrix.cc }} + case "${{ matrix.docker_image || matrix.os }}" in + macos-*) + #brew update + brew install autoconf automake libtool nasm pkg-config \ + harfbuzz freetype fribidi fontconfig + ;; + windows-*) + pre="$MINGW_PACKAGE_PREFIX" + pacman --noconfirm -S \ + automake autoconf libtool nasm make \ + $pre-pkg-config $pre-gcc \ + $pre-fribidi $pre-freetype $pre-harfbuzz $pre-fontconfig \ + $pre-libpng + ;; + alpine:*) + apk add nasm ${{ matrix.cc }} musl-dev \ + make automake autoconf libtool pkgconf \ + fontconfig-dev freetype-dev fribidi-dev harfbuzz-dev \ + libpng-dev + ;; + oldlibs) + : # Everything is preinstalled + ;; + *) + sudo apt-get update #&& sudo apt-get upgrade + ubver="$(apt-cache search libubsan | awk '/^libubsan[0-9]* / {print substr($1, 9)}' | sort -rn | head -n1)" + asver="$(apt-cache search libasan | awk '/^libasan[0-9]* / {print substr($1, 8)}' | sort -rn | head -n1)" + sudo apt-get install -y --no-install-recommends \ + autoconf automake make libtool \ + libfontconfig1-dev libfreetype6-dev libfribidi-dev \ + libharfbuzz-dev nasm ${{ matrix.cc }} \ + libpng-dev libubsan"$ubver" libasan"$asver" + ;; + esac + + - name: Determine Sanitizer Flags + id: sanitizer + run: | + aflags="-fsanitize=address" + uflags="-fsanitize=undefined -fsanitize=float-cast-overflow" + if [ "${{ startsWith(matrix.cc, 'clang') }}" = "true" ] ; then + # Clang's UBSAN exits with code zero even if issues were found + # This options will instead force an SIGILL, but remove a report being printed + # see https://reviews.llvm.org/D35085 + uflags="$uflags -fsanitize-undefined-trap-on-error" + fi + + # UBSAN: Alpine and MSys2 doesn't ship the UBSAN library, + # but when SIGILL'ing the lib is not needed + # ASAN: Not supported with musl and in Windows + case "${{ matrix.docker_image || matrix.os }}" in + alpine*|windows-*) + flags="$uflags -fsanitize-undefined-trap-on-error" ;; + *) + flags="$aflags $uflags" ;; + esac + + if [ -n "$flags" ] ; then + flags="$flags -fno-sanitize-recover=all" + fi + + if [ "${{ matrix.cc }}" = "tcc" ] || [ "${{ matrix.skip_tests }}" = "yes" ] ; then + flags="" fi + + echo "SANFLAGS=$flags" + echo "flags=${flags}" >> $GITHUB_OUTPUT + + - name: Customize compiler + if: matrix.api == 'app' && matrix.cc == 'gcc' + run: > + gcc -dumpspecs + | sed 's/-lmsvcrt/-lucrtapp/g; s/-lkernel32/-lwindowsapp/g; + s/-ladvapi32//g; s/-lshell32//g; s/-luser32//g' + > /tmp/windowsapp.specs + - name: configure - run: ./autogen.sh && CC="${{ matrix.cc }}" ./configure + run: | + export CC="${{ matrix.cc }}\ + ${{ matrix.extra_cflags && ' ' }}${{ matrix.extra_cflags }}\ + ${{ steps.sanitizer.outputs.flags && ' ' }}${{ steps.sanitizer.outputs.flags }}" + export LD="${{ matrix.ld }}" + export ART_SAMPLES="${{ env.ART_SAMPLES }}" - - name: compile - run: test "x${{ matrix.do_distc }}" = "xyes" || make -j 2 + ./autogen.sh + ./configure --enable-compare --enable-fuzz - name: distcheck - run: if [ "x${{ matrix.do_distc }}" = "xyes" ] ; then make -j 2 distcheck; fi + if: matrix.do_distc == 'yes' + run: make -j '${{ steps.cpus.outputs.count }}' distcheck + + - name: compile + run: make -j '${{ steps.cpus.outputs.count }}' + + - name: ensure internal functions are namespaced + if: startsWith(matrix.os, 'ubuntu-') + run: | + test -f libass/.libs/libass.a || (echo "Static lib is missing!"; exit 1) + set +e + list="$(nm libass/.libs/libass.a | grep ' T ' | grep -v ' ass_')" + case "$?" in + 1) + : # All good + ;; + 0) + echo "There are non-namespaced functions! Prefix them with 'ass_'." + echo "$list" + exit 1 + ;; + *) + echo "Internal grep error occured!" + echo "$list" + exit 2 + ;; + esac + + - name: run tests + if: matrix.skip_tests != 'yes' + run: | + ART_REG_SKIP="${{ matrix.art_reg_skip }}" make -j 1 check + - name: Coverity scan + if: > + matrix.do_coverity == 'yes' + && github.repository == 'libass/libass' + && github.event_name != 'pull_request' env: COVERITY_SCAN_TOKEN: ${{ secrets.COVERITY_SCAN_TOKEN }} PROJECT_NAME: libass/libass @@ -63,76 +272,78 @@ jobs: SCAN_URL: https://scan.coverity.com RES_DIR: cov-int run: | - if [ "x${{ matrix.do_coverity }}" = "xyes" ] \ - && [ "x${{ github.repository }}" = "xlibass/libass" ] \ - && [ "x${{ github.event_name }}" != "xpull_request" ] + exit_code=0 + echo "Running Coverity ..." + # Remove previous build output + make clean + # The upstream script is borked and always exits with 1 even on success + # To get meaningful success/error status we're using our own script + # but we still want to be informed about upstream script changes + if curl -s https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh \ + | shasum -a 256 \ + | grep -Eq '^234d71b4a5257a79559e66dd3ba5765576d2af4845da83af4975b77b14ab536b ' then - exit_code=0 - echo "Running Coverity ..." - # Remove previous build output - make clean - # The upstream script is borked and always exits with 1 even on success - # To get meaningful success/error status we're using our own script - # but we still want to be informed about upstream script changes - if curl -s https://scan.coverity.com/scripts/travisci_build_coverity_scan.sh \ - | shasum -a 256 \ - | grep -Eq '^234d71b4a5257a79559e66dd3ba5765576d2af4845da83af4975b77b14ab536b ' - then - : remote unchanged - else - echo "Coverity's travis script changed!" - exit_code=1 - fi - - # Check if we are within quoata - quota_res="$(curl -s --form project="$PROJECT_NAME" \ - --form token="$COVERITY_SCAN_TOKEN" \ - "$SCAN_URL"/api/upload_permitted)" - if [ "$?" -ne 0 ] || [ "x$quota_res" = "xAccess denied" ] ; then - echo "Coverity denied access or did not respond!" - exit 1 - elif echo "$quota_res" | grep -Eq 'upload_permitted": *true' ; then - echo "Within Coverity quota." - else - echo "Exceeding Coverity quota! Try again later." - echo "$quota_res" | grep -Eo 'next_upload_permitted_at":[^,}]*' - exit 0 - fi - - # Download cov tool and make it available - wget -nv "$TOOL_URL""$(uname)" \ - --post-data "project=$PROJECT_NAME&token=$COVERITY_SCAN_TOKEN" \ - -O cov-analysis-tool.tar.gz - mkdir cov-analysis-tool - tar xzf cov-analysis-tool.tar.gz --strip 1 -C cov-analysis-tool - export PATH="$(pwd)/cov-analysis-tool/bin:$PATH" - - # Coverity Build - echo "Starting Coverity build..." - #mkdir "$RES_DIR" # already done by cov-build - COVERITY_UNSUPPORTED=1 cov-build --dir "$RES_DIR" make -j 2 - cov-import-scm --dir "$RES_DIR" --scm git --log "$RES_DIR/scm_log.txt" 2>&1 - - # Submit results to Coverity's server - tar czf libass.tar.gz "$RES_DIR" - upstat="$(curl --silent --write-out "\n%{http_code}\n" \ - --form project="PROJECT_NAME" \ - --form token="$COVERITY_SCAN_TOKEN" \ - --form email="$NOTIFY_EMAIL" \ - --form file=@libass.tar.gz \ - --form version="${{ github.sha }}" \ - --form description="GitHubActions CI build" \ - "$UPLOAD_URL")" - if [ "$?" -ne 0 ] ; then - echo "Upload failed (curl error)" - exit_code=1 - elif echo "$upstat" | tail -n 1 | grep -Eq '^2[0-9]{2}$' ; then - echo "Upload successful." - else - echo "Upload failed (server error)" - exit_code=1 - fi - echo "$upstat" | head - - exit $exit_code + : remote unchanged + else + echo "Coverity's travis script changed!" + exit_code=1 + fi + + # Check if we are within quoata + quota_res="$(curl -s --form project="$PROJECT_NAME" \ + --form token="$COVERITY_SCAN_TOKEN" \ + "$SCAN_URL"/api/upload_permitted)" + if [ "$?" -ne 0 ] || [ "x$quota_res" = "xAccess denied" ] ; then + echo "Coverity denied access or did not respond!" + exit 1 + elif echo "$quota_res" | grep -Eq 'upload_permitted": *true' ; then + echo "Within Coverity quota." + else + echo "Exceeding Coverity quota! Try again later." + echo "$quota_res" | grep -Eo 'next_upload_permitted_at":[^,}]*' + exit 0 fi + + # Download cov tool and make it available + wget -nv "$TOOL_URL""$(uname)" \ + --post-data "project=$PROJECT_NAME&token=$COVERITY_SCAN_TOKEN" \ + -O cov-analysis-tool.tar.gz + mkdir cov-analysis-tool + tar xzf cov-analysis-tool.tar.gz --strip 1 -C cov-analysis-tool + export PATH="$(pwd)/cov-analysis-tool/bin:$PATH" + + # Coverity Build + echo "Starting Coverity build..." + #mkdir "$RES_DIR" # already done by cov-build + COVERITY_UNSUPPORTED=1 cov-build --dir "$RES_DIR" make -j '${{ steps.cpus.outputs.count }}' + cov-import-scm --dir "$RES_DIR" --scm git --log "$RES_DIR/scm_log.txt" 2>&1 + + # Submit results to Coverity's server + tar czf libass.tar.gz "$RES_DIR" + upstat="$(curl --silent --write-out "\n%{http_code}\n" \ + --form project="PROJECT_NAME" \ + --form token="$COVERITY_SCAN_TOKEN" \ + --form email="$NOTIFY_EMAIL" \ + --form file=@libass.tar.gz \ + --form version="${{ github.sha }}" \ + --form description="GitHubActions CI build" \ + "$UPLOAD_URL")" + if [ "$?" -ne 0 ] ; then + echo "Upload failed (curl error)" + exit_code=1 + elif echo "$upstat" | tail -n 1 | grep -Eq '^2[0-9]{2}$' ; then + echo "Upload successful." + else + echo "Upload failed (server error)" + exit_code=1 + fi + echo "$upstat" | head + + exit $exit_code + + + - name: Stop Docker + if: matrix.docker_image + shell: bash + run: | + docker rm --force dockerciimage |