Skip to content

Commit d47f338

Browse files
Merge pull request #29 from well-typed/tcard/windows
Windows CI workflow
2 parents 674fb36 + 9b46bbb commit d47f338

1 file changed

Lines changed: 302 additions & 0 deletions

File tree

.github/workflows/windows.yml

Lines changed: 302 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,302 @@
1+
name: "Windows tests"
2+
3+
# Notes:
4+
#
5+
# * On Windows, we only test supported versions of GHC using the LLVM/Clang that
6+
# is installed with that version of GHC.
7+
# * GHC 9.2 still uses GCC, not LLVM/Clang, so we do not test it.
8+
# * Windows dynamically loads libraries (DLLs) by searching for them in the
9+
# `PATH`. Make sure that `PATH` is configured correctly when executing
10+
# programs.
11+
# * The `PATH` must be configured with Windows-style paths. Use `cygpath` to
12+
# convert paths when necessary.
13+
#
14+
# Maintenance:
15+
#
16+
# * Configure supported GHC versions in
17+
# `on.workflow_dispatch.inputs.ghc_versions.default`.
18+
# * Configure GHC versions tested on pull requests in
19+
# `jobs.setup.steps.("Setup GHC versions (PR)")`. This should generally be a
20+
# subset of the supported versions.
21+
# * Configure GHC versions tested on merge/push in
22+
# `jobs.setup.steps.("Setup GHC versions (MQ and push to main)")`. This
23+
# should generally be all supported versions.
24+
25+
on:
26+
push:
27+
branches:
28+
- main
29+
pull_request:
30+
merge_group:
31+
workflow_dispatch:
32+
inputs:
33+
ghc_versions:
34+
description: "Comma-separated list of GHC version strings"
35+
default: '"9.4","9.6","9.8","9.10","9.12","9.14"'
36+
required: true
37+
enable_cache:
38+
description: "Enable cache (restore and save)"
39+
default: true
40+
required: true
41+
debug_tmate:
42+
description: "Debug with tmate after {init, checkout, setup, build, test}"
43+
default: "off"
44+
required: true
45+
46+
concurrency:
47+
group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.ref }}
48+
cancel-in-progress: true
49+
50+
jobs:
51+
setup:
52+
name: "Windows: Setup"
53+
runs-on: ubuntu-latest
54+
timeout-minutes: 5
55+
56+
outputs:
57+
matrix: ${{ steps.setup-matrix.outputs.matrix }}
58+
59+
defaults:
60+
run:
61+
shell: bash
62+
63+
steps:
64+
- name: "Setup GHC versions (PR)"
65+
if: ${{ github.event_name == 'pull_request' }}
66+
run: echo GHC_VERSIONS='"9.4","9.14"' | tee -a "${GITHUB_ENV}"
67+
68+
- name: "Setup GHC versions (MQ and push to main)"
69+
if: ${{ github.event_name == 'merge_group' || github.event_name == 'push' }}
70+
run: echo GHC_VERSIONS='"9.4","9.6","9.8","9.10","9.12","9.14"' | tee -a "${GITHUB_ENV}"
71+
72+
- name: "Setup GHC versions (dispatch)"
73+
if: ${{ github.event_name == 'workflow_dispatch' }}
74+
run: echo GHC_VERSIONS='${{ inputs.ghc_versions }}' | tee -a "${GITHUB_ENV}"
75+
76+
- name: "Setup matrix"
77+
id: setup-matrix
78+
run: echo "matrix={\"ghc\":[ ${GHC_VERSIONS} ]}" | tee -a "${GITHUB_OUTPUT}"
79+
80+
check-success:
81+
name: "Windows: Check success"
82+
runs-on: ubuntu-latest
83+
timeout-minutes: 5
84+
85+
needs:
86+
- build-and-test
87+
88+
defaults:
89+
run:
90+
shell: bash
91+
92+
if: ${{ !cancelled() }}
93+
94+
steps:
95+
- name: "Report failure"
96+
if: ${{ needs.build-and-test.result == 'failure' }}
97+
run: |
98+
echo "Some jobs failed"
99+
exit 1
100+
101+
- name: "Report success"
102+
if: ${{ needs.build-and-test.result == 'success' }}
103+
run: |
104+
echo "All jobs succeeded"
105+
exit 0
106+
107+
build-and-test:
108+
name: "Windows: GHC ${{ matrix.ghc }}"
109+
runs-on: windows-latest
110+
timeout-minutes: 45
111+
112+
needs:
113+
- setup
114+
115+
strategy:
116+
matrix: ${{ fromJSON(needs.setup.outputs.matrix) }}
117+
fail-fast: false
118+
119+
env:
120+
# Increment to reset the cache. Caches of the cabal store generally grow
121+
# in size: new packages are added, but never removed, unless we start with
122+
# a fresh cabal store once in a while. The cache-reset-number is part of
123+
# the cache key, and if it is incremented, then the "Restore cache" step
124+
# fails to restore a cache, meaning we will start with an empty cabal
125+
# store that we will then save a new cache for.
126+
cache-reset-number: 0
127+
128+
defaults:
129+
run:
130+
shell: bash
131+
132+
steps:
133+
- name: "[init] Create tmate session"
134+
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_tmate == 'init' }}
135+
uses: mxschmitt/action-tmate@v3
136+
137+
- name: "[init] Debug initial environment (PowerShell)"
138+
shell: powershell
139+
run: |
140+
Write-Host '== $PATH ======================================================================='
141+
foreach ($dir in ($env:PATH).Split(';')) {
142+
Write-Host $dir
143+
}
144+
145+
- name: "[init] Debug initial environment (Bash)"
146+
run: |
147+
echo '== $PATH ======================================================================='
148+
echo "${PATH}" | tr ':' '\n'
149+
150+
- name: "[checkout] Configure git to use LF"
151+
run: |
152+
git config --global core.autocrlf false
153+
git config --global core.eol lf
154+
155+
- name: "[checkout] Checkout repository"
156+
uses: actions/checkout@v4
157+
158+
- name: "[checkout] Create tmate session"
159+
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_tmate == 'checkout' }}
160+
uses: mxschmitt/action-tmate@v3
161+
162+
- name: "[setup] Setup Haskell"
163+
id: setup-haskell
164+
uses: haskell-actions/setup@v2
165+
with:
166+
ghc-version: ${{ matrix.ghc }}
167+
cabal-version: 3.16
168+
169+
- name: "[setup] Debug Haskell"
170+
run: |
171+
echo '== which ghc ==================================================================='
172+
which ghc
173+
echo '== ghc --version ==============================================================='
174+
ghc --version
175+
echo '== ghc --info =================================================================='
176+
ghc --info
177+
echo '== ghc-pkg list ================================================================'
178+
ghc-pkg list
179+
180+
- name: "[setup] Filter PATH"
181+
# To ensure that we never use any executables or DLLs from a version of
182+
# LLVM/Clang that is already installed, targeting MSVC, we remove them
183+
# from the `PATH`.
184+
shell: powershell
185+
run: |
186+
$oldPathDirs = ($env:PATH).Split(';')
187+
$newPathDirs = $oldPathDirs | Where-Object { $_ -notlike '*LLVM*' }
188+
$newPath = $newPathDirs -join ';'
189+
echo "PATH=$newPATH" | Out-File -FilePath $env:GITHUB_ENV -Encoding UTF8 -Append
190+
191+
- name: "[setup] Configure LLVM/Clang"
192+
run: |
193+
parentDir="$(dirname "$(dirname "$(which ghc)")")"
194+
LLVM_PATH="${parentDir}/mingw"
195+
if [ ! -d "${LLVM_PATH}" ] ; then
196+
LLVM_PATH="${parentDir}/ghc/${{ steps.setup-haskell.outputs.ghc-version }}/mingw"
197+
if [ ! -d "${LLVM_PATH}" ] ; then
198+
echo "error: LLVM path not found"
199+
false
200+
fi
201+
fi
202+
echo LLVM_PATH="${LLVM_PATH}" | tee -a "${GITHUB_ENV}"
203+
cygpath -w "${LLVM_PATH}/bin" | tee -a "${GITHUB_PATH}"
204+
LLVM_CONFIG="${LLVM_PATH}/bin/llvm-config.exe"
205+
if [ -x "${LLVM_CONFIG}" ] ; then
206+
echo LLVM_CONFIG="${LLVM_CONFIG}" | tee -a "${GITHUB_ENV}"
207+
fi
208+
209+
- name: "[setup] Debug LLVM/Clang"
210+
run: |
211+
echo '== which clang ================================================================='
212+
which clang
213+
echo '== clang --version ============================================================='
214+
clang --version
215+
echo '== $LLVM_PATH =================================================================='
216+
echo "${LLVM_PATH}"
217+
echo '== ls $LLVM_PATH ==============================================================='
218+
ls "${LLVM_PATH}"
219+
echo '== ls $LLVM_PATH/bin ==========================================================='
220+
ls "${LLVM_PATH}/bin"
221+
echo '== $LLVM_CONFIG ================================================================'
222+
if [ -z "${LLVM_CONFIG}" ] ; then
223+
echo 'NOT SET'
224+
else
225+
echo "${LLVM_CONFIG}"
226+
echo '== $LLVM_CONFIG --includedir ==================================================='
227+
"${LLVM_CONFIG}" --includedir
228+
echo '== $LLVM_CONFIG --libdir ======================================================='
229+
"${LLVM_CONFIG}" --libdir
230+
fi
231+
232+
- name: "[setup] Debug configured environment (PowerShell)"
233+
shell: powershell
234+
run: |
235+
Write-Host '== $PATH ======================================================================='
236+
foreach ($dir in ($env:PATH).Split(';')) {
237+
Write-Host $dir
238+
}
239+
240+
- name: "[setup] Debug configured environment (Bash)"
241+
run: |
242+
echo '== $PATH ======================================================================='
243+
echo "${PATH}" | tr ':' '\n'
244+
245+
- name: "[setup] Configure project"
246+
run: |
247+
cabal configure \
248+
--enable-tests \
249+
--enable-benchmarks \
250+
--disable-documentation \
251+
--ghc-options="-Werror"
252+
cat cabal.project.local
253+
254+
- name: "[setup] Generate Cabal plan"
255+
run: cabal build all --dry-run
256+
257+
- name: "[setup] Restore cache"
258+
# Do not restore the cache if it is disabled in a manual dispatch.
259+
if: ${{ github.event_name != 'workflow_dispatch' || inputs.enable_cache }}
260+
id: restore-cache
261+
uses: actions/cache/restore@v4
262+
env:
263+
key: build-and-test-${{ env.cache-reset-number }}-windows-ghc-${{ steps.setup-haskell.outputs.ghc-version }}-cabal-${{ steps.setup-haskell.outputs.cabal-version }}
264+
with:
265+
path: ${{ steps.setup-haskell.outputs.cabal-store }}
266+
key: ${{ env.key }}-plan-${{ hashFiles('dist-newstyle/cache/plan.json') }}
267+
restore-keys: ${{ env.key }}-
268+
269+
- name: "[setup] Create tmate session"
270+
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_tmate == 'setup' }}
271+
uses: mxschmitt/action-tmate@v3
272+
273+
- name: "[build] Build dependencies"
274+
run: cabal build all --only-dependencies
275+
276+
- name: "[build] Save cache"
277+
# Do not save the cache if it is disabled in a manual dispatch or it
278+
# already exists.
279+
if: ${{ (github.event_name != 'workflow_dispatch' || inputs.enable_cache) && (steps.restore-cache.outputs.cache-hit != 'true') }}
280+
uses: actions/cache/save@v4
281+
with:
282+
path: ${{ steps.setup-haskell.outputs.cabal-store }}
283+
key: ${{steps.restore-cache.outputs.cache-primary-key }}
284+
285+
- name: "[build] Build all"
286+
run: cabal build all
287+
288+
- name: "[build] Debug build"
289+
run: |
290+
echo '== ldd test-clang-bindings.exe ================================================='
291+
find dist-newstyle -type f -name test-clang-bindings.exe | xargs ldd
292+
293+
- name: "[build] Create tmate session"
294+
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_tmate == 'build' }}
295+
uses: mxschmitt/action-tmate@v3
296+
297+
- name: "[test] Test all"
298+
run: cabal test -j1 all --test-show-details=direct
299+
300+
- name: "[test] Create tmate session"
301+
if: ${{ github.event_name == 'workflow_dispatch' && inputs.debug_tmate == 'test' }}
302+
uses: mxschmitt/action-tmate@v3

0 commit comments

Comments
 (0)