xref: /curl/.github/workflows/macos.yml (revision 71cf0d1f)
1# Copyright (C) Daniel Stenberg, <daniel@haxx.se>, et al.
2#
3# SPDX-License-Identifier: curl
4
5name: macOS
6
7'on':
8  push:
9    branches:
10      - master
11      - '*/ci'
12    paths-ignore:
13      - '**/*.md'
14      - '.circleci/**'
15      - 'appveyor.*'
16      - 'packages/**'
17      - 'plan9/**'
18      - 'projects/**'
19      - 'winbuild/**'
20  pull_request:
21    branches:
22      - master
23    paths-ignore:
24      - '**/*.md'
25      - '.circleci/**'
26      - 'appveyor.*'
27      - 'packages/**'
28      - 'plan9/**'
29      - 'projects/**'
30      - 'winbuild/**'
31
32concurrency:
33  group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }}
34  cancel-in-progress: true
35
36permissions: {}
37
38# Deprecated Apple APIs and the macos-version-min value required to avoid
39# deprecation warnings with llvm/clang:
40#
41# - 10.7  Lion (2011)          - GSS
42# - 10.8  Mountain Lion (2012) - CFURLCreateDataAndPropertiesFromResource (used by curl Secure Transport code)
43# - 10.9  Maverick (2013)      - LDAP
44# - 10.14 Mojave (2018)        - Secure Transport
45#
46# For Secure Transport, curl implements features that require a target
47# newer than the 10.8 required by `CFURLCreateDataAndPropertiesFromResource`.
48# In this case `-Wno-deprecated-declarations` still comes handy to pacify
49# deprecation warnings, though the real solution would be to avoid calling
50# that function.
51
52env:
53  LDFLAGS: -w  # suppress 'object file was built for newer macOS version than being linked' warnings
54  MAKEFLAGS: -j 4
55
56jobs:
57  autotools:
58    name: 'AM ${{ matrix.compiler }} ${{ matrix.name }}'
59    runs-on: 'macos-latest'
60    timeout-minutes: 60
61    env:
62      DEVELOPER_DIR: "/Applications/Xcode${{ matrix.xcode && format('_{0}', matrix.xcode) || '' }}.app/Contents/Developer"
63      CC: ${{ matrix.compiler }}
64      CFLAGS: '-mmacosx-version-min=${{ matrix.macos-version-min }}'
65    strategy:
66      fail-fast: false
67      matrix:
68        include:
69          - name: '!ssl !debug brotli zstd'
70            compiler: clang
71            install: brotli zstd
72            configure: --without-ssl --enable-websockets --with-brotli --with-zstd
73            macos-version-min: '10.9'
74          - name: '!ssl !debug'
75            compiler: gcc-12
76            configure: --without-ssl --enable-websockets
77            macos-version-min: '10.9'
78          - name: '!ssl'
79            compiler: clang
80            configure: --enable-debug --without-ssl --enable-websockets
81            macos-version-min: '10.9'
82          - name: '!ssl libssh2 AppleIDN'
83            compiler: clang
84            configure: --enable-debug --with-libssh2=$(brew --prefix libssh2) --without-ssl --with-apple-idn --enable-websockets
85            macos-version-min: '10.9'
86          - name: 'OpenSSL libssh c-ares'
87            compiler: clang
88            install: libssh
89            configure: --enable-debug --with-libssh --with-openssl=$(brew --prefix openssl) --enable-ares --enable-websockets
90            macos-version-min: '10.9'
91          - name: 'OpenSSL libssh'
92            compiler: llvm@15
93            install: libssh
94            configure: --enable-debug --with-libssh --with-openssl=$(brew --prefix openssl) --enable-websockets
95            macos-version-min: '10.9'
96          - name: '!ssl c-ares'
97            compiler: clang
98            configure: --enable-debug --enable-ares --without-ssl --enable-websockets
99            macos-version-min: '10.9'
100          - name: '!ssl HTTP-only'
101            compiler: clang
102            configure: |
103              --enable-debug \
104              --disable-alt-svc --disable-dict --disable-file --disable-ftp --disable-gopher --disable-imap \
105              --disable-ldap --disable-pop3 --disable-rtmp --disable-rtsp --disable-scp --disable-sftp \
106              --disable-shared --disable-smb --disable-smtp --disable-telnet --disable-tftp --disable-unix-sockets \
107              --without-brotli --without-gssapi --without-libidn2 --without-libpsl --without-librtmp --without-libssh2 \
108              --without-nghttp2 --without-ntlm-auth --without-ssl --without-zlib --without-zstd
109
110            macos-version-min: '10.15'  # Catalina (2019)
111          - name: 'SecureTransport libssh2'
112            compiler: clang
113            configure: --enable-debug --with-secure-transport --enable-websockets --with-libssh2=$(brew --prefix libssh2)
114            macos-version-min: '10.8'
115          - name: 'SecureTransport libssh2 10.12'
116            compiler: clang
117            configure: --enable-debug --with-secure-transport --enable-websockets --with-libssh2=$(brew --prefix libssh2)
118            macos-version-min: '10.12'  # for monotonic timers
119            cflags: '-Wno-deprecated-declarations'
120          - name: 'SecureTransport libssh2'
121            compiler: gcc-12
122            configure: --enable-debug --with-secure-transport --enable-websockets --with-libssh2=$(brew --prefix libssh2)
123            macos-version-min: '10.8'
124          - name: 'LibreSSL +examples'
125            compiler: clang
126            install: libressl
127            configure: --enable-debug --with-openssl=$(brew --prefix libressl) --enable-websockets
128            macos-version-min: '10.9'
129          - name: 'OpenSSL'
130            compiler: clang
131            configure: --enable-debug --with-openssl=$(brew --prefix openssl) --enable-websockets
132            macos-version-min: '10.9'
133          - name: 'OpenSSL event-based'
134            compiler: clang
135            configure: --enable-debug --with-openssl=$(brew --prefix openssl) --enable-websockets
136            macos-version-min: '10.9'
137            tflags: -e
138          - name: 'OpenSSL libssh2 !ldap 10.15'
139            compiler: clang
140            configure: --enable-debug --disable-ldap --with-openssl=$(brew --prefix openssl) --enable-websockets
141            macos-version-min: '10.15'
142    steps:
143      - name: 'brew install'
144        # Run this command with retries because of spurious failures seen
145        # while running the tests, for example
146        # https://github.com/curl/curl/runs/4095721123?check_suite_focus=true
147        run: |
148          echo automake libtool pkg-config libpsl libssh2 nghttp2 stunnel ${{ matrix.install }} | xargs -Ix -n1 echo brew '"x"' > /tmp/Brewfile
149          while [[ $? == 0 ]]; do for i in 1 2 3; do brew update && brew bundle install --no-lock --file /tmp/Brewfile && break 2 || { echo Error: wait to try again; sleep 10; } done; false Too many retries; done
150
151      - name: 'brew unlink openssl'
152        run: |
153          if test -d $(brew --prefix)/include/openssl; then
154            brew unlink openssl
155          fi
156
157      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
158
159      - name: 'toolchain versions'
160        run: |
161          [[ '${{ matrix.compiler }}' = 'llvm'* ]] && CC="$(brew --prefix ${{ matrix.compiler }})/bin/clang"
162          [[ '${{ matrix.compiler }}' = 'gcc'* ]] && \
163            grep -h -r -E -o '.+[0-9.]+\.sdk/' "$(dirname "$("${CC}" -print-libgcc-file-name)")/include-fixed" | sed -E 's/^\t+//g' | tr -d '"' | sort -u || true
164          which "${CC}"; "${CC}" --version || true
165          xcodebuild -version || true
166          xcrun --sdk macosx --show-sdk-path 2>/dev/null || true
167          xcrun --sdk macosx --show-sdk-version || true
168          echo '::group::macros predefined'; "${CC}" -dM -E - < /dev/null | sort || true; echo '::endgroup::'
169          echo '::group::brew packages installed'; ls -l "$(brew --prefix)/opt"; echo '::endgroup::'
170
171      - name: 'autoreconf'
172        run: autoreconf -fi
173
174      - name: 'configure'
175        run: |
176          [[ '${{ matrix.compiler }}' = 'llvm'* ]] && CC="$(brew --prefix ${{ matrix.compiler }})/bin/clang"
177          CFLAGS+=' ${{ matrix.cflags }}'
178          if [[ '${{ matrix.compiler }}' = 'gcc'* ]]; then
179            libgccdir="$(dirname "$("${CC}" -print-libgcc-file-name)")"
180            echo '::group::gcc include-fixed details'; find "${libgccdir}/include-fixed" | sort; echo '::endgroup::'
181            for f in dispatch os AvailabilityInternal.h stdio.h; do
182              if [ -r "${libgccdir}/include-fixed/${f}" ]; then
183                echo "Zap gcc hack: '${libgccdir}/include-fixed/${f}'"
184                mv "${libgccdir}/include-fixed/${f}" "${libgccdir}/include-fixed/${f}-BAK"
185              fi
186            done
187          fi
188          if [[ '${{ matrix.compiler }}' = 'llvm'* ]]; then
189            options+=" --target=$(uname -m)-apple-darwin"
190            CC+=" --target=$(uname -m)-apple-darwin"
191          fi
192          if [ '${{ matrix.compiler }}' != 'clang' ]; then
193            options+=" --with-sysroot=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)"
194            CFLAGS+=" --sysroot=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)"
195          fi
196          mkdir bld && cd bld && ../configure --enable-unity --enable-test-bundles --enable-warnings --enable-werror \
197            --disable-dependency-tracking \
198            --with-libpsl=$(brew --prefix libpsl) \
199            ${{ matrix.configure }} ${options}
200
201      - name: 'configure log'
202        if: ${{ !cancelled() }}
203        run: cat bld/config.log || true
204
205      - name: 'curl_config.h'
206        run: |
207          echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::'
208          cat bld/lib/curl_config.h | grep -F '#define' | sort || true
209
210      - name: 'build-cert'
211        if: contains(matrix.configure, '--with-secure-transport')
212        run: |
213          make -C bld/tests/certs clean-certs
214          make -C bld/tests/certs build-certs -j1
215
216      - name: 'make'
217        run: make -C bld V=1
218
219      - name: 'curl version'
220        run: bld/src/curl --disable --version
221
222      - name: 'make tests'
223        run: make -C bld V=1 -C tests
224
225      - name: 'pip3 install'
226        run: |
227          python3 -m venv $HOME/venv
228          source $HOME/venv/bin/activate
229          python3 -m pip install impacket
230
231      - name: 'run tests'
232        timeout-minutes: 20
233        run: |
234          export TFLAGS='-j10 ${{ matrix.tflags }}'
235          TFLAGS+=' ~2037 ~2041'  # flaky
236          if [[ '${{ matrix.compiler }}' = 'gcc'* ]]; then
237            TFLAGS+=' ~RTSP'  # 567 568 569 570 571 572 577 689 3100
238            TFLAGS+=' ~1156 ~1539'  # HTTP Content-Range, Content-Length
239            if [[ '${{ matrix.configure }}' = *'--with-secure-transport'* ]]; then
240              TFLAGS+=' ~2100'  # 2100:'HTTP GET using DoH' https://github.com/curl/curl/actions/runs/9942146678/job/27462937524#step:15:5059
241              TFLAGS+=' ~HTTP/2'  # 2400 2401 2402 2403 2404 2406, Secure Transport + nghttp2
242            else
243              TFLAGS+=' ~2402 ~2404'  # non-Secure Transport + nghttp2
244            fi
245          fi
246          if [[ '${{ matrix.configure }}' = *'--with-secure-transport'* ]]; then
247            TFLAGS+=' ~313'  # Secure Transport does not support crl file
248            TFLAGS+=' ~1631 ~1632'  # Secure Transport is not able to shutdown ftp over https gracefully yet
249          fi
250          source $HOME/venv/bin/activate
251          rm -f $HOME/.curlrc
252          make -C bld V=1 test-ci
253
254      - name: 'make examples'
255        if: ${{ contains(matrix.build.name, '+examples') }}
256        run: make -C bld V=1 examples
257
258  cmake:
259    name: 'CM ${{ matrix.compiler }} ${{ matrix.build.name }}'
260    runs-on: 'macos-latest'
261    timeout-minutes: 30
262    env:
263      DEVELOPER_DIR: "/Applications/Xcode${{ matrix.xcode && format('_{0}', matrix.xcode) || '' }}.app/Contents/Developer"
264      CC: ${{ matrix.compiler }}
265    strategy:
266      fail-fast: false
267      matrix:
268        compiler: [clang, llvm@15, gcc-12]
269        build:
270          - name: 'OpenSSL ws gsasl AppleIDN'
271            install: gsasl
272            generate: -DOPENSSL_ROOT_DIR=$(brew --prefix openssl) -DCURL_USE_GSASL=ON -DUSE_APPLE_IDN=ON -DENABLE_WEBSOCKETS=ON
273            macos-version-min: '10.9'
274          - name: 'OpenSSL +static libssh'
275            install: libssh
276            generate: -DOPENSSL_ROOT_DIR=$(brew --prefix openssl) -DBUILD_STATIC_LIBS=ON -DCURL_USE_LIBSSH2=OFF -DCURL_USE_LIBSSH=ON
277            macos-version-min: '10.9'
278          - name: 'SecureTransport ws debug'
279            generate: -DCURL_USE_SECTRANSP=ON -DENABLE_WEBSOCKETS=ON -DENABLE_DEBUG=ON
280            macos-version-min: '10.8'
281          - name: 'LibreSSL !ldap heimdal c-ares +examples'
282            install: libressl heimdal
283            generate: -DOPENSSL_ROOT_DIR=$(brew --prefix libressl) -DENABLE_ARES=ON -DCURL_USE_GSSAPI=ON -DGSS_ROOT_DIR=$(brew --prefix heimdal) -DCURL_DISABLE_LDAP=ON
284            macos-version-min: '10.15'
285          - name: 'wolfSSL !ldap brotli zstd'
286            install: brotli wolfssl zstd
287            generate: -DCURL_USE_WOLFSSL=ON -DCURL_BROTLI=ON -DCURL_ZSTD=ON -DCURL_DISABLE_LDAP=ON
288            macos-version-min: '10.15'
289          - name: 'GnuTLS !ldap krb5'
290            install: gnutls nettle krb5
291            generate: -DCURL_USE_GNUTLS=ON -DCURL_USE_OPENSSL=OFF -DCURL_USE_GSSAPI=ON -DGSS_ROOT_DIR=$(brew --prefix krb5) -DCURL_DISABLE_LDAP=ON
292            macos-version-min: '10.15'
293          - name: 'OpenSSL torture !FTP'
294            generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=$(brew --prefix openssl) -DCURL_BROTLI=ON -DCURL_ZSTD=ON -DENABLE_WEBSOCKETS=ON
295            tflags: -t --shallow=25 !FTP
296            macos-version-min: '10.9'
297            torture: true
298          - name: 'OpenSSL torture FTP'
299            generate: -DENABLE_DEBUG=ON -DBUILD_SHARED_LIBS=OFF -DENABLE_THREADED_RESOLVER=OFF -DOPENSSL_ROOT_DIR=$(brew --prefix openssl) -DCURL_BROTLI=ON -DCURL_ZSTD=ON
300            tflags: -t --shallow=20 FTP
301            macos-version-min: '10.9'
302            torture: true
303        exclude:
304          - { compiler: llvm@15, build: { macos-version-min: '10.15' } }
305          - { compiler: llvm@15, build: { macos-version-min: '10.9' } }
306          - { compiler: gcc-12, build: { torture: true } }
307    steps:
308      - name: 'brew install'
309        run: |
310          echo ninja pkg-config libpsl libssh2 nghttp2 stunnel ${{ matrix.build.install }} | xargs -Ix -n1 echo brew '"x"' > /tmp/Brewfile
311          while [[ $? == 0 ]]; do for i in 1 2 3; do brew update && brew bundle install --no-lock --file /tmp/Brewfile && break 2 || { echo Error: wait to try again; sleep 10; } done; false Too many retries; done
312
313      - name: 'brew unlink openssl'
314        run: |
315          if test -d $(brew --prefix)/include/openssl; then
316            brew unlink openssl
317          fi
318
319      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
320
321      - name: 'toolchain versions'
322        run: |
323          [[ '${{ matrix.compiler }}' = 'llvm'* ]] && CC="$(brew --prefix ${{ matrix.compiler }})/bin/clang"
324          [[ '${{ matrix.compiler }}' = 'gcc'* ]] && \
325            grep -h -r -E -o '.+[0-9.]+\.sdk/' "$(dirname "$("${CC}" -print-libgcc-file-name)")/include-fixed" | sed -E 's/^\t+//g' | tr -d '"' | sort -u || true
326          which "${CC}"; "${CC}" --version || true
327          xcodebuild -version || true
328          xcrun --sdk macosx --show-sdk-path 2>/dev/null || true
329          xcrun --sdk macosx --show-sdk-version || true
330          echo '::group::macros predefined'; "${CC}" -dM -E - < /dev/null | sort || true; echo '::endgroup::'
331          echo '::group::brew packages installed'; ls -l "$(brew --prefix)/opt"; echo '::endgroup::'
332
333      - name: 'cmake configure'
334        run: |
335          [[ '${{ matrix.compiler }}' = 'llvm'* ]] && CC="$(brew --prefix ${{ matrix.compiler }})/bin/clang"
336          if [[ '${{ matrix.compiler }}' = 'gcc'* ]]; then
337            libgccdir="$(dirname "$("${CC}" -print-libgcc-file-name)")"
338            echo '::group::gcc include-fixed details'; find "${libgccdir}/include-fixed" | sort; echo '::endgroup::'
339            for f in dispatch os AvailabilityInternal.h stdio.h; do
340              if [ -r "${libgccdir}/include-fixed/${f}" ]; then
341                echo "Zap gcc hack: '${libgccdir}/include-fixed/${f}'"
342                mv "${libgccdir}/include-fixed/${f}" "${libgccdir}/include-fixed/${f}-BAK"
343              fi
344            done
345          fi
346          cmake -B bld -G Ninja -DCMAKE_UNITY_BUILD=ON -DCURL_TEST_BUNDLES=ON -DCURL_WERROR=ON \
347            -DCMAKE_OSX_DEPLOYMENT_TARGET=${{ matrix.build.macos-version-min }} \
348            "-DCMAKE_C_COMPILER_TARGET=$(uname -m | sed 's/arm64/aarch64/')-apple-darwin$(uname -r)" \
349            ${{ matrix.build.generate }}
350
351      - name: 'configure log'
352        if: ${{ !cancelled() }}
353        run: cat bld/CMakeFiles/CMakeConfigureLog.yaml 2>/dev/null || true
354
355      - name: 'curl_config.h'
356        run: |
357          echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::'
358          cat bld/lib/curl_config.h | grep -F '#define' | sort || true
359
360      - name: 'build-cert'
361        if: contains(matrix.build.generate, '-DCURL_USE_SECTRANSP=ON')
362        run: |
363          ninja -C bld clean-certs
364          ninja -C bld build-certs -j 1
365
366      - name: 'cmake build'
367        run: ninja -C bld --verbose
368
369      - name: 'curl version'
370        run: bld/src/curl --disable --version
371
372      - name: 'cmake build tests'
373        run: ninja -C bld testdeps
374
375      - name: 'pip3 install'
376        run: |
377          python3 -m venv $HOME/venv
378          source $HOME/venv/bin/activate
379          python3 -m pip install impacket
380
381      - name: 'cmake run tests'
382        timeout-minutes: ${{ matrix.build.torture && 20 || 10 }}
383        run: |
384          export TFLAGS='-j10 ${{ matrix.build.tflags }}'
385          if [ -z '${{ matrix.build.torture }}' ]; then
386            TFLAGS+=' ~2037 ~2041'  # flaky
387            if [[ '${{ matrix.compiler }}' = 'gcc'* ]]; then
388              TFLAGS+=' ~RTSP'  # 567 568 569 570 571 572 577 689 3100
389              TFLAGS+=' ~1156 ~1539'  # HTTP Content-Range, Content-Length
390              if [[ '${{ matrix.build.generate }}' = *'-DCURL_USE_SECTRANSP=ON'* ]]; then
391                TFLAGS+=' ~2100'  # 2100:'HTTP GET using DoH' https://github.com/curl/curl/actions/runs/9942146678/job/27462937524#step:15:5059
392                TFLAGS+=' ~HTTP/2'  # 2400 2401 2402 2403 2404 2406, Secure Transport + nghttp2
393              else
394                TFLAGS+=' ~2402 ~2404'  # non-Secure Transport + nghttp2
395              fi
396            fi
397            if [[ '${{ matrix.build.generate }}' = *'-DCURL_USE_SECTRANSP=ON'* ]]; then
398              TFLAGS+=' ~313'  # Secure Transport does not support crl file
399              TFLAGS+=' ~1631 ~1632'  # Secure Transport is not able to shutdown ftp over https gracefully yet
400            fi
401          fi
402          source $HOME/venv/bin/activate
403          rm -f $HOME/.curlrc
404          ninja -C bld test-ci
405
406      - name: 'cmake build examples'
407        if: ${{ contains(matrix.name, '+examples') }}
408        run: make -C bld VERBOSE=1
409
410  combinations:  # Test buildability with host OS, Xcode / SDK, compiler, target-OS, Secure Transport/not, built tool, combinations
411    if: true  # Set to `true` to enable this test matrix. It runs quickly.
412    name: "${{ matrix.build == 'cmake' && 'CM' || 'AM' }} ${{ matrix.compiler }} ${{ matrix.image }} ${{ matrix.xcode }} ${{ matrix.config }}"
413    runs-on: ${{ matrix.image }}
414    timeout-minutes: 10
415    env:
416      DEVELOPER_DIR: "/Applications/Xcode${{ matrix.xcode && format('_{0}', matrix.xcode) || '' }}.app/Contents/Developer"
417      CC: ${{ matrix.compiler }}
418    strategy:
419      fail-fast: false
420      matrix:
421        config: [SecureTransport]  # also: OpenSSL
422        compiler: [gcc-12, gcc-13, gcc-14, llvm@15, clang]
423        # Xcode support matrix as of 2024-07, with default macOS SDK versions and OS names, years:
424        # * = default Xcode on the runner.
425        # macos-12: 13.1, 13.2.1, 13.3.1, 13.4.1, 14.0.1, 14.1,*14.2
426        # macos-13:                                       14.1, 14.2, 14.3.1,*15.0.1, 15.1, 15.2
427        # macos-14:                                                   14.3.1, 15.0.1, 15.1, 15.2, 15.3,*15.4, 16.0
428        # macOSSDK: 12.0, 12.1,   12.3,   12.3,   12.3,   13.0, 13.1, 13.3,   14.0,   14.2, 14.2, 14.4, 14.5, 15.0
429        #           Monterey (2021)                       Ventura (2022)      Sonoma (2023)                   Sequoia (2024)
430        # https://github.com/actions/runner-images/tree/main/images/macos
431        # https://en.wikipedia.org/wiki/MacOS_version_history
432        image: [macos-12, macos-13, macos-14]
433        # Can skip these to reduce jobs:
434        #   13.1, 13.2.1 are fairly old.
435        #   13.3.1, 14.0.1 have the same default macOS SDK as 13.4.1 and identical test results.
436        #   15.1 has the same default macOS SDK as 15.2 and identical test result.
437        #   14.1, 15.4 not revealing new fallouts.
438        #xcode: ['13.1', '13.2.1', '13.3.1', '13.4.1', '14.0.1', '14.1', '14.2', '14.3.1', '15.0.1', '15.1', '15.2', '15.3', '15.4', '16.0']  # all Xcode
439        #xcode: ['13.1', '13.2.1', '13.4.1', '14.1', '14.2', '14.3.1', '15.0.1', '15.2', '15.3', '15.4', '16.0']  # all SDK
440        #xcode: ['13.4.1', '14.2', '14.3.1', '15.0.1', '15.2', '15.3', '16.0']  # coverage
441        xcode: ['']  # default Xcodes
442        macos-version-min: ['10.8']
443        build: [autotools, cmake]
444        exclude:
445          # Combinations uncovered by runner images:
446          - { image: macos-12, xcode: '14.3.1' }
447          - { image: macos-12, xcode: '15.0.1' }
448          - { image: macos-12, xcode: '15.1'   }
449          - { image: macos-12, xcode: '15.2'   }
450          - { image: macos-12, xcode: '15.3'   }
451          - { image: macos-12, xcode: '15.4'   }
452          - { image: macos-12, xcode: '16.0'   }
453          - { image: macos-13, xcode: '13.1'   }
454          - { image: macos-13, xcode: '13.2.1' }
455          - { image: macos-13, xcode: '13.3.1' }
456          - { image: macos-13, xcode: '13.4.1' }
457          - { image: macos-13, xcode: '14.0.1' }
458          - { image: macos-13, xcode: '15.3'   }
459          - { image: macos-13, xcode: '15.4'   }
460          - { image: macos-13, xcode: '16.0'   }
461          - { image: macos-14, xcode: '13.1'   }
462          - { image: macos-14, xcode: '13.2.1' }
463          - { image: macos-14, xcode: '13.3.1' }
464          - { image: macos-14, xcode: '13.4.1' }
465          - { image: macos-14, xcode: '14.0.1' }
466          - { image: macos-14, xcode: '14.1'   }
467          - { image: macos-14, xcode: '14.2'   }
468          # Reduce build combinations, by dropping less interesting ones
469          - { compiler: gcc-12, config: SecureTransport }
470          - { compiler: gcc-13, build: cmake }
471          - { compiler: gcc-13, image: macos-13 }
472          - { compiler: gcc-14, config: SecureTransport }
473    steps:
474      - name: 'install autotools'
475        if: ${{ matrix.build == 'autotools' }}
476        run: |
477          echo automake libtool | xargs -Ix -n1 echo brew '"x"' > /tmp/Brewfile
478          while [[ $? == 0 ]]; do for i in 1 2 3; do brew update && brew bundle install --no-lock --file /tmp/Brewfile && break 2 || { echo Error: wait to try again; sleep 10; } done; false Too many retries; done
479
480      - uses: actions/checkout@692973e3d937129bcbf40652eb9f2f61becf3332 # v4
481
482      - name: 'toolchain versions'
483        run: |
484          [[ '${{ matrix.compiler }}' = 'llvm'* ]] && CC="$(brew --prefix ${{ matrix.compiler }})/bin/clang"
485          [[ '${{ matrix.compiler }}' = 'gcc'* ]] && \
486            grep -h -r -E -o '.+[0-9.]+\.sdk/' "$(dirname "$("${CC}" -print-libgcc-file-name)")/include-fixed" | sed -E 's/^\t+//g' | tr -d '"' | sort -u || true
487          which "${CC}"; "${CC}" --version || true
488          xcodebuild -version || true
489          xcrun --sdk macosx --show-sdk-path 2>/dev/null || true
490          xcrun --sdk macosx --show-sdk-version || true
491          echo '::group::macros predefined'; "${CC}" -dM -E - < /dev/null | sort || true; echo '::endgroup::'
492          echo '::group::brew packages preinstalled'; ls -l "$(brew --prefix)/opt"; echo '::endgroup::'
493
494      - name: 'autoreconf'
495        if: ${{ matrix.build == 'autotools' }}
496        run: autoreconf -fi
497
498      - name: 'configure / ${{ matrix.build }}'
499        run: |
500          [[ '${{ matrix.compiler }}' = 'llvm'* ]] && CC="$(brew --prefix ${{ matrix.compiler }})/bin/clang"
501
502          # gcc ships with an `include-fixed` header set, which overrides SDK
503          # headers with the intent of making them compatible with gcc. The
504          # source for these headers is:
505          #   https://github.com/gcc-mirror/gcc/tree/master/fixincludes
506          # with extra Apple-specific patches applied from here for Homebrew:
507          #   https://github.com/iains/gcc-12-branch
508          #
509          # They pass through a generator phase at build-time which seems to
510          # pick the SDK installed on the build machine (maintained by the
511          # Homebrew project in our case) and patches it according to a set
512          # of rules in `inclhack.def`.
513          #
514          # Homebrew builds and ships different binaries for different macOS
515          # versions and CPUs, built on machines using the same OS version as
516          # the target one. Each of these machines have a particular version
517          # of Apple CommandLineTools with a default SDK version installed with
518          # them.
519          #
520          # Then this binary gets installed onto the end-user machine,
521          # matching the OS version at the time of installation.
522          #
523          # The problem with this approach is that the SDK version picked up
524          # at gcc build-time has a high chance of being or becoming out of
525          # sync with actual SDK installed on the end-user machine. This
526          # can happen after upgrading the OS, Xcode, selecting an SDK version
527          # manually, or other reasons.
528          #
529          # When the SDK versions do not match, the gcc hacks, instead of
530          # improving compatibility the SDK, are actively _breaking_
531          # compatibility, in an unexpected, hard to diagnose way.
532          #
533          # The SDK version used for gcc-hacks is not advertised. We can
534          # extract the major SDK version from the generated gcc-hack header
535          # files, assuming someone knows what to look for and where.
536          #
537          # Basically it also means that the same `gcc-N` Homebrew package
538          # behaves differently depending on the OS it was built on. Causing
539          # an explosion of build combination. It may also mean that a minor
540          # gcc version bump is built against a different SDK version, and due
541          # to the extra patch for the hack applied by Homebrew, there may
542          # be extra changes as well.
543          #
544          # For GHA runners, it means that the default Xcode + OS combo is
545          # broken in 8 out of 12 combinations (66%) have an SDK mismatch,
546          # and 9 fail to build (75%). These are the 3 lucky default
547          # combinations that worked to build curl:
548          #   macos-14 + Xcode 15.0.1 + gcc-12, gcc-14
549          #
550          # Of all possible valid GHA runner, gcc, manually selected Xcode
551          # combinations, 40% are broken.
552          #
553          # Compared to mainline llvm: llvm ships the same binaries regardless
554          # of build-OS or environent, it contains no SDK-version-specific
555          # hacks, and has no 3rd party patches. This still leaves some
556          # occasional issues, but works much closer to expectations.
557          #
558          # Some of these hacks are helpful, in particular for fixing this
559          # issue via math.h:
560          #   /Applications/Xcode_14.3.1.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/math.h:53:5: error: #error "Unsupported value of
561          #      53 | #   error "Unsupported value of __FLT_EVAL_METHOD__."
562          #
563          # Errors seen in available CI combinations:
564          #   error: two or more data types in declaration specifiers # fatal error: AvailabilityInternalLegacy.h: No such file or directory
565          #     gcc-13 + macos-14 + Xcode 14.3.1
566          #   error: two or more data types in declaration specifiers
567          #     gcc-13 + macos-12 + Xcode 14.1, 14.2
568          #     gcc-13 + Xcode 15.0.1, 15.1, 5.2
569          #   error: expected ';' before 'extern'
570          #     gcc-12, gcc-14 + macos-12 + Xcode 14.1, 14.2
571          #   error: unknown type name 'dispatch_queue_t'
572          #     gcc-12 + macos-13 + Xcode 15.0.1, 15.1, 15.2
573          #   error: type defaults to 'int' in declaration of 'DISPATCH_DECL_FACTORY_CLASS_SWIFT' [-Wimplicit-int]
574          #     gcc-14 macos-13 Xcode 15.0.1, 15.1, 15.2
575          #   error: unknown type name 'FILE'
576          #     Xcode 16.0
577          #
578          # Unbreak Homebrew gcc builds by moving problematic SDK header overlay
579          # directories/files out of the way:
580          if [[ '${{ matrix.compiler }}' = 'gcc'* ]]; then
581            # E.g.:
582            #   $(brew --prefix)/Cellar/gcc@11/11.4.0/lib/gcc/11/gcc/aarch64-apple-darwin23/11/include-fixed
583            #   $(brew --prefix)/Cellar/gcc@11/11.4.0/lib/gcc/11/gcc/x86_64-apple-darwin21/11/include-fixed
584            #   $(brew --prefix)/Cellar/gcc/14.1.0_1/lib/gcc/14/gcc/x86_64-apple-darwin21/14/include-fixed
585            libgccdir="$(dirname "$("${CC}" -print-libgcc-file-name)")"
586            echo '::group::gcc include-fixed details'; find "${libgccdir}/include-fixed" | sort; echo '::endgroup::'
587            patch_out='dispatch os AvailabilityInternal.h'
588            patch_out+=' stdio.h'  # for Xcode 16 error: unknown type name 'FILE'
589            for f in ${patch_out}; do
590              if [ -r "${libgccdir}/include-fixed/${f}" ]; then
591                echo "Zap gcc hack: '${libgccdir}/include-fixed/${f}'"
592                mv "${libgccdir}/include-fixed/${f}" "${libgccdir}/include-fixed/${f}-BAK"
593              fi
594            done
595          fi
596
597          if [ '${{ matrix.build }}' = 'autotools' ]; then
598            export CFLAGS
599            if [[ '${{ matrix.compiler }}' = 'llvm'* ]]; then
600              options+=" --target=$(uname -m)-apple-darwin"
601              CC+=" --target=$(uname -m)-apple-darwin"
602            fi
603            if [ '${{ matrix.compiler }}' != 'clang' ]; then
604              options+=" --with-sysroot=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)"
605              CFLAGS+=" --sysroot=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)"
606            fi
607            [ '${{ matrix.config }}' = 'OpenSSL' ]         && options+=" --with-openssl=$(brew --prefix openssl)"
608            [ '${{ matrix.config }}' = 'SecureTransport' ] && options+=' --with-secure-transport'
609            CFLAGS+=' -mmacosx-version-min=${{ matrix.macos-version-min }}'
610            # would pick up nghttp2, libidn2, but libssh2 is disabled by default
611            mkdir bld && cd bld && ../configure --enable-unity --enable-test-bundles --enable-warnings --enable-werror \
612              --disable-dependency-tracking \
613              --disable-docs --disable-manual \
614              --without-nghttp2 --without-libidn2 \
615              --without-libpsl \
616              ${options}
617          else
618            [ '${{ matrix.config }}' = 'OpenSSL' ]         && options+=' -DCURL_USE_OPENSSL=ON'
619            [ '${{ matrix.config }}' = 'SecureTransport' ] && options+=' -DCURL_USE_SECTRANSP=ON'
620            # would pick up nghttp2, libidn2, and libssh2
621            cmake -B bld -DCMAKE_UNITY_BUILD=ON -DCURL_TEST_BUNDLES=ON -DCURL_WERROR=ON \
622              -DCMAKE_OSX_DEPLOYMENT_TARGET=${{ matrix.macos-version-min }} \
623              "-DCMAKE_IGNORE_PREFIX_PATH=$(brew --prefix)" \
624              "-DCMAKE_C_COMPILER_TARGET=$(uname -m | sed 's/arm64/aarch64/')-apple-darwin$(uname -r)" \
625              -DBUILD_LIBCURL_DOCS=OFF -DBUILD_MISC_DOCS=OFF -DENABLE_CURL_MANUAL=OFF \
626              -DUSE_NGHTTP2=OFF -DUSE_LIBIDN2=OFF \
627              -DCURL_USE_LIBPSL=OFF -DCURL_USE_LIBSSH2=OFF \
628              ${options}
629          fi
630
631      - name: 'configure log'
632        if: ${{ !cancelled() }}
633        run: cat bld/config.log bld/CMakeFiles/CMakeConfigureLog.yaml 2>/dev/null || true
634
635      - name: 'curl_config.h'
636        run: |
637          echo '::group::raw'; cat bld/lib/curl_config.h || true; echo '::endgroup::'
638          cat bld/lib/curl_config.h | grep -F '#define' | sort || true
639
640      - name: 'build / ${{ matrix.build }}'
641        run: make -C bld V=1 VERBOSE=1
642
643      - name: 'curl version'
644        run: bld/src/curl --disable --version
645