Skip to content

Build

Build #199

Workflow file for this run

name: Build
on:
pull_request:
paths:
- .github/workflows/build.yml
- build-exe/**
push:
branches:
- main
tags-ignore:
- "**"
paths:
- .github/workflows/build.yml
- build-exe/**
workflow_dispatch:
inputs:
sign:
description: Sign binaries
type: choice
options:
- 'no'
- test
- production
default: test
cldr-version:
description: CLDR Version
type: string
required: false
iconv-version:
description: Iconv Version
type: string
required: false
gettext-version:
description: Gettext Version
type: string
required: false
jobs:
test-bash-scripts:
name: Test bash scripts
runs-on: ubuntu-latest
container:
image: mvdan/shfmt:v3.9.0-alpine
options: --entrypoint /bin/sh
steps:
-
name: Checkout
uses: actions/checkout@v4
-
name: Check scripts
run: |
files="$(shfmt --find .)"
if [ $? -ne 0 ] || [ -z "$files" ]; then
echo 'Failed to list shell scripts'
exit 1
fi
rc=0
for file in $files; do
printf '# %s... ' "$file"
if shfmt --diff --simplify --indent 4 --case-indent --func-next-line -- "$file"; then
echo 'ok.'
else
rc=1
printf '\n\n'
fi
done
exit $rc
test-pwsh-scripts:
name: Test PowerShell scripts
runs-on: windows-latest
steps:
-
name: Install PSScriptAnalyzer
run: Install-Module -Name PSScriptAnalyzer -RequiredVersion 1.23.0 -Force -SkipPublisherCheck -Verbose
-
name: Checkout
uses: actions/checkout@v4
-
name: Check scripts
run: |
$rc = 0
$files = Get-ChildItem -Path . -Recurse -File -Filter *.ps1
foreach ($file in $files) {
Write-Host -Object "# $($file.Name)... " -NoNewline
$records = Invoke-ScriptAnalyzer -Path $file -ExcludeRule PSAvoidUsingWriteHost,PSUseShouldProcessForStateChangingFunctions,PSUseSingularNouns
if (-not($records)) {
Write-Host -Object 'ok.'
} else {
$rc = 1
Write-Host -Object 'ERRORS!'
foreach ($record in $records) {
Write-Host -Object " - line $($record.Line): $($record.RuleName))`n $($record.Message)`n"
}
Write-Host -Object ''
}
}
exit $rc
exe:
name: ${{ matrix.bits}}-bit ${{ matrix.link }} executables
needs:
- test-bash-scripts
- test-pwsh-scripts
runs-on: windows-2022
strategy:
matrix:
bits:
- 32
- 64
link:
- shared
- static
env:
CYGWIN_NOWINPATH: 1
CHERE_INVOKING: 1
defaults:
run:
shell: C:\cygwin\bin\bash.exe --login -o igncr -o errexit -o pipefail {0}
steps:
-
name: Configure git
shell: cmd
run: git config --global core.autocrlf input
-
name: Resolve versions
shell: pwsh
run: |
$v = '${{ github.event.inputs.cldr-version }}'.Trim()
if ($v -eq '') { $v = '45' }
Write-Output -InputObject "CLDR_VERSION=$v" >>"$env:GITHUB_ENV"
$v = '${{ github.event.inputs.iconv-version }}'.Trim()
if ($v -eq '') { $v = '1.17' }
Write-Output -InputObject "ICONV_VERSION=$v" >>"$env:GITHUB_ENV"
$v = '${{ github.event.inputs.gettext-version }}'.Trim()
if ($v -eq '') { $v = '0.22.5a' }
Write-Output -InputObject "GETTEXT_VERSION=$v" >>"$env:GITHUB_ENV"
-
name: Checkout
uses: actions/checkout@v4
-
name: Restore cache
id: restore-cache
uses: actions/cache/restore@v4
with:
key: build-exe-${{ matrix.link }}-${{ matrix.bits }}
path: |
src\downloads
C:\cygwin-packages
-
name: Set variables
id: vars
shell: pwsh
run: ./build-exe/vars.ps1 -Bits ${{ matrix.bits }} -Link ${{ matrix.link }} -InstalledPath installed -Sign '${{ github.event.inputs.sign }}'
-
name: Install Cygwin
uses: cygwin/cygwin-install-action@v4
timeout-minutes: 60
with:
packages: ${{ steps.vars.outputs.cygwin-packages }}
install-dir: C:\cygwin
site: ${{ steps.vars.outputs.cygwin-mirror }}
add-to-path: false
-
name: Setup Cygwin environment
run: |
PATH='${{ steps.vars.outputs.cygwin-path }}'
cd -- "$(cygpath -ua '${{ github.workspace }}')"
mkdir -p src/downloads
mkdir -p installed/bin
mkdir -p installed/include
mkdir -p installed/lib
mkdir files-unsigned
mkdir files-signed
mkdir installer-unsigned
mkdir installer-signed
cp build-exe/license-for-distribution.txt installed/license.txt
mkdir -p "$HOME"
printf "\nPATH='${{ steps.vars.outputs.cygwin-path }}'\nexport PATH\n" >>$HOME/.bash_profile
-
name: Dump Cygwin environment
run: |
printf 'Current working directory: %s\n' "$(pwd)"
echo 'PATH contains:'
IFS=:
for p in $PATH; do
printf -- '- %s\n' "$p"
done
-
name: Install apt-cyg
run: |
wget --output-document=/usr/bin/apt-cyg https://raw.githubusercontent.com/transcode-open/apt-cyg/refs/tags/v1/apt-cyg
chmod +x /usr/bin/apt-cyg
-
name: Download CLDR
working-directory: src\downloads
shell: pwsh
run: |
if (Test-Path -LiteralPath "cldr-$env:CLDR_VERSION.zip" -PathType Leaf) {
Write-Host -Object 'Already downloaded'
} else {
Invoke-WebRequest -Uri "https://unicode.org/Public/cldr/$env:CLDR_VERSION/core.zip" -OutFile "cldr-$env:CLDR_VERSION.zip"
Write-Host -Object 'Downloaded'
}
-
name: Download iconv
working-directory: src\downloads
shell: pwsh
run: |
if (Test-Path -LiteralPath "libiconv-$env:ICONV_VERSION.tar.gz" -PathType Leaf) {
Write-Host -Object 'Already downloaded'
} else {
Invoke-WebRequest -Uri "${{ steps.vars.outputs.iconv-source-url }}" -OutFile "libiconv-$env:ICONV_VERSION.tar.gz"
Write-Host -Object 'Downloaded'
}
-
name: Download gettext
working-directory: src\downloads
shell: pwsh
run: |
if (Test-Path -LiteralPath "gettext-$env:GETTEXT_VERSION.tar.gz" -PathType Leaf) {
Write-Host -Object 'Already downloaded'
} else {
Invoke-WebRequest -Uri "${{ steps.vars.outputs.gettext-source-url }}" -OutFile "gettext-$env:GETTEXT_VERSION.tar.gz"
Write-Host -Object 'Downloaded'
}
-
name: Extract CLDR
run: |
unzip -p src/downloads/cldr-$CLDR_VERSION.zip LICENSE > installed/license-cldr.txt
mkdir -p installed/lib/gettext/common/supplemental
unzip -p src/downloads/cldr-$CLDR_VERSION.zip common/supplemental/plurals.xml >installed/lib/gettext/common/supplemental/plurals.xml
-
name: Extract iconv
run: |
tar x -C src -z -f src/downloads/libiconv-$ICONV_VERSION.tar.gz
cp src/libiconv-$ICONV_VERSION/COPYING installed/license-iconv.txt
-
name: Extract gettext
run: |
tar x -C src -z -f src/downloads/gettext-$GETTEXT_VERSION.tar.gz
cp src/gettext-$GETTEXT_VERSION/COPYING installed/license-gettext.txt
-
name: Install gettext-develop
run: apt-cyg install libgomp1 gettext-devel
-
name: Update iconv translations
working-directory: src\libiconv-${{ env.ICONV_VERSION }}
run: ../../build-exe/update-po-files.sh po po/libiconv.pot libiconv '${{ steps.vars.outputs.iconv-tp-version }}'
-
name: Update gettext-examples translations
working-directory: src\gettext-${{ env.GETTEXT_VERSION }}
run: ../../build-exe/update-po-files.sh gettext-tools/examples/po gettext-tools/examples/po/gettext-examples.pot gettext-examples '${{ steps.vars.outputs.gettext-tp-version }}'
-
name: Update gettext-runtime translations
working-directory: src\gettext-${{ env.GETTEXT_VERSION }}
run: ../../build-exe/update-po-files.sh gettext-runtime/po gettext-runtime/po/gettext-runtime.pot gettext-runtime '${{ steps.vars.outputs.gettext-tp-version }}'
-
name: Update gettext-tools translations
working-directory: src\gettext-${{ env.GETTEXT_VERSION }}
run: ../../build-exe/update-po-files.sh gettext-tools/po gettext-tools/po/gettext-tools.pot gettext-tools '${{ steps.vars.outputs.gettext-tp-version }}'
-
name: Uninstall gettext-develop
run: apt-cyg remove gettext-devel libgomp1
-
name: Configure iconv (1st time)
id: iconv-configure
working-directory: src\libiconv-${{ env.ICONV_VERSION }}
run: |
mkdir build
cd build
../configure ${{ steps.vars.outputs.configure-args }}
-
name: Compile iconv (1st time)
working-directory: src\libiconv-${{ env.ICONV_VERSION }}\build
run: make --jobs=$(nproc)
-
name: Check iconv (1st time)
working-directory: src\libiconv-${{ env.ICONV_VERSION }}\build
run: make --jobs=$(nproc) check
-
name: Install iconv (1st time)
working-directory: src\libiconv-${{ env.ICONV_VERSION }}\build
run: make --jobs=$(nproc) install-strip
-
name: Configure gettext
id: gettext-configure
working-directory: src\gettext-${{ env.GETTEXT_VERSION }}
run: |
mkdir build
cd build
../configure ${{ steps.vars.outputs.configure-args }} ${{ steps.vars.outputs.configure-args-gettext }}
-
name: Ignore gettext C tests
if: steps.vars.outputs.gettext-ignore-tests-c
working-directory: src\gettext-${{ env.GETTEXT_VERSION }}
run: for f in ${{ steps.vars.outputs.gettext-ignore-tests-c }}; do echo 'int main() { return 0; }' >$f; done
-
name: Compile gettext/gnulib-local
working-directory: src\gettext-${{ env.GETTEXT_VERSION }}\build\gnulib-local
run: make --jobs=$(nproc)
-
name: Check gettext/gnulib-local
working-directory: src\gettext-${{ env.GETTEXT_VERSION }}\build\gnulib-local
run: make --jobs=$(nproc) check
-
name: Install gettext/gnulib-local
working-directory: src\gettext-${{ env.GETTEXT_VERSION }}\build\gnulib-local
run: make --jobs=$(nproc) install-strip
-
name: Compile gettext/gettext-runtime
working-directory: src\gettext-${{ env.GETTEXT_VERSION }}\build\gettext-runtime
run: make --jobs=$(nproc)
-
name: Check gettext/gettext-runtime
working-directory: src\gettext-${{ env.GETTEXT_VERSION }}\build\gettext-runtime
run: make --jobs=$(nproc) check
-
name: Install gettext/gettext-runtime
working-directory: src\gettext-${{ env.GETTEXT_VERSION }}\build\gettext-runtime
run: make --jobs=$(nproc) install-strip
-
name: Compile gettext/libtextstyle
working-directory: src\gettext-${{ env.GETTEXT_VERSION }}\build\libtextstyle
run: make --jobs=$(nproc)
-
name: Check gettext/libtextstyle
working-directory: src\gettext-${{ env.GETTEXT_VERSION }}\build\libtextstyle
run: make --jobs=$(nproc) check
-
name: Install gettext/libtextstyle
working-directory: src\gettext-${{ env.GETTEXT_VERSION }}\build\libtextstyle
run: make --jobs=$(nproc) install-strip
-
name: Compile gettext/gettext-tools
working-directory: src\gettext-${{ env.GETTEXT_VERSION }}\build\gettext-tools
run: make --jobs=$(nproc)
-
name: Check gettext/gettext-tools
working-directory: src\gettext-${{ env.GETTEXT_VERSION }}\build\gettext-tools
run: XFAIL_TESTS='${{ steps.vars.outputs.gettext-xfail-gettext-tools }}' make --jobs=$(nproc) check
-
name: Install gettext/gettext-tools
working-directory: src\gettext-${{ env.GETTEXT_VERSION }}\build\gettext-tools
run: make --jobs=$(nproc) install-strip
-
# We need to rebuild iconv because it depends on gettext's libintl in order to be localizable
name: Configure iconv (2nd time)
working-directory: src\libiconv-${{ env.ICONV_VERSION }}
run: |
rm -rf build
mkdir build
cd build
../configure ${{ steps.vars.outputs.configure-args }} \
--enable-extra-encodings
-
name: Compile iconv (2nd time)
working-directory: src\libiconv-${{ env.ICONV_VERSION }}\build
run: make --jobs=$(nproc)
-
name: Check iconv (2nd time)
working-directory: src\libiconv-${{ env.ICONV_VERSION }}\build
run: make --jobs=$(nproc) check
-
name: Install iconv (2nd time)
working-directory: src\libiconv-${{ env.ICONV_VERSION }}\build
run: make --jobs=$(nproc) install-strip
-
name: Prepare build log
id: prepare-build-log
if: (success() || failure()) && steps.iconv-configure.outcome == 'success'
run: |
mkdir build-log
if [ -d src/libiconv-$ICONV_VERSION/build ]; then
tar c -J -f build-log/iconv.tar.xz src/libiconv-$ICONV_VERSION/build
fi
if [ -d src/gettext-$GETTEXT_VERSION/build ]; then
tar c -J -f build-log/gettext.tar.xz src/gettext-$GETTEXT_VERSION/build
fi
if find installed -mindepth 1 -maxdepth 1 | read; then
tar c -J -f build-log/installed.tar.xz installed
fi
ls -al build-log
-
name: Copy built assets
run: ./build-exe/create-output.sh installed files-unsigned
-
name: Delete install directory
run: rm -rf installed
-
name: Process dependencies
shell: pwsh
run: >
./build-exe/process-dependencies.ps1
-Bits ${{ matrix.bits }}
-Link ${{ matrix.link }}
-Path files-unsigned
-MinGWPath C:\cygwin\usr\${{ steps.vars.outputs.mingw-host }}
-
name: Check bitness
run: ./build-exe/check-bits.sh ${{ matrix.bits }} files-unsigned
-
name: Check if iconv program load translations correctly
shell: pwsh
run: |
$env:LANGUAGE = 'it'
$stdout = & .\files-unsigned\bin\iconv.exe --help
if (-not($?)) {
throw "iconv.exe failed"
}
$stdout = $stdout -join "`n"
if (!$stdout.Contains('formato di input')) {
throw "iconv.exe didn't load the translations.`nIts output is:`n$stdout"
}
Write-Host "iconv.exe correctly loaded the translations when LANGUAGE=$env:LANGUAGE`nIts localized output is`n$stdout"
-
name: Check if gettext programs load translations correctly
shell: pwsh
run: |
$env:LANGUAGE = 'it'
$stdout = & .\files-unsigned\bin\xgettext.exe --help
if (-not($?)) {
throw "xgettext.exe failed"
}
$stdout = $stdout -join "`n"
if (!$stdout.Contains('impostazione predefinita')) {
throw "xgettext.exe didn't load the translations.`nIts output is:`n$stdout"
}
Write-Host "xgettext.exe correctly loaded the translations when LANGUAGE=$env:LANGUAGE`nIts localized output is`n$stdout"
-
name: Check cldr-plurals
shell: pwsh
run: |
$pot = @'
msgid ""
msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"MIME-Version: 1.0\n"
"Content-Transfer-Encoding: 8bit\n"
msgid "Hi"
msgstr ""
'@
$lang = 'zu'
Remove-Item -LiteralPath Env:GETTEXTCLDRDIR -ErrorAction SilentlyContinue
$po = $pot | & .\files-unsigned\bin\msginit.exe --input=- --output-file=- --locale="$lang"
$rule = $po -match "Plural-Forms:"
if ($rule) {
throw "Without GETTEXTCLDRDIR there shouldn't be a Plural-Forms header in the .po file for the locale $lang, but we found it: $rule"
}
Write-Output -InputObject "As expected, without GETTEXTCLDRDIR there isn't a Plural-Forms header in the .po file for the locale $lang."
$env:GETTEXTCLDRDIR = "$PWD\files-unsigned\lib\gettext"
$po = $pot | & .\files-unsigned\bin\msginit.exe --input=- --output-file=- --locale="$lang"
$rule = $po -match "Plural-Forms:"
if ('${{ steps.vars.outputs.cldr-plural-works }}' -eq 'yes') {
if (-not($rule)) {
throw "With GETTEXTCLDRDIR there should be a Plural-Forms header in the .po file for the locale $lang, but there isn't."
}
Write-Output -InputObject "With GETTEXTCLDRDIR there is a Plural-Forms header ($rule) in the .po file for the locale $lang."
} else {
if ($rule) {
throw "Even with GETTEXTCLDRDIR there shouldn't be a Plural-Forms header in the .po file for the locale $lang, but we found it: $rule"
}
Write-Output -InputObject "As expected, even with GETTEXTCLDRDIR there isn't a Plural-Forms header in the .po file for the locale $lang."
}
-
name: Upload unsigned files
if: steps.vars.outputs.signpath-signing-policy
id: upload-files-unsigned
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.link }}-${{ matrix.bits }}-files-unsigned
path: files-unsigned
if-no-files-found: error
retention-days: 1
-
name: Sign files
if: steps.vars.outputs.signpath-signing-policy
id: sign-files
uses: signpath/github-action-submit-signing-request@v1
with:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: 98c3accc-92c9-4962-b150-ff1f5c6356b8
project-slug: gettext-iconv-windows
signing-policy-slug: '${{ steps.vars.outputs.signpath-signing-policy }}'
artifact-configuration-slug: ${{ steps.vars.outputs.signpath-artifactconfiguration-files }}
github-artifact-id: ${{ steps.upload-files-unsigned.outputs.artifact-id }}
wait-for-completion: true
output-artifact-directory: files-signed
parameters: |
iconvPEVersion: "${{ env.ICONV_VERSION }}"
gettextPEVersion: "${{ env.GETTEXT_VERSION }}"
gettextPENameLibGettextLib: "${{ steps.vars.outputs.gettext-pename-libgettextlib }}"
gettextPEVersionLibGettextLib: "${{ steps.vars.outputs.gettext-peversion-libgettextlib }}"
gettextPENameLibGettextSrc: "${{ steps.vars.outputs.gettext-pename-libgettextsrc }}"
gettextPEVersionLibGettextSrc: "${{ steps.vars.outputs.gettext-peversion-libgettextsrc }}"
gettextPEVersionLibIntl: "${{ steps.vars.outputs.gettext-peversion-libintl }}"
gettextPEVersionLibTextStyle: "${{ steps.vars.outputs.gettext-peversion-libtextstyle }}"
-
name: Check signatures
if: steps.vars.outputs.signpath-signing-policy
shell: pwsh
run: >
./build-exe/check-signature.ps1
-Path files-signed
-CanBeInvalid ${{ steps.vars.outputs.signatures-canbeinvalid }}
-
name: Create files archive
shell: pwsh
run: |
if ('${{ steps.vars.outputs.signpath-signing-policy }}') {
Set-Location -LiteralPath 'files-signed'
} else {
Set-Location -LiteralPath 'files-unsigned'
}
& 7z.exe a -bd -bt -mx9 -r -sse -tzip ..\gettext${{ env.GETTEXT_VERSION }}-iconv${{ env.ICONV_VERSION }}-${{ matrix.link }}-${{ matrix.bits }}.zip
-
name: Upload files archive
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.link }}-${{ matrix.bits }}-files
path: gettext${{ env.GETTEXT_VERSION }}-iconv${{ env.ICONV_VERSION }}-${{ matrix.link }}-${{ matrix.bits }}.zip
if-no-files-found: error
compression-level: 0
-
name: Prepare installer files
run: |
if [ -n '${{ steps.vars.outputs.signpath-signing-policy }}' ]; then
mv files-signed installer-files
else
mv files-unsigned installer-files
fi
-
name: Create installer
shell: pwsh
run: >
./build-exe/create-installer.ps1
-Bits ${{ matrix.bits }}
-Link ${{ matrix.link }}
-SourceDirectory installer-files
-OutputDirectory installer-unsigned
-
name: Check bitness
run: ./build-exe/check-bits.sh 32 installer-unsigned/gettext${{ env.GETTEXT_VERSION }}-iconv${{ env.ICONV_VERSION }}-${{ matrix.link }}-${{ matrix.bits }}.exe
-
name: Upload unsigned installer
if: steps.vars.outputs.signpath-signing-policy
id: upload-installer-unsigned
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.link }}-${{ matrix.bits }}-installer-unsigned
path: installer-unsigned\gettext${{ env.GETTEXT_VERSION }}-iconv${{ env.ICONV_VERSION }}-${{ matrix.link }}-${{ matrix.bits }}.exe
if-no-files-found: error
compression-level: 0
retention-days: 1
-
name: Sign installer
if: steps.vars.outputs.signpath-signing-policy
id: sign-installer
uses: signpath/github-action-submit-signing-request@v1
with:
api-token: '${{ secrets.SIGNPATH_API_TOKEN }}'
organization-id: 98c3accc-92c9-4962-b150-ff1f5c6356b8
project-slug: gettext-iconv-windows
signing-policy-slug: '${{ steps.vars.outputs.signpath-signing-policy }}'
artifact-configuration-slug: gh_sign_installer
github-artifact-id: ${{ steps.upload-installer-unsigned.outputs.artifact-id }}
wait-for-completion: true
output-artifact-directory: installer-signed
-
name: Check signature
if: steps.vars.outputs.signpath-signing-policy
shell: pwsh
run: >
./build-exe/check-signature.ps1
-Path installer-signed
-CanBeInvalid ${{ steps.vars.outputs.signatures-canbeinvalid }}
-
name: Move installer
run: |
if [ -n '${{ steps.vars.outputs.signpath-signing-policy }}' ]; then
mv installer-signed/*.exe .
else
mv installer-unsigned/*.exe .
fi
-
name: Upload installer
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.link }}-${{ matrix.bits }}-installer
path: gettext${{ env.GETTEXT_VERSION }}-iconv${{ env.ICONV_VERSION }}-${{ matrix.link }}-${{ matrix.bits }}.exe
if-no-files-found: error
compression-level: 0
-
name: Upload build log
if: always() && steps.prepare-build-log.outcome == 'success'
uses: actions/upload-artifact@v4
with:
name: ${{ matrix.link }}-${{ matrix.bits }}-log
if-no-files-found: ignore
retention-days: 1
include-hidden-files: true
compression-level: 0
path: build-log
-
name: Persist cache
if: always() && steps.restore-cache.outputs.cache-hit != 'true'
uses: actions/cache/save@v4
with:
key: ${{ steps.restore-cache.outputs.cache-primary-key }}
path: |
src\downloads
C:\cygwin-packages