archruby 0.2.0 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (47) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +3 -0
  3. data/README.md +1 -1
  4. data/Rakefile +46 -0
  5. data/TODO.rtf +20 -0
  6. data/architecture.yml +51 -0
  7. data/archruby.gemspec +3 -0
  8. data/bin/archruby +1 -0
  9. data/lib/archruby.rb +14 -2
  10. data/lib/archruby/architecture/architecture.rb +6 -6
  11. data/lib/archruby/architecture/config_definition.rb +13 -13
  12. data/lib/archruby/architecture/constraint_break.rb +1 -1
  13. data/lib/archruby/architecture/dependency.rb +3 -1
  14. data/lib/archruby/architecture/file_content.rb +2 -2
  15. data/lib/archruby/architecture/module_definition.rb +33 -20
  16. data/lib/archruby/architecture/parser.rb +25 -11
  17. data/lib/archruby/architecture/type_inference_checker.rb +29 -13
  18. data/lib/archruby/presenters/dsm.rb +163 -0
  19. data/lib/archruby/presenters/dsm/cell_dsm.rb +17 -0
  20. data/lib/archruby/presenters/dsm/dsm_css.css +77 -0
  21. data/lib/archruby/presenters/graph.rb +12 -9
  22. data/lib/archruby/ruby/parser.rb +131 -125
  23. data/lib/archruby/ruby/type_inference/dependency_organizer.rb +53 -0
  24. data/lib/archruby/ruby/type_inference/ruby/class_dependency.rb +22 -0
  25. data/lib/archruby/ruby/type_inference/ruby/internal_method_invocation.rb +20 -0
  26. data/lib/archruby/ruby/type_inference/ruby/method_definition.rb +20 -0
  27. data/lib/archruby/ruby/type_inference/ruby/parser_for_typeinference.rb +537 -0
  28. data/lib/archruby/ruby/type_inference/ruby/process_method_arguments.rb +155 -0
  29. data/lib/archruby/ruby/type_inference/ruby/process_method_body.rb +427 -0
  30. data/lib/archruby/ruby/type_inference/ruby/process_method_params.rb +276 -0
  31. data/lib/archruby/ruby/type_inference/type_inference_checker.rb +126 -0
  32. data/lib/archruby/version.rb +1 -1
  33. data/spec/architecture/file_content_spec.rb +2 -1
  34. data/spec/architecture/module_definition_spec.rb +9 -9
  35. data/spec/dummy_app/lib/teste_class.rb +6 -0
  36. data/spec/ruby/type_inference/dependency_organizer_spec.rb +20 -0
  37. data/spec/ruby/type_inference/fixtures/homebrew_bottles_class.rb +139 -0
  38. data/spec/ruby/type_inference/fixtures/homebrew_brew_teste.rb +1323 -0
  39. data/spec/ruby/type_inference/fixtures/rails_action_view_class_teste.rb +89 -0
  40. data/spec/ruby/type_inference/fixtures/rails_active_record_class.rb +99 -0
  41. data/spec/ruby/type_inference/fixtures/rails_teste_active_record.rb +55 -0
  42. data/spec/ruby/type_inference/fixtures/teste2.rb +16 -0
  43. data/spec/ruby/type_inference/fixtures/teste_class_and_args.rb +49 -0
  44. data/spec/ruby/type_inference/fixtures/teste_class_and_args2.rb +11 -0
  45. data/spec/ruby/type_inference/parser_for_typeinference_spec.rb +69 -0
  46. data/spec/ruby/type_inference/type_inference_checker_spec.rb +47 -0
  47. metadata +84 -3
