archruby 0.2.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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