diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 5cf6b50..a4918a7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -45,7 +45,7 @@ jobs: SETUP_RUBY_VERSION=${SETUP_RUBY_VERSION//jdx-ruby} SETUP_RUBY_VERSION=${SETUP_RUBY_VERSION//@} if [[ "$SETUP_RUBY_VERSION" == "3.2."* ]]; then - echo SETUP_RUBY_VERSION=${SETUP_RUBY_VERSION} >> $GITHUB_ENV + echo "SETUP_RUBY_VERSION=${SETUP_RUBY_VERSION}" >> "$GITHUB_ENV" fi # Static gem extensions typically require the same Ruby version available already @@ -58,13 +58,21 @@ jobs: - name: Install Homebrew run: | /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)" - echo "/home/linuxbrew/.linuxbrew/bin" >> $GITHUB_PATH - echo "/home/linuxbrew/.linuxbrew/sbin" >> $GITHUB_PATH - eval "$(/home/linuxbrew/.linuxbrew/bin/brew shellenv)" + if [[ "$RUNNER_OS" == "Linux" ]]; then + brew_prefix="/home/linuxbrew/.linuxbrew" + else + brew_prefix="/opt/homebrew" + fi + echo "${brew_prefix}/bin" >> "$GITHUB_PATH" + echo "${brew_prefix}/sbin" >> "$GITHUB_PATH" + eval "$("${brew_prefix}/bin/brew" shellenv)" - name: Tap repo run: bin/setup + - name: Trust tap + run: brew trust jdx/ruby + - name: Fix Bundler permissions (ARM Linux) if: runner.os == 'Linux' && runner.arch == 'ARM64' run: chmod -R go-w /home/linuxbrew/.linuxbrew/Homebrew/Library/Homebrew/vendor/bundle || true diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 832e09e..381318a 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -18,7 +18,7 @@ jobs: runs-on: ubuntu-latest steps: - name: Set up Homebrew - uses: Homebrew/actions/setup-homebrew@2ebcf16054461267868620b1414507f3ccc765c1 # main + uses: Homebrew/actions/setup-homebrew@1141dcc5d01333e0891094766c3b2cf2ae3df7df # main - run: brew test-bot --only-tap-syntax build: diff --git a/Abstract/jdx-ruby-32.rb b/Abstract/jdx-ruby-32.rb index a51d11f..a91bff4 100644 --- a/Abstract/jdx-ruby-32.rb +++ b/Abstract/jdx-ruby-32.rb @@ -206,10 +206,21 @@ def install if OS.linux? # Don't restrict to a specific GCC compiler binary we used (e.g. gcc-5). inreplace lib/"ruby/#{abi_version}/#{abi_arch}/rbconfig.rb" do |s| + rbconfig = s.to_s s.gsub! ENV.cxx, "c++" s.gsub! ENV.cc, "cc" + s.gsub!(/(CONFIG\["CC"\] = )"gcc-\d+"/, '\\1"cc"') if rbconfig.match?(/CONFIG\["CC"\] = "gcc-\d+"/) + s.gsub!(/(CONFIG\["LDSHARED"\] = )"gcc-\d+/, '\\1"cc') if rbconfig.match?(/CONFIG\["LDSHARED"\] = "gcc-\d+/) + s.gsub!(/(CONFIG\["CXX"\] = )"g\+\+-\d+"/, '\\1"c++"') if rbconfig.match?(/CONFIG\["CXX"\] = "g\+\+-\d+"/) # Change e.g. `CONFIG["AR"] = "gcc-ar-11"` to `CONFIG["AR"] = "ar"` s.gsub!(/(CONFIG\[".+"\] = )"gcc-(.*)-\d+"/, '\\1"\\2"') + [ + %r{ ?-I/home/linuxbrew/\.linuxbrew/opt/(?:glibc@[^ /]+|linux-headers@[^ /]+)/include}, + %r{ ?-L/home/linuxbrew/\.linuxbrew/opt/glibc@[^ /]+/lib}, + %r{ ?-B/home/linuxbrew/\.linuxbrew/opt/glibc@[^ /]+/lib}, + %r{ ?-Wl,-rpath-link=/home/linuxbrew/\.linuxbrew/opt/glibc@[^ /]+/lib}, + / -nostdinc/, + ].each { |pattern| s.gsub!(pattern, "") if pattern.match?(rbconfig) } # C++ compiler might have been disabled because we break it with glibc@* builds s.sub!(/(CONFIG\["CXX"\] = )"false"/, '\\1"c++"') if build.without? "yjit" end @@ -298,8 +309,8 @@ def test # Test gems that require portable dependency headers # These were failing before we included headers in the tarball # See: https://github.com/jdx/mise/discussions/7268#discussioncomment-15298593 - system testpath/"bin/gem", "install", "openssl" # requires openssl headers - system testpath/"bin/gem", "install", "psych" # requires libyaml headers + install_default_native_gem ruby, "openssl" # requires openssl headers + install_default_native_gem ruby, "psych" # requires libyaml headers # Test that gem upgrades work for bundled gems with executables # This was failing due to shell polyglot format not being detected by RubyGems diff --git a/Abstract/jdx-ruby-33.rb b/Abstract/jdx-ruby-33.rb index 1740eba..74a0850 100644 --- a/Abstract/jdx-ruby-33.rb +++ b/Abstract/jdx-ruby-33.rb @@ -191,10 +191,21 @@ def install if OS.linux? # Don't restrict to a specific GCC compiler binary we used (e.g. gcc-5). inreplace lib/"ruby/#{abi_version}/#{abi_arch}/rbconfig.rb" do |s| + rbconfig = s.to_s s.gsub! ENV.cxx, "c++" s.gsub! ENV.cc, "cc" + s.gsub!(/(CONFIG\["CC"\] = )"gcc-\d+"/, '\\1"cc"') if rbconfig.match?(/CONFIG\["CC"\] = "gcc-\d+"/) + s.gsub!(/(CONFIG\["LDSHARED"\] = )"gcc-\d+/, '\\1"cc') if rbconfig.match?(/CONFIG\["LDSHARED"\] = "gcc-\d+/) + s.gsub!(/(CONFIG\["CXX"\] = )"g\+\+-\d+"/, '\\1"c++"') if rbconfig.match?(/CONFIG\["CXX"\] = "g\+\+-\d+"/) # Change e.g. `CONFIG["AR"] = "gcc-ar-11"` to `CONFIG["AR"] = "ar"` s.gsub!(/(CONFIG\[".+"\] = )"gcc-(.*)-\d+"/, '\\1"\\2"') + [ + %r{ ?-I/home/linuxbrew/\.linuxbrew/opt/(?:glibc@[^ /]+|linux-headers@[^ /]+)/include}, + %r{ ?-L/home/linuxbrew/\.linuxbrew/opt/glibc@[^ /]+/lib}, + %r{ ?-B/home/linuxbrew/\.linuxbrew/opt/glibc@[^ /]+/lib}, + %r{ ?-Wl,-rpath-link=/home/linuxbrew/\.linuxbrew/opt/glibc@[^ /]+/lib}, + / -nostdinc/, + ].each { |pattern| s.gsub!(pattern, "") if pattern.match?(rbconfig) } # C++ compiler might have been disabled because we break it with glibc@* builds s.sub!(/(CONFIG\["CXX"\] = )"false"/, '\\1"c++"') if build.without? "yjit" end @@ -282,8 +293,8 @@ def test # Test gems that require portable dependency headers # These were failing before we included headers in the tarball # See: https://github.com/jdx/mise/discussions/7268#discussioncomment-15298593 - system testpath/"bin/gem", "install", "openssl" # requires openssl headers - system testpath/"bin/gem", "install", "psych" # requires libyaml headers + install_default_native_gem ruby, "openssl" # requires openssl headers + install_default_native_gem ruby, "psych" # requires libyaml headers # Test that gem upgrades work for bundled gems with executables # This was failing due to shell polyglot format not being detected by RubyGems diff --git a/Abstract/jdx-ruby-34.rb b/Abstract/jdx-ruby-34.rb index 2dbee8c..68b41fb 100644 --- a/Abstract/jdx-ruby-34.rb +++ b/Abstract/jdx-ruby-34.rb @@ -192,10 +192,21 @@ def install if OS.linux? # Don't restrict to a specific GCC compiler binary we used (e.g. gcc-5). inreplace lib/"ruby/#{abi_version}/#{abi_arch}/rbconfig.rb" do |s| + rbconfig = s.to_s s.gsub! ENV.cxx, "c++" s.gsub! ENV.cc, "cc" + s.gsub!(/(CONFIG\["CC"\] = )"gcc-\d+"/, '\\1"cc"') if rbconfig.match?(/CONFIG\["CC"\] = "gcc-\d+"/) + s.gsub!(/(CONFIG\["LDSHARED"\] = )"gcc-\d+/, '\\1"cc') if rbconfig.match?(/CONFIG\["LDSHARED"\] = "gcc-\d+/) + s.gsub!(/(CONFIG\["CXX"\] = )"g\+\+-\d+"/, '\\1"c++"') if rbconfig.match?(/CONFIG\["CXX"\] = "g\+\+-\d+"/) # Change e.g. `CONFIG["AR"] = "gcc-ar-11"` to `CONFIG["AR"] = "ar"` s.gsub!(/(CONFIG\[".+"\] = )"gcc-(.*)-\d+"/, '\\1"\\2"') + [ + %r{ ?-I/home/linuxbrew/\.linuxbrew/opt/(?:glibc@[^ /]+|linux-headers@[^ /]+)/include}, + %r{ ?-L/home/linuxbrew/\.linuxbrew/opt/glibc@[^ /]+/lib}, + %r{ ?-B/home/linuxbrew/\.linuxbrew/opt/glibc@[^ /]+/lib}, + %r{ ?-Wl,-rpath-link=/home/linuxbrew/\.linuxbrew/opt/glibc@[^ /]+/lib}, + / -nostdinc/, + ].each { |pattern| s.gsub!(pattern, "") if pattern.match?(rbconfig) } # C++ compiler might have been disabled because we break it with glibc@* builds s.sub!(/(CONFIG\["CXX"\] = )"false"/, '\\1"c++"') if build.without? "yjit" end @@ -285,8 +296,8 @@ def test # Test gems that require portable dependency headers # These were failing before we included headers in the tarball # See: https://github.com/jdx/mise/discussions/7268#discussioncomment-15298593 - system testpath/"bin/gem", "install", "openssl" # requires openssl headers - system testpath/"bin/gem", "install", "psych" # requires libyaml headers + install_default_native_gem ruby, "openssl" # requires openssl headers + install_default_native_gem ruby, "psych" # requires libyaml headers # Test that gem upgrades work for bundled gems with executables # This was failing due to shell polyglot format not being detected by RubyGems diff --git a/Abstract/jdx-ruby.rb b/Abstract/jdx-ruby.rb index 6844f25..8dc0b36 100644 --- a/Abstract/jdx-ruby.rb +++ b/Abstract/jdx-ruby.rb @@ -184,10 +184,21 @@ def install if OS.linux? # Don't restrict to a specific GCC compiler binary we used (e.g. gcc-5). inreplace lib/"ruby/#{abi_version}/#{abi_arch}/rbconfig.rb" do |s| + rbconfig = s.to_s s.gsub! ENV.cxx, "c++" s.gsub! ENV.cc, "cc" + s.gsub!(/(CONFIG\["CC"\] = )"gcc-\d+"/, '\\1"cc"') if rbconfig.match?(/CONFIG\["CC"\] = "gcc-\d+"/) + s.gsub!(/(CONFIG\["LDSHARED"\] = )"gcc-\d+/, '\\1"cc') if rbconfig.match?(/CONFIG\["LDSHARED"\] = "gcc-\d+/) + s.gsub!(/(CONFIG\["CXX"\] = )"g\+\+-\d+"/, '\\1"c++"') if rbconfig.match?(/CONFIG\["CXX"\] = "g\+\+-\d+"/) # Change e.g. `CONFIG["AR"] = "gcc-ar-11"` to `CONFIG["AR"] = "ar"` s.gsub!(/(CONFIG\[".+"\] = )"gcc-(.*)-\d+"/, '\\1"\\2"') + [ + %r{ ?-I/home/linuxbrew/\.linuxbrew/opt/(?:glibc@[^ /]+|linux-headers@[^ /]+)/include}, + %r{ ?-L/home/linuxbrew/\.linuxbrew/opt/glibc@[^ /]+/lib}, + %r{ ?-B/home/linuxbrew/\.linuxbrew/opt/glibc@[^ /]+/lib}, + %r{ ?-Wl,-rpath-link=/home/linuxbrew/\.linuxbrew/opt/glibc@[^ /]+/lib}, + / -nostdinc/, + ].each { |pattern| s.gsub!(pattern, "") if pattern.match?(rbconfig) } # C++ compiler might have been disabled because we break it with glibc@* builds s.sub!(/(CONFIG\["CXX"\] = )"false"/, '\\1"c++"') if build.without? "yjit" end @@ -277,8 +288,8 @@ def test # Test gems that require portable dependency headers # These were failing before we included headers in the tarball # See: https://github.com/jdx/mise/discussions/7268#discussioncomment-15298593 - system testpath/"bin/gem", "install", "openssl" # requires openssl headers - system testpath/"bin/gem", "install", "psych" # requires libyaml headers + install_default_native_gem ruby, "openssl" # requires openssl headers + install_default_native_gem ruby, "psych" # requires libyaml headers # Test that gem upgrades work for bundled gems with executables # This was failing due to shell polyglot format not being detected by RubyGems diff --git a/Abstract/portable-formula.rb b/Abstract/portable-formula.rb index 9d3ee2b..e14642b 100644 --- a/Abstract/portable-formula.rb +++ b/Abstract/portable-formula.rb @@ -45,8 +45,27 @@ def install end def test - refute_match(/Homebrew libraries/, - shell_output("#{HOMEBREW_BREW_FILE} linkage #{full_name}")) + linkage_output = shell_output("#{HOMEBREW_BREW_FILE} linkage #{full_name}") + if OS.linux? + homebrew_libraries = [] + in_homebrew_libraries = false + linkage_output.each_line do |line| + if line.chomp == "Homebrew libraries:" + in_homebrew_libraries = true + homebrew_libraries.clear + next + end + next unless in_homebrew_libraries + break unless line.start_with?(" ") + + homebrew_libraries << line + end + + unexpected_libraries = homebrew_libraries.reject { |line| line.match?(/\((?:gcc|glibc)\)\s*\z/) } + assert_empty unexpected_libraries, "Unexpected Homebrew linkage:\n#{unexpected_libraries.join}" + else + refute_match(/Homebrew libraries/, linkage_output) + end super end @@ -85,16 +104,24 @@ def patch_rbconfig_for_portable_native_gems(abi_version, abi_arch) # Prefer the relocated portable Ruby prefix when building native gems. # This lets mkmf find headers, static libraries, and pkg-config files # copied into the package even when PKG_CONFIG_PATH is not set. + # mkmf reads MAKEFILE_CONFIG, while callers often inspect CONFIG. module RbConfig portable_prefix = File.expand_path("..", File.dirname(RbConfig.ruby)) portable_include = File.join(portable_prefix, "include") portable_lib = File.join(portable_prefix, "lib") portable_pkgconfig = File.join(portable_lib, "pkgconfig") - - CONFIG["CPPFLAGS"] = "-I#{portable_include} #{CONFIG["CPPFLAGS"]}" - CONFIG["LDFLAGS"] = "-L#{portable_lib} #{CONFIG["LDFLAGS"]}" - CONFIG["DLDFLAGS"] = "-L#{portable_lib} #{CONFIG["DLDFLAGS"]}" - CONFIG["PKG_CONFIG_PATH"] = [portable_pkgconfig, CONFIG["PKG_CONFIG_PATH"]] + portable_cppflags = "-include stdbool.h -I#{portable_include}" + + [CONFIG, MAKEFILE_CONFIG].each do |config| + config["CPPFLAGS"] = "#{portable_cppflags} #{config["CPPFLAGS"]}" + config["LDFLAGS"] = "-L#{portable_lib} #{config["LDFLAGS"]}" + config["DLDFLAGS"] = "-L#{portable_lib} #{config["DLDFLAGS"]}" + config["PKG_CONFIG_PATH"] = [portable_pkgconfig, config["PKG_CONFIG_PATH"]] + .compact + .reject(&:empty?) + .join(File::PATH_SEPARATOR) + end + ENV["PKG_CONFIG_PATH"] = [portable_pkgconfig, ENV["PKG_CONFIG_PATH"]] .compact .reject(&:empty?) .join(File::PATH_SEPARATOR) @@ -102,6 +129,17 @@ module RbConfig RUBY end end + + def install_default_native_gem(ruby, gem_name) + version = shell_output("#{ruby} -r#{gem_name} -e 'puts Gem.loaded_specs.fetch(#{gem_name.dump}).version'").chomp + system Pathname(ruby).dirname/"gem", "install", gem_name, "--version", version, "--force" + rescue + Dir[Pathname(ruby).dirname.parent/"lib/ruby/gems/*/extensions/**/#{gem_name}-#{version}/mkmf.log"].each do |log| + ohai log + puts File.read(log) + end + raise + end end class PortableFormula < Formula diff --git a/bin/setup b/bin/setup index c363a95..23fe97d 100755 --- a/bin/setup +++ b/bin/setup @@ -3,9 +3,16 @@ set -euo pipefail set -x DIRNAME="$(basename "$(pwd)")" +TAP_DIR="$(brew --repo)/Library/Taps/jdx/homebrew-$DIRNAME" + if brew tap | grep -F "jdx/$DIRNAME"; then brew untap "jdx/$DIRNAME" --force fi brew tap "jdx/$DIRNAME" . -rm -rf "$(brew --repo)/Library/Taps/jdx/homebrew-$DIRNAME" -ln -sf "$(pwd)" "$(brew --repo)/Library/Taps/jdx/homebrew-$DIRNAME" +rm -rf "$TAP_DIR" +if [[ "${CI:-}" == "true" ]]; then + mkdir -p "$TAP_DIR" + git archive HEAD | tar -x -C "$TAP_DIR" +else + ln -sf "$(pwd)" "$TAP_DIR" +fi diff --git a/cmd/jdx-package.rb b/cmd/jdx-package.rb index 6a4b14a..ca21b55 100755 --- a/cmd/jdx-package.rb +++ b/cmd/jdx-package.rb @@ -51,9 +51,6 @@ def run deps = Dependency.expand(Formula[name], cache_key: "jdx-package-#{name}") do |_dependent, dep| next Dependable::PRUNE if dep.test? || dep.optional? next Dependable::PRUNE if dep.name == "rustup" && args.without_yjit? - if !args.without_yjit? && (dep.name.start_with?("glibc@") || dep.name == "linux-headers@4.4") - next Dependable::PRUNE - end next unless bottled_dep_allowlist.match?(dep.name) @@ -64,10 +61,13 @@ def run puts "Bottled deps: #{bottled_deps.inspect}" puts "Other deps: #{deps.inspect}" - safe_system HOMEBREW_BREW_FILE, "install", *verbose, *bottled_deps if bottled_deps.any? + install_flags = ["install"] + install_flags << "--skip-post-install" if OS.linux? + + safe_system HOMEBREW_BREW_FILE, *install_flags, *verbose, *bottled_deps if bottled_deps.any? # Build bottles for all other dependencies. - safe_system HOMEBREW_BREW_FILE, "install", "--build-bottle", *verbose, *deps if deps.any? + safe_system HOMEBREW_BREW_FILE, *install_flags, "--build-bottle", *verbose, *deps if deps.any? # Build the main bottle safe_system HOMEBREW_BREW_FILE, "install", "--build-bottle", *name_flags, *verbose, name # Uninstall the dependencies we linked in