| 1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
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
176
177
178
179
180
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
 | module OS
  module Mac
    module Xcode
      extend self
      V4_BUNDLE_ID = "com.apple.dt.Xcode"
      V3_BUNDLE_ID = "com.apple.Xcode"
      V4_BUNDLE_PATH = Pathname.new("/Applications/Xcode.app")
      # Locate the "current Xcode folder" via xcode-select. See:
      # man xcode-select
      # NOTE!! use Xcode.prefix rather than this generally!
      def folder
        @folder ||= `xcode-select -print-path 2>/dev/null`.strip
      end
      # Xcode 4.3 tools hang if "/" is set
      def bad_xcode_select_path?
        folder == "/"
      end
      def latest_version
        case MacOS.version
        when "10.4"         then "2.5"
        when "10.5"         then "3.1.4"
        when "10.6"         then "3.2.6"
        when "10.7"         then "4.6.3"
        when "10.8"         then "5.1"
        when "10.9"         then "5.1"
        else
          # Default to newest known version of Xcode for unreleased OSX versions.
          if MacOS.version > "10.9"
            "5.1"
          else
            raise "Mac OS X '#{MacOS.version}' is invalid"
          end
        end
      end
      def outdated?
        version < latest_version
      end
      def without_clt?
        installed? && version >= "4.3" && !MacOS::CLT.installed?
      end
      def prefix
        @prefix ||= begin
          path = Pathname.new(folder)
          if path != CLT::MAVERICKS_PKG_PATH and path.absolute? \
             and File.executable? "#{path}/usr/bin/make"
            path
          # TODO remove this branch when 10.10 is released
          elsif File.executable? "#{V4_BUNDLE_PATH}/Contents/Developer/usr/bin/make"
            # fallback for broken Xcode 4.3 installs
            Pathname.new("#{V4_BUNDLE_PATH}/Contents/Developer")
          elsif (path = bundle_path)
            path += "Contents/Developer"
            path if File.executable? "#{path}/usr/bin/make"
          end
        end
      end
      # 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: http://superuser.com/questions/390757
      def bundle_path
        MacOS.app_with_bundle_id(V4_BUNDLE_ID, V3_BUNDLE_ID)
      end
      def installed?
        not prefix.nil?
      end
      def version
        # 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.
        @version ||= uncached_version
      end
      def uncached_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.
        return "0" unless OS.mac?
        # this shortcut makes version work for people who don't realise you
        # need to install the CLI tools
        # TODO investigate and update the above comment; what does this mean
        # for modern installs (Xcode 5 on 10.8/9)?
        xcode43build = Pathname.new("#{prefix}/usr/bin/xcodebuild")
        if xcode43build.file?
          `#{xcode43build} -version 2>/dev/null` =~ /Xcode (\d(\.\d)*)/
          return $1 if $1
        end
        xcodebuild = which "xcodebuild"
        raise unless xcodebuild && xcodebuild != xcode43build
        `xcodebuild -version 2>/dev/null` =~ /Xcode (\d(\.\d)*)/
        raise if $1.nil? or not $?.success?
        $1
      rescue
        # For people who's xcode-select is unset, or who have installed
        # xcode-gcc-installer or whatever other combinations we can try and
        # supprt. See https://github.com/Homebrew/homebrew/wiki/Xcode
        case MacOS.llvm_build_version.to_i
        when 1..2063 then "3.1.0"
        when 2064..2065 then "3.1.4"
        when 2366..2325
          # we have no data for this range so we are guessing
          "3.2.0"
        when 2326
          # also applies to "3.2.3"
          "3.2.4"
        when 2327..2333 then "3.2.5"
        when 2335
          # this build number applies to 3.2.6, 4.0 and 4.1
          # https://github.com/Homebrew/homebrew/wiki/Xcode
          "4.0"
        else
          case (MacOS.clang_version.to_f * 10).to_i
          when 0       then "dunno"
          when 1..14   then "3.2.2"
          when 15      then "3.2.4"
          when 16      then "3.2.5"
          when 17..20  then "4.0"
          when 21      then "4.1"
          when 22..30  then "4.2"
          when 31      then "4.3"
          when 40      then "4.4"
          when 41      then "4.5"
          when 42      then "4.6"
          when 50      then "5.0"
          when 51      then "5.1"
          else "5.1"
          end
        end
      end
      def provides_autotools?
        version < "4.3"
      end
      def provides_gcc?
        version < "4.3"
      end
      def provides_cvs?
        version < "5.0"
      end
      def default_prefix?
        if version < "4.3"
          %r{^/Developer} === prefix
        else
          %r{^/Applications/Xcode.app} === prefix
        end
      end
    end
    module CLT
      extend self
      STANDALONE_PKG_ID = "com.apple.pkg.DeveloperToolsCLILeo"
      FROM_XCODE_PKG_ID = "com.apple.pkg.DeveloperToolsCLI"
      MAVERICKS_PKG_ID = "com.apple.pkg.CLTools_Executables"
      MAVERICKS_PKG_PATH = Pathname.new("/Library/Developer/CommandLineTools")
      # Returns true even if outdated tools are installed, e.g.
      # tools from Xcode 4.x on 10.9
      def installed?
        !!detect_version
      end
      def latest_version
        if MacOS.version >= "10.8"
          "503.0.38"
        else
          "425.0.28"
        end
      end
      def outdated?
        version = `/usr/bin/clang --version`[%r{clang-(\d+\.\d+\.\d+)}, 1]
        return true unless version
        version < latest_version
      end
      # Version string (a pretty long one) of the CLT package.
      # Note, that different ways to install the CLTs lead to different
      # version numbers.
      def version
        @version ||= detect_version
      end
      def detect_version
        [MAVERICKS_PKG_ID, STANDALONE_PKG_ID, FROM_XCODE_PKG_ID].find do |id|
          version = MacOS.pkgutil_info(id)[/version: (.+)$/, 1]
          return version if version
        end
      end
    end
  end
end
 |