From 0a6b7b2756268f93c737f4421705d7ca889a2075 Mon Sep 17 00:00:00 2001 From: Claudia Date: Fri, 13 Nov 2020 09:38:29 +0100 Subject: [PATCH] Check flavour to prevent mixed architectures MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit **What it does** - Prevent mixing-and-matching architectures in a single Homebrew installation by asserting the current architecture to be equal to the saved Homebrew flavour. - Give clear and useful guidance when called with the wrong architecture. **Rationale** - We must keep users from mixing-and-matching architectures in a single Homebrew installation, be it deliberately or accidentally, even if the user has chosen a non-standard installation location. - While Homebrew already compares prefixes at formula install time, the check at hand is more general and works with non-standard install locations. **How it’s implemented** - The flavour of the Homebrew installation is stored in the Git config variable `homebrew.flavour`. - Possible values of `homebrew.flavour` are `arm64`, `x86_64` and not set. - `homebrew.flavour` being not set means that Homebrew either couldn’t determine the flavour yet or that it isn’t running under macOS. **Compatibility** - Work with non-standard install locations. This is implemented through picking the current architecture on the initial `brew update` invocation, then persisting it as a Git config value on the Homebrew repository. - Work with our alternative installation method, where the directory tree is typically not a Git repository at install time. - Works as expected when running on OSes other than macOS. - Works on older macOS versions, which do not have the `CFBundleIsArchitectureLoadable` yet. - Works as expected when running on Intel-only Macs. - Only check formula installation, uninstallation and upgrading. **Performance considerations** - Do not try to detect Rosetta 2 unless absolutely necessary (i.e. only while preparing an error message). --- Library/Homebrew/brew.sh | 4 ++ Library/Homebrew/cmd/update.sh | 12 +++++ Library/Homebrew/install.rb | 49 ++++++++++++++----- .../Homebrew/utils/rosetta_installed.swift | 4 ++ 4 files changed, 57 insertions(+), 12 deletions(-) create mode 100644 Library/Homebrew/utils/rosetta_installed.swift diff --git a/Library/Homebrew/brew.sh b/Library/Homebrew/brew.sh index d0b15e080fe30..326a272f83ee0 100644 --- a/Library/Homebrew/brew.sh +++ b/Library/Homebrew/brew.sh @@ -23,7 +23,9 @@ if [[ -n "$HOMEBREW_MACOS" ]] then HOMEBREW_DEFAULT_CACHE="${HOME}/Library/Caches/Homebrew" HOMEBREW_DEFAULT_LOGS="${HOME}/Library/Logs/Homebrew" + HOMEBREW_DEFAULT_PREFIX="/usr/local" HOMEBREW_DEFAULT_TEMP="/private/tmp" + HOMEBREW_MACOS_ARM_DEFAULT_PREFIX="/opt/homebrew" else CACHE_HOME="${XDG_CACHE_HOME:-${HOME}/.cache}" HOMEBREW_DEFAULT_CACHE="${CACHE_HOME}/Homebrew" @@ -429,6 +431,8 @@ export HOMEBREW_MACOS_VERSION_NUMERIC export HOMEBREW_USER_AGENT export HOMEBREW_USER_AGENT_CURL export HOMEBREW_BOTTLE_DEFAULT_DOMAIN +export HOMEBREW_DEFAULT_PREFIX +export HOMEBREW_MACOS_ARM_DEFAULT_PREFIX if [[ -n "$HOMEBREW_MACOS" && -x "/usr/bin/xcode-select" ]] then diff --git a/Library/Homebrew/cmd/update.sh b/Library/Homebrew/cmd/update.sh index 49baaaa4ede9b..ec1ac12721284 100644 --- a/Library/Homebrew/cmd/update.sh +++ b/Library/Homebrew/cmd/update.sh @@ -45,6 +45,18 @@ git_init_if_necessary() { trap - EXIT fi + if [[ -n "${HOMEBREW_MACOS_VERSION}" ]] && + [[ -z "$(git config --get homebrew.flavour 2>/dev/null)" ]] + then + if [[ "${HOMEBREW_PREFIX}" = "${HOMEBREW_MACOS_ARM_DEFAULT_PREFIX}" ]]; then + git config --replace-all homebrew.flavour "arm64" 2>/dev/null + elif [[ "${HOMEBREW_PREFIX}" = "${HOMEBREW_DEFAULT_PREFIX}" ]]; then + git config --replace-all homebrew.flavour "x86_64" 2>/dev/null + else + git config --replace-all homebrew.flavour "$(uname -m)" 2>/dev/null + fi + fi + [[ -d "$HOMEBREW_LIBRARY/Taps/homebrew/homebrew-core" ]] || return safe_cd "$HOMEBREW_LIBRARY/Taps/homebrew/homebrew-core" if [[ ! -d ".git" ]] diff --git a/Library/Homebrew/install.rb b/Library/Homebrew/install.rb index 155a4809966cd..14748f5b4de4e 100644 --- a/Library/Homebrew/install.rb +++ b/Library/Homebrew/install.rb @@ -14,7 +14,7 @@ module Install module_function def perform_preinstall_checks(all_fatal: false, cc: nil) - check_prefix + check_flavour_matches_architecture check_cpu attempt_directory_creation check_cc_argv(cc) @@ -29,19 +29,44 @@ def perform_build_from_source_checks(all_fatal: false) Diagnostic.checks(:build_from_source_checks, fatal: all_fatal) end - def check_prefix - if Hardware::CPU.intel? && HOMEBREW_PREFIX.to_s == HOMEBREW_MACOS_ARM_DEFAULT_PREFIX - odie "Cannot install in Homebrew on Intel processor in ARM default prefix (#{HOMEBREW_PREFIX})!" - elsif Hardware::CPU.arm? && HOMEBREW_PREFIX.to_s == HOMEBREW_DEFAULT_PREFIX - odie <<~EOS - Cannot install in Homebrew on ARM processor in Intel default prefix (#{HOMEBREW_PREFIX})! - Please create a new installation in #{HOMEBREW_MACOS_ARM_DEFAULT_PREFIX} using one of the - "Alternative Installs" from: - #{Formatter.url("https://docs.brew.sh/Installation")} - You can migrate your previously installed formula list with: - brew bundle dump + def check_flavour_matches_architecture + homebrew_flavour = HOMEBREW_REPOSITORY.cd do + Utils.popen_read("git", "config", "--get", "homebrew.flavour").chomp.presence + end + + if !homebrew_flavour || + (Hardware::CPU.intel? && homebrew_flavour == "x86_64") || + (Hardware::CPU.arm? && homebrew_flavour == "arm64") + return + end + + error_title = "This Homebrew installation in #{HOMEBREW_PREFIX} only works on #{homebrew_flavour} processors." + odie error_title unless Hardware::CPU.arm? + + ohai "Checking whether Rosetta is installed" + rosetta_installed = quiet_system( + "/usr/bin/swift", + "#{HOMEBREW_REPOSITORY}/Library/Homebrew/utils/rosetta_installed.swift", + ) + rosetta_instructions = if rosetta_installed + <<~EOS + To use it, you can: + - run your Terminal from Rosetta 2 or + - run 'arch -x86_64 brew' instead of 'brew'. EOS + else + "To use it, you need to install Rosetta 2 first." end + + odie <<~EOS + #{error_title} + #{rosetta_instructions} + Or create a new installation in #{HOMEBREW_MACOS_ARM_DEFAULT_PREFIX} using one of the + "Alternative Installs" from: + #{Formatter.url("https://docs.brew.sh/Installation")} + You can migrate your previously installed formula list with: + brew bundle dump + EOS end def check_cpu diff --git a/Library/Homebrew/utils/rosetta_installed.swift b/Library/Homebrew/utils/rosetta_installed.swift new file mode 100644 index 0000000000000..480baba90ddb1 --- /dev/null +++ b/Library/Homebrew/utils/rosetta_installed.swift @@ -0,0 +1,4 @@ +#!/usr/bin/swift + +import CoreFoundation +exit(CFBundleIsArchitectureLoadable(CPU_TYPE_X86_64) ? 0 : 1)