@@ -0,0 +1,6 @@
1
+ module Teste
2
+ module Name
3
+ class Clazz
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe Archruby::Ruby::TypeInference::DependencyOrganizer do
4
+ before do
5
+ @fixtures_path = File.expand_path('../fixtures', __FILE__)
6
+ end
7
+
8
+ it "mantains the dependencies and methods invocations correctly" do
9
+ parser = Archruby::Ruby::TypeInference::Ruby::ParserForTypeinference.new
10
+ file_content = File.read("#{@fixtures_path}/rails_action_view_class_teste.rb")
11
+ dependencies, methods_calls = parser.parse(file_content)
12
+ dependency_organizer = Archruby::Ruby::TypeInference::DependencyOrganizer.new
13
+ dependency_organizer.add_dependencies(dependencies)
14
+ dependency_organizer.add_method_calls(methods_calls)
15
+ # acho que vou manter um hash com o nome da classe e o valor vai
16
+ # ser um set com todas as dependencias dessa classe
17
+
18
+ # Tenho que decidir o que vou fazer com as chamadas de metodo ainda
19
+ end
20
+ end
@@ -0,0 +1,139 @@
1
+ require "tab"
2
+ require "os/mac"
3
+ require "extend/ARGV"
4
+
5
+ def built_as_bottle?(f)
6
+ return false unless f.installed?
7
+ tab = Tab.for_keg(f.installed_prefix)
8
+ tab.built_as_bottle
9
+ end
10
+
11
+ def bottle_file_outdated?(f, file)
12
+ filename = file.basename.to_s
13
+ return unless f.bottle && filename.match(Pathname::BOTTLE_EXTNAME_RX)
14
+
15
+ bottle_ext = filename[bottle_native_regex, 1]
16
+ bottle_url_ext = f.bottle.url[bottle_native_regex, 1]
17
+
18
+ bottle_ext && bottle_url_ext && bottle_ext != bottle_url_ext
19
+ end
20
+
21
+ def bottle_native_regex
22
+ /(\.#{bottle_tag}\.bottle\.(\d+\.)?tar\.gz)$/o
23
+ end
24
+
25
+ def bottle_tag
26
+ if MacOS.version >= :lion
27
+ MacOS.cat
28
+ elsif MacOS.version == :snow_leopard
29
+ Hardware::CPU.is_64_bit? ? :snow_leopard : :snow_leopard_32
30
+ else
31
+ # Return, e.g., :tiger_g3, :leopard_g5_64, :leopard_64 (which is Intel)
32
+ if Hardware::CPU.type == :ppc
33
+ tag = "#{MacOS.cat}_#{Hardware::CPU.family}".to_sym
34
+ else
35
+ tag = MacOS.cat
36
+ end
37
+ MacOS.prefer_64_bit? ? "#{tag}_64".to_sym : tag
38
+ end
39
+ end
40
+
41
+ def bottle_receipt_path(bottle_file)
42
+ Utils.popen_read("tar", "-tzf", bottle_file, "*/*/INSTALL_RECEIPT.json").chomp
43
+ end
44
+
45
+ def bottle_resolve_formula_names(bottle_file)
46
+ receipt_file_path = bottle_receipt_path bottle_file
47
+ receipt_file = Utils.popen_read("tar", "-xOzf", bottle_file, receipt_file_path)
48
+ name = receipt_file_path.split("/").first
49
+ tap = Tab.from_file_content(receipt_file, "#{bottle_file}/#{receipt_file_path}").tap
50
+
51
+ if tap.nil? || tap == "Homebrew/homebrew"
52
+ full_name = name
53
+ else
54
+ full_name = "#{tap.sub("homebrew-", "")}/#{name}"
55
+ end
56
+
57
+ [name, full_name]
58
+ end
59
+
60
+ def bottle_resolve_version(bottle_file)
61
+ PkgVersion.parse bottle_receipt_path(bottle_file).split("/")[1]
62
+ end
63
+
64
+ class Bintray
65
+ def self.package(formula_name)
66
+ formula_name.to_s.tr("+", "x")
67
+ end
68
+
69
+ def self.repository(tap = nil)
70
+ return "bottles" if tap.nil? || tap == "Homebrew/homebrew"
71
+ "bottles-#{tap.sub(%r{^homebrew/(homebrew-)?}i, "")}"
72
+ end
73
+ end
74
+
75
+ class BottleCollector
76
+ def initialize
77
+ @checksums = {}
78
+ end
79
+
80
+ def fetch_checksum_for(tag)
81
+ tag = find_matching_tag(tag)
82
+ return self[tag], tag if tag
83
+ end
84
+
85
+ def keys
86
+ @checksums.keys
87
+ end
88
+
89
+ def [](key)
90
+ @checksums[key]
91
+ end
92
+
93
+ def []=(key, value)
94
+ @checksums[key] = value
95
+ end
96
+
97
+ def key?(key)
98
+ @checksums.key?(key)
99
+ end
100
+
101
+ private
102
+
103
+ def find_matching_tag(tag)
104
+ if key?(tag)
105
+ tag
106
+ else
107
+ find_altivec_tag(tag) || find_or_later_tag(tag)
108
+ end
109
+ end
110
+
111
+ # This allows generic Altivec PPC bottles to be supported in some
112
+ # formulae, while also allowing specific bottles in others; e.g.,
113
+ # sometimes a formula has just :tiger_altivec, other times it has
114
+ # :tiger_g4, :tiger_g5, etc.
115
+ def find_altivec_tag(tag)
116
+ if tag.to_s =~ /(\w+)_(g4|g4e|g5)$/
117
+ altivec_tag = "#{$1}_altivec".to_sym
118
+ altivec_tag if key?(altivec_tag)
119
+ end
120
+ end
121
+
122
+ # Allows a bottle tag to specify a specific OS or later,
123
+ # so the same bottle can target multiple OSs.
124
+ # Not used in core, used in taps.
125
+ def find_or_later_tag(tag)
126
+ begin
127
+ tag_version = MacOS::Version.from_symbol(tag)
128
+ rescue ArgumentError
129
+ return
130
+ end
131
+
132
+ keys.find do |key|
133
+ if key.to_s.end_with?("_or_later")
134
+ later_tag = key.to_s[/(\w+)_or_later$/, 1].to_sym
135
+ MacOS::Version.from_symbol(later_tag) <= tag_version
136
+ end
137
+ end
138
+ end
139
+ end
@@ -0,0 +1,1323 @@
1
+ require "cmd/missing"
2
+ require "formula"
3
+ require "keg"
4
+ require "language/python"
5
+ require "version"
6
+
7
+ class Volumes
8
+ def initialize
9
+ @volumes = get_mounts
10
+ end
11
+
12
+ def which(path)
13
+ vols = get_mounts path
14
+
15
+ # no volume found
16
+ if vols.empty?
17
+ return -1
18
+ end
19
+
20
+ vol_index = @volumes.index(vols[0])
21
+ # volume not found in volume list
22
+ if vol_index.nil?
23
+ return -1
24
+ end
25
+ vol_index
26
+ end
27
+
28
+ def get_mounts(path = nil)
29
+ vols = []
30
+ # get the volume of path, if path is nil returns all volumes
31
+
32
+ args = %w[/bin/df -P]
33
+ args << path if path
34
+
35
+ Utils.popen_read(*args) do |io|
36
+ io.each_line do |line|
37
+ case line.chomp
38
+ # regex matches: /dev/disk0s2 489562928 440803616 48247312 91% /
39
+ when /^.+\s+[0-9]+\s+[0-9]+\s+[0-9]+\s+[0-9]{1,3}%\s+(.+)/
40
+ vols << $1
41
+ end
42
+ end
43
+ end
44
+ vols
45
+ end
46
+ end
47
+
48
+ class Checks
49
+ ############# HELPERS
50
+ # Finds files in HOMEBREW_PREFIX *and* /usr/local.
51
+ # Specify paths relative to a prefix eg. "include/foo.h".
52
+ # Sets @found for your convenience.
53
+ def find_relative_paths(*relative_paths)
54
+ @found = %W[#{HOMEBREW_PREFIX} /usr/local].uniq.inject([]) do |found, prefix|
55
+ found + relative_paths.map { |f| File.join(prefix, f) }.select { |f| File.exist? f }
56
+ end
57
+ end
58
+
59
+ def inject_file_list(list, str)
60
+ list.inject(str) { |s, f| s << " #{f}\n" }
61
+ end
62
+
63
+ # Git will always be on PATH because of the wrapper script in
64
+ # Library/ENV/scm, so we check if there is a *real*
65
+ # git here to avoid multiple warnings.
66
+ def git?
67
+ return @git if instance_variable_defined?(:@git)
68
+ @git = system "git --version >/dev/null 2>&1"
69
+ end
70
+ ############# END HELPERS
71
+
72
+ # Sorry for the lack of an indent here, the diff would have been unreadable.
73
+ # See https://github.com/Homebrew/homebrew/pull/9986
74
+ def check_path_for_trailing_slashes
75
+ bad_paths = ENV["PATH"].split(File::PATH_SEPARATOR).select { |p| p[-1..-1] == "/" }
76
+ return if bad_paths.empty?
77
+ s = <<-EOS.undent
78
+ Some directories in your path end in a slash.
79
+ Directories in your path should not end in a slash. This can break other
80
+ doctor checks. The following directories should be edited:
81
+ EOS
82
+ bad_paths.each { |p| s << " #{p}" }
83
+ s
84
+ end
85
+
86
+ # Installing MacGPG2 interferes with Homebrew in a big way
87
+ # https://github.com/GPGTools/MacGPG2
88
+ def check_for_macgpg2
89
+ return if File.exist? "/usr/local/MacGPG2/share/gnupg/VERSION"
90
+
91
+ suspects = %w[
92
+ /Applications/start-gpg-agent.app
93
+ /Library/Receipts/libiconv1.pkg
94
+ /usr/local/MacGPG2
95
+ ]
96
+
97
+ if suspects.any? { |f| File.exist? f } then <<-EOS.undent
98
+ You may have installed MacGPG2 via the package installer.
99
+ Several other checks in this script will turn up problems, such as stray
100
+ dylibs in /usr/local and permissions issues with share and man in /usr/local/.
101
+ EOS
102
+ end
103
+ end
104
+
105
+ def __check_stray_files(dir, pattern, white_list, message)
106
+ return unless File.directory?(dir)
107
+
108
+ files = Dir.chdir(dir) do
109
+ Dir[pattern].select { |f| File.file?(f) && !File.symlink?(f) } - Dir.glob(white_list)
110
+ end.map { |file| File.join(dir, file) }
111
+
112
+ inject_file_list(files, message) unless files.empty?
113
+ end
114
+
115
+ def check_for_stray_dylibs
116
+ # Dylibs which are generally OK should be added to this list,
117
+ # with a short description of the software they come with.
118
+ white_list = [
119
+ "libfuse.2.dylib", # MacFuse
120
+ "libfuse_ino64.2.dylib", # MacFuse
121
+ "libmacfuse_i32.2.dylib", # OSXFuse MacFuse compatibility layer
122
+ "libmacfuse_i64.2.dylib", # OSXFuse MacFuse compatibility layer
123
+ "libosxfuse_i32.2.dylib", # OSXFuse
124
+ "libosxfuse_i64.2.dylib", # OSXFuse
125
+ "libTrAPI.dylib", # TrAPI / Endpoint Security VPN
126
+ "libntfs-3g.*.dylib", # NTFS-3G
127
+ "libntfs.*.dylib", # NTFS-3G
128
+ "libublio.*.dylib", # NTFS-3G
129
+ ]
130
+
131
+ __check_stray_files "/usr/local/lib", "*.dylib", white_list, <<-EOS.undent
132
+ Unbrewed dylibs were found in /usr/local/lib.
133
+ If you didn't put them there on purpose they could cause problems when
134
+ building Homebrew formulae, and may need to be deleted.
135
+
136
+ Unexpected dylibs:
137
+ EOS
138
+ end
139
+
140
+ def check_for_stray_static_libs
141
+ # Static libs which are generally OK should be added to this list,
142
+ # with a short description of the software they come with.
143
+ white_list = [
144
+ "libsecurity_agent_client.a", # OS X 10.8.2 Supplemental Update
145
+ "libsecurity_agent_server.a", # OS X 10.8.2 Supplemental Update
146
+ "libntfs-3g.a", # NTFS-3G
147
+ "libntfs.a", # NTFS-3G
148
+ "libublio.a", # NTFS-3G
149
+ ]
150
+
151
+ __check_stray_files "/usr/local/lib", "*.a", white_list, <<-EOS.undent
152
+ Unbrewed static libraries were found in /usr/local/lib.
153
+ If you didn't put them there on purpose they could cause problems when
154
+ building Homebrew formulae, and may need to be deleted.
155
+
156
+ Unexpected static libraries:
157
+ EOS
158
+ end
159
+
160
+ def check_for_stray_pcs
161
+ # Package-config files which are generally OK should be added to this list,
162
+ # with a short description of the software they come with.
163
+ white_list = [
164
+ "fuse.pc", # OSXFuse/MacFuse
165
+ "macfuse.pc", # OSXFuse MacFuse compatibility layer
166
+ "osxfuse.pc", # OSXFuse
167
+ "libntfs-3g.pc", # NTFS-3G
168
+ "libublio.pc", # NTFS-3G
169
+ ]
170
+
171
+ __check_stray_files "/usr/local/lib/pkgconfig", "*.pc", white_list, <<-EOS.undent
172
+ Unbrewed .pc files were found in /usr/local/lib/pkgconfig.
173
+ If you didn't put them there on purpose they could cause problems when
174
+ building Homebrew formulae, and may need to be deleted.
175
+
176
+ Unexpected .pc files:
177
+ EOS
178
+ end
179
+
180
+ def check_for_stray_las
181
+ white_list = [
182
+ "libfuse.la", # MacFuse
183
+ "libfuse_ino64.la", # MacFuse
184
+ "libosxfuse_i32.la", # OSXFuse
185
+ "libosxfuse_i64.la", # OSXFuse
186
+ "libntfs-3g.la", # NTFS-3G
187
+ "libntfs.la", # NTFS-3G
188
+ "libublio.la", # NTFS-3G
189
+ ]
190
+
191
+ __check_stray_files "/usr/local/lib", "*.la", white_list, <<-EOS.undent
192
+ Unbrewed .la files were found in /usr/local/lib.
193
+ If you didn't put them there on purpose they could cause problems when
194
+ building Homebrew formulae, and may need to be deleted.
195
+
196
+ Unexpected .la files:
197
+ EOS
198
+ end
199
+
200
+ def check_for_stray_headers
201
+ white_list = [
202
+ "fuse.h", # MacFuse
203
+ "fuse/**/*.h", # MacFuse
204
+ "macfuse/**/*.h", # OSXFuse MacFuse compatibility layer
205
+ "osxfuse/**/*.h", # OSXFuse
206
+ "ntfs/**/*.h", # NTFS-3G
207
+ "ntfs-3g/**/*.h", # NTFS-3G
208
+ ]
209
+
210
+ __check_stray_files "/usr/local/include", "**/*.h", white_list, <<-EOS.undent
211
+ Unbrewed header files were found in /usr/local/include.
212
+ If you didn't put them there on purpose they could cause problems when
213
+ building Homebrew formulae, and may need to be deleted.
214
+
215
+ Unexpected header files:
216
+ EOS
217
+ end
218
+
219
+ def check_for_other_package_managers
220
+ ponk = MacOS.macports_or_fink
221
+ unless ponk.empty?
222
+ <<-EOS.undent
223
+ You have MacPorts or Fink installed:
224
+ #{ponk.join(", ")}
225
+
226
+ This can cause trouble. You don't have to uninstall them, but you may want to
227
+ temporarily move them out of the way, e.g.
228
+
229
+ sudo mv /opt/local ~/macports
230
+ EOS
231
+ end
232
+ end
233
+
234
+ def check_for_broken_symlinks
235
+ broken_symlinks = []
236
+
237
+ Keg::PRUNEABLE_DIRECTORIES.each do |d|
238
+ next unless d.directory?
239
+ d.find do |path|
240
+ if path.symlink? && !path.resolved_path_exists?
241
+ broken_symlinks << path
242
+ end
243
+ end
244
+ end
245
+ unless broken_symlinks.empty? then <<-EOS.undent
246
+ Broken symlinks were found. Remove them with `brew prune`:
247
+ #{broken_symlinks * "\n "}
248
+ EOS
249
+ end
250
+ end
251
+
252
+ def check_for_unsupported_osx
253
+ if MacOS.version >= "10.11" then <<-EOS.undent
254
+ You are using OS X #{MacOS.version}.
255
+ We do not provide support for this pre-release version.
256
+ You may encounter build failures or other breakage.
257
+ EOS
258
+ end
259
+ end
260
+
261
+ # TODO: distill down into single method definition a la BuildToolsError
262
+ if MacOS.version >= "10.9"
263
+ def check_for_installed_developer_tools
264
+ unless MacOS::Xcode.installed? || MacOS::CLT.installed? then <<-EOS.undent
265
+ No developer tools installed.
266
+ Install the Command Line Tools:
267
+ xcode-select --install
268
+ EOS
269
+ end
270
+ end
271
+
272
+ # TODO: remove when 10.11 is released
273
+ if MacOS.version >= "10.11"
274
+ def check_xcode_up_to_date
275
+ if MacOS::Xcode.installed? && MacOS::Xcode.outdated?
276
+ <<-EOS.undent
277
+ Your Xcode (#{MacOS::Xcode.version}) is outdated
278
+ Please update to Xcode #{MacOS::Xcode.latest_version}.
279
+ Xcode can be updated from
280
+ https://developer.apple.com/xcode/downloads/
281
+ EOS
282
+ end
283
+ end
284
+ else
285
+ def check_xcode_up_to_date
286
+ if MacOS::Xcode.installed? && MacOS::Xcode.outdated?
287
+ <<-EOS.undent
288
+ Your Xcode (#{MacOS::Xcode.version}) is outdated
289
+ Please update to Xcode #{MacOS::Xcode.latest_version}.
290
+ Xcode can be updated from the App Store.
291
+ EOS
292
+ end
293
+ end
294
+ end
295
+
296
+ def check_clt_up_to_date
297
+ if MacOS::CLT.installed? && MacOS::CLT.outdated? then <<-EOS.undent
298
+ A newer Command Line Tools release is available.
299
+ Update them from Software Update in the App Store.
300
+ EOS
301
+ end
302
+ end
303
+ elsif MacOS.version == "10.8" || MacOS.version == "10.7"
304
+ def check_for_installed_developer_tools
305
+ unless MacOS::Xcode.installed? || MacOS::CLT.installed? then <<-EOS.undent
306
+ No developer tools installed.
307
+ You should install the Command Line Tools.
308
+ The standalone package can be obtained from
309
+ https://developer.apple.com/downloads
310
+ or it can be installed via Xcode's preferences.
311
+ EOS
312
+ end
313
+ end
314
+
315
+ def check_xcode_up_to_date
316
+ if MacOS::Xcode.installed? && MacOS::Xcode.outdated? then <<-EOS.undent
317
+ Your Xcode (#{MacOS::Xcode.version}) is outdated
318
+ Please update to Xcode #{MacOS::Xcode.latest_version}.
319
+ Xcode can be updated from
320
+ https://developer.apple.com/xcode/downloads/
321
+ EOS
322
+ end
323
+ end
324
+
325
+ def check_clt_up_to_date
326
+ if MacOS::CLT.installed? && MacOS::CLT.outdated? then <<-EOS.undent
327
+ A newer Command Line Tools release is available.
328
+ The standalone package can be obtained from
329
+ https://developer.apple.com/downloads
330
+ or it can be installed via Xcode's preferences.
331
+ EOS
332
+ end
333
+ end
334
+ else
335
+ def check_for_installed_developer_tools
336
+ unless MacOS::Xcode.installed? then <<-EOS.undent
337
+ Xcode is not installed. Most formulae need Xcode to build.
338
+ It can be installed from
339
+ https://developer.apple.com/xcode/downloads/
340
+ EOS
341
+ end
342
+ end
343
+
344
+ def check_xcode_up_to_date
345
+ if MacOS::Xcode.installed? && MacOS::Xcode.outdated? then <<-EOS.undent
346
+ Your Xcode (#{MacOS::Xcode.version}) is outdated
347
+ Please update to Xcode #{MacOS::Xcode.latest_version}.
348
+ Xcode can be updated from
349
+ https://developer.apple.com/xcode/downloads/
350
+ EOS
351
+ end
352
+ end
353
+ end
354
+
355
+ def check_for_osx_gcc_installer
356
+ if (MacOS.version < "10.7" || MacOS::Xcode.version > "4.1") && \
357
+ MacOS.clang_version == "2.1"
358
+ message = <<-EOS.undent
359
+ You seem to have osx-gcc-installer installed.
360
+ Homebrew doesn't support osx-gcc-installer. It causes many builds to fail and
361
+ is an unlicensed distribution of really old Xcode files.
362
+ EOS
363
+ if MacOS.version >= :mavericks
364
+ message += <<-EOS.undent
365
+ Please run `xcode-select --install` to install the CLT.
366
+ EOS
367
+ elsif MacOS.version >= :lion
368
+ message += <<-EOS.undent
369
+ Please install the CLT or Xcode #{MacOS::Xcode.latest_version}.
370
+ EOS
371
+ else
372
+ message += <<-EOS.undent
373
+ Please install Xcode #{MacOS::Xcode.latest_version}.
374
+ EOS
375
+ end
376
+ end
377
+ end
378
+
379
+ def check_for_stray_developer_directory
380
+ # if the uninstaller script isn't there, it's a good guess neither are
381
+ # any troublesome leftover Xcode files
382
+ uninstaller = Pathname.new("/Developer/Library/uninstall-developer-folder")
383
+ if MacOS::Xcode.version >= "4.3" && uninstaller.exist? then <<-EOS.undent
384
+ You have leftover files from an older version of Xcode.
385
+ You should delete them using:
386
+ #{uninstaller}
387
+ EOS
388
+ end
389
+ end
390
+
391
+ def check_for_bad_install_name_tool
392
+ return if MacOS.version < "10.9"
393
+
394
+ libs = Pathname.new("/usr/bin/install_name_tool").dynamically_linked_libraries
395
+
396
+ # otool may not work, for example if the Xcode license hasn't been accepted yet
397
+ return if libs.empty?
398
+
399
+ unless libs.include? "/usr/lib/libxcselect.dylib" then <<-EOS.undent
400
+ You have an outdated version of /usr/bin/install_name_tool installed.
401
+ This will cause binary package installations to fail.
402
+ This can happen if you install osx-gcc-installer or RailsInstaller.
403
+ To restore it, you must reinstall OS X or restore the binary from
404
+ the OS packages.
405
+ EOS
406
+ end
407
+ end
408
+
409
+ def __check_subdir_access(base)
410
+ target = HOMEBREW_PREFIX+base
411
+ return unless target.exist?
412
+
413
+ cant_read = []
414
+
415
+ target.find do |d|
416
+ next unless d.directory?
417
+ cant_read << d unless d.writable_real?
418
+ end
419
+
420
+ cant_read.sort!
421
+ if cant_read.length > 0
422
+ s = <<-EOS.undent
423
+ Some directories in #{target} aren't writable.
424
+ This can happen if you "sudo make install" software that isn't managed
425
+ by Homebrew. If a brew tries to add locale information to one of these
426
+ directories, then the install will fail during the link step.
427
+ You should probably `chown` them:
428
+
429
+ EOS
430
+ cant_read.each { |f| s << " #{f}\n" }
431
+ s
432
+ end
433
+ end
434
+
435
+ def check_access_share_locale
436
+ __check_subdir_access "share/locale"
437
+ end
438
+
439
+ def check_access_share_man
440
+ __check_subdir_access "share/man"
441
+ end
442
+
443
+ def check_access_usr_local
444
+ return unless HOMEBREW_PREFIX.to_s == "/usr/local"
445
+
446
+ unless File.writable_real?("/usr/local") then <<-EOS.undent
447
+ The /usr/local directory is not writable.
448
+ Even if this directory was writable when you installed Homebrew, other
449
+ software may change permissions on this directory. Some versions of the
450
+ "InstantOn" component of Airfoil are known to do this.
451
+
452
+ You should probably change the ownership and permissions of /usr/local
453
+ back to your user account.
454
+ EOS
455
+ end
456
+ end
457
+
458
+ def check_tmpdir_sticky_bit
459
+ world_writable = HOMEBREW_TEMP.stat.mode & 0777 == 0777
460
+ if world_writable && !HOMEBREW_TEMP.sticky? then <<-EOS.undent
461
+ #{HOMEBREW_TEMP} is world-writable but does not have the sticky bit set.
462
+ Please run "Repair Disk Permissions" in Disk Utility.
463
+ EOS
464
+ end
465
+ end
466
+
467
+ (Keg::TOP_LEVEL_DIRECTORIES + ["lib/pkgconfig"]).each do |d|
468
+ define_method("check_access_#{d.sub("/", "_")}") do
469
+ dir = HOMEBREW_PREFIX.join(d)
470
+ if dir.exist? && !dir.writable_real? then <<-EOS.undent
471
+ #{dir} isn't writable.
472
+
473
+ This can happen if you "sudo make install" software that isn't managed by
474
+ by Homebrew. If a formula tries to write a file to this directory, the
475
+ install will fail during the link step.
476
+
477
+ You should probably `chown` #{dir}
478
+ EOS
479
+ end
480
+ end
481
+ end
482
+
483
+ def check_access_site_packages
484
+ if Language::Python.homebrew_site_packages.exist? && !Language::Python.homebrew_site_packages.writable_real?
485
+ <<-EOS.undent
486
+ #{Language::Python.homebrew_site_packages} isn't writable.
487
+ This can happen if you "sudo pip install" software that isn't managed
488
+ by Homebrew. If you install a formula with Python modules, the install
489
+ will fail during the link step.
490
+
491
+ You should probably `chown` #{Language::Python.homebrew_site_packages}
492
+ EOS
493
+ end
494
+ end
495
+
496
+ def check_access_logs
497
+ if HOMEBREW_LOGS.exist? && !HOMEBREW_LOGS.writable_real?
498
+ <<-EOS.undent
499
+ #{HOMEBREW_LOGS} isn't writable.
500
+ Homebrew writes debugging logs to this location.
501
+ You should probably `chown` #{HOMEBREW_LOGS}
502
+ EOS
503
+ end
504
+ end
505
+
506
+ def check_access_cache
507
+ if HOMEBREW_CACHE.exist? && !HOMEBREW_CACHE.writable_real?
508
+ <<-EOS.undent
509
+ #{HOMEBREW_CACHE} isn't writable.
510
+ This can happen if you run `brew install` or `brew fetch` as another user.
511
+ Homebrew caches downloaded files to this location.
512
+ You should probably `chown` #{HOMEBREW_CACHE}
513
+ EOS
514
+ end
515
+ end
516
+
517
+ def check_access_cellar
518
+ if HOMEBREW_CELLAR.exist? && !HOMEBREW_CELLAR.writable_real?
519
+ <<-EOS.undent
520
+ #{HOMEBREW_CELLAR} isn't writable.
521
+ You should `chown` #{HOMEBREW_CELLAR}
522
+ EOS
523
+ end
524
+ end
525
+
526
+ def check_access_prefix_opt
527
+ opt = HOMEBREW_PREFIX.join("opt")
528
+ if opt.exist? && !opt.writable_real?
529
+ <<-EOS.undent
530
+ #{opt} isn't writable.
531
+ You should `chown` #{opt}
532
+ EOS
533
+ end
534
+ end
535
+
536
+ def check_ruby_version
537
+ ruby_version = MacOS.version >= "10.9" ? "2.0" : "1.8"
538
+ if RUBY_VERSION[/\d\.\d/] != ruby_version then <<-EOS.undent
539
+ Ruby version #{RUBY_VERSION} is unsupported on #{MacOS.version}. Homebrew
540
+ is developed and tested on Ruby #{ruby_version}, and may not work correctly
541
+ on other Rubies. Patches are accepted as long as they don't cause breakage
542
+ on supported Rubies.
543
+ EOS
544
+ end
545
+ end
546
+
547
+ def check_homebrew_prefix
548
+ unless HOMEBREW_PREFIX.to_s == "/usr/local"
549
+ <<-EOS.undent
550
+ Your Homebrew is not installed to /usr/local
551
+ You can install Homebrew anywhere you want, but some brews may only build
552
+ correctly if you install in /usr/local. Sorry!
553
+ EOS
554
+ end
555
+ end
556
+
557
+ def check_xcode_prefix
558
+ prefix = MacOS::Xcode.prefix
559
+ return if prefix.nil?
560
+ if prefix.to_s.match(" ")
561
+ <<-EOS.undent
562
+ Xcode is installed to a directory with a space in the name.
563
+ This will cause some formulae to fail to build.
564
+ EOS
565
+ end
566
+ end
567
+
568
+ def check_xcode_prefix_exists
569
+ prefix = MacOS::Xcode.prefix
570
+ return if prefix.nil?
571
+ unless prefix.exist?
572
+ <<-EOS.undent
573
+ The directory Xcode is reportedly installed to doesn't exist:
574
+ #{prefix}
575
+ You may need to `xcode-select` the proper path if you have moved Xcode.
576
+ EOS
577
+ end
578
+ end
579
+
580
+ def check_xcode_select_path
581
+ if !MacOS::CLT.installed? && !File.file?("#{MacOS.active_developer_dir}/usr/bin/xcodebuild")
582
+ path = MacOS::Xcode.bundle_path
583
+ path = "/Developer" if path.nil? || !path.directory?
584
+ <<-EOS.undent
585
+ Your Xcode is configured with an invalid path.
586
+ You should change it to the correct path:
587
+ sudo xcode-select -switch #{path}
588
+ EOS
589
+ end
590
+ end
591
+
592
+ def check_user_path_1
593
+ $seen_prefix_bin = false
594
+ $seen_prefix_sbin = false
595
+
596
+ out = nil
597
+
598
+ paths.each do |p|
599
+ case p
600
+ when "/usr/bin"
601
+ unless $seen_prefix_bin
602
+ # only show the doctor message if there are any conflicts
603
+ # rationale: a default install should not trigger any brew doctor messages
604
+ conflicts = Dir["#{HOMEBREW_PREFIX}/bin/*"].
605
+ map { |fn| File.basename fn }.
606
+ select { |bn| File.exist? "/usr/bin/#{bn}" }
607
+
608
+ if conflicts.size > 0
609
+ out = <<-EOS.undent
610
+ /usr/bin occurs before #{HOMEBREW_PREFIX}/bin
611
+ This means that system-provided programs will be used instead of those
612
+ provided by Homebrew. The following tools exist at both paths:
613
+
614
+ #{conflicts * "\n "}
615
+
616
+ Consider setting your PATH so that #{HOMEBREW_PREFIX}/bin
617
+ occurs before /usr/bin. Here is a one-liner:
618
+ echo 'export PATH="#{HOMEBREW_PREFIX}/bin:$PATH"' >> #{shell_profile}
619
+ EOS
620
+ end
621
+ end
622
+ when "#{HOMEBREW_PREFIX}/bin"
623
+ $seen_prefix_bin = true
624
+ when "#{HOMEBREW_PREFIX}/sbin"
625
+ $seen_prefix_sbin = true
626
+ end
627
+ end
628
+ out
629
+ end
630
+
631
+ def check_user_path_2
632
+ unless $seen_prefix_bin
633
+ <<-EOS.undent
634
+ Homebrew's bin was not found in your PATH.
635
+ Consider setting the PATH for example like so
636
+ echo 'export PATH="#{HOMEBREW_PREFIX}/bin:$PATH"' >> #{shell_profile}
637
+ EOS
638
+ end
639
+ end
640
+
641
+ def check_user_path_3
642
+ # Don't complain about sbin not being in the path if it doesn't exist
643
+ sbin = (HOMEBREW_PREFIX+"sbin")
644
+ if sbin.directory? && sbin.children.length > 0
645
+ unless $seen_prefix_sbin
646
+ <<-EOS.undent
647
+ Homebrew's sbin was not found in your PATH but you have installed
648
+ formulae that put executables in #{HOMEBREW_PREFIX}/sbin.
649
+ Consider setting the PATH for example like so
650
+ echo 'export PATH="#{HOMEBREW_PREFIX}/sbin:$PATH"' >> #{shell_profile}
651
+ EOS
652
+ end
653
+ end
654
+ end
655
+
656
+ def check_user_curlrc
657
+ if %w[CURL_HOME HOME].any? { |key| ENV[key] && File.exist?("#{ENV[key]}/.curlrc") } then <<-EOS.undent
658
+ You have a curlrc file
659
+ If you have trouble downloading packages with Homebrew, then maybe this
660
+ is the problem? If the following command doesn't work, then try removing
661
+ your curlrc:
662
+ curl http://github.com
663
+ EOS
664
+ end
665
+ end
666
+
667
+ def check_for_unsupported_curl_vars
668
+ # Support for SSL_CERT_DIR seemed to be removed in the 10.10.5 update.
669
+ if MacOS.version >= :yosemite && !ENV["SSL_CERT_DIR"].nil? then <<-EOS.undent
670
+ SSL_CERT_DIR support was removed from Apple's curl.
671
+ If fetching formulae fails you should:
672
+ unset SSL_CERT_DIR
673
+ and remove it from #{shell_profile} if present.
674
+ EOS
675
+ end
676
+ end
677
+
678
+ def check_which_pkg_config
679
+ binary = which "pkg-config"
680
+ return if binary.nil?
681
+
682
+ mono_config = Pathname.new("/usr/bin/pkg-config")
683
+ if mono_config.exist? && mono_config.realpath.to_s.include?("Mono.framework") then <<-EOS.undent
684
+ You have a non-Homebrew 'pkg-config' in your PATH:
685
+ /usr/bin/pkg-config => #{mono_config.realpath}
686
+
687
+ This was most likely created by the Mono installer. `./configure` may
688
+ have problems finding brew-installed packages using this other pkg-config.
689
+
690
+ Mono no longer installs this file as of 3.0.4. You should
691
+ `sudo rm /usr/bin/pkg-config` and upgrade to the latest version of Mono.
692
+ EOS
693
+ elsif binary.to_s != "#{HOMEBREW_PREFIX}/bin/pkg-config" then <<-EOS.undent
694
+ You have a non-Homebrew 'pkg-config' in your PATH:
695
+ #{binary}
696
+
697
+ `./configure` may have problems finding brew-installed packages using
698
+ this other pkg-config.
699
+ EOS
700
+ end
701
+ end
702
+
703
+ def check_for_gettext
704
+ find_relative_paths("lib/libgettextlib.dylib",
705
+ "lib/libintl.dylib",
706
+ "include/libintl.h")
707
+
708
+ return if @found.empty?
709
+
710
+ # Our gettext formula will be caught by check_linked_keg_only_brews
711
+ f = Formulary.factory("gettext") rescue nil
712
+ return if f && f.linked_keg.directory? && @found.all? do |path|
713
+ Pathname.new(path).realpath.to_s.start_with? "#{HOMEBREW_CELLAR}/gettext"
714
+ end
715
+
716
+ s = <<-EOS.undent_________________________________________________________72
717
+ gettext files detected at a system prefix
718
+ These files can cause compilation and link failures, especially if they
719
+ are compiled with improper architectures. Consider removing these files:
720
+ EOS
721
+ inject_file_list(@found, s)
722
+ end
723
+
724
+ def check_for_iconv
725
+ unless find_relative_paths("lib/libiconv.dylib", "include/iconv.h").empty?
726
+ if (f = Formulary.factory("libiconv") rescue nil) && f.linked_keg.directory?
727
+ unless f.keg_only? then <<-EOS.undent
728
+ A libiconv formula is installed and linked
729
+ This will break stuff. For serious. Unlink it.
730
+ EOS
731
+ end
732
+ else
733
+ s = <<-EOS.undent_________________________________________________________72
734
+ libiconv files detected at a system prefix other than /usr
735
+ Homebrew doesn't provide a libiconv formula, and expects to link against
736
+ the system version in /usr. libiconv in other prefixes can cause
737
+ compile or link failure, especially if compiled with improper
738
+ architectures. OS X itself never installs anything to /usr/local so
739
+ it was either installed by a user or some other third party software.
740
+
741
+ tl;dr: delete these files:
742
+ EOS
743
+ inject_file_list(@found, s)
744
+ end
745
+ end
746
+ end
747
+
748
+ def check_for_config_scripts
749
+ return unless HOMEBREW_CELLAR.exist?
750
+ real_cellar = HOMEBREW_CELLAR.realpath
751
+
752
+ scripts = []
753
+
754
+ whitelist = %W[
755
+ /usr/bin /usr/sbin
756
+ /usr/X11/bin /usr/X11R6/bin /opt/X11/bin
757
+ #{HOMEBREW_PREFIX}/bin #{HOMEBREW_PREFIX}/sbin
758
+ /Applications/Server.app/Contents/ServerRoot/usr/bin
759
+ /Applications/Server.app/Contents/ServerRoot/usr/sbin
760
+ ].map(&:downcase)
761
+
762
+ paths.each do |p|
763
+ next if whitelist.include?(p.downcase) || !File.directory?(p)
764
+
765
+ realpath = Pathname.new(p).realpath.to_s
766
+ next if realpath.start_with?(real_cellar.to_s, HOMEBREW_CELLAR.to_s)
767
+
768
+ scripts += Dir.chdir(p) { Dir["*-config"] }.map { |c| File.join(p, c) }
769
+ end
770
+
771
+ unless scripts.empty?
772
+ s = <<-EOS.undent
773
+ "config" scripts exist outside your system or Homebrew directories.
774
+ `./configure` scripts often look for *-config scripts to determine if
775
+ software packages are installed, and what additional flags to use when
776
+ compiling and linking.
777
+
778
+ Having additional scripts in your path can confuse software installed via
779
+ Homebrew if the config script overrides a system or Homebrew provided
780
+ script of the same name. We found the following "config" scripts:
781
+
782
+ EOS
783
+
784
+ s << scripts.map { |f| " #{f}" }.join("\n")
785
+ end
786
+ end
787
+
788
+ def check_DYLD_vars
789
+ found = ENV.keys.grep(/^DYLD_/)
790
+ unless found.empty?
791
+ s = <<-EOS.undent
792
+ Setting DYLD_* vars can break dynamic linking.
793
+ Set variables:
794
+ EOS
795
+ s << found.map { |e| " #{e}: #{ENV.fetch(e)}\n" }.join
796
+ if found.include? "DYLD_INSERT_LIBRARIES"
797
+ s += <<-EOS.undent
798
+
799
+ Setting DYLD_INSERT_LIBRARIES can cause Go builds to fail.
800
+ Having this set is common if you use this software:
801
+ http://asepsis.binaryage.com/
802
+ EOS
803
+ end
804
+ s
805
+ end
806
+ end
807
+
808
+ def check_for_symlinked_cellar
809
+ return unless HOMEBREW_CELLAR.exist?
810
+ if HOMEBREW_CELLAR.symlink?
811
+ <<-EOS.undent
812
+ Symlinked Cellars can cause problems.
813
+ Your Homebrew Cellar is a symlink: #{HOMEBREW_CELLAR}
814
+ which resolves to: #{HOMEBREW_CELLAR.realpath}
815
+
816
+ The recommended Homebrew installations are either:
817
+ (A) Have Cellar be a real directory inside of your HOMEBREW_PREFIX
818
+ (B) Symlink "bin/brew" into your prefix, but don't symlink "Cellar".
819
+
820
+ Older installations of Homebrew may have created a symlinked Cellar, but this can
821
+ cause problems when two formula install to locations that are mapped on top of each
822
+ other during the linking step.
823
+ EOS
824
+ end
825
+ end
826
+
827
+ def check_for_multiple_volumes
828
+ return unless HOMEBREW_CELLAR.exist?
829
+ volumes = Volumes.new
830
+
831
+ # Find the volumes for the TMP folder & HOMEBREW_CELLAR
832
+ real_cellar = HOMEBREW_CELLAR.realpath
833
+
834
+ tmp = Pathname.new(Dir.mktmpdir("doctor", HOMEBREW_TEMP))
835
+ real_temp = tmp.realpath.parent
836
+
837
+ where_cellar = volumes.which real_cellar
838
+ where_temp = volumes.which real_temp
839
+
840
+ Dir.delete tmp
841
+
842
+ unless where_cellar == where_temp then <<-EOS.undent
843
+ Your Cellar and TEMP directories are on different volumes.
844
+ OS X won't move relative symlinks across volumes unless the target file already
845
+ exists. Brews known to be affected by this are Git and Narwhal.
846
+
847
+ You should set the "HOMEBREW_TEMP" environmental variable to a suitable
848
+ directory on the same volume as your Cellar.
849
+ EOS
850
+ end
851
+ end
852
+
853
+ def check_filesystem_case_sensitive
854
+ volumes = Volumes.new
855
+ case_sensitive_vols = [HOMEBREW_PREFIX, HOMEBREW_REPOSITORY, HOMEBREW_CELLAR, HOMEBREW_TEMP].select do |dir|
856
+ # We select the dir as being case-sensitive if either the UPCASED or the
857
+ # downcased variant is missing.
858
+ # Of course, on a case-insensitive fs, both exist because the os reports so.
859
+ # In the rare situation when the user has indeed a downcased and an upcased
860
+ # dir (e.g. /TMP and /tmp) this check falsely thinks it is case-insensitive
861
+ # but we don't care beacuse: 1. there is more than one dir checked, 2. the
862
+ # check is not vital and 3. we would have to touch files otherwise.
863
+ upcased = Pathname.new(dir.to_s.upcase)
864
+ downcased = Pathname.new(dir.to_s.downcase)
865
+ dir.exist? && !(upcased.exist? && downcased.exist?)
866
+ end.map { |case_sensitive_dir| volumes.get_mounts(case_sensitive_dir) }.uniq
867
+ return if case_sensitive_vols.empty?
868
+ <<-EOS.undent
869
+ The filesystem on #{case_sensitive_vols.join(",")} appears to be case-sensitive.
870
+ The default OS X filesystem is case-insensitive. Please report any apparent problems.
871
+ EOS
872
+ end
873
+
874
+ def __check_git_version
875
+ # https://help.github.com/articles/https-cloning-errors
876
+ `git --version`.chomp =~ /git version ((?:\d+\.?)+)/
877
+
878
+ if $1 && Version.new($1) < Version.new("1.7.10")
879
+ git_upgrade_cmd = Formula["git"].any_version_installed? ? "upgrade" : "install"
880
+
881
+ <<-EOS.undent
882
+ An outdated version of Git was detected in your PATH.
883
+ Git 1.7.10 or newer is required to perform checkouts over HTTPS from GitHub.
884
+ Please upgrade: brew #{git_upgrade_cmd} git
885
+ EOS
886
+ end
887
+ end
888
+
889
+ def check_for_git
890
+ if git?
891
+ __check_git_version
892
+ else <<-EOS.undent
893
+ Git could not be found in your PATH.
894
+ Homebrew uses Git for several internal functions, and some formulae use Git
895
+ checkouts instead of stable tarballs. You may want to install Git:
896
+ brew install git
897
+ EOS
898
+ end
899
+ end
900
+
901
+ def check_git_newline_settings
902
+ return unless git?
903
+
904
+ autocrlf = `git config --get core.autocrlf`.chomp
905
+
906
+ if autocrlf == "true" then <<-EOS.undent
907
+ Suspicious Git newline settings found.
908
+
909
+ The detected Git newline settings will cause checkout problems:
910
+ core.autocrlf = #{autocrlf}
911
+
912
+ If you are not routinely dealing with Windows-based projects,
913
+ consider removing these by running:
914
+ `git config --global core.autocrlf input`
915
+ EOS
916
+ end
917
+ end
918
+
919
+ def check_git_origin
920
+ return unless git? && (HOMEBREW_REPOSITORY/".git").exist?
921
+
922
+ HOMEBREW_REPOSITORY.cd do
923
+ origin = `git config --get remote.origin.url`.strip
924
+
925
+ if origin.empty? then <<-EOS.undent
926
+ Missing git origin remote.
927
+
928
+ Without a correctly configured origin, Homebrew won't update
929
+ properly. You can solve this by adding the Homebrew remote:
930
+ cd #{HOMEBREW_REPOSITORY}
931
+ git remote add origin https://github.com/Homebrew/homebrew.git
932
+ EOS
933
+ elsif origin !~ /(mxcl|Homebrew)\/homebrew(\.git)?$/ then <<-EOS.undent
934
+ Suspicious git origin remote found.
935
+
936
+ With a non-standard origin, Homebrew won't pull updates from
937
+ the main repository. The current git origin is:
938
+ #{origin}
939
+
940
+ Unless you have compelling reasons, consider setting the
941
+ origin remote to point at the main repository, located at:
942
+ https://github.com/Homebrew/homebrew.git
943
+ EOS
944
+ end
945
+ end
946
+ end
947
+
948
+ def check_for_autoconf
949
+ return unless MacOS::Xcode.provides_autotools?
950
+
951
+ autoconf = which("autoconf")
952
+ safe_autoconfs = %w[/usr/bin/autoconf /Developer/usr/bin/autoconf]
953
+ unless autoconf.nil? || safe_autoconfs.include?(autoconf.to_s) then <<-EOS.undent
954
+ An "autoconf" in your path blocks the Xcode-provided version at:
955
+ #{autoconf}
956
+
957
+ This custom autoconf may cause some Homebrew formulae to fail to compile.
958
+ EOS
959
+ end
960
+ end
961
+
962
+ def __check_linked_brew(f)
963
+ f.rack.subdirs.each do |prefix|
964
+ prefix.find do |src|
965
+ next if src == prefix
966
+ dst = HOMEBREW_PREFIX + src.relative_path_from(prefix)
967
+ return true if dst.symlink? && src == dst.resolved_path
968
+ end
969
+ end
970
+
971
+ false
972
+ end
973
+
974
+ def check_for_linked_keg_only_brews
975
+ return unless HOMEBREW_CELLAR.exist?
976
+
977
+ linked = Formula.installed.select do |f|
978
+ f.keg_only? && __check_linked_brew(f)
979
+ end
980
+
981
+ unless linked.empty?
982
+ s = <<-EOS.undent
983
+ Some keg-only formula are linked into the Cellar.
984
+ Linking a keg-only formula, such as gettext, into the cellar with
985
+ `brew link <formula>` will cause other formulae to detect them during
986
+ the `./configure` step. This may cause problems when compiling those
987
+ other formulae.
988
+
989
+ Binaries provided by keg-only formulae may override system binaries
990
+ with other strange results.
991
+
992
+ You may wish to `brew unlink` these brews:
993
+
994
+ EOS
995
+ linked.each { |f| s << " #{f.full_name}\n" }
996
+ s
997
+ end
998
+ end
999
+
1000
+ def check_for_other_frameworks
1001
+ # Other frameworks that are known to cause problems when present
1002
+ %w[expat.framework libexpat.framework libcurl.framework].
1003
+ map { |frmwrk| "/Library/Frameworks/#{frmwrk}" }.
1004
+ select { |frmwrk| File.exist? frmwrk }.
1005
+ map do |frmwrk|
1006
+ <<-EOS.undent
1007
+ #{frmwrk} detected
1008
+ This can be picked up by CMake's build system and likely cause the build to
1009
+ fail. You may need to move this file out of the way to compile CMake.
1010
+ EOS
1011
+ end.join
1012
+ end
1013
+
1014
+ def check_tmpdir
1015
+ tmpdir = ENV["TMPDIR"]
1016
+ "TMPDIR #{tmpdir.inspect} doesn't exist." unless tmpdir.nil? || File.directory?(tmpdir)
1017
+ end
1018
+
1019
+ def check_missing_deps
1020
+ return unless HOMEBREW_CELLAR.exist?
1021
+ missing = Set.new
1022
+ Homebrew.missing_deps(Formula.installed).each_value do |deps|
1023
+ missing.merge(deps)
1024
+ end
1025
+
1026
+ if missing.any? then <<-EOS.undent
1027
+ Some installed formula are missing dependencies.
1028
+ You should `brew install` the missing dependencies:
1029
+
1030
+ brew install #{missing.sort_by(&:full_name) * " "}
1031
+
1032
+ Run `brew missing` for more details.
1033
+ EOS
1034
+ end
1035
+ end
1036
+
1037
+ def check_git_status
1038
+ return unless git?
1039
+ HOMEBREW_REPOSITORY.cd do
1040
+ unless `git status --untracked-files=all --porcelain -- Library/Homebrew/ 2>/dev/null`.chomp.empty?
1041
+ <<-EOS.undent_________________________________________________________72
1042
+ You have uncommitted modifications to Homebrew
1043
+ If this a surprise to you, then you should stash these modifications.
1044
+ Stashing returns Homebrew to a pristine state but can be undone
1045
+ should you later need to do so for some reason.
1046
+ cd #{HOMEBREW_LIBRARY} && git stash && git clean -d -f
1047
+ EOS
1048
+ end
1049
+ end
1050
+ end
1051
+
1052
+ def check_for_enthought_python
1053
+ if which "enpkg" then <<-EOS.undent
1054
+ Enthought Python was found in your PATH.
1055
+ This can cause build problems, as this software installs its own
1056
+ copies of iconv and libxml2 into directories that are picked up by
1057
+ other build systems.
1058
+ EOS
1059
+ end
1060
+ end
1061
+
1062
+ def check_for_library_python
1063
+ if File.exist?("/Library/Frameworks/Python.framework") then <<-EOS.undent
1064
+ Python is installed at /Library/Frameworks/Python.framework
1065
+
1066
+ Homebrew only supports building against the System-provided Python or a
1067
+ brewed Python. In particular, Pythons installed to /Library can interfere
1068
+ with other software installs.
1069
+ EOS
1070
+ end
1071
+ end
1072
+
1073
+ def check_for_old_homebrew_share_python_in_path
1074
+ s = ""
1075
+ ["", "3"].map do |suffix|
1076
+ if paths.include?((HOMEBREW_PREFIX/"share/python#{suffix}").to_s)
1077
+ s += "#{HOMEBREW_PREFIX}/share/python#{suffix} is not needed in PATH.\n"
1078
+ end
1079
+ end
1080
+ unless s.empty?
1081
+ s += <<-EOS.undent
1082
+ Formerly homebrew put Python scripts you installed via `pip` or `pip3`
1083
+ (or `easy_install`) into that directory above but now it can be removed
1084
+ from your PATH variable.
1085
+ Python scripts will now install into #{HOMEBREW_PREFIX}/bin.
1086
+ You can delete anything, except 'Extras', from the #{HOMEBREW_PREFIX}/share/python
1087
+ (and #{HOMEBREW_PREFIX}/share/python3) dir and install affected Python packages
1088
+ anew with `pip install --upgrade`.
1089
+ EOS
1090
+ end
1091
+ end
1092
+
1093
+ def check_for_bad_python_symlink
1094
+ return unless which "python"
1095
+ `python -V 2>&1` =~ /Python (\d+)\./
1096
+ # This won't be the right warning if we matched nothing at all
1097
+ return if $1.nil?
1098
+ unless $1 == "2" then <<-EOS.undent
1099
+ python is symlinked to python#{$1}
1100
+ This will confuse build scripts and in general lead to subtle breakage.
1101
+ EOS
1102
+ end
1103
+ end
1104
+
1105
+ def check_for_non_prefixed_coreutils
1106
+ gnubin = "#{Formulary.factory("coreutils").prefix}/libexec/gnubin"
1107
+ if paths.include? gnubin then <<-EOS.undent
1108
+ Putting non-prefixed coreutils in your path can cause gmp builds to fail.
1109
+ EOS
1110
+ end
1111
+ end
1112
+
1113
+ def check_for_non_prefixed_findutils
1114
+ default_names = Tab.for_name("findutils").with? "default-names"
1115
+ if default_names then <<-EOS.undent
1116
+ Putting non-prefixed findutils in your path can cause python builds to fail.
1117
+ EOS
1118
+ end
1119
+ end
1120
+
1121
+ def check_for_pydistutils_cfg_in_home
1122
+ if File.exist? "#{ENV["HOME"]}/.pydistutils.cfg" then <<-EOS.undent
1123
+ A .pydistutils.cfg file was found in $HOME, which may cause Python
1124
+ builds to fail. See:
1125
+ https://bugs.python.org/issue6138
1126
+ https://bugs.python.org/issue4655
1127
+ EOS
1128
+ end
1129
+ end
1130
+
1131
+ def check_for_outdated_homebrew
1132
+ return unless git?
1133
+ HOMEBREW_REPOSITORY.cd do
1134
+ if File.directory? ".git"
1135
+ local = `git rev-parse -q --verify refs/remotes/origin/master`.chomp
1136
+ remote = /^([a-f0-9]{40})/.match(`git ls-remote origin refs/heads/master 2>/dev/null`)
1137
+ if remote.nil? || local == remote[0]
1138
+ return
1139
+ end
1140
+ end
1141
+
1142
+ timestamp = if File.directory? ".git"
1143
+ `git log -1 --format="%ct" HEAD`.to_i
1144
+ else
1145
+ HOMEBREW_LIBRARY.mtime.to_i
1146
+ end
1147
+
1148
+ if Time.now.to_i - timestamp > 60 * 60 * 24 then <<-EOS.undent
1149
+ Your Homebrew is outdated.
1150
+ You haven't updated for at least 24 hours. This is a long time in brewland!
1151
+ To update Homebrew, run `brew update`.
1152
+ EOS
1153
+ end
1154
+ end
1155
+ end
1156
+
1157
+ def check_for_unlinked_but_not_keg_only
1158
+ return unless HOMEBREW_CELLAR.exist?
1159
+ unlinked = HOMEBREW_CELLAR.children.reject do |rack|
1160
+ if !rack.directory?
1161
+ true
1162
+ elsif !(HOMEBREW_REPOSITORY/"Library/LinkedKegs"/rack.basename).directory?
1163
+ begin
1164
+ Formulary.from_rack(rack).keg_only?
1165
+ rescue FormulaUnavailableError, TapFormulaAmbiguityError
1166
+ false
1167
+ end
1168
+ else
1169
+ true
1170
+ end
1171
+ end.map(&:basename)
1172
+
1173
+ unless unlinked.empty? then <<-EOS.undent
1174
+ You have unlinked kegs in your Cellar
1175
+ Leaving kegs unlinked can lead to build-trouble and cause brews that depend on
1176
+ those kegs to fail to run properly once built. Run `brew link` on these:
1177
+
1178
+ #{unlinked * "\n "}
1179
+ EOS
1180
+ end
1181
+ end
1182
+
1183
+ def check_xcode_license_approved
1184
+ # If the user installs Xcode-only, they have to approve the
1185
+ # license or no "xc*" tool will work.
1186
+ if `/usr/bin/xcrun clang 2>&1` =~ /license/ && !$?.success? then <<-EOS.undent
1187
+ You have not agreed to the Xcode license.
1188
+ Builds will fail! Agree to the license by opening Xcode.app or running:
1189
+ sudo xcodebuild -license
1190
+ EOS
1191
+ end
1192
+ end
1193
+
1194
+ def check_for_latest_xquartz
1195
+ return unless MacOS::XQuartz.version
1196
+ return if MacOS::XQuartz.provided_by_apple?
1197
+
1198
+ installed_version = Version.new(MacOS::XQuartz.version)
1199
+ latest_version = Version.new(MacOS::XQuartz.latest_version)
1200
+
1201
+ return if installed_version >= latest_version
1202
+
1203
+ <<-EOS.undent
1204
+ Your XQuartz (#{installed_version}) is outdated
1205
+ Please install XQuartz #{latest_version}:
1206
+ https://xquartz.macosforge.org
1207
+ EOS
1208
+ end
1209
+
1210
+ def check_for_old_env_vars
1211
+ if ENV["HOMEBREW_KEEP_INFO"]
1212
+ <<-EOS.undent
1213
+ `HOMEBREW_KEEP_INFO` is no longer used
1214
+ info files are no longer deleted by default; you may
1215
+ remove this environment variable.
1216
+ EOS
1217
+ end
1218
+ end
1219
+
1220
+ def check_for_pth_support
1221
+ homebrew_site_packages = Language::Python.homebrew_site_packages
1222
+ return unless homebrew_site_packages.directory?
1223
+ return if Language::Python.reads_brewed_pth_files?("python") != false
1224
+ return unless Language::Python.in_sys_path?("python", homebrew_site_packages)
1225
+ user_site_packages = Language::Python.user_site_packages "python"
1226
+ <<-EOS.undent
1227
+ Your default Python does not recognize the Homebrew site-packages
1228
+ directory as a special site-packages directory, which means that .pth
1229
+ files will not be followed. This means you will not be able to import
1230
+ some modules after installing them with Homebrew, like wxpython. To fix
1231
+ this for the current user, you can run:
1232
+
1233
+ mkdir -p #{user_site_packages}
1234
+ echo 'import site; site.addsitedir("#{homebrew_site_packages}")' >> #{user_site_packages}/homebrew.pth
1235
+ EOS
1236
+ end
1237
+
1238
+ def check_for_external_cmd_name_conflict
1239
+ cmds = paths.flat_map { |p| Dir["#{p}/brew-*"] }.uniq
1240
+ cmds = cmds.select { |cmd| File.file?(cmd) && File.executable?(cmd) }
1241
+ cmd_map = {}
1242
+ cmds.each do |cmd|
1243
+ cmd_name = File.basename(cmd, ".rb")
1244
+ cmd_map[cmd_name] ||= []
1245
+ cmd_map[cmd_name] << cmd
1246
+ end
1247
+ cmd_map.reject! { |_cmd_name, cmd_paths| cmd_paths.size == 1 }
1248
+ return if cmd_map.empty?
1249
+ s = "You have external commands with conflicting names."
1250
+ cmd_map.each do |cmd_name, cmd_paths|
1251
+ s += "\n\nFound command `#{cmd_name}` in following places:\n"
1252
+ s += cmd_paths.map { |f| " #{f}" }.join("\n")
1253
+ end
1254
+ s
1255
+ end
1256
+
1257
+ def all
1258
+ methods.map(&:to_s).grep(/^check_/)
1259
+ end
1260
+ end # end class Checks
1261
+
1262
+ module Homebrew
1263
+ def doctor
1264
+ checks = Checks.new
1265
+
1266
+ if ARGV.include? "--list-checks"
1267
+ puts checks.all.sort
1268
+ exit
1269
+ end
1270
+
1271
+ inject_dump_stats(checks) if ARGV.switch? "D"
1272
+
1273
+ if ARGV.named.empty?
1274
+ methods = checks.all.sort
1275
+ methods << "check_for_linked_keg_only_brews" << "check_for_outdated_homebrew"
1276
+ methods = methods.reverse.uniq.reverse
1277
+ else
1278
+ methods = ARGV.named
1279
+ end
1280
+
1281
+ first_warning = true
1282
+ methods.each do |method|
1283
+ begin
1284
+ out = checks.send(method)
1285
+ rescue NoMethodError
1286
+ Homebrew.failed = true
1287
+ puts "No check available by the name: #{method}"
1288
+ next
1289
+ end
1290
+ unless out.nil? || out.empty?
1291
+ if first_warning
1292
+ puts <<-EOS.undent
1293
+ #{Tty.white}Please note that these warnings are just used to help the Homebrew maintainers
1294
+ with debugging if you file an issue. If everything you use Homebrew for is
1295
+ working fine: please don't worry and just ignore them. Thanks!#{Tty.reset}
1296
+ EOS
1297
+ end
1298
+
1299
+ puts
1300
+ opoo out
1301
+ Homebrew.failed = true
1302
+ first_warning = false
1303
+ end
1304
+ end
1305
+
1306
+ puts "Your system is ready to brew." unless Homebrew.failed?
1307
+ end
1308
+
1309
+ def inject_dump_stats(checks)
1310
+ checks.extend Module.new {
1311
+ def send(method, *)
1312
+ time = Time.now
1313
+ super
1314
+ ensure
1315
+ $times[method] = Time.now - time
1316
+ end
1317
+ }
1318
+ $times = {}
1319
+ at_exit do
1320
+ puts $times.sort_by { |_k, v| v }.map { |k, v| "#{k}: #{v}" }
1321
+ end
1322
+ end
1323
+ end