From 5582849ae74a8b79a3226aa7a74372b3c3d43d21 Mon Sep 17 00:00:00 2001 From: Bo Anderson Date: Fri, 8 Mar 2024 21:26:25 +0000 Subject: [PATCH] Reproducible builds for native compiled binaries --- Library/Homebrew/build.rb | 8 +++++--- Library/Homebrew/extend/ENV/super.rb | 1 + Library/Homebrew/extend/os/mac/development_tools.rb | 12 ++++++++++++ Library/Homebrew/extend/os/mac/extend/ENV/shared.rb | 8 +------- Library/Homebrew/extend/os/mac/extend/ENV/super.rb | 3 +++ Library/Homebrew/os/mac/xcode.rb | 4 ++-- Library/Homebrew/shims/super/cc | 12 +++++++++++- 7 files changed, 35 insertions(+), 13 deletions(-) diff --git a/Library/Homebrew/build.rb b/Library/Homebrew/build.rb index d57205f1a5..663b2f04b5 100644 --- a/Library/Homebrew/build.rb +++ b/Library/Homebrew/build.rb @@ -139,12 +139,14 @@ class Build with_env( # For head builds, HOMEBREW_FORMULA_PREFIX should include the commit, # which is not known until after the formula has been staged. - HOMEBREW_FORMULA_PREFIX: formula.prefix, + HOMEBREW_FORMULA_PREFIX: formula.prefix, + # https://reproducible-builds.org/docs/build-path/ + HOMEBREW_FORMULA_BUILDPATH: formula.buildpath, # https://reproducible-builds.org/docs/source-date-epoch/ - SOURCE_DATE_EPOCH: formula.source_modified_time.to_i.to_s, + SOURCE_DATE_EPOCH: formula.source_modified_time.to_i.to_s, # Avoid make getting confused about timestamps. # https://github.com/Homebrew/homebrew-core/pull/87470 - TZ: "UTC0", + TZ: "UTC0", ) do formula.patch diff --git a/Library/Homebrew/extend/ENV/super.rb b/Library/Homebrew/extend/ENV/super.rb index 5813648146..6beeb4cb2f 100644 --- a/Library/Homebrew/extend/ENV/super.rb +++ b/Library/Homebrew/extend/ENV/super.rb @@ -109,6 +109,7 @@ module Superenv # D - Generate debugging information # f - Pass `-no_fixup_chains` to `ld` whenever it # is invoked with `-undefined dynamic_lookup` + # o - Pass `-oso_prefix` to `ld` whenever it is invoked # # These flags will also be present: # a - apply fix for apr-1-config path diff --git a/Library/Homebrew/extend/os/mac/development_tools.rb b/Library/Homebrew/extend/os/mac/development_tools.rb index d86403c7d6..df43d554bb 100644 --- a/Library/Homebrew/extend/os/mac/development_tools.rb +++ b/Library/Homebrew/extend/os/mac/development_tools.rb @@ -35,6 +35,18 @@ class DevelopmentTools :clang end + sig { returns(Version) } + def ld64_version + @ld64_version ||= begin + json = Utils.popen_read("/usr/bin/ld", "-version_details") + if $CHILD_STATUS.success? + Version.parse(JSON.parse(json)["version"]) + else + Version::NULL + end + end + end + sig { returns(T::Boolean) } def curl_handles_most_https_certificates? # The system Curl is too old for some modern HTTPS certificates on diff --git a/Library/Homebrew/extend/os/mac/extend/ENV/shared.rb b/Library/Homebrew/extend/os/mac/extend/ENV/shared.rb index 2f975c02fc..393f1901dc 100644 --- a/Library/Homebrew/extend/os/mac/extend/ENV/shared.rb +++ b/Library/Homebrew/extend/os/mac/extend/ENV/shared.rb @@ -35,15 +35,9 @@ module SharedEnvExtension sig { returns(T::Boolean) } def no_fixup_chains_support? - return false if MacOS.version <= :catalina - - # NOTE: `-version_details` is supported in Xcode 10.2 at the earliest. - ld_version_details = JSON.parse(Utils.safe_popen_read("/usr/bin/ld", "-version_details")) - ld_version = Version.parse(ld_version_details["version"]) - # This is supported starting Xcode 13, which ships ld64-711. # https://developer.apple.com/documentation/xcode-release-notes/xcode-13-release-notes # https://en.wikipedia.org/wiki/Xcode#Xcode_11.0_-_14.x_(since_SwiftUI_framework)_2 - ld_version >= 711 + DevelopmentTools.ld64_version >= 711 end end diff --git a/Library/Homebrew/extend/os/mac/extend/ENV/super.rb b/Library/Homebrew/extend/os/mac/extend/ENV/super.rb index bc11102b34..bfcd39695e 100644 --- a/Library/Homebrew/extend/os/mac/extend/ENV/super.rb +++ b/Library/Homebrew/extend/os/mac/extend/ENV/super.rb @@ -140,6 +140,9 @@ module Superenv # See: https://github.com/python/cpython/issues/97524 # https://github.com/pybind/pybind11/pull/4301 no_fixup_chains + + # Strip build prefixes from linker where supported, for deterministic builds. + append_to_cccfg "o" if DevelopmentTools.ld64_version >= 512 end def no_weak_imports diff --git a/Library/Homebrew/os/mac/xcode.rb b/Library/Homebrew/os/mac/xcode.rb index cdb2d5b90f..aa159b69f9 100644 --- a/Library/Homebrew/os/mac/xcode.rb +++ b/Library/Homebrew/os/mac/xcode.rb @@ -50,7 +50,7 @@ module OS when "10.14" then "10.2" when "10.13" then "9.0" when "10.12" then "8.0" - else "2.0" + else "7.3" end end @@ -373,7 +373,7 @@ module OS when "10.14" then "10.0.0" when "10.13" then "9.0.0" when "10.12" then "8.0.0" - else "1.0.0" + else "7.3.0" end end diff --git a/Library/Homebrew/shims/super/cc b/Library/Homebrew/shims/super/cc index 2706649680..aa4b71bb84 100755 --- a/Library/Homebrew/shims/super/cc +++ b/Library/Homebrew/shims/super/cc @@ -29,7 +29,7 @@ class Cmd CXX_REGEX = /(?:c|g|clang)\+\+/.freeze attr_reader :config, :prefix, :cellar, :opt, :cachedir, :tmpdir, :sysroot, :deps - attr_reader :archflags, :optflags, :keg_regex, :formula_prefix + attr_reader :archflags, :optflags, :keg_regex, :formula_prefix, :formula_buildpath def initialize(arg0, args) @arg0 = arg0 @@ -45,6 +45,7 @@ class Cmd @optflags = ENV.fetch("HOMEBREW_OPTFLAGS", "").split @deps = Set.new(ENV.fetch("HOMEBREW_DEPENDENCIES", "").split(",")) @formula_prefix = ENV["HOMEBREW_FORMULA_PREFIX"] + @formula_buildpath = ENV["HOMEBREW_FORMULA_BUILDPATH"] # matches opt or cellar prefix and formula name @keg_regex = %r{(#{Regexp.escape(opt)}|#{Regexp.escape(cellar)})/([\w+-.@]+)} end @@ -310,6 +311,9 @@ class Cmd args += path_flags("-isystem", isystem_paths) + path_flags("-I", include_paths) # Add -nostdinc when building against glibc@2.13 to avoid mixing system and brewed glibc headers. args << "-nostdinc" if @deps.include?("glibc@2.13") + # Ideally this would be -ffile-prefix-map, but that requires a minimum of GCC 8, LLVM Clang 10 or Apple Clang 12 + # and detecting the version dynamically based on what `HOMEBREW_CC` may have been rewritten to point to is awkward + args << "-fdebug-prefix-map=#{formula_buildpath}=." if formula_buildpath args end @@ -319,10 +323,12 @@ class Cmd args << "-headerpad_max_install_names" args << "-no_weak_imports" if no_weak_imports? args << "-no_fixup_chains" if no_fixup_chains? + args << "-oso_prefix" << formula_buildpath if oso_prefix? && formula_buildpath when :ccld, :cxxld args << "-Wl,-headerpad_max_install_names" args << "-Wl,-no_weak_imports" if no_weak_imports? args << "-Wl,-no_fixup_chains" if no_fixup_chains? + args << "-Wl,-oso_prefix,#{formula_buildpath}" if oso_prefix? && formula_buildpath end args end @@ -431,6 +437,10 @@ class Cmd @args.include?("-undefineddynamic_lookup") end + def oso_prefix? + config.include?("o") + end + def calls_ld? return true if mode == :ld return false unless [:ccld, :cxxld].include?(mode)