]> git.ipfire.org Git - thirdparty/curl.git/commitdiff
GHA/macos: let gcc dictate the configured Apple SDK
authorViktor Szakats <commit@vsz.me>
Fri, 8 Nov 2024 00:29:50 +0000 (01:29 +0100)
committerViktor Szakats <commit@vsz.me>
Fri, 8 Nov 2024 09:48:12 +0000 (10:48 +0100)
As discovered earlier, Homebrew gcc is built against a specific Apple
SDK version and doesn't work when matched up with a different version,
e.g. the one advertised as default by the macos runner image.

Before this patch this was resolved with brute force by zapping the
hack-layer gcc component to avoid the bad interference. This worked
for us, but it's fragile, accidental and doesn't translate to
real-world build environments. Thus, impractical.

Avoid this by explicitly selecting the SDK version gcc was built for and
meant to be used with, as shown by `gcc --print-sysroot`.

It assumes that the gcc binaries preinstalled on the runner images
always ship with the SDK version they reference. It also assumes
this works with and without `brew update`.

Also:
- add 4 quick build-only jobs to test all gcc/macos combos.
- list SDKs offered via CommandLineTools.

Suggested-by: Bo Anderson
Ref: https://github.com/Homebrew/homebrew-core/issues/194778#issuecomment-2462764619

Follow-up to c349bd668c91f2484ae21c0f361ddf497143093c #14097
Closes #15518

.github/workflows/macos.yml

index 68c44c70f9e5d67c85bf996464a6729dad478ced..40f6602941b9b3ae685cf745a78442ce231472cf 100644 (file)
@@ -217,6 +217,7 @@ jobs:
           xcodebuild -version || true
           xcrun --sdk macosx --show-sdk-path 2>/dev/null || true
           xcrun --sdk macosx --show-sdk-version || true
+          ls -l /Library/Developer/CommandLineTools/SDKs || true
           echo '::group::macros predefined'; "${CC}" -dM -E - < /dev/null | sort || true; echo '::endgroup::'
           echo '::group::brew packages installed'; ls -l "$(brew --prefix)/opt"; echo '::endgroup::'
 
@@ -226,32 +227,28 @@ jobs:
 
       - name: 'configure'
         run: |
+          if [[ '${{ matrix.compiler }}' = 'gcc'* ]]; then
+            sysroot="$("${CC}" --print-sysroot)"  # Must match the SDK gcc was built for
+          else
+            sysroot="$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)"
+          fi
+
           if [[ '${{ matrix.compiler }}' = 'llvm'* ]]; then
             CC="$(brew --prefix ${{ matrix.compiler }})/bin/clang"
-            CC+=" --sysroot=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)"
+            CC+=" --sysroot=${sysroot}"
             CC+=" --target=$(uname -m)-apple-darwin"
           fi
 
-          if [[ '${{ matrix.compiler }}' = 'gcc'* ]]; then
-            libgccdir="$(dirname "$("${CC}" -print-libgcc-file-name)")"
-            echo '::group::gcc include-fixed details'; find "${libgccdir}/include-fixed" | sort; echo '::endgroup::'
-            for f in dispatch os AvailabilityInternal.h stdio.h; do
-              if [ -r "${libgccdir}/include-fixed/${f}" ]; then
-                echo "Zap gcc hack: '${libgccdir}/include-fixed/${f}'"
-                mv "${libgccdir}/include-fixed/${f}" "${libgccdir}/include-fixed/${f}-BAK"
-              fi
-            done
-          fi
           if [ -n '${{ matrix.build.configure }}' ]; then
             export CFLAGS
             if [[ '${{ matrix.compiler }}' = 'llvm'* ]]; then
               options+=" --target=$(uname -m)-apple-darwin"
             fi
-            CFLAGS+=' ${{ matrix.build.cflags }}'
             if [ '${{ matrix.compiler }}' != 'clang' ]; then
-              options+=" --with-sysroot=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)"
-              CFLAGS+=" --sysroot=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)"
+              options+=" --with-sysroot=${sysroot}"
+              CFLAGS+=" --sysroot=${sysroot}"
             fi
+            CFLAGS+=' ${{ matrix.build.cflags }}'
             CFLAGS+=' -mmacosx-version-min=${{ matrix.build.macos-version-min }}'
             mkdir bld && cd bld && ../configure --enable-unity --enable-test-bundles --enable-warnings --enable-werror \
               --disable-dependency-tracking \
@@ -260,7 +257,7 @@ jobs:
           else
             cmake -B bld -G Ninja -DCMAKE_UNITY_BUILD=ON -DCURL_TEST_BUNDLES=ON -DCURL_WERROR=ON \
               -DCMAKE_OSX_DEPLOYMENT_TARGET=${{ matrix.build.macos-version-min }} \
