summaryrefslogtreecommitdiffstats
path: root/.github/workflows/ghci.yml
diff options
context:
space:
mode:
Diffstat (limited to '.github/workflows/ghci.yml')
-rw-r--r--.github/workflows/ghci.yml401
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