Module: OS::Mac::Xcode Private

Defined in:
os/mac/xcode.rb

Overview

This module is part of a private API. This module may only be used in the Homebrew/brew repository. Third parties should avoid using this module if possible, as it may be removed or changed without warning.

Helper module for querying Xcode information.

Constant Summary collapse

DEFAULT_BUNDLE_PATH =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

::Pathname.new("/Applications/Xcode.app").freeze
BUNDLE_ID =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

"com.apple.dt.Xcode"
OLD_BUNDLE_ID =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

"com.apple.Xcode"
APPLE_DEVELOPER_DOWNLOAD_URL =

This constant is part of a private API. This constant may only be used in the Homebrew/brew repository. Third parties should avoid using this constant if possible, as it may be removed or changed without warning.

"https://developer.apple.com/download/all/"

Class Method Summary collapse

Class Method Details

.below_minimum_version? ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:

  • (Boolean)


57
58
59
60
61
# File 'os/mac/xcode.rb', line 57

def self.below_minimum_version?
  return false unless installed?

  version < minimum_version
end

.bundle_path ⇒ ::Pathname?

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



110
111
112
113
114
115
116
117
118
# File 'os/mac/xcode.rb', line 110

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

  # Ask Spotlight where Xcode is. If the user didn't install the
  # helper tools and installed Xcode in a non-conventional place, this
  # is our only option. See: https://superuser.com/questions/390757
  MacOS.app_with_bundle_id(BUNDLE_ID, OLD_BUNDLE_ID)
end

.default_prefix? ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:

  • (Boolean)


212
213
214
# File 'os/mac/xcode.rb', line 212

def self.default_prefix?
  prefix.to_s == "/Applications/Xcode.app/Contents/Developer"
end

.detect_version ⇒ String?

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
# File 'os/mac/xcode.rb', line 181

def self.detect_version
  # This is a separate function as you can't cache the value out of a block
  # if return is used in the middle, which we do many times in here.
  if (xcode_prefix = prefix)
    # Fast path that will probably almost always work unless `xcode-select -p` is misconfigured
    version_plist = xcode_prefix.parent/"version.plist"
    if version_plist.file?
      require "plist"
      data = Plist.parse_xml(version_plist, marshal: false)
      version = data["CFBundleShortVersionString"] if data
      return version if version
    end

    %W[
      #{prefix}/usr/bin/xcodebuild
      #{which("xcodebuild")}
    ].uniq.each do |xcodebuild_path|
      next unless File.executable? xcodebuild_path

      xcodebuild_output = Utils.popen_read(xcodebuild_path, "-version")
      next unless $CHILD_STATUS.success?

      xcode_version = xcodebuild_output[/Xcode (\d+(?:\.\d+)*)/, 1]
      return xcode_version if xcode_version
    end
  end

  nil
end

.installation_instructions ⇒ String

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



141
142
143
144
145
146
147
148
149
150
151
152
# File 'os/mac/xcode.rb', line 141

def self.installation_instructions
  if OS::Mac.version.prerelease?
    <<~EOS
      Xcode can be installed from:
        #{Formatter.url(APPLE_DEVELOPER_DOWNLOAD_URL)}
    EOS
  else
    <<~EOS
      Xcode can be installed from the App Store.
    EOS
  end
end

.installed? ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:

  • (Boolean)


121
122
123
# File 'os/mac/xcode.rb', line 121

def self.installed?
  !prefix.nil?
end

.latest_sdk_version? ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:

  • (Boolean)


64
65
66
# File 'os/mac/xcode.rb', line 64

def self.latest_sdk_version?
  OS::Mac.full_version >= OS::Mac.latest_sdk_version
end

.latest_version(macos: MacOS.version, arm64: ::Hardware::CPU.physical_cpu_arm64?) ⇒ String

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Bump these when a new version is available from the App Store and our CI systems have been updated. This may be a beta version for a beta macOS. Pass both macos and arm64 when querying a different system.

Parameters:

  • macos (MacOSVersion) (defaults to: MacOS.version)
  • arm64 (Boolean) (defaults to: ::Hardware::CPU.physical_cpu_arm64?)

Returns:



20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
# File 'os/mac/xcode.rb', line 20