-              "-DCMAKE_OSX_SYSROOT=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)" \
+              "-DCMAKE_OSX_SYSROOT=${sysroot}" \
               "-DCMAKE_C_COMPILER_TARGET=$(uname -m | sed 's/arm64/aarch64/')-apple-darwin$(uname -r)" \
               ${{ matrix.build.generate }}
           fi
@@ -410,8 +407,7 @@ jobs:
           # Reduce build combinations, by dropping less interesting ones
           - { compiler: gcc-12, config: SecureTransport }
           - { compiler: gcc-13, build: cmake }
-          - { compiler: gcc-13, image: macos-13 }
-          - { compiler: gcc-14, config: SecureTransport }
+          - { compiler: gcc-14, build: autotools }
     steps:
       - name: 'install autotools'
         if: ${{ matrix.build == 'autotools' }}
@@ -429,6 +425,7 @@ jobs:
           xcodebuild -version || true
           xcrun --sdk macosx --show-sdk-path 2>/dev/null || true
           xcrun --sdk macosx --show-sdk-version || true
+          ls -l /Library/Developer/CommandLineTools/SDKs || true
           echo '::group::macros predefined'; "${CC}" -dM -E - < /dev/null | sort || true; echo '::endgroup::'
           echo '::group::brew packages preinstalled'; ls -l "$(brew --prefix)/opt"; echo '::endgroup::'
 
@@ -438,116 +435,26 @@ jobs:
 
       - name: 'configure / ${{ matrix.build }}'
         run: |
+          if [[ '${{ matrix.compiler }}' = 'gcc'* ]]; then
+            sysroot="$("${CC}" --print-sysroot)"  # Must match the SDK gcc was built for
+          else
+            sysroot="$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)"
+          fi
+
           if [[ '${{ matrix.compiler }}' = 'llvm'* ]]; then
             CC="$(brew --prefix ${{ matrix.compiler }})/bin/clang"
-            CC+=" --sysroot=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)"
+            CC+=" --sysroot=${sysroot}"
             CC+=" --target=$(uname -m)-apple-darwin"
           fi
 
