Module: OS::Mac Private

Defined in:
Library/Homebrew/os/mac.rb,
Library/Homebrew/os/linux.rb,
Library/Homebrew/cask/macos.rb,
Library/Homebrew/os/mac/sdk.rb,
Library/Homebrew/os/mac/xcode.rb,
Library/Homebrew/os/mac/version.rb,
Library/Homebrew/os/mac/xquartz.rb

Overview

This module is part of a private API. You should avoid using this module if possible, as it may be removed or be changed in the future.

rubocop:disable Style/Documentation

Defined Under Namespace

Modules: CLT, Xcode Classes: CLTSDKLocator, SDK, Version, XcodeSDKLocator

Constant Summary collapse

X11 =

This constant is part of a private API. You should avoid using this constant if possible, as it may be removed or be changed in the future.

rubocop:disable Style/MutableConstant

XQuartz = Module.new do # rubocop:disable Style/MutableConstant
  module_function

  DEFAULT_BUNDLE_PATH = Pathname.new("Applications/Utilities/XQuartz.app").freeze
  FORGE_BUNDLE_ID = "org.macosforge.xquartz.X11"
  FORGE_PKG_ID = "org.macosforge.xquartz.pkg"

  PKGINFO_VERSION_MAP = {
    "2.6.34"  => "2.6.3",
    "2.7.4"   => "2.7.0",
    "2.7.14"  => "2.7.1",
    "2.7.28"  => "2.7.2",
    "2.7.32"  => "2.7.3",
    "2.7.43"  => "2.7.4",
    "2.7.50"  => "2.7.5_rc1",
    "2.7.51"  => "2.7.5_rc2",
    "2.7.52"  => "2.7.5_rc3",
    "2.7.53"  => "2.7.5_rc4",
    "2.7.54"  => "2.7.5",
    "2.7.61"  => "2.7.6",
    "2.7.73"  => "2.7.7",
    "2.7.86"  => "2.7.8",
    "2.7.94"  => "2.7.9",
    "2.7.108" => "2.7.10",
    "2.7.112" => "2.7.11",
  }.freeze

  # This returns the version number of XQuartz, not of the upstream X.org.
  # The X11.app distributed by Apple is also XQuartz, and therefore covered
  # by this method.
  def version
    if @version ||= detect_version
      ::Version.new @version
    else
      ::Version::NULL
    end
  end

  def detect_version
    if (path = bundle_path) && path.exist? && (version = version_from_mdls(path))
      version
    else
      version_from_pkgutil
    end
  end

  def minimum_version
    # Update this a little later than latest_version to give people
    # time to upgrade.
    "2.7.11"
  end

  # - https://xquartz.macosforge.org/trac/wiki
  # - https://xquartz.macosforge.org/trac/wiki/Releases
  def latest_version
    "2.7.11"
  end

  def bundle_path
    # Use the default location if it exists.
    return DEFAULT_BUNDLE_PATH if DEFAULT_BUNDLE_PATH.exist?

    # Ask Spotlight where XQuartz is. If the user didn't install XQuartz
    # in the conventional place, this is our only option.
    MacOS.app_with_bundle_id(FORGE_BUNDLE_ID)
  end

  def version_from_mdls(path)
    version = Utils.popen_read(
      "/usr/bin/mdls", "-raw", "-nullMarker", "", "-name", "kMDItemVersion", path.to_s
    ).strip
    version unless version.empty?
  end

  # Upstream XQuartz *does* have a pkg-info entry, so if we can't get it
  # from mdls, we can try pkgutil. This is very slow.
  def version_from_pkgutil
    str = MacOS.pkgutil_info(FORGE_PKG_ID)[/version: (\d\.\d\.\d+)$/, 1]
    PKGINFO_VERSION_MAP.fetch(str, str)
  end

  # This should really be private, but for compatibility reasons it must
  # remain public. New code should use `MacOS::X11.bin`, `MacOS::X11.lib` and
  # `MacOS::X11.include` instead, as that accounts for Xcode-only systems.
  def prefix
    @prefix ||= Pathname.new("/opt/X11") if Pathname.new("/opt/X11/lib/libpng.dylib").exist?
  end

  def installed?
    !version.null? && !prefix.nil?
  end

  def outdated?
    return false unless installed?

    version < latest_version
  end

  def bin
    prefix/"bin"
  end

  def include
    prefix/"include"
  end

  def lib
    prefix/"lib"
  end

  def share
    prefix/"share"
  end
