diff --git a/Library/Homebrew/cmd/gist-logs.rb b/Library/Homebrew/cmd/gist-logs.rb index dba4217ee27fa..829cec0c4b410 100644 --- a/Library/Homebrew/cmd/gist-logs.rb +++ b/Library/Homebrew/cmd/gist-logs.rb @@ -73,9 +73,7 @@ def gistify_logs(formula, args:) EOS end - if args.new_issue? - url = GitHub.create_issue(formula.tap, "#{formula.name} failed to build on #{MacOS.full_version}", url) - end + url = GitHub.create_issue(formula.tap, "#{formula.name} failed to build on #{OS_VERSION}", url) if args.new_issue? puts url if url end diff --git a/Library/Homebrew/extend/os/linkage_checker.rb b/Library/Homebrew/extend/os/linkage_checker.rb index 3e8904e5eb0ef..175e201324b0e 100644 --- a/Library/Homebrew/extend/os/linkage_checker.rb +++ b/Library/Homebrew/extend/os/linkage_checker.rb @@ -1,4 +1,8 @@ # typed: strict # frozen_string_literal: true -require "extend/os/linux/linkage_checker" if OS.linux? +if OS.mac? + require "extend/os/mac/linkage_checker" +else + require "extend/os/linux/linkage_checker" +end diff --git a/Library/Homebrew/extend/os/mac/formula.rb b/Library/Homebrew/extend/os/mac/formula.rb index 0d65ad84fee9c..a5dbd1684c62c 100644 --- a/Library/Homebrew/extend/os/mac/formula.rb +++ b/Library/Homebrew/extend/os/mac/formula.rb @@ -3,9 +3,31 @@ class Formula undef valid_platform? + undef std_cmake_args sig { returns(T::Boolean) } def valid_platform? requirements.none?(LinuxRequirement) end + + sig { + params( + install_prefix: T.any(String, Pathname), + install_libdir: T.any(String, Pathname), + find_framework: String, + ).returns(T::Array[String]) + } + def std_cmake_args(install_prefix: prefix, install_libdir: "lib", find_framework: "LAST") + args = generic_std_cmake_args(install_prefix: install_prefix, install_libdir: install_libdir, + find_framework: find_framework) + + # Avoid false positives for clock_gettime support on 10.11. + # CMake cache entries for other weak symbols may be added here as needed. + args << "-DHAVE_CLOCK_GETTIME:INTERNAL=0" if MacOS.version == "10.11" && MacOS::Xcode.version >= "8.0" + + # Ensure CMake is using the same SDK we are using. + args << "-DCMAKE_OSX_SYSROOT=#{MacOS.sdk_for_formula(self).path}" if MacOS.sdk_root_needed? + + args + end end diff --git a/Library/Homebrew/extend/os/mac/linkage_checker.rb b/Library/Homebrew/extend/os/mac/linkage_checker.rb new file mode 100644 index 0000000000000..9baba42a7d250 --- /dev/null +++ b/Library/Homebrew/extend/os/mac/linkage_checker.rb @@ -0,0 +1,13 @@ +# typed: true +# frozen_string_literal: true + +class LinkageChecker + undef system_libraries_exist_in_cache? + + private + + def system_libraries_exist_in_cache? + # In macOS Big Sur and later, system libraries do not exist on-disk and instead exist in a cache. + MacOS.version >= :big_sur + end +end diff --git a/Library/Homebrew/formula.rb b/Library/Homebrew/formula.rb index 1b04c8484d2f4..2ab55beccdccb 100644 --- a/Library/Homebrew/formula.rb +++ b/Library/Homebrew/formula.rb @@ -1646,7 +1646,7 @@ def std_cargo_args(root: prefix, path: ".") ).returns(T::Array[String]) } def std_cmake_args(install_prefix: prefix, install_libdir: "lib", find_framework: "LAST") - args = %W[ + %W[ -DCMAKE_INSTALL_PREFIX=#{install_prefix} -DCMAKE_INSTALL_LIBDIR=#{install_libdir} -DCMAKE_BUILD_TYPE=Release @@ -1655,16 +1655,8 @@ def std_cmake_args(install_prefix: prefix, install_libdir: "lib", find_framework -Wno-dev -DBUILD_TESTING=OFF ] - - # Avoid false positives for clock_gettime support on 10.11. - # CMake cache entries for other weak symbols may be added here as needed. - args << "-DHAVE_CLOCK_GETTIME:INTERNAL=0" if MacOS.version == "10.11" && MacOS::Xcode.version >= "8.0" - - # Ensure CMake is using the same SDK we are using. - args << "-DCMAKE_OSX_SYSROOT=#{MacOS.sdk_for_formula(self).path}" if MacOS.sdk_root_needed? - - args end + alias generic_std_cmake_args std_cmake_args # Standard parameters for Go builds. sig { diff --git a/Library/Homebrew/linkage_checker.rb b/Library/Homebrew/linkage_checker.rb index b3b00bbeb36ed..d3befd6423ab7 100644 --- a/Library/Homebrew/linkage_checker.rb +++ b/Library/Homebrew/linkage_checker.rb @@ -196,9 +196,8 @@ def check_dylibs(rebuild_cache:) if (dep = dylib_to_dep(dylib)) @broken_deps[dep] |= [dylib] - elsif MacOS.version >= :big_sur && dylib_found_via_dlopen(dylib) + elsif system_libraries_exist_in_cache? && dylib_found_via_dlopen(dylib) # If we cannot associate the dylib with a dependency, then it may be a system library. - # In macOS Big Sur and later, system libraries do not exist on-disk and instead exist in a cache. # If dlopen finds the dylib, then the linkage is not broken. @system_dylibs << dylib elsif !system_framework?(dylib) @@ -227,6 +226,11 @@ def check_dylibs(rebuild_cache:) end alias generic_check_dylibs check_dylibs + def system_libraries_exist_in_cache? + false + end + alias generic_system_libraries_exist_in_cache? system_libraries_exist_in_cache? + def dylib_found_via_dlopen(dylib) Fiddle.dlopen(dylib).close true diff --git a/Library/Homebrew/os/linux.rb b/Library/Homebrew/os/linux.rb index e813f1f13bee0..877255268def7 100644 --- a/Library/Homebrew/os/linux.rb +++ b/Library/Homebrew/os/linux.rb @@ -55,49 +55,60 @@ module Mac raise "Loaded OS::Linux on generic OS!" if ENV["HOMEBREW_TEST_GENERIC_OS"] def self.version + # odeprecated "`MacOS.version` on Linux" MacOSVersion::NULL end def self.full_version + # odeprecated "`MacOS.full_version` on Linux" MacOSVersion::NULL end def self.languages + # odeprecated "`MacOS.languages` on Linux" @languages ||= Array(ENV["LANG"]&.slice(/[a-z]+/)).uniq end def self.language + # odeprecated "`MacOS.language` on Linux" languages.first end def self.sdk_root_needed? + # odeprecated "`MacOS.sdk_root_needed?` on Linux" false end def self.sdk_path_if_needed(_version = nil) + # odeprecated "`MacOS.sdk_path_if_needed` on Linux" nil end def self.sdk_path(_version = nil) + # odeprecated "`MacOS.sdk_path` on Linux" nil end module Xcode def self.version + # odeprecated "`MacOS::Xcode.version` on Linux" ::Version::NULL end def self.installed? + # odeprecated "`MacOS::Xcode.installed?` on Linux" false end end module CLT def self.version + # odeprecated "`MacOS::CLT.version` on Linux" ::Version::NULL end def self.installed? + # odeprecated "`MacOS::CLT.installed?` on Linux" false end end diff --git a/Library/Homebrew/os/mac.rb b/Library/Homebrew/os/mac.rb index 84a1775947eb3..ed9a875e04627 100644 --- a/Library/Homebrew/os/mac.rb +++ b/Library/Homebrew/os/mac.rb @@ -7,6 +7,9 @@ require "os/mac/sdk" require "os/mac/keg" +# TODO: remove this once the `MacOS` module is undefined on Linux +require "simulate_system" + module OS # Helper module for querying system information on macOS. module Mac @@ -21,6 +24,7 @@ module Mac # using the standard Ruby Comparable methods. sig { returns(MacOSVersion) } def self.version + # odeprecated "`MacOS.version` on Linux" if Homebrew::SimulateSystem.simulating_or_running_on_linux? @version ||= full_version.strip_patch end @@ -28,6 +32,7 @@ def self.version # using the standard Ruby Comparable methods. sig { returns(MacOSVersion) } def self.full_version + # odeprecated "`MacOS.full_version` on Linux" if Homebrew::SimulateSystem.simulating_or_running_on_linux? @full_version ||= if ENV["HOMEBREW_FAKE_EL_CAPITAN"] # for Portable Ruby building MacOSVersion.new("10.11.6") else @@ -60,6 +65,7 @@ def self.preferred_perl_version end def self.languages + # odeprecated "`MacOS.languages` on Linux" if Homebrew::SimulateSystem.simulating_or_running_on_linux? return @languages if @languages os_langs = Utils.popen_read("defaults", "read", "-g", "AppleLanguages") @@ -73,6 +79,7 @@ def self.languages end def self.language + # odeprecated "`MacOS.language` on Linux" if Homebrew::SimulateSystem.simulating_or_running_on_linux? languages.first end @@ -83,6 +90,7 @@ def self.active_developer_dir sig { returns(T::Boolean) } def self.sdk_root_needed? + # odeprecated "`MacOS.sdk_root_needed?` on Linux" if Homebrew::SimulateSystem.simulating_or_running_on_linux? if MacOS::CLT.installed? # If there's no CLT SDK, return false return false unless MacOS::CLT.provides_sdk? @@ -131,11 +139,13 @@ def self.sdk_for_formula(formula, version = nil, check_only_runtime_requirements # Returns the path to an SDK or nil, following the rules set by {sdk}. def self.sdk_path(version = nil) + # odeprecated "`MacOS.sdk_path` on Linux" if Homebrew::SimulateSystem.simulating_or_running_on_linux? s = sdk(version) s&.path end def self.sdk_path_if_needed(version = nil) + # odeprecated "`MacOS.sdk_path_if_needed` on Linux" if Homebrew::SimulateSystem.simulating_or_running_on_linux? # Prefer CLT SDK when both Xcode and the CLT are installed. # Expected results: # 1. On Xcode-only systems, return the Xcode SDK. diff --git a/Library/Homebrew/os/mac/xcode.rb b/Library/Homebrew/os/mac/xcode.rb index 9402bf5d6e11d..e589d2d670c8e 100755 --- a/Library/Homebrew/os/mac/xcode.rb +++ b/Library/Homebrew/os/mac/xcode.rb @@ -126,6 +126,7 @@ def self.bundle_path sig { returns(T::Boolean) } def self.installed? + # odeprecated "`MacOS::Xcode.installed?` on Linux" if Homebrew::SimulateSystem.simulating_or_running_on_linux? !prefix.nil? end @@ -174,6 +175,7 @@ def self.update_instructions sig { returns(::Version) } def self.version + # odeprecated "`MacOS::Xcode.version` on Linux" if Homebrew::SimulateSystem.simulating_or_running_on_linux? # may return a version string # that is guessed based on the compiler, so do not # use it in order to check if Xcode is installed. @@ -264,6 +266,7 @@ module CLT # Returns true even if outdated tools are installed. sig { returns(T::Boolean) } def self.installed? + # odeprecated "`MacOS::CLT.installed?` on Linux" if Homebrew::SimulateSystem.simulating_or_running_on_linux? !version.null? end @@ -397,6 +400,7 @@ def self.detect_version_from_clang_version # version numbers. sig { returns(::Version) } def self.version + # odeprecated "`MacOS::CLT.version` on Linux" if Homebrew::SimulateSystem.simulating_or_running_on_linux? if @version ||= detect_version ::Version.new @version else diff --git a/Library/Homebrew/readall.rb b/Library/Homebrew/readall.rb index bfe714e02d9f6..8faa67554a6fa 100644 --- a/Library/Homebrew/readall.rb +++ b/Library/Homebrew/readall.rb @@ -11,6 +11,10 @@ module Readall class << self include Cachable + # TODO: remove this once the `MacOS` module is undefined on Linux + MACOS_MODULE_REGEX = /\b(MacOS|OS::Mac)(\.|::)\b/.freeze + private_constant :MACOS_MODULE_REGEX + private :cache def valid_ruby_syntax?(ruby_files) @@ -59,7 +63,9 @@ def valid_formulae?(tap, bottle_tag: nil) flags: [], ignore_errors: false) readall_formula = readall_formula_class.new(formula_name, file, :stable, tap: tap) readall_formula.to_hash - cache[:valid_formulae][file] = if readall_formula.on_system_blocks_exist? + # TODO: Remove check for MACOS_MODULE_REGEX once the `MacOS` module is undefined on Linux + cache[:valid_formulae][file] = if readall_formula.on_system_blocks_exist? || + formula_contents.match?(MACOS_MODULE_REGEX) [bottle_tag, *cache[:valid_formulae][file]] else true diff --git a/Library/Homebrew/rubocops/lines.rb b/Library/Homebrew/rubocops/lines.rb index e44623e4595f4..fbb5c8208186a 100644 --- a/Library/Homebrew/rubocops/lines.rb +++ b/Library/Homebrew/rubocops/lines.rb @@ -441,6 +441,21 @@ def audit_formula(_node, _class_node, _parent_class_node, body_node) end end + # This cop makes sure the `MacOS` module is not used in Linux-facing formula code + # + # @api private + class MacOSOnLinux < FormulaCop + include OnSystemConditionalsHelper + + ON_MACOS_BLOCKS = [:macos, *MACOS_VERSION_OPTIONS].map { |os| :"on_#{os}" }.freeze + + def audit_formula(_node, _class_node, _parent_class_node, body_node) + audit_macos_references(body_node, + allowed_methods: OnSystemConditionals::NO_ON_SYSTEM_METHOD_NAMES, + allowed_blocks: OnSystemConditionals::NO_ON_SYSTEM_BLOCK_NAMES + ON_MACOS_BLOCKS) + end + end + # This cop makes sure that the `generate_completions_from_executable` DSL is used. # # @api private diff --git a/Library/Homebrew/rubocops/shared/on_system_conditionals_helper.rb b/Library/Homebrew/rubocops/shared/on_system_conditionals_helper.rb index c6e0142afb1d4..bee4847630a5a 100644 --- a/Library/Homebrew/rubocops/shared/on_system_conditionals_helper.rb +++ b/Library/Homebrew/rubocops/shared/on_system_conditionals_helper.rb @@ -17,6 +17,7 @@ module OnSystemConditionalsHelper BASE_OS_OPTIONS = [:macos, :linux].freeze MACOS_VERSION_OPTIONS = MacOSVersion::SYMBOLS.keys.freeze ON_SYSTEM_OPTIONS = [*ARCH_OPTIONS, *BASE_OS_OPTIONS, *MACOS_VERSION_OPTIONS, :system].freeze + MACOS_MODULE_NAMES = ["MacOS", "OS::Mac"].freeze MACOS_VERSION_CONDITIONALS = { "==" => nil, @@ -82,7 +83,7 @@ def audit_arch_conditionals(body_node, allowed_methods: [], allowed_blocks: []) ARCH_OPTIONS.each do |arch_option| else_method = (arch_option == :arm) ? :on_intel : :on_arm if_arch_node_search(body_node, arch: :"#{arch_option}?") do |if_node, else_node| - next if if_node_is_allowed?(if_node, allowed_methods: allowed_methods, allowed_blocks: allowed_blocks) + next if node_is_allowed?(if_node, allowed_methods: allowed_methods, allowed_blocks: allowed_blocks) if_statement_problem(if_node, "if Hardware::CPU.#{arch_option}?", "on_#{arch_option}", else_method: else_method, else_node: else_node) @@ -93,7 +94,7 @@ def audit_arch_conditionals(body_node, allowed_methods: [], allowed_blocks: []) hardware_cpu_search(body_node, method: method) do |method_node| # These should already be caught by `if_arch_node_search` next if method_node.parent.source.start_with? "if #{method_node.source}" - next if if_node_is_allowed?(method_node, allowed_methods: allowed_methods, allowed_blocks: allowed_blocks) + next if node_is_allowed?(method_node, allowed_methods: allowed_methods, allowed_blocks: allowed_blocks) offending_node(method_node) problem "Don't use `#{method_node.source}`, use `on_arm` and `on_intel` blocks instead." @@ -109,7 +110,7 @@ def audit_base_os_conditionals(body_node, allowed_methods: [], allowed_blocks: [ [:linux?, :on_macos] end if_base_os_node_search(body_node, base_os: os_method) do |if_node, else_node| - next if if_node_is_allowed?(if_node, allowed_methods: allowed_methods, allowed_blocks: allowed_blocks) + next if node_is_allowed?(if_node, allowed_methods: allowed_methods, allowed_blocks: allowed_blocks) if_statement_problem(if_node, "if OS.#{os_method}", "on_#{base_os_option}", else_method: else_method, else_node: else_node) @@ -121,7 +122,7 @@ def audit_macos_version_conditionals(body_node, allowed_methods: [], allowed_blo recommend_on_system: true) MACOS_VERSION_OPTIONS.each do |macos_version_option| if_macos_version_node_search(body_node, os_version: macos_version_option) do |if_node, operator, else_node| - next if if_node_is_allowed?(if_node, allowed_methods: allowed_methods, allowed_blocks: allowed_blocks) + next if node_is_allowed?(if_node, allowed_methods: allowed_methods, allowed_blocks: allowed_blocks) autocorrect = else_node.blank? && MACOS_VERSION_CONDITIONALS.key?(operator.to_s) on_system_method_string = if recommend_on_system && operator == :< @@ -141,7 +142,7 @@ def audit_macos_version_conditionals(body_node, allowed_methods: [], allowed_blo macos_version_comparison_search(body_node, os_version: macos_version_option) do |method_node| # These should already be caught by `if_macos_version_node_search` next if method_node.parent.source.start_with? "if #{method_node.source}" - next if if_node_is_allowed?(method_node, allowed_methods: allowed_methods, allowed_blocks: allowed_blocks) + next if node_is_allowed?(method_node, allowed_methods: allowed_methods, allowed_blocks: allowed_blocks) offending_node(method_node) problem "Don't use `#{method_node.source}`, use `on_{macos_version}` blocks instead." @@ -149,6 +150,17 @@ def audit_macos_version_conditionals(body_node, allowed_methods: [], allowed_blo end end + def audit_macos_references(body_node, allowed_methods: [], allowed_blocks: []) + MACOS_MODULE_NAMES.each do |macos_module_name| + find_const(body_node, macos_module_name) do |node| + next if node_is_allowed?(node, allowed_methods: allowed_methods, allowed_blocks: allowed_blocks) + + offending_node(node) + problem "Don't use `#{macos_module_name}` where it could be called on Linux." + end + end + end + private def if_statement_problem(if_node, if_statement_string, on_system_method_string, @@ -170,10 +182,10 @@ def if_statement_problem(if_node, if_statement_string, on_system_method_string, end end - def if_node_is_allowed?(if_node, allowed_methods: [], allowed_blocks: []) + def node_is_allowed?(node, allowed_methods: [], allowed_blocks: []) # TODO: check to see if it's legal valid = T.let(false, T::Boolean) - if_node.each_ancestor do |ancestor| + node.each_ancestor do |ancestor| valid_method_names = case ancestor.type when :def allowed_methods diff --git a/Library/Homebrew/test/rubocops/text/macos_on_linux_spec.rb b/Library/Homebrew/test/rubocops/text/macos_on_linux_spec.rb new file mode 100644 index 0000000000000..c72c2395feed1 --- /dev/null +++ b/Library/Homebrew/test/rubocops/text/macos_on_linux_spec.rb @@ -0,0 +1,127 @@ +# frozen_string_literal: true + +require "rubocops/lines" + +describe RuboCop::Cop::FormulaAudit::MacOSOnLinux do + subject(:cop) { described_class.new } + + it "reports an offense when `MacOS` is used in the `Formula` class" do + expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo < Formula + desc "foo" + if MacOS::Xcode.version >= "12.0" + ^^^^^ FormulaAudit/MacOSOnLinux: Don't use `MacOS` where it could be called on Linux. + url 'https://brew.sh/linux-1.0.tgz' + end + end + RUBY + end + + it "reports an offense when `MacOS` is used in a `resource` block" do + expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo < Formula + desc "foo" + url 'https://brew.sh/linux-1.0.tgz' + + resource "foo" do + url "https://brew.sh/linux-1.0.tgz" if MacOS::full_version >= "12.0" + ^^^^^ FormulaAudit/MacOSOnLinux: Don't use `MacOS` where it could be called on Linux. + end + end + RUBY + end + + it "reports an offense when `MacOS` is used in an `on_linux` block" do + expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo < Formula + desc "foo" + on_linux do + if MacOS::Xcode.version >= "12.0" + ^^^^^ FormulaAudit/MacOSOnLinux: Don't use `MacOS` where it could be called on Linux. + url 'https://brew.sh/linux-1.0.tgz' + end + end + end + RUBY + end + + it "reports an offense when `MacOS` is used in an `on_arm` block" do + expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo < Formula + desc "foo" + on_arm do + if MacOS::Xcode.version >= "12.0" + ^^^^^ FormulaAudit/MacOSOnLinux: Don't use `MacOS` where it could be called on Linux. + url 'https://brew.sh/linux-1.0.tgz' + end + end + end + RUBY + end + + it "reports an offense when `MacOS` is used in an `on_intel` block" do + expect_offense(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo < Formula + desc "foo" + on_intel do + if MacOS::Xcode.version >= "12.0" + ^^^^^ FormulaAudit/MacOSOnLinux: Don't use `MacOS` where it could be called on Linux. + url 'https://brew.sh/linux-1.0.tgz' + end + end + end + RUBY + end + + it "reports no offenses when `MacOS` is used in an `on_macos` block" do + expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo < Formula + desc "foo" + on_macos do + if MacOS::Xcode.version >= "12.0" + url 'https://brew.sh/linux-1.0.tgz' + end + end + end + RUBY + end + + it "reports no offenses when `MacOS` is used in an `on_ventura` block" do + expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo < Formula + desc "foo" + on_ventura :or_older do + if MacOS::Xcode.version >= "12.0" + url 'https://brew.sh/linux-1.0.tgz' + end + end + end + RUBY + end + + it "reports no offenses when `MacOS` is used in the `install` method" do + expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo < Formula + desc "foo" + url 'https://brew.sh/linux-1.0.tgz' + + def install + MacOS.version + end + end + RUBY + end + + it "reports no offenses when `MacOS` is used in the `test` block" do + expect_no_offenses(<<~RUBY, "/homebrew-core/Formula/foo.rb") + class Foo < Formula + desc "foo" + url 'https://brew.sh/linux-1.0.tgz' + + test do + MacOS.version + end + end + RUBY + end +end diff --git a/Library/Homebrew/utils/curl.rb b/Library/Homebrew/utils/curl.rb index 5f594cc8df07a..8973d43d3c4f3 100644 --- a/Library/Homebrew/utils/curl.rb +++ b/Library/Homebrew/utils/curl.rb @@ -320,12 +320,7 @@ def curl_check_http_content(url, url_type, specs: {}, user_agents: [:default], r break if http_status_ok?(details[:status_code]) end - unless details[:status_code] - # Hack around https://github.com/Homebrew/brew/issues/3199 - return if MacOS.version == :el_capitan - - return "The #{url_type} #{url} is not reachable" - end + return "The #{url_type} #{url} is not reachable" unless details[:status_code] unless http_status_ok?(details[:status_code]) return if details[:responses].any? do |response|