-          # gcc ships with an `include-fixed` header set, which overrides SDK
-          # headers with the intent of making them compatible with gcc. The
-          # source for these headers is:
-          #   https://github.com/gcc-mirror/gcc/tree/master/fixincludes
-          # with extra Apple-specific patches applied from here for Homebrew:
-          #   https://github.com/iains/gcc-12-branch
-          #
-          # They pass through a generator phase at build-time which seems to
-          # pick the SDK installed on the build machine (maintained by the
-          # Homebrew project in our case) and patches it according to a set
-          # of rules in `inclhack.def`.
-          #
-          # Homebrew builds and ships different binaries for different macOS
-          # versions and CPUs, built on machines using the same OS version as
-          # the target one. Each of these machines have a particular version
-          # of Apple CommandLineTools with a default SDK version installed with
-          # them.
-          #
-          # Then this binary gets installed onto the end-user machine,
-          # matching the OS version at the time of installation.
-          #
-          # The problem with this approach is that the SDK version picked up
-          # at gcc build-time has a high chance of being or becoming out of
-          # sync with actual SDK installed on the end-user machine. This
-          # can happen after upgrading the OS, Xcode, selecting an SDK version
-          # manually, or other reasons.
-          #
-          # When the SDK versions do not match, the gcc hacks, instead of
-          # improving compatibility the SDK, are actively _breaking_
-          # compatibility, in an unexpected, hard to diagnose way.
-          #
-          # The SDK version used for gcc-hacks is not advertised. We can
-          # extract the major SDK version from the generated gcc-hack header
-          # files, assuming someone knows what to look for and where.
-          #
-          # Basically it also means that the same `gcc-N` Homebrew package
-          # behaves differently depending on the OS it was built on. Causing
-          # an explosion of build combination. It may also mean that a minor
-          # gcc version bump is built against a different SDK version, and due
-          # to the extra patch for the hack applied by Homebrew, there may
-          # be extra changes as well.
-          #
-          # For GHA runners, it means that the default Xcode + OS combo have
-          # and SDK mismatch in 8 out of 12 combinations (66%). All fail to
-          # build, plus one more with matching SDK. This is 9 in total (75%)
-          # that fail to build out of the box. These are the 3 lucky default
-          # combinations that worked to build curl:
-          #   macos-14 + Xcode 15.0.1 + gcc-12, gcc-14
-          #
-          # Of all possible valid GHA runner, gcc, manually selected Xcode
-          # combinations, 40% are broken.
-          #
-          # Compared to mainline llvm: llvm ships the same binaries regardless
-          # of build-OS or environment, it contains no SDK-version-specific
-          # hacks, and has no 3rd party patches. This still leaves some
-          # occasional issues, but works much closer to expectations.
-          #
-          # Some of these hacks are helpful, in particular for fixing this
-          # issue via math.h:
-          #   /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
-          #      53 | #   error "Unsupported value of __FLT_EVAL_METHOD__."
-          #
-          # Errors seen in available CI combinations:
-          #   error: two or more data types in declaration specifiers # fatal error: AvailabilityInternalLegacy.h: No such file or directory
-          #     gcc-13 + macos-14 + Xcode 14.3.1
-          #   error: two or more data types in declaration specifiers
-          #     gcc-13 + macos-12 + Xcode 14.1, 14.2
-          #     gcc-13 + Xcode 15.0.1, 15.1, 5.2
-          #   error: expected ';' before 'extern'
-          #     gcc-12, gcc-14 + macos-12 + Xcode 14.1, 14.2
-          #   error: unknown type name 'dispatch_queue_t'
-          #     gcc-12 + macos-13 + Xcode 15.0.1, 15.1, 15.2
-          #   error: type defaults to 'int' in declaration of 'DISPATCH_DECL_FACTORY_CLASS_SWIFT' [-Wimplicit-int]
-          #     gcc-14 macos-13 Xcode 15.0.1, 15.1, 15.2
-          #   error: unknown type name 'FILE'
-          #     Xcode 16.0
-          #
-          # Unbreak Homebrew gcc builds by moving problematic SDK header overlay
-          # directories/files out of the way:
-          if [[ '${{ matrix.compiler }}' = 'gcc'* ]]; then
-            # E.g.:
-            #   $(brew --prefix)/Cellar/gcc@11/11.4.0/lib/gcc/11/gcc/aarch64-apple-darwin23/11/include-fixed
-            #   $(brew --prefix)/Cellar/gcc@11/11.4.0/lib/gcc/11/gcc/x86_64-apple-darwin21/11/include-fixed
-            #   $(brew --prefix)/Cellar/gcc/14.1.0_1/lib/gcc/14/gcc/x86_64-apple-darwin21/14/include-fixed
-            libgccdir="$(dirname "$("${CC}" -print-libgcc-file-name)")"
-            echo '::group::gcc include-fixed details'; find "${libgccdir}/include-fixed" | sort; echo '::endgroup::'
-            patch_out='dispatch os AvailabilityInternal.h'
-            patch_out+=' stdio.h'  # for Xcode 16 error: unknown type name 'FILE'
-            for f in ${patch_out}; do
-              if [ -r "${libgccdir}/include-fixed/${f}" ]; then
-                echo "Zap gcc hack: '${libgccdir}/include-fixed/${f}'"
-                mv "${libgccdir}/include-fixed/${f}" "${libgccdir}/include-fixed/${f}-BAK"
-              fi
-            done
-          fi
-
           if [ '${{ matrix.build }}' = 'autotools' ]; then
             export CFLAGS
             if [[ '${{ matrix.compiler }}' = 'llvm'* ]]; then
               options+=" --target=$(uname -m)-apple-darwin"
             fi
             if [ '${{ matrix.compiler }}' != 'clang' ]; then
-              options+=" --with-sysroot=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)"
-              CFLAGS+=" --sysroot=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)"
+              options+=" --with-sysroot=${sysroot}"
+              CFLAGS+=" --sysroot=${sysroot}"
             fi
             [ '${{ matrix.config }}' = 'OpenSSL' ]         && options+=" --with-openssl=$(brew --prefix openssl)"
             [ '${{ matrix.config }}' = 'SecureTransport' ] && options+=' --with-secure-transport'
@@ -565,7 +472,7 @@ jobs:
             # would pick up nghttp2, libidn2, and libssh2
             cmake -B bld -DCMAKE_UNITY_BUILD=ON -DCURL_TEST_BUNDLES=ON -DCURL_WERROR=ON \
               -DCMAKE_OSX_DEPLOYMENT_TARGET=${{ matrix.macos-version-min }} \
-              "-DCMAKE_OSX_SYSROOT=$(xcrun --sdk macosx --show-sdk-path 2>/dev/null)" \
+              "-DCMAKE_OSX_SYSROOT=${sysroot}" \
               "-DCMAKE_C_COMPILER_TARGET=$(uname -m | sed 's/arm64/aarch64/')-apple-darwin$(uname -r)" \
               "-DCMAKE_IGNORE_PREFIX_PATH=$(brew --prefix)" \
               -DBUILD_LIBCURL_DOCS=OFF -DBUILD_MISC_DOCS=OFF -DENABLE_CURL_MANUAL=OFF \