end

Class Method Summary collapse

Class Method Details

.active_developer_dirObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


79
80
81
# File 'Library/Homebrew/os/mac.rb', line 79

def active_developer_dir
  @active_developer_dir ||= Utils.popen_read("/usr/bin/xcode-select", "-print-path").strip
end

.app_with_bundle_id(*ids) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


177
178
179
180
181
182
# File 'Library/Homebrew/os/mac.rb', line 177

def app_with_bundle_id(*ids)
  path = mdfind(*ids)
         .reject { |p| p.include?("/Backups.backupdb/") }
         .first
  Pathname.new(path) unless path.nil? || path.empty?
end

.full_versionObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This can be compared to numerics, strings, or symbols using the standard Ruby Comparable methods.


30
31
32
# File 'Library/Homebrew/os/mac.rb', line 30

def full_version
  @full_version ||= Version.new((ENV["HOMEBREW_MACOS_VERSION"] || ENV["HOMEBREW_OSX_VERSION"]).chomp)
end

.full_version=(version) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


34
35
36
37
# File 'Library/Homebrew/os/mac.rb', line 34

def full_version=(version)
  @full_version = Version.new(version.chomp)
  @version = nil
end

.languageObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


75
76
77
# File 'Library/Homebrew/os/mac.rb', line 75

def language
  languages.first
end

.languagesObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


62
63
64
65
66
67
68
69
70
71
72
73
# File 'Library/Homebrew/os/mac.rb', line 62

def languages
  return @languages if @languages

  os_langs = Utils.popen_read("defaults", "read", "-g", "AppleLanguages")
  if os_langs.blank?
    # User settings don't exist so check the system-wide one.
    os_langs = Utils.popen_read("defaults", "read", "/Library/Preferences/.GlobalPreferences", "AppleLanguages")
  end
  os_langs = os_langs.scan(/[^ \n"(),]+/)

  @languages = os_langs
end

.latest_sdk_versionObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


39
40
41
42
# File 'Library/Homebrew/os/mac.rb', line 39

def latest_sdk_version
  # TODO: bump version when new Xcode macOS SDK is released
  Version.new "10.15"
end

.latest_stable_versionObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


44
45
46
47
48
49
# File 'Library/Homebrew/os/mac.rb', line 44

def latest_stable_version
  # TODO: bump version when new macOS is released and also update
  # references in docs/Installation.md and
  # https://github.com/Homebrew/install/blob/HEAD/install
  Version.new "10.15"
end

.macports_or_finkObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

See these issues for some history:


148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
# File 'Library/Homebrew/os/mac.rb', line 148

def macports_or_fink
  paths = []

  # First look in the path because MacPorts is relocatable and Fink
  # may become relocatable in the future.
  %w[port fink].each do |ponk|
    path = which(ponk)
    paths << path unless path.nil?
  end

  # Look in the standard locations, because even if port or fink are
  # not in the path they can still break builds if the build scripts
  # have these paths baked in.
  %w[/sw/bin/fink /opt/local/bin/port].each do |ponk|
    path = Pathname.new(ponk)
    paths << path if path.exist?
  end

  # Finally, some users make their MacPorts or Fink directories
  # read-only in order to try out Homebrew, but this doesn't work as
  # some build scripts error out when trying to read from these now
  # unreadable paths.
  %w[/sw /opt/local].map { |p| Pathname.new(p) }.each do |path|
    paths << path if path.exist? && !path.readable?
  end

  paths.uniq
end

.mdfind(*ids) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


184
185
186
187
188
# File 'Library/Homebrew/os/mac.rb', line 184

def mdfind(*ids)
  (@mdfind ||= {}).fetch(ids) do
    @mdfind[ids] = Utils.popen_read("/usr/bin/mdfind", mdfind_query(*ids)).split("\n")
  end
end

.mdfind_query(*ids) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


196
197
198
# File 'Library/Homebrew/os/mac.rb', line 196

def mdfind_query(*ids)
  ids.map! { |id| "kMDItemCFBundleIdentifier == #{id}" }.join(" || ")
end

.outdated_release?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)

51
52
53
54
55
56
# File 'Library/Homebrew/os/mac.rb', line 51

def outdated_release?
  # TODO: bump version when new macOS is released and also update
  # references in docs/Installation.md and
  # https://github.com/Homebrew/install/blob/HEAD/install
  version < "10.13"
end

.pkgutil_info(id) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


190
191
192
193
194
# File 'Library/Homebrew/os/mac.rb', line 190

def pkgutil_info(id)
  (@pkginfo ||= {}).fetch(id) do |key|
    @pkginfo[key] = Utils.popen_read("/usr/sbin/pkgutil", "--pkg-info", key).strip
  end
end

.prerelease?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)