def self.latest_version(macos: MacOS.version, arm64: ::Hardware::CPU.physical_cpu_arm64?)
  macos = macos.strip_patch
  case macos
  when "27" then "27.0"
  when "26" then arm64 ? "27.0" : "26.6"
  when "15" then "26.3"
  when "14" then "16.2"
  when "13" then "15.2"
  when "12" then "14.2"
  when "11" then "13.2.1"
  else
    raise "macOS '#{macos}' is invalid" unless macos.prerelease?

    # Assume matching yearly Xcode release
    "#{macos}.0"
  end
end

.minimum_version ⇒ String

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Bump these if things are badly broken (e.g. no SDK for this macOS) without this. Generally this will be the first Xcode release on that macOS version (which may initially be a beta if that version of macOS is also in beta).

Returns:



43
44
45
46
47
48
49
50
51
52
53
54
# File 'os/mac/xcode.rb', line 43

def self.minimum_version
  macos = MacOS.version
  case macos
  when "15" then "16.0"
  when "14" then "15.0"
  when "13" then "14.1"
  when "12" then "13.1"
  when "11" then "12.2"
  else
    "#{macos}.0"
  end
end

.needs_clt_installed? ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:

  • (Boolean)


69
70
71
72
73
# File 'os/mac/xcode.rb', line 69

def self.needs_clt_installed?
  return false if latest_sdk_version?

  without_clt?
end

.outdated? ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:

  • (Boolean)


76
77
78
79
80
# File 'os/mac/xcode.rb', line 76

def self.outdated?
  return false unless installed?

  version < latest_version
end

.prefix ⇒ ::Pathname?

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns a Pathname object corresponding to Xcode.app's Developer directory or nil if Xcode.app is not installed.

Returns:



90
91
92
93
94
95
96
97
98
99
100
101
102
# File 'os/mac/xcode.rb', line 90

def self.prefix
  @prefix ||= T.let(begin
    dir = MacOS.active_developer_dir

    if dir.empty? || dir == CLT::PKG_PATH || !File.directory?(dir)
      path = bundle_path
      path/"Contents/Developer" if path
    else
      # Use cleanpath to avoid pathological trailing slash
      ::Pathname.new(dir).cleanpath
    end
  end, T.nilable(::Pathname))
end

.sdk(version = nil) ⇒ SDK?

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:



131
132
133
# File 'os/mac/xcode.rb', line 131

def self.sdk(version = nil)
  sdk_locator.sdk_if_applicable(version)
end

.sdk_locator ⇒ XcodeSDKLocator

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



126
127
128
# File 'os/mac/xcode.rb', line 126

def self.sdk_locator
  @sdk_locator ||= T.let(XcodeSDKLocator.new, T.nilable(OS::Mac::XcodeSDKLocator))
end

.sdk_path(version = nil) ⇒ ::Pathname?

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Parameters:

Returns:



136
137
138
# File 'os/mac/xcode.rb', line 136

def self.sdk_path(version = nil)
  sdk(version)&.path
end

.toolchain_path ⇒ ::Pathname

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



105
106
107
# File 'os/mac/xcode.rb', line 105

def self.toolchain_path
  Pathname("#{prefix}/Toolchains/XcodeDefault.xctoolchain")
end

.update_instructions ⇒ String

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:



155
156
157
158
159
160
161
162
163
164
165
166
# File 'os/mac/xcode.rb', line 155

def self.update_instructions
  if OS::Mac.version.prerelease?
    <<~EOS
      Xcode can be updated from:
        #{Formatter.url(APPLE_DEVELOPER_DOWNLOAD_URL)}
    EOS
  else
    <<~EOS
      Xcode can be updated from the App Store.
    EOS
  end
end

.version ⇒ ::Version

This method is part of an internal API. This method may only be used internally in repositories owned by Homebrew, except in casks or formulae. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Get the Xcode version.

Returns:



172
173
174
175
176
177
178
# File 'os/mac/xcode.rb', line 172

def self.version
  if @version ||= T.let(detect_version, T.nilable(String))
    ::Version.new @version
  else
    ::Version::NULL
  end
end

.without_clt? ⇒ Boolean

This method is part of a private API. This method may only be used in the Homebrew/brew repository. Third parties should avoid using this method if possible, as it may be removed or changed without warning.

Returns:

  • (Boolean)


83
84
85
# File 'os/mac/xcode.rb', line 83

def self.without_clt?
  !MacOS::CLT.installed?
end