58
59
60
# File 'Library/Homebrew/os/mac.rb', line 58

def prerelease?
  version > latest_stable_version
end

.sdk(v = nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


112
113
114
# File 'Library/Homebrew/os/mac.rb', line 112

def sdk(v = nil)
  sdk_locator.sdk_if_applicable(v)
end

.sdk_for_formula(f, v = nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


116
117
118
119
120
121
# File 'Library/Homebrew/os/mac.rb', line 116

def sdk_for_formula(f, v = nil)
  # If the formula requires Xcode, don't return the CLT SDK
  return Xcode.sdk if f.requirements.any? { |req| req.is_a? XcodeRequirement }

  sdk(v)
end

.sdk_locatorObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

If a specific SDK is requested:

  1. The requested SDK is returned, if it's installed.
  2. If the requested SDK is not installed, the newest SDK (if any SDKs are available) is returned.
  3. If no SDKs are available, nil is returned.

If no specific SDK is requested, the SDK matching the OS version is returned, if available. Otherwise, the latest SDK is returned.


104
105
106
107
108
109
110
# File 'Library/Homebrew/os/mac.rb', line 104

def sdk_locator
  if CLT.installed? && CLT.provides_sdk?
    CLT.sdk_locator
  else
    Xcode.sdk_locator
  end
end

.sdk_pathObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns the path to an SDK or nil, following the rules set by sdk.


124
125
126
127
# File 'Library/Homebrew/os/mac.rb', line 124

def sdk_path(v = nil)
  s = sdk(v)
  s&.path
end

.sdk_path_if_needed(_v = nil) ⇒ Object

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.


129
130
131
132
133
134
135
136
137
138
139
140
141
# File 'Library/Homebrew/os/mac.rb', line 129

def sdk_path_if_needed(v = nil)
  # Prefer CLT SDK when both Xcode and the CLT are installed.
  # Expected results:
  # 1. On Xcode-only systems, return the Xcode SDK.
  # 2. On Xcode-and-CLT systems where headers are provided by the system, return nil.
  # 3. On CLT-only systems with no CLT SDK, return nil.
  # 4. On CLT-only systems with a CLT SDK, where headers are provided by the system, return nil.
  # 5. On CLT-only systems with a CLT SDK, where headers are not provided by the system, return the CLT SDK.

  return unless sdk_root_needed?

  sdk_path(v)
end

.sdk_root_needed?Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)

83
84
85
86
87
88
89
90
91
92
# File 'Library/Homebrew/os/mac.rb', line 83

def sdk_root_needed?
  if MacOS::CLT.installed?
    # If there's no CLT SDK, return false
    return false unless MacOS::CLT.provides_sdk?
    # If the CLT is installed and headers are provided by the system, return false
    return false unless MacOS::CLT.separate_header_package?
  end

  true
end

.system_dir?(dir) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)

386
387
388
# File 'Library/Homebrew/cask/macos.rb', line 386

def system_dir?(dir)
  SYSTEM_DIRS.include?(Pathname.new(dir).expand_path)
end

.undeletable?(path) ⇒ Boolean

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

Returns:

  • (Boolean)

390
391
392
# File 'Library/Homebrew/cask/macos.rb', line 390

def undeletable?(path)
  UNDELETABLE_PATHS.include?(Pathname.new(path).expand_path)
end

.versionObject

This method is part of a private API. You should avoid using this method if possible, as it may be removed or be changed in the future.

This can be compared to numerics, strings, or symbols using the standard Ruby Comparable methods.


24
25
26
# File 'Library/Homebrew/os/mac.rb', line 24

def version
  @version ||= Version.new(full_version.to_s[/^\d+\.\d+/])
end