diff options
| -rw-r--r-- | Library/Homebrew/cmd/doctor.rb | 1324 | ||||
| -rw-r--r-- | Library/Homebrew/cmd/install.rb | 4 | ||||
| -rw-r--r-- | Library/Homebrew/cmd/missing.rb | 26 | ||||
| -rw-r--r-- | Library/Homebrew/cmd/update.rb | 4 | ||||
| -rw-r--r-- | Library/Homebrew/diagnostic.rb | 1344 | ||||
| -rw-r--r-- | Library/Homebrew/exceptions.rb | 4 | ||||
| -rw-r--r-- | Library/Homebrew/test/test_integration_cmds.rb | 57 | 
7 files changed, 1411 insertions, 1352 deletions
| diff --git a/Library/Homebrew/cmd/doctor.rb b/Library/Homebrew/cmd/doctor.rb index 142c7fe0e..fe230c8bc 100644 --- a/Library/Homebrew/cmd/doctor.rb +++ b/Library/Homebrew/cmd/doctor.rb @@ -1,1318 +1,15 @@ -require "cmd/missing" -require "formula" -require "keg" -require "language/python" -require "version" - -class Volumes -  def initialize -    @volumes = get_mounts -  end - -  def which(path) -    vols = get_mounts path - -    # no volume found -    if vols.empty? -      return -1 -    end - -    vol_index = @volumes.index(vols[0]) -    # volume not found in volume list -    if vol_index.nil? -      return -1 -    end -    vol_index -  end - -  def get_mounts(path = nil) -    vols = [] -    # get the volume of path, if path is nil returns all volumes - -    args = %w[/bin/df -P] -    args << path if path - -    Utils.popen_read(*args) do |io| -      io.each_line do |line| -        case line.chomp -          # regex matches: /dev/disk0s2   489562928 440803616  48247312    91%    / -        when /^.+\s+[0-9]+\s+[0-9]+\s+[0-9]+\s+[0-9]{1,3}%\s+(.+)/ -          vols << $1 -        end -      end -    end -    vols -  end -end - -class Checks -  ############# HELPERS -  # Finds files in HOMEBREW_PREFIX *and* /usr/local. -  # Specify paths relative to a prefix eg. "include/foo.h". -  # Sets @found for your convenience. -  def find_relative_paths(*relative_paths) -    @found = %W[#{HOMEBREW_PREFIX} /usr/local].uniq.inject([]) do |found, prefix| -      found + relative_paths.map { |f| File.join(prefix, f) }.select { |f| File.exist? f } -    end -  end - -  def inject_file_list(list, str) -    list.inject(str) { |s, f| s << "    #{f}\n" } -  end -  ############# END HELPERS - -  # Sorry for the lack of an indent here, the diff would have been unreadable. -  # See https://github.com/Homebrew/homebrew/pull/9986 -  def check_path_for_trailing_slashes -    bad_paths = ENV["PATH"].split(File::PATH_SEPARATOR).select { |p| p[-1..-1] == "/" } -    return if bad_paths.empty? -    inject_file_list bad_paths, <<-EOS.undent -    Some directories in your path end in a slash. -    Directories in your path should not end in a slash. This can break other -    doctor checks. The following directories should be edited: -    EOS -  end - -  # Installing MacGPG2 interferes with Homebrew in a big way -  # https://github.com/GPGTools/MacGPG2 -  def check_for_macgpg2 -    return if File.exist? "/usr/local/MacGPG2/share/gnupg/VERSION" - -    suspects = %w[ -      /Applications/start-gpg-agent.app -      /Library/Receipts/libiconv1.pkg -      /usr/local/MacGPG2 -    ] - -    if suspects.any? { |f| File.exist? f } then <<-EOS.undent -      You may have installed MacGPG2 via the package installer. -      Several other checks in this script will turn up problems, such as stray -      dylibs in /usr/local and permissions issues with share and man in /usr/local/. -    EOS -    end -  end - -  # Anaconda installs multiple system & brew dupes, including OpenSSL, Python, -  # sqlite, libpng, Qt, etc. Regularly breaks compile on Vim, MacVim and others. -  # Is flagged as part of the *-config script checks below, but people seem -  # to ignore those as warnings rather than extremely likely breakage. -  def check_for_anaconda -    return unless which("anaconda") -    return unless which("python") - -    anaconda = which("anaconda").realpath.dirname -    python_binary = Utils.popen_read which("python"), "-c", "import sys; sys.stdout.write(sys.executable)" -    python = Pathname.new(python_binary).realpath.dirname - -    # Only warn if Python lives with Anaconda, since is most problematic case. -    if python == anaconda then <<-EOS.undent -      Anaconda is known to frequently break Homebrew builds, including Vim and -      MacVim, due to bundling many duplicates of system and Homebrew-available -      tools. - -      If you encounter a build failure please temporarily remove Anaconda -      from your $PATH and attempt the build again prior to reporting the -      failure to us. Thanks! -    EOS -    end -  end - -  def __check_stray_files(dir, pattern, white_list, message) -    return unless File.directory?(dir) - -    files = Dir.chdir(dir) do -      Dir[pattern].select { |f| File.file?(f) && !File.symlink?(f) } - Dir.glob(white_list) -    end.map { |file| File.join(dir, file) } - -    inject_file_list(files, message) unless files.empty? -  end - -  def check_for_stray_dylibs -    # Dylibs which are generally OK should be added to this list, -    # with a short description of the software they come with. -    white_list = [ -      "libfuse.2.dylib", # MacFuse -      "libfuse_ino64.2.dylib", # MacFuse -      "libmacfuse_i32.2.dylib", # OSXFuse MacFuse compatibility layer -      "libmacfuse_i64.2.dylib", # OSXFuse MacFuse compatibility layer -      "libosxfuse_i32.2.dylib", # OSXFuse -      "libosxfuse_i64.2.dylib", # OSXFuse -      "libTrAPI.dylib", # TrAPI / Endpoint Security VPN -      "libntfs-3g.*.dylib", # NTFS-3G -      "libntfs.*.dylib", # NTFS-3G -      "libublio.*.dylib", # NTFS-3G -      "libUFSDNTFS.dylib", # Paragon NTFS -      "libUFSDExtFS.dylib", # Paragon ExtFS -    ] - -    __check_stray_files "/usr/local/lib", "*.dylib", white_list, <<-EOS.undent -    Unbrewed dylibs were found in /usr/local/lib. -    If you didn't put them there on purpose they could cause problems when -    building Homebrew formulae, and may need to be deleted. - -    Unexpected dylibs: -  EOS -  end - -  def check_for_stray_static_libs -    # Static libs which are generally OK should be added to this list, -    # with a short description of the software they come with. -    white_list = [ -      "libsecurity_agent_client.a", # OS X 10.8.2 Supplemental Update -      "libsecurity_agent_server.a", # OS X 10.8.2 Supplemental Update -      "libntfs-3g.a", # NTFS-3G -      "libntfs.a", # NTFS-3G -      "libublio.a", # NTFS-3G -    ] - -    __check_stray_files "/usr/local/lib", "*.a", white_list, <<-EOS.undent -    Unbrewed static libraries were found in /usr/local/lib. -    If you didn't put them there on purpose they could cause problems when -    building Homebrew formulae, and may need to be deleted. - -    Unexpected static libraries: -  EOS -  end - -  def check_for_stray_pcs -    # Package-config files which are generally OK should be added to this list, -    # with a short description of the software they come with. -    white_list = [ -      "fuse.pc", # OSXFuse/MacFuse -      "macfuse.pc", # OSXFuse MacFuse compatibility layer -      "osxfuse.pc", # OSXFuse -      "libntfs-3g.pc", # NTFS-3G -      "libublio.pc", # NTFS-3G -    ] - -    __check_stray_files "/usr/local/lib/pkgconfig", "*.pc", white_list, <<-EOS.undent -    Unbrewed .pc files were found in /usr/local/lib/pkgconfig. -    If you didn't put them there on purpose they could cause problems when -    building Homebrew formulae, and may need to be deleted. - -    Unexpected .pc files: -  EOS -  end - -  def check_for_stray_las -    white_list = [ -      "libfuse.la", # MacFuse -      "libfuse_ino64.la", # MacFuse -      "libosxfuse_i32.la", # OSXFuse -      "libosxfuse_i64.la", # OSXFuse -      "libntfs-3g.la", # NTFS-3G -      "libntfs.la", # NTFS-3G -      "libublio.la", # NTFS-3G -    ] - -    __check_stray_files "/usr/local/lib", "*.la", white_list, <<-EOS.undent -    Unbrewed .la files were found in /usr/local/lib. -    If you didn't put them there on purpose they could cause problems when -    building Homebrew formulae, and may need to be deleted. - -    Unexpected .la files: -  EOS -  end - -  def check_for_stray_headers -    white_list = [ -      "fuse.h", # MacFuse -      "fuse/**/*.h", # MacFuse -      "macfuse/**/*.h", # OSXFuse MacFuse compatibility layer -      "osxfuse/**/*.h", # OSXFuse -      "ntfs/**/*.h", # NTFS-3G -      "ntfs-3g/**/*.h", # NTFS-3G -    ] - -    __check_stray_files "/usr/local/include", "**/*.h", white_list, <<-EOS.undent -    Unbrewed header files were found in /usr/local/include. -    If you didn't put them there on purpose they could cause problems when -    building Homebrew formulae, and may need to be deleted. - -    Unexpected header files: -  EOS -  end - -  def check_for_other_package_managers -    ponk = MacOS.macports_or_fink -    unless ponk.empty? -      <<-EOS.undent -      You have MacPorts or Fink installed: -        #{ponk.join(", ")} - -      This can cause trouble. You don't have to uninstall them, but you may want to -      temporarily move them out of the way, e.g. - -        sudo mv /opt/local ~/macports -    EOS -    end -  end - -  def check_for_broken_symlinks -    broken_symlinks = [] - -    Keg::PRUNEABLE_DIRECTORIES.each do |d| -      next unless d.directory? -      d.find do |path| -        if path.symlink? && !path.resolved_path_exists? -          broken_symlinks << path -        end -      end -    end -    return if broken_symlinks.empty? -    inject_file_list broken_symlinks, <<-EOS.undent -    Broken symlinks were found. Remove them with `brew prune`: -    EOS -  end - -  def check_for_unsupported_osx -    if !ARGV.homebrew_developer? && OS::Mac.prerelease? then <<-EOS.undent -    You are using OS X #{MacOS.version}. -    We do not provide support for this pre-release version. -    You may encounter build failures or other breakages. -    EOS -    end -  end - -  # TODO: distill down into single method definition a la BuildToolsError -  if MacOS.version >= "10.9" -    def check_for_installed_developer_tools -      unless MacOS::Xcode.installed? || MacOS::CLT.installed? then <<-EOS.undent -      No developer tools installed. -      Install the Command Line Tools: -        xcode-select --install -      EOS -      end -    end - -    if OS::Mac.prerelease? -      def check_xcode_up_to_date -        if MacOS::Xcode.installed? && MacOS::Xcode.outdated? -          <<-EOS.undent -        Your Xcode (#{MacOS::Xcode.version}) is outdated -        Please update to Xcode #{MacOS::Xcode.latest_version}. -        Xcode can be updated from -          https://developer.apple.com/xcode/downloads/ -        EOS -        end -      end -    else -      def check_xcode_up_to_date -        if MacOS::Xcode.installed? && MacOS::Xcode.outdated? -          <<-EOS.undent -        Your Xcode (#{MacOS::Xcode.version}) is outdated -        Please update to Xcode #{MacOS::Xcode.latest_version}. -        Xcode can be updated from the App Store. -        EOS -        end -      end -    end - -    def check_clt_up_to_date -      if MacOS::CLT.installed? && MacOS::CLT.outdated? then <<-EOS.undent -      A newer Command Line Tools release is available. -      Update them from Software Update in the App Store. -      EOS -      end -    end -  elsif MacOS.version == "10.8" || MacOS.version == "10.7" -    def check_for_installed_developer_tools -      unless MacOS::Xcode.installed? || MacOS::CLT.installed? then <<-EOS.undent -      No developer tools installed. -      You should install the Command Line Tools. -      The standalone package can be obtained from -        https://developer.apple.com/downloads -      or it can be installed via Xcode's preferences. -      EOS -      end -    end - -    def check_xcode_up_to_date -      if MacOS::Xcode.installed? && MacOS::Xcode.outdated? then <<-EOS.undent -      Your Xcode (#{MacOS::Xcode.version}) is outdated -      Please update to Xcode #{MacOS::Xcode.latest_version}. -      Xcode can be updated from -        https://developer.apple.com/xcode/downloads/ -      EOS -      end -    end - -    def check_clt_up_to_date -      if MacOS::CLT.installed? && MacOS::CLT.outdated? then <<-EOS.undent -      A newer Command Line Tools release is available. -      The standalone package can be obtained from -        https://developer.apple.com/downloads -      or it can be installed via Xcode's preferences. -      EOS -      end -    end -  else -    def check_for_installed_developer_tools -      unless MacOS::Xcode.installed? then <<-EOS.undent -      Xcode is not installed. Most formulae need Xcode to build. -      It can be installed from -        https://developer.apple.com/xcode/downloads/ -      EOS -      end -    end - -    def check_xcode_up_to_date -      if MacOS::Xcode.installed? && MacOS::Xcode.outdated? then <<-EOS.undent -      Your Xcode (#{MacOS::Xcode.version}) is outdated -      Please update to Xcode #{MacOS::Xcode.latest_version}. -      Xcode can be updated from -        https://developer.apple.com/xcode/downloads/ -      EOS -      end -    end -  end - -  def check_for_osx_gcc_installer -    if (MacOS.version < "10.7" || MacOS::Xcode.version > "4.1") && \ -       MacOS.clang_version == "2.1" -      message = <<-EOS.undent -    You seem to have osx-gcc-installer installed. -    Homebrew doesn't support osx-gcc-installer. It causes many builds to fail and -    is an unlicensed distribution of really old Xcode files. -    EOS -      if MacOS.version >= :mavericks -        message += <<-EOS.undent -          Please run `xcode-select --install` to install the CLT. -        EOS -      elsif MacOS.version >= :lion -        message += <<-EOS.undent -        Please install the CLT or Xcode #{MacOS::Xcode.latest_version}. -      EOS -      else -        message += <<-EOS.undent -        Please install Xcode #{MacOS::Xcode.latest_version}. -      EOS -      end -    end -  end - -  def check_for_stray_developer_directory -    # if the uninstaller script isn't there, it's a good guess neither are -    # any troublesome leftover Xcode files -    uninstaller = Pathname.new("/Developer/Library/uninstall-developer-folder") -    if MacOS::Xcode.version >= "4.3" && uninstaller.exist? then <<-EOS.undent -    You have leftover files from an older version of Xcode. -    You should delete them using: -      #{uninstaller} -    EOS -    end -  end - -  def check_for_bad_install_name_tool -    return if MacOS.version < "10.9" - -    libs = Pathname.new("/usr/bin/install_name_tool").dynamically_linked_libraries - -    # otool may not work, for example if the Xcode license hasn't been accepted yet -    return if libs.empty? - -    unless libs.include? "/usr/lib/libxcselect.dylib" then <<-EOS.undent -    You have an outdated version of /usr/bin/install_name_tool installed. -    This will cause binary package installations to fail. -    This can happen if you install osx-gcc-installer or RailsInstaller. -    To restore it, you must reinstall OS X or restore the binary from -    the OS packages. -    EOS -    end -  end - -  def __check_subdir_access(base) -    target = HOMEBREW_PREFIX+base -    return unless target.exist? - -    cant_read = [] - -    target.find do |d| -      next unless d.directory? -      cant_read << d unless d.writable_real? -    end - -    return if cant_read.empty? -    inject_file_list cant_read.sort, <<-EOS.undent -      Some directories in #{target} aren't writable. -      This can happen if you "sudo make install" software that isn't managed -      by Homebrew. If a brew tries to add locale information to one of these -      directories, then the install will fail during the link step. - -      You should probably `sudo chown -R $(whoami)` them: -    EOS -  end - -  def check_access_share_locale -    __check_subdir_access "share/locale" -  end - -  def check_access_share_man -    __check_subdir_access "share/man" -  end - -  def check_access_homebrew_repository -    unless HOMEBREW_REPOSITORY.writable_real? then <<-EOS.undent -      The #{HOMEBREW_REPOSITORY} is not writable. - -      You should probably change the ownership and permissions of #{HOMEBREW_REPOSITORY} -      back to your user account. -        sudo chown -R $(whoami) #{HOMEBREW_REPOSITORY} -    EOS -    end -  end - -  def check_access_usr_local -    return unless HOMEBREW_PREFIX.to_s == "/usr/local" - -    unless HOMEBREW_PREFIX.writable_real? then <<-EOS.undent -    The /usr/local directory is not writable. -    Even if this directory was writable when you installed Homebrew, other -    software may change permissions on this directory. Some versions of the -    "InstantOn" component of Airfoil or running Cocktail cleanup/optimizations -    are known to do this. - -    You should probably change the ownership and permissions of /usr/local -    back to your user account. -      sudo chown -R $(whoami):admin /usr/local -    EOS -    end -  end - -  def check_tmpdir_sticky_bit -    # Repair Disk Permissions was removed(?) in El Capitan. -    # https://support.apple.com/en-us/HT201560 -    if MacOS.version < "10.11" -      fix_message = "Please run \"Repair Disk Permissions\" in Disk Utility." -    else -      fix_message = "Please execute `sudo chmod +t #{HOMEBREW_TEMP}` in your Terminal" -    end - -    world_writable = HOMEBREW_TEMP.stat.mode & 0777 == 0777 -    if world_writable && !HOMEBREW_TEMP.sticky? then <<-EOS.undent -    #{HOMEBREW_TEMP} is world-writable but does not have the sticky bit set. -    #{fix_message} -    EOS -    end -  end - -  (Keg::TOP_LEVEL_DIRECTORIES + ["lib/pkgconfig"]).each do |d| -    define_method("check_access_#{d.sub("/", "_")}") do -      dir = HOMEBREW_PREFIX.join(d) -      if dir.exist? && !dir.writable_real? then <<-EOS.undent -      #{dir} isn't writable. - -      This can happen if you "sudo make install" software that isn't managed -      by Homebrew. If a formula tries to write a file to this directory, the -      install will fail during the link step. - -      You should probably change the ownership and permissions of #{dir} -      back to your user account. -        sudo chown -R $(whoami) #{dir} -      EOS -      end -    end -  end - -  def check_access_site_packages -    if Language::Python.homebrew_site_packages.exist? && !Language::Python.homebrew_site_packages.writable_real? -      <<-EOS.undent -      #{Language::Python.homebrew_site_packages} isn't writable. -      This can happen if you "sudo pip install" software that isn't managed -      by Homebrew. If you install a formula with Python modules, the install -      will fail during the link step. - -      You should probably change the ownership and permissions of #{Language::Python.homebrew_site_packages} -      back to your user account. -        sudo chown -R $(whoami) #{Language::Python.homebrew_site_packages} -    EOS -    end -  end - -  def check_access_logs -    if HOMEBREW_LOGS.exist? && !HOMEBREW_LOGS.writable_real? -      <<-EOS.undent -      #{HOMEBREW_LOGS} isn't writable. -      Homebrew writes debugging logs to this location. - -      You should probably change the ownership and permissions of #{HOMEBREW_LOGS} -      back to your user account. -        sudo chown -R $(whoami) #{HOMEBREW_LOGS} -    EOS -    end -  end - -  def check_access_cache -    if HOMEBREW_CACHE.exist? && !HOMEBREW_CACHE.writable_real? -      <<-EOS.undent -      #{HOMEBREW_CACHE} isn't writable. -      This can happen if you run `brew install` or `brew fetch` as another user. -      Homebrew caches downloaded files to this location. - -      You should probably change the ownership and permissions of #{HOMEBREW_CACHE} -      back to your user account. -        sudo chown -R $(whoami) #{HOMEBREW_CACHE} -    EOS -    end -  end - -  def check_access_cellar -    if HOMEBREW_CELLAR.exist? && !HOMEBREW_CELLAR.writable_real? -      <<-EOS.undent -      #{HOMEBREW_CELLAR} isn't writable. - -      You should probably change the ownership and permissions of #{HOMEBREW_CELLAR} -      back to your user account. -        sudo chown -R $(whoami) #{HOMEBREW_CELLAR} -    EOS -    end -  end - -  def check_access_prefix_opt -    opt = HOMEBREW_PREFIX.join("opt") -    if opt.exist? && !opt.writable_real? -      <<-EOS.undent -      #{opt} isn't writable. -      You should probably change the ownership and permissions of #{opt} -      back to your user account. -        sudo chown -R $(whoami) #{opt} -    EOS -    end -  end - -  def check_ruby_version -    ruby_version = MacOS.version >= "10.9" ? "2.0" : "1.8" -    if RUBY_VERSION[/\d\.\d/] != ruby_version then <<-EOS.undent -    Ruby version #{RUBY_VERSION} is unsupported on #{MacOS.version}. Homebrew -    is developed and tested on Ruby #{ruby_version}, and may not work correctly -    on other Rubies. Patches are accepted as long as they don't cause breakage -    on supported Rubies. -    EOS -    end -  end - -  def check_homebrew_prefix -    unless HOMEBREW_PREFIX.to_s == "/usr/local" -      <<-EOS.undent -      Your Homebrew is not installed to /usr/local -      You can install Homebrew anywhere you want, but some brews may only build -      correctly if you install in /usr/local. Sorry! -    EOS -    end -  end - -  def check_xcode_prefix -    prefix = MacOS::Xcode.prefix -    return if prefix.nil? -    if prefix.to_s.match(" ") -      <<-EOS.undent -      Xcode is installed to a directory with a space in the name. -      This will cause some formulae to fail to build. -    EOS -    end -  end - -  def check_xcode_prefix_exists -    prefix = MacOS::Xcode.prefix -    return if prefix.nil? -    unless prefix.exist? -      <<-EOS.undent -      The directory Xcode is reportedly installed to doesn't exist: -        #{prefix} -      You may need to `xcode-select` the proper path if you have moved Xcode. -    EOS -    end -  end - -  def check_xcode_select_path -    if !MacOS::CLT.installed? && !File.file?("#{MacOS.active_developer_dir}/usr/bin/xcodebuild") -      path = MacOS::Xcode.bundle_path -      path = "/Developer" if path.nil? || !path.directory? -      <<-EOS.undent -      Your Xcode is configured with an invalid path. -      You should change it to the correct path: -        sudo xcode-select -switch #{path} -    EOS -    end -  end - -  def check_user_path_1 -    $seen_prefix_bin = false -    $seen_prefix_sbin = false - -    out = nil - -    paths.each do |p| -      case p -      when "/usr/bin" -        unless $seen_prefix_bin -          # only show the doctor message if there are any conflicts -          # rationale: a default install should not trigger any brew doctor messages -          conflicts = Dir["#{HOMEBREW_PREFIX}/bin/*"]. -                      map { |fn| File.basename fn }. -                      select { |bn| File.exist? "/usr/bin/#{bn}" } - -          if conflicts.size > 0 -            out = inject_file_list conflicts, <<-EOS.undent -            /usr/bin occurs before #{HOMEBREW_PREFIX}/bin -            This means that system-provided programs will be used instead of those -            provided by Homebrew. The following tools exist at both paths: -            EOS - -            out += <<-EOS.undent - -            Consider setting your PATH so that #{HOMEBREW_PREFIX}/bin -            occurs before /usr/bin. Here is a one-liner: -                echo 'export PATH="#{HOMEBREW_PREFIX}/bin:$PATH"' >> #{shell_profile} -          EOS -          end -        end -      when "#{HOMEBREW_PREFIX}/bin" -        $seen_prefix_bin = true -      when "#{HOMEBREW_PREFIX}/sbin" -        $seen_prefix_sbin = true -      end -    end -    out -  end - -  def check_user_path_2 -    unless $seen_prefix_bin -      <<-EOS.undent -      Homebrew's bin was not found in your PATH. -      Consider setting the PATH for example like so -          echo 'export PATH="#{HOMEBREW_PREFIX}/bin:$PATH"' >> #{shell_profile} -    EOS -    end -  end - -  def check_user_path_3 -    # Don't complain about sbin not being in the path if it doesn't exist -    sbin = (HOMEBREW_PREFIX+"sbin") -    if sbin.directory? && sbin.children.length > 0 -      unless $seen_prefix_sbin -        <<-EOS.undent -        Homebrew's sbin was not found in your PATH but you have installed -        formulae that put executables in #{HOMEBREW_PREFIX}/sbin. -        Consider setting the PATH for example like so -            echo 'export PATH="#{HOMEBREW_PREFIX}/sbin:$PATH"' >> #{shell_profile} -      EOS -      end -    end -  end - -  def check_for_bad_curl -    if MacOS.version <= "10.8" && !Formula["curl"].installed? then <<-EOS.undent -      The system curl on 10.8 and below is often incapable of supporting -      modern secure connections & will fail on fetching formulae. - -      We recommend you: -        brew install curl -      EOS -    end -  end - -  def check_user_curlrc -    if %w[CURL_HOME HOME].any? { |key| ENV[key] && File.exist?("#{ENV[key]}/.curlrc") } then <<-EOS.undent -    You have a curlrc file -    If you have trouble downloading packages with Homebrew, then maybe this -    is the problem? If the following command doesn't work, then try removing -    your curlrc: -      curl https://github.com -    EOS -    end -  end - -  def check_for_unsupported_curl_vars -    # Support for SSL_CERT_DIR seemed to be removed in the 10.10.5 update. -    if MacOS.version >= :yosemite && !ENV["SSL_CERT_DIR"].nil? then <<-EOS.undent -      SSL_CERT_DIR support was removed from Apple's curl. -      If fetching formulae fails you should: -        unset SSL_CERT_DIR -      and remove it from #{shell_profile} if present. -      EOS -    end -  end - -  def check_which_pkg_config -    binary = which "pkg-config" -    return if binary.nil? - -    mono_config = Pathname.new("/usr/bin/pkg-config") -    if mono_config.exist? && mono_config.realpath.to_s.include?("Mono.framework") then <<-EOS.undent -    You have a non-Homebrew 'pkg-config' in your PATH: -      /usr/bin/pkg-config => #{mono_config.realpath} - -    This was most likely created by the Mono installer. `./configure` may -    have problems finding brew-installed packages using this other pkg-config. - -    Mono no longer installs this file as of 3.0.4. You should -    `sudo rm /usr/bin/pkg-config` and upgrade to the latest version of Mono. -    EOS -    elsif binary.to_s != "#{HOMEBREW_PREFIX}/bin/pkg-config" then <<-EOS.undent -    You have a non-Homebrew 'pkg-config' in your PATH: -      #{binary} - -    `./configure` may have problems finding brew-installed packages using -    this other pkg-config. -    EOS -    end -  end - -  def check_for_gettext -    find_relative_paths("lib/libgettextlib.dylib", -                        "lib/libintl.dylib", -                        "include/libintl.h") - -    return if @found.empty? - -    # Our gettext formula will be caught by check_linked_keg_only_brews -    f = Formulary.factory("gettext") rescue nil -    return if f && f.linked_keg.directory? && @found.all? do |path| -      Pathname.new(path).realpath.to_s.start_with? "#{HOMEBREW_CELLAR}/gettext" -    end - -    s = <<-EOS.undent_________________________________________________________72 -      gettext files detected at a system prefix -      These files can cause compilation and link failures, especially if they -      are compiled with improper architectures. Consider removing these files: -      EOS -    inject_file_list(@found, s) -  end - -  def check_for_iconv -    unless find_relative_paths("lib/libiconv.dylib", "include/iconv.h").empty? -      if (f = Formulary.factory("libiconv") rescue nil) && f.linked_keg.directory? -        unless f.keg_only? then <<-EOS.undent -        A libiconv formula is installed and linked -        This will break stuff. For serious. Unlink it. -        EOS -        end -      else -        s = <<-EOS.undent_________________________________________________________72 -          libiconv files detected at a system prefix other than /usr -          Homebrew doesn't provide a libiconv formula, and expects to link against -          the system version in /usr. libiconv in other prefixes can cause -          compile or link failure, especially if compiled with improper -          architectures. OS X itself never installs anything to /usr/local so -          it was either installed by a user or some other third party software. - -          tl;dr: delete these files: -          EOS -        inject_file_list(@found, s) -      end -    end -  end - -  def check_for_config_scripts -    return unless HOMEBREW_CELLAR.exist? -    real_cellar = HOMEBREW_CELLAR.realpath - -    scripts = [] - -    whitelist = %W[ -      /usr/bin /usr/sbin -      /usr/X11/bin /usr/X11R6/bin /opt/X11/bin -      #{HOMEBREW_PREFIX}/bin #{HOMEBREW_PREFIX}/sbin -      /Applications/Server.app/Contents/ServerRoot/usr/bin -      /Applications/Server.app/Contents/ServerRoot/usr/sbin -    ].map(&:downcase) - -    paths.each do |p| -      next if whitelist.include?(p.downcase) || !File.directory?(p) - -      realpath = Pathname.new(p).realpath.to_s -      next if realpath.start_with?(real_cellar.to_s, HOMEBREW_CELLAR.to_s) - -      scripts += Dir.chdir(p) { Dir["*-config"] }.map { |c| File.join(p, c) } -    end - -    return if scripts.empty? -    inject_file_list scripts, <<-EOS.undent -      "config" scripts exist outside your system or Homebrew directories. -      `./configure` scripts often look for *-config scripts to determine if -      software packages are installed, and what additional flags to use when -      compiling and linking. - -      Having additional scripts in your path can confuse software installed via -      Homebrew if the config script overrides a system or Homebrew provided -      script of the same name. We found the following "config" scripts: -    EOS -  end - -  def check_DYLD_vars -    found = ENV.keys.grep(/^DYLD_/) -    return if found.empty? -    s = inject_file_list found.map { |e| "#{e}: #{ENV.fetch(e)}" }, <<-EOS.undent -      Setting DYLD_* vars can break dynamic linking. -      Set variables: -    EOS -    if found.include? "DYLD_INSERT_LIBRARIES" -      s += <<-EOS.undent - -      Setting DYLD_INSERT_LIBRARIES can cause Go builds to fail. -      Having this set is common if you use this software: -        http://asepsis.binaryage.com/ -      EOS -    end -    s -  end - -  def check_for_symlinked_cellar -    return unless HOMEBREW_CELLAR.exist? -    if HOMEBREW_CELLAR.symlink? -      <<-EOS.undent -      Symlinked Cellars can cause problems. -      Your Homebrew Cellar is a symlink: #{HOMEBREW_CELLAR} -                      which resolves to: #{HOMEBREW_CELLAR.realpath} - -      The recommended Homebrew installations are either: -      (A) Have Cellar be a real directory inside of your HOMEBREW_PREFIX -      (B) Symlink "bin/brew" into your prefix, but don't symlink "Cellar". - -      Older installations of Homebrew may have created a symlinked Cellar, but this can -      cause problems when two formula install to locations that are mapped on top of each -      other during the linking step. -    EOS -    end -  end - -  def check_for_multiple_volumes -    return unless HOMEBREW_CELLAR.exist? -    volumes = Volumes.new - -    # Find the volumes for the TMP folder & HOMEBREW_CELLAR -    real_cellar = HOMEBREW_CELLAR.realpath - -    tmp = Pathname.new(Dir.mktmpdir("doctor", HOMEBREW_TEMP)) -    real_temp = tmp.realpath.parent - -    where_cellar = volumes.which real_cellar -    where_temp = volumes.which real_temp - -    Dir.delete tmp - -    unless where_cellar == where_temp then <<-EOS.undent -    Your Cellar and TEMP directories are on different volumes. -    OS X won't move relative symlinks across volumes unless the target file already -    exists. Brews known to be affected by this are Git and Narwhal. - -    You should set the "HOMEBREW_TEMP" environmental variable to a suitable -    directory on the same volume as your Cellar. -    EOS -    end -  end - -  def check_filesystem_case_sensitive -    volumes = Volumes.new -    case_sensitive_vols = [HOMEBREW_PREFIX, HOMEBREW_REPOSITORY, HOMEBREW_CELLAR, HOMEBREW_TEMP].select do |dir| -      # We select the dir as being case-sensitive if either the UPCASED or the -      # downcased variant is missing. -      # Of course, on a case-insensitive fs, both exist because the os reports so. -      # In the rare situation when the user has indeed a downcased and an upcased -      # dir (e.g. /TMP and /tmp) this check falsely thinks it is case-insensitive -      # but we don't care beacuse: 1. there is more than one dir checked, 2. the -      # check is not vital and 3. we would have to touch files otherwise. -      upcased = Pathname.new(dir.to_s.upcase) -      downcased = Pathname.new(dir.to_s.downcase) -      dir.exist? && !(upcased.exist? && downcased.exist?) -    end.map { |case_sensitive_dir| volumes.get_mounts(case_sensitive_dir) }.uniq -    return if case_sensitive_vols.empty? -    <<-EOS.undent -    The filesystem on #{case_sensitive_vols.join(",")} appears to be case-sensitive. -    The default OS X filesystem is case-insensitive. Please report any apparent problems. -  EOS -  end - -  def __check_git_version -    # https://help.github.com/articles/https-cloning-errors -    `git --version`.chomp =~ /git version ((?:\d+\.?)+)/ - -    if $1 && Version.new($1) < Version.new("1.7.10") -      git_upgrade_cmd = Formula["git"].any_version_installed? ? "upgrade" : "install" - -      <<-EOS.undent -        An outdated version of Git was detected in your PATH. -        Git 1.7.10 or newer is required to perform checkouts over HTTPS from GitHub. -        Please upgrade: brew #{git_upgrade_cmd} git -      EOS -    end -  end - -  def check_for_git -    if Utils.git_available? -      __check_git_version -    else <<-EOS.undent -    Git could not be found in your PATH. -    Homebrew uses Git for several internal functions, and some formulae use Git -    checkouts instead of stable tarballs. You may want to install Git: -      brew install git -    EOS -    end -  end - -  def check_git_newline_settings -    return unless Utils.git_available? - -    autocrlf = `git config --get core.autocrlf`.chomp - -    if autocrlf == "true" then <<-EOS.undent -    Suspicious Git newline settings found. - -    The detected Git newline settings will cause checkout problems: -      core.autocrlf = #{autocrlf} - -    If you are not routinely dealing with Windows-based projects, -    consider removing these by running: -    `git config --global core.autocrlf input` -    EOS -    end -  end - -  def check_git_origin -    return if !Utils.git_available? || !(HOMEBREW_REPOSITORY/".git").exist? - -    origin = Homebrew.git_origin - -    if origin.nil? then <<-EOS.undent -      Missing git origin remote. - -      Without a correctly configured origin, Homebrew won't update -      properly. You can solve this by adding the Homebrew remote: -        cd #{HOMEBREW_REPOSITORY} -        git remote add origin https://github.com/Homebrew/homebrew.git -      EOS -    elsif origin !~ /(mxcl|Homebrew)\/homebrew(\.git)?$/ then <<-EOS.undent -      Suspicious git origin remote found. - -      With a non-standard origin, Homebrew won't pull updates from -      the main repository. The current git origin is: -        #{origin} - -      Unless you have compelling reasons, consider setting the -      origin remote to point at the main repository, located at: -        https://github.com/Homebrew/homebrew.git -      EOS -    end -  end - -  def check_for_autoconf -    return unless MacOS::Xcode.provides_autotools? - -    autoconf = which("autoconf") -    safe_autoconfs = %w[/usr/bin/autoconf /Developer/usr/bin/autoconf] -    unless autoconf.nil? || safe_autoconfs.include?(autoconf.to_s) then <<-EOS.undent -    An "autoconf" in your path blocks the Xcode-provided version at: -      #{autoconf} - -    This custom autoconf may cause some Homebrew formulae to fail to compile. -    EOS -    end -  end - -  def __check_linked_brew(f) -    f.installed_prefixes.each do |prefix| -      prefix.find do |src| -        next if src == prefix -        dst = HOMEBREW_PREFIX + src.relative_path_from(prefix) -        return true if dst.symlink? && src == dst.resolved_path -      end -    end - -    false -  end - -  def check_for_linked_keg_only_brews -    return unless HOMEBREW_CELLAR.exist? - -    linked = Formula.installed.select do |f| -      f.keg_only? && __check_linked_brew(f) -    end - -    return if linked.empty? -    inject_file_list linked.map(&:full_name), <<-EOS.undent -    Some keg-only formula are linked into the Cellar. -    Linking a keg-only formula, such as gettext, into the cellar with -    `brew link <formula>` will cause other formulae to detect them during -    the `./configure` step. This may cause problems when compiling those -    other formulae. - -    Binaries provided by keg-only formulae may override system binaries -    with other strange results. - -    You may wish to `brew unlink` these brews: -    EOS -  end - -  def check_for_other_frameworks -    # Other frameworks that are known to cause problems when present -    %w[expat.framework libexpat.framework libcurl.framework]. -      map { |frmwrk| "/Library/Frameworks/#{frmwrk}" }. -      select { |frmwrk| File.exist? frmwrk }. -      map do |frmwrk| -        <<-EOS.undent -      #{frmwrk} detected -      This can be picked up by CMake's build system and likely cause the build to -      fail. You may need to move this file out of the way to compile CMake. -      EOS -      end.join -  end - -  def check_tmpdir -    tmpdir = ENV["TMPDIR"] -    "TMPDIR #{tmpdir.inspect} doesn't exist." unless tmpdir.nil? || File.directory?(tmpdir) -  end - -  def check_missing_deps -    return unless HOMEBREW_CELLAR.exist? -    missing = Set.new -    Homebrew.missing_deps(Formula.installed).each_value do |deps| -      missing.merge(deps) -    end - -    if missing.any? then <<-EOS.undent -    Some installed formula are missing dependencies. -    You should `brew install` the missing dependencies: - -        brew install #{missing.sort_by(&:full_name) * " "} - -    Run `brew missing` for more details. -    EOS -    end -  end - -  def check_git_status -    return unless Utils.git_available? -    HOMEBREW_REPOSITORY.cd do -      unless `git status --untracked-files=all --porcelain -- Library/Homebrew/ 2>/dev/null`.chomp.empty? -        <<-EOS.undent_________________________________________________________72 -      You have uncommitted modifications to Homebrew -      If this is a surprise to you, then you should stash these modifications. -      Stashing returns Homebrew to a pristine state but can be undone -      should you later need to do so for some reason. -          cd #{HOMEBREW_LIBRARY} && git stash && git clean -d -f -      EOS -      end -    end -  end - -  def check_for_enthought_python -    if which "enpkg" then <<-EOS.undent -    Enthought Python was found in your PATH. -    This can cause build problems, as this software installs its own -    copies of iconv and libxml2 into directories that are picked up by -    other build systems. -    EOS -    end -  end - -  def check_for_library_python -    if File.exist?("/Library/Frameworks/Python.framework") then <<-EOS.undent -    Python is installed at /Library/Frameworks/Python.framework - -    Homebrew only supports building against the System-provided Python or a -    brewed Python. In particular, Pythons installed to /Library can interfere -    with other software installs. -    EOS -    end -  end - -  def check_for_old_homebrew_share_python_in_path -    s = "" -    ["", "3"].map do |suffix| -      if paths.include?((HOMEBREW_PREFIX/"share/python#{suffix}").to_s) -        s += "#{HOMEBREW_PREFIX}/share/python#{suffix} is not needed in PATH.\n" -      end -    end -    unless s.empty? -      s += <<-EOS.undent -      Formerly homebrew put Python scripts you installed via `pip` or `pip3` -      (or `easy_install`) into that directory above but now it can be removed -      from your PATH variable. -      Python scripts will now install into #{HOMEBREW_PREFIX}/bin. -      You can delete anything, except 'Extras', from the #{HOMEBREW_PREFIX}/share/python -      (and #{HOMEBREW_PREFIX}/share/python3) dir and install affected Python packages -      anew with `pip install --upgrade`. -    EOS -    end -  end - -  def check_for_bad_python_symlink -    return unless which "python" -    `python -V 2>&1` =~ /Python (\d+)\./ -    # This won't be the right warning if we matched nothing at all -    return if $1.nil? -    unless $1 == "2" then <<-EOS.undent -    python is symlinked to python#{$1} -    This will confuse build scripts and in general lead to subtle breakage. -    EOS -    end -  end - -  def check_for_non_prefixed_coreutils -    gnubin = "#{Formulary.factory("coreutils").prefix}/libexec/gnubin" -    if paths.include? gnubin then <<-EOS.undent -      Putting non-prefixed coreutils in your path can cause gmp builds to fail. -      EOS -    end -  end - -  def check_for_non_prefixed_findutils -    default_names = Tab.for_name("findutils").with? "default-names" -    if default_names then <<-EOS.undent -      Putting non-prefixed findutils in your path can cause python builds to fail. -      EOS -    end -  end - -  def check_for_pydistutils_cfg_in_home -    if File.exist? "#{ENV["HOME"]}/.pydistutils.cfg" then <<-EOS.undent -    A .pydistutils.cfg file was found in $HOME, which may cause Python -    builds to fail. See: -      https://bugs.python.org/issue6138 -      https://bugs.python.org/issue4655 -    EOS -    end -  end - -  def check_for_outdated_homebrew -    return unless Utils.git_available? -    HOMEBREW_REPOSITORY.cd do -      if File.directory? ".git" -        local = `git rev-parse -q --verify refs/remotes/origin/master`.chomp -        remote = /^([a-f0-9]{40})/.match(`git ls-remote origin refs/heads/master 2>/dev/null`) -        if remote.nil? || local == remote[0] -          return -        end -      end - -      timestamp = if File.directory? ".git" -        `git log -1 --format="%ct" HEAD`.to_i -      else -        HOMEBREW_LIBRARY.mtime.to_i -      end - -      if Time.now.to_i - timestamp > 60 * 60 * 24 then <<-EOS.undent -      Your Homebrew is outdated. -      You haven't updated for at least 24 hours. This is a long time in brewland! -      To update Homebrew, run `brew update`. -      EOS -      end -    end -  end - -  def check_for_unlinked_but_not_keg_only -    unlinked = Formula.racks.reject do |rack| -      if !(HOMEBREW_REPOSITORY/"Library/LinkedKegs"/rack.basename).directory? -        begin -          Formulary.from_rack(rack).keg_only? -        rescue FormulaUnavailableError, TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError -          false -        end -      else -        true -      end -    end.map(&:basename) - -    return if unlinked.empty? -    inject_file_list unlinked, <<-EOS.undent -    You have unlinked kegs in your Cellar -    Leaving kegs unlinked can lead to build-trouble and cause brews that depend on -    those kegs to fail to run properly once built. Run `brew link` on these: -    EOS -  end - -  def check_xcode_license_approved -    # If the user installs Xcode-only, they have to approve the -    # license or no "xc*" tool will work. -    if `/usr/bin/xcrun clang 2>&1` =~ /license/ && !$?.success? then <<-EOS.undent -      You have not agreed to the Xcode license. -      Builds will fail! Agree to the license by opening Xcode.app or running: -          sudo xcodebuild -license -      EOS -    end -  end - -  def check_for_latest_xquartz -    return unless MacOS::XQuartz.version -    return if MacOS::XQuartz.provided_by_apple? - -    installed_version = Version.new(MacOS::XQuartz.version) -    latest_version    = Version.new(MacOS::XQuartz.latest_version) - -    return if installed_version >= latest_version - -    <<-EOS.undent -      Your XQuartz (#{installed_version}) is outdated -      Please install XQuartz #{latest_version}: -        https://xquartz.macosforge.org -    EOS -  end - -  def check_for_old_env_vars -    if ENV["HOMEBREW_KEEP_INFO"] -      <<-EOS.undent -      `HOMEBREW_KEEP_INFO` is no longer used -      info files are no longer deleted by default; you may -      remove this environment variable. -      EOS -    end -  end - -  def check_for_pth_support -    homebrew_site_packages = Language::Python.homebrew_site_packages -    return unless homebrew_site_packages.directory? -    return if Language::Python.reads_brewed_pth_files?("python") != false -    return unless Language::Python.in_sys_path?("python", homebrew_site_packages) -    user_site_packages = Language::Python.user_site_packages "python" -    <<-EOS.undent -      Your default Python does not recognize the Homebrew site-packages -      directory as a special site-packages directory, which means that .pth -      files will not be followed. This means you will not be able to import -      some modules after installing them with Homebrew, like wxpython. To fix -      this for the current user, you can run: - -        mkdir -p #{user_site_packages} -        echo 'import site; site.addsitedir("#{homebrew_site_packages}")' >> #{user_site_packages}/homebrew.pth -    EOS -  end - -  def check_for_external_cmd_name_conflict -    cmds = paths.flat_map { |p| Dir["#{p}/brew-*"] }.uniq -    cmds = cmds.select { |cmd| File.file?(cmd) && File.executable?(cmd) } -    cmd_map = {} -    cmds.each do |cmd| -      cmd_name = File.basename(cmd, ".rb") -      cmd_map[cmd_name] ||= [] -      cmd_map[cmd_name] << cmd -    end -    cmd_map.reject! { |_cmd_name, cmd_paths| cmd_paths.size == 1 } -    return if cmd_map.empty? -    s = "You have external commands with conflicting names.\n" -    cmd_map.each do |cmd_name, cmd_paths| -      s += inject_file_list cmd_paths, <<-EOS.undent - -        Found command `#{cmd_name}` in following places: -      EOS -    end -    s -  end - -  def all -    methods.map(&:to_s).grep(/^check_/) -  end -end # end class Checks +require "diagnostic"  module Homebrew    def doctor -    checks = Checks.new +    checks = Diagnostic::Checks.new      if ARGV.include? "--list-checks"        puts checks.all.sort        exit      end -    inject_dump_stats(checks) if ARGV.switch? "D" +    checks.inject_dump_stats! if ARGV.switch? "D"      if ARGV.named.empty?        methods = checks.all.sort @@ -1349,19 +46,4 @@ module Homebrew      puts "Your system is ready to brew." unless Homebrew.failed?    end - -  def inject_dump_stats(checks) -    checks.extend Module.new { -      def send(method, *) -        time = Time.now -        super -      ensure -        $times[method] = Time.now - time -      end -    } -    $times = {} -    at_exit do -      puts $times.sort_by { |_k, v| v }.map { |k, v| "#{k}: #{v}" } -    end -  end  end diff --git a/Library/Homebrew/cmd/install.rb b/Library/Homebrew/cmd/install.rb index dbd8f5c04..c2ef9a334 100644 --- a/Library/Homebrew/cmd/install.rb +++ b/Library/Homebrew/cmd/install.rb @@ -1,5 +1,5 @@  require "blacklist" -require "cmd/doctor" +require "diagnostic"  require "cmd/search"  require "formula_installer"  require "tap" @@ -159,7 +159,7 @@ module Homebrew    end    def check_xcode -    checks = Checks.new +    checks = Diagnostic::Checks.new      %w[        check_for_unsupported_osx        check_for_bad_install_name_tool diff --git a/Library/Homebrew/cmd/missing.rb b/Library/Homebrew/cmd/missing.rb index 6805548a8..3d70e5f35 100644 --- a/Library/Homebrew/cmd/missing.rb +++ b/Library/Homebrew/cmd/missing.rb @@ -1,30 +1,8 @@  require "formula"  require "tab" +require "diagnostic"  module Homebrew -  def missing_deps(ff) -    missing = {} -    ff.each do |f| -      missing_deps = f.recursive_dependencies do |dependent, dep| -        if dep.optional? || dep.recommended? -          tab = Tab.for_formula(dependent) -          Dependency.prune unless tab.with?(dep) -        elsif dep.build? -          Dependency.prune -        end -      end - -      missing_deps.map!(&:to_formula) -      missing_deps.reject! { |d| d.installed_prefixes.any? } - -      unless missing_deps.empty? -        yield f.full_name, missing_deps if block_given? -        missing[f.full_name] = missing_deps -      end -    end -    missing -  end -    def missing      return unless HOMEBREW_CELLAR.exist? @@ -34,7 +12,7 @@ module Homebrew        ARGV.resolved_formulae      end -    missing_deps(ff) do |name, missing| +    Diagnostic.missing_deps(ff) do |name, missing|        print "#{name}: " if ff.size > 1        puts "#{missing * " "}"      end diff --git a/Library/Homebrew/cmd/update.rb b/Library/Homebrew/cmd/update.rb index 9e2541434..436175b25 100644 --- a/Library/Homebrew/cmd/update.rb +++ b/Library/Homebrew/cmd/update.rb @@ -1,5 +1,5 @@  require "cmd/tap" -require "cmd/doctor" +require "diagnostic"  require "formula_versions"  require "migrator"  require "formulary" @@ -15,7 +15,7 @@ module Homebrew      end      # check permissions -    checks = Checks.new +    checks = Diagnostic::Checks.new      %w[        check_access_usr_local        check_access_homebrew_repository diff --git a/Library/Homebrew/diagnostic.rb b/Library/Homebrew/diagnostic.rb new file mode 100644 index 000000000..3dd7f566f --- /dev/null +++ b/Library/Homebrew/diagnostic.rb @@ -0,0 +1,1344 @@ +require "keg" +require "language/python" +require "formula" +require "version" + +module Homebrew +  module Diagnostic +    def self.missing_deps(ff) +      missing = {} +      ff.each do |f| +        missing_deps = f.recursive_dependencies do |dependent, dep| +          if dep.optional? || dep.recommended? +            tab = Tab.for_formula(dependent) +            Dependency.prune unless tab.with?(dep) +          elsif dep.build? +            Dependency.prune +          end +        end + +        missing_deps.map!(&:to_formula) +        missing_deps.reject! { |d| d.installed_prefixes.any? } + +        unless missing_deps.empty? +          yield f.full_name, missing_deps if block_given? +          missing[f.full_name] = missing_deps +        end +      end +      missing +    end + +    class Volumes +      def initialize +        @volumes = get_mounts +      end + +      def which(path) +        vols = get_mounts path + +        # no volume found +        if vols.empty? +          return -1 +        end + +        vol_index = @volumes.index(vols[0]) +        # volume not found in volume list +        if vol_index.nil? +          return -1 +        end +        vol_index +      end + +      def get_mounts(path = nil) +        vols = [] +        # get the volume of path, if path is nil returns all volumes + +        args = %w[/bin/df -P] +        args << path if path + +        Utils.popen_read(*args) do |io| +          io.each_line do |line| +            case line.chomp +              # regex matches: /dev/disk0s2   489562928 440803616  48247312    91%    / +            when /^.+\s+[0-9]+\s+[0-9]+\s+[0-9]+\s+[0-9]{1,3}%\s+(.+)/ +              vols << $1 +            end +          end +        end +        vols +      end +    end + +    class Checks +      ############# HELPERS +      # Finds files in HOMEBREW_PREFIX *and* /usr/local. +      # Specify paths relative to a prefix eg. "include/foo.h". +      # Sets @found for your convenience. +      def find_relative_paths(*relative_paths) +        @found = [HOMEBREW_PREFIX, "/usr/local"].uniq.inject([]) do |found, prefix| +          found + relative_paths.map { |f| File.join(prefix, f) }.select { |f| File.exist? f } +        end +      end + +      def inject_file_list(list, str) +        list.inject(str) { |s, f| s << "    #{f}\n" } +      end +      ############# END HELPERS + +      def inject_dump_stats! +        self.extend Module.new { +          def send(method, *) +            time = Time.now +            super +          ensure +            $times[method] = Time.now - time +          end +        } +        $times = {} +        at_exit do +          puts $times.sort_by { |_k, v| v }.map { |k, v| "#{k}: #{v}" } +        end +      end + +      # See https://github.com/Homebrew/homebrew/pull/9986 +      def check_path_for_trailing_slashes +        bad_paths = ENV["PATH"].split(File::PATH_SEPARATOR).select { |p| p[-1..-1] == "/" } +        return if bad_paths.empty? +        inject_file_list bad_paths, <<-EOS.undent +          Some directories in your path end in a slash. +          Directories in your path should not end in a slash. This can break other +          doctor checks. The following directories should be edited: +        EOS +      end + +      # Installing MacGPG2 interferes with Homebrew in a big way +      # https://github.com/GPGTools/MacGPG2 +      def check_for_macgpg2 +        return if File.exist? "/usr/local/MacGPG2/share/gnupg/VERSION" + +        suspects = %w[ +          /Applications/start-gpg-agent.app +          /Library/Receipts/libiconv1.pkg +          /usr/local/MacGPG2 +        ] + +        if suspects.any? { |f| File.exist? f } then <<-EOS.undent +          You may have installed MacGPG2 via the package installer. +          Several other checks in this script will turn up problems, such as stray +          dylibs in /usr/local and permissions issues with share and man in /usr/local/. +        EOS +        end +      end + +      # Anaconda installs multiple system & brew dupes, including OpenSSL, Python, +      # sqlite, libpng, Qt, etc. Regularly breaks compile on Vim, MacVim and others. +      # Is flagged as part of the *-config script checks below, but people seem +      # to ignore those as warnings rather than extremely likely breakage. +      def check_for_anaconda +        return unless which("anaconda") +        return unless which("python") + +        anaconda = which("anaconda").realpath.dirname +        python_binary = Utils.popen_read which("python"), "-c", "import sys; sys.stdout.write(sys.executable)" +        python = Pathname.new(python_binary).realpath.dirname + +        # Only warn if Python lives with Anaconda, since is most problematic case. +        if python == anaconda then <<-EOS.undent +          Anaconda is known to frequently break Homebrew builds, including Vim and +          MacVim, due to bundling many duplicates of system and Homebrew-available +          tools. + +          If you encounter a build failure please temporarily remove Anaconda +          from your $PATH and attempt the build again prior to reporting the +          failure to us. Thanks! +        EOS +        end +      end + +      def __check_stray_files(dir, pattern, white_list, message) +        return unless File.directory?(dir) + +        files = Dir.chdir(dir) do +          Dir[pattern].select { |f| File.file?(f) && !File.symlink?(f) } - Dir.glob(white_list) +        end.map { |file| File.join(dir, file) } + +        inject_file_list(files, message) unless files.empty? +      end + +      def check_for_stray_dylibs +        # Dylibs which are generally OK should be added to this list, +        # with a short description of the software they come with. +        white_list = [ +          "libfuse.2.dylib", # MacFuse +          "libfuse_ino64.2.dylib", # MacFuse +          "libmacfuse_i32.2.dylib", # OSXFuse MacFuse compatibility layer +          "libmacfuse_i64.2.dylib", # OSXFuse MacFuse compatibility layer +          "libosxfuse_i32.2.dylib", # OSXFuse +          "libosxfuse_i64.2.dylib", # OSXFuse +          "libTrAPI.dylib", # TrAPI / Endpoint Security VPN +          "libntfs-3g.*.dylib", # NTFS-3G +          "libntfs.*.dylib", # NTFS-3G +          "libublio.*.dylib", # NTFS-3G +          "libUFSDNTFS.dylib", # Paragon NTFS +          "libUFSDExtFS.dylib", # Paragon ExtFS +        ] + +        __check_stray_files "/usr/local/lib", "*.dylib", white_list, <<-EOS.undent +        Unbrewed dylibs were found in /usr/local/lib. +        If you didn't put them there on purpose they could cause problems when +        building Homebrew formulae, and may need to be deleted. + +        Unexpected dylibs: +      EOS +      end + +      def check_for_stray_static_libs +        # Static libs which are generally OK should be added to this list, +        # with a short description of the software they come with. +        white_list = [ +          "libsecurity_agent_client.a", # OS X 10.8.2 Supplemental Update +          "libsecurity_agent_server.a", # OS X 10.8.2 Supplemental Update +          "libntfs-3g.a", # NTFS-3G +          "libntfs.a", # NTFS-3G +          "libublio.a", # NTFS-3G +        ] + +        __check_stray_files "/usr/local/lib", "*.a", white_list, <<-EOS.undent +        Unbrewed static libraries were found in /usr/local/lib. +        If you didn't put them there on purpose they could cause problems when +        building Homebrew formulae, and may need to be deleted. + +        Unexpected static libraries: +      EOS +      end + +      def check_for_stray_pcs +        # Package-config files which are generally OK should be added to this list, +        # with a short description of the software they come with. +        white_list = [ +          "fuse.pc", # OSXFuse/MacFuse +          "macfuse.pc", # OSXFuse MacFuse compatibility layer +          "osxfuse.pc", # OSXFuse +          "libntfs-3g.pc", # NTFS-3G +          "libublio.pc", # NTFS-3G +        ] + +        __check_stray_files "/usr/local/lib/pkgconfig", "*.pc", white_list, <<-EOS.undent +        Unbrewed .pc files were found in /usr/local/lib/pkgconfig. +        If you didn't put them there on purpose they could cause problems when +        building Homebrew formulae, and may need to be deleted. + +        Unexpected .pc files: +      EOS +      end + +      def check_for_stray_las +        white_list = [ +          "libfuse.la", # MacFuse +          "libfuse_ino64.la", # MacFuse +          "libosxfuse_i32.la", # OSXFuse +          "libosxfuse_i64.la", # OSXFuse +          "libntfs-3g.la", # NTFS-3G +          "libntfs.la", # NTFS-3G +          "libublio.la", # NTFS-3G +        ] + +        __check_stray_files "/usr/local/lib", "*.la", white_list, <<-EOS.undent +        Unbrewed .la files were found in /usr/local/lib. +        If you didn't put them there on purpose they could cause problems when +        building Homebrew formulae, and may need to be deleted. + +        Unexpected .la files: +      EOS +      end + +      def check_for_stray_headers +        white_list = [ +          "fuse.h", # MacFuse +          "fuse/**/*.h", # MacFuse +          "macfuse/**/*.h", # OSXFuse MacFuse compatibility layer +          "osxfuse/**/*.h", # OSXFuse +          "ntfs/**/*.h", # NTFS-3G +          "ntfs-3g/**/*.h", # NTFS-3G +        ] + +        __check_stray_files "/usr/local/include", "**/*.h", white_list, <<-EOS.undent +        Unbrewed header files were found in /usr/local/include. +        If you didn't put them there on purpose they could cause problems when +        building Homebrew formulae, and may need to be deleted. + +        Unexpected header files: +      EOS +      end + +      def check_for_other_package_managers +        ponk = MacOS.macports_or_fink +        unless ponk.empty? +          <<-EOS.undent +          You have MacPorts or Fink installed: +            #{ponk.join(", ")} + +          This can cause trouble. You don't have to uninstall them, but you may want to +          temporarily move them out of the way, e.g. + +            sudo mv /opt/local ~/macports +        EOS +        end +      end + +      def check_for_broken_symlinks +        broken_symlinks = [] + +        Keg::PRUNEABLE_DIRECTORIES.each do |d| +          next unless d.directory? +          d.find do |path| +            if path.symlink? && !path.resolved_path_exists? +              broken_symlinks << path +            end +          end +        end +        return if broken_symlinks.empty? +        inject_file_list broken_symlinks, <<-EOS.undent +        Broken symlinks were found. Remove them with `brew prune`: +        EOS +      end + +      def check_for_unsupported_osx +        if !ARGV.homebrew_developer? && OS::Mac.prerelease? then <<-EOS.undent +        You are using OS X #{MacOS.version}. +        We do not provide support for this pre-release version. +        You may encounter build failures or other breakages. +        EOS +        end +      end + +      # TODO: distill down into single method definition a la BuildToolsError +      if MacOS.version >= "10.9" +        def check_for_installed_developer_tools +          unless MacOS::Xcode.installed? || MacOS::CLT.installed? then <<-EOS.undent +          No developer tools installed. +          Install the Command Line Tools: +            xcode-select --install +          EOS +          end +        end + +        if OS::Mac.prerelease? +          def check_xcode_up_to_date +            if MacOS::Xcode.installed? && MacOS::Xcode.outdated? +              <<-EOS.undent +            Your Xcode (#{MacOS::Xcode.version}) is outdated +            Please update to Xcode #{MacOS::Xcode.latest_version}. +            Xcode can be updated from +              https://developer.apple.com/xcode/downloads/ +            EOS +            end +          end +        else +          def check_xcode_up_to_date +            if MacOS::Xcode.installed? && MacOS::Xcode.outdated? +              <<-EOS.undent +            Your Xcode (#{MacOS::Xcode.version}) is outdated +            Please update to Xcode #{MacOS::Xcode.latest_version}. +            Xcode can be updated from the App Store. +            EOS +            end +          end +        end + +        def check_clt_up_to_date +          if MacOS::CLT.installed? && MacOS::CLT.outdated? then <<-EOS.undent +          A newer Command Line Tools release is available. +          Update them from Software Update in the App Store. +          EOS +          end +        end +      elsif MacOS.version == "10.8" || MacOS.version == "10.7" +        def check_for_installed_developer_tools +          unless MacOS::Xcode.installed? || MacOS::CLT.installed? then <<-EOS.undent +          No developer tools installed. +          You should install the Command Line Tools. +          The standalone package can be obtained from +            https://developer.apple.com/downloads +          or it can be installed via Xcode's preferences. +          EOS +          end +        end + +        def check_xcode_up_to_date +          if MacOS::Xcode.installed? && MacOS::Xcode.outdated? then <<-EOS.undent +          Your Xcode (#{MacOS::Xcode.version}) is outdated +          Please update to Xcode #{MacOS::Xcode.latest_version}. +          Xcode can be updated from +            https://developer.apple.com/xcode/downloads/ +          EOS +          end +        end + +        def check_clt_up_to_date +          if MacOS::CLT.installed? && MacOS::CLT.outdated? then <<-EOS.undent +          A newer Command Line Tools release is available. +          The standalone package can be obtained from +            https://developer.apple.com/downloads +          or it can be installed via Xcode's preferences. +          EOS +          end +        end +      else +        def check_for_installed_developer_tools +          unless MacOS::Xcode.installed? then <<-EOS.undent +          Xcode is not installed. Most formulae need Xcode to build. +          It can be installed from +            https://developer.apple.com/xcode/downloads/ +          EOS +          end +        end + +        def check_xcode_up_to_date +          if MacOS::Xcode.installed? && MacOS::Xcode.outdated? then <<-EOS.undent +          Your Xcode (#{MacOS::Xcode.version}) is outdated +          Please update to Xcode #{MacOS::Xcode.latest_version}. +          Xcode can be updated from +            https://developer.apple.com/xcode/downloads/ +          EOS +          end +        end +      end + +      def check_for_osx_gcc_installer +        if (MacOS.version < "10.7" || MacOS::Xcode.version > "4.1") && \ +          MacOS.clang_version == "2.1" +          message = <<-EOS.undent +        You seem to have osx-gcc-installer installed. +        Homebrew doesn't support osx-gcc-installer. It causes many builds to fail and +        is an unlicensed distribution of really old Xcode files. +        EOS +          if MacOS.version >= :mavericks +            message += <<-EOS.undent +              Please run `xcode-select --install` to install the CLT. +            EOS +          elsif MacOS.version >= :lion +            message += <<-EOS.undent +            Please install the CLT or Xcode #{MacOS::Xcode.latest_version}. +          EOS +          else +            message += <<-EOS.undent +            Please install Xcode #{MacOS::Xcode.latest_version}. +          EOS +          end +        end +      end + +      def check_for_stray_developer_directory +        # if the uninstaller script isn't there, it's a good guess neither are +        # any troublesome leftover Xcode files +        uninstaller = Pathname.new("/Developer/Library/uninstall-developer-folder") +        if MacOS::Xcode.version >= "4.3" && uninstaller.exist? then <<-EOS.undent +        You have leftover files from an older version of Xcode. +        You should delete them using: +          #{uninstaller} +        EOS +        end +      end + +      def check_for_bad_install_name_tool +        return if MacOS.version < "10.9" + +        libs = Pathname.new("/usr/bin/install_name_tool").dynamically_linked_libraries + +        # otool may not work, for example if the Xcode license hasn't been accepted yet +        return if libs.empty? + +        unless libs.include? "/usr/lib/libxcselect.dylib" then <<-EOS.undent +        You have an outdated version of /usr/bin/install_name_tool installed. +        This will cause binary package installations to fail. +        This can happen if you install osx-gcc-installer or RailsInstaller. +        To restore it, you must reinstall OS X or restore the binary from +        the OS packages. +        EOS +        end +      end + +      def __check_subdir_access(base) +        target = HOMEBREW_PREFIX+base +        return unless target.exist? + +        cant_read = [] + +        target.find do |d| +          next unless d.directory? +          cant_read << d unless d.writable_real? +        end + +        return if cant_read.empty? +        inject_file_list cant_read.sort, <<-EOS.undent +          Some directories in #{target} aren't writable. +          This can happen if you "sudo make install" software that isn't managed +          by Homebrew. If a brew tries to add locale information to one of these +          directories, then the install will fail during the link step. + +          You should probably `sudo chown -R $(whoami)` them: +        EOS +      end + +      def check_access_share_locale +        __check_subdir_access "share/locale" +      end + +      def check_access_share_man +        __check_subdir_access "share/man" +      end + +      def check_access_homebrew_repository +        unless HOMEBREW_REPOSITORY.writable_real? then <<-EOS.undent +          The #{HOMEBREW_REPOSITORY} is not writable. + +          You should probably change the ownership and permissions of #{HOMEBREW_REPOSITORY} +          back to your user account. +            sudo chown -R $(whoami) #{HOMEBREW_REPOSITORY} +        EOS +        end +      end + +      def check_access_usr_local +        return unless HOMEBREW_PREFIX.to_s == "/usr/local" + +        unless HOMEBREW_PREFIX.writable_real? then <<-EOS.undent +        The /usr/local directory is not writable. +        Even if this directory was writable when you installed Homebrew, other +        software may change permissions on this directory. Some versions of the +        "InstantOn" component of Airfoil or running Cocktail cleanup/optimizations +        are known to do this. + +        You should probably change the ownership and permissions of /usr/local +        back to your user account. +          sudo chown -R $(whoami):admin /usr/local +        EOS +        end +      end + +      def check_tmpdir_sticky_bit +        # Repair Disk Permissions was removed(?) in El Capitan. +        # https://support.apple.com/en-us/HT201560 +        if MacOS.version < "10.11" +          fix_message = "Please run \"Repair Disk Permissions\" in Disk Utility." +        else +          fix_message = "Please execute `sudo chmod +t #{HOMEBREW_TEMP}` in your Terminal" +        end + +        world_writable = HOMEBREW_TEMP.stat.mode & 0777 == 0777 +        if world_writable && !HOMEBREW_TEMP.sticky? then <<-EOS.undent +        #{HOMEBREW_TEMP} is world-writable but does not have the sticky bit set. +        #{fix_message} +        EOS +        end +      end + +      (Keg::TOP_LEVEL_DIRECTORIES + ["lib/pkgconfig"]).each do |d| +        define_method("check_access_#{d.sub("/", "_")}") do +          dir = HOMEBREW_PREFIX.join(d) +          if dir.exist? && !dir.writable_real? then <<-EOS.undent +          #{dir} isn't writable. + +          This can happen if you "sudo make install" software that isn't managed +          by Homebrew. If a formula tries to write a file to this directory, the +          install will fail during the link step. + +          You should probably change the ownership and permissions of #{dir} +          back to your user account. +            sudo chown -R $(whoami) #{dir} +          EOS +          end +        end +      end + +      def check_access_site_packages +        if Language::Python.homebrew_site_packages.exist? && !Language::Python.homebrew_site_packages.writable_real? +          <<-EOS.undent +          #{Language::Python.homebrew_site_packages} isn't writable. +          This can happen if you "sudo pip install" software that isn't managed +          by Homebrew. If you install a formula with Python modules, the install +          will fail during the link step. + +          You should probably change the ownership and permissions of #{Language::Python.homebrew_site_packages} +          back to your user account. +            sudo chown -R $(whoami) #{Language::Python.homebrew_site_packages} +        EOS +        end +      end + +      def check_access_logs +        if HOMEBREW_LOGS.exist? && !HOMEBREW_LOGS.writable_real? +          <<-EOS.undent +          #{HOMEBREW_LOGS} isn't writable. +          Homebrew writes debugging logs to this location. + +          You should probably change the ownership and permissions of #{HOMEBREW_LOGS} +          back to your user account. +            sudo chown -R $(whoami) #{HOMEBREW_LOGS} +        EOS +        end +      end + +      def check_access_cache +        if HOMEBREW_CACHE.exist? && !HOMEBREW_CACHE.writable_real? +          <<-EOS.undent +          #{HOMEBREW_CACHE} isn't writable. +          This can happen if you run `brew install` or `brew fetch` as another user. +          Homebrew caches downloaded files to this location. + +          You should probably change the ownership and permissions of #{HOMEBREW_CACHE} +          back to your user account. +            sudo chown -R $(whoami) #{HOMEBREW_CACHE} +        EOS +        end +      end + +      def check_access_cellar +        if HOMEBREW_CELLAR.exist? && !HOMEBREW_CELLAR.writable_real? +          <<-EOS.undent +          #{HOMEBREW_CELLAR} isn't writable. + +          You should probably change the ownership and permissions of #{HOMEBREW_CELLAR} +          back to your user account. +            sudo chown -R $(whoami) #{HOMEBREW_CELLAR} +        EOS +        end +      end + +      def check_access_prefix_opt +        opt = HOMEBREW_PREFIX.join("opt") +        if opt.exist? && !opt.writable_real? +          <<-EOS.undent +          #{opt} isn't writable. +          You should probably change the ownership and permissions of #{opt} +          back to your user account. +            sudo chown -R $(whoami) #{opt} +        EOS +        end +      end + +      def check_ruby_version +        ruby_version = MacOS.version >= "10.9" ? "2.0" : "1.8" +        if RUBY_VERSION[/\d\.\d/] != ruby_version then <<-EOS.undent +        Ruby version #{RUBY_VERSION} is unsupported on #{MacOS.version}. Homebrew +        is developed and tested on Ruby #{ruby_version}, and may not work correctly +        on other Rubies. Patches are accepted as long as they don't cause breakage +        on supported Rubies. +        EOS +        end +      end + +      def check_homebrew_prefix +        unless HOMEBREW_PREFIX.to_s == "/usr/local" +          <<-EOS.undent +          Your Homebrew is not installed to /usr/local +          You can install Homebrew anywhere you want, but some brews may only build +          correctly if you install in /usr/local. Sorry! +        EOS +        end +      end + +      def check_xcode_prefix +        prefix = MacOS::Xcode.prefix +        return if prefix.nil? +        if prefix.to_s.match(" ") +          <<-EOS.undent +          Xcode is installed to a directory with a space in the name. +          This will cause some formulae to fail to build. +        EOS +        end +      end + +      def check_xcode_prefix_exists +        prefix = MacOS::Xcode.prefix +        return if prefix.nil? +        unless prefix.exist? +          <<-EOS.undent +          The directory Xcode is reportedly installed to doesn't exist: +            #{prefix} +          You may need to `xcode-select` the proper path if you have moved Xcode. +        EOS +        end +      end + +      def check_xcode_select_path +        if !MacOS::CLT.installed? && !File.file?("#{MacOS.active_developer_dir}/usr/bin/xcodebuild") +          path = MacOS::Xcode.bundle_path +          path = "/Developer" if path.nil? || !path.directory? +          <<-EOS.undent +          Your Xcode is configured with an invalid path. +          You should change it to the correct path: +            sudo xcode-select -switch #{path} +        EOS +        end +      end + +      def check_user_path_1 +        $seen_prefix_bin = false +        $seen_prefix_sbin = false + +        out = nil + +        paths.each do |p| +          case p +          when "/usr/bin" +            unless $seen_prefix_bin +              # only show the doctor message if there are any conflicts +              # rationale: a default install should not trigger any brew doctor messages +              conflicts = Dir["#{HOMEBREW_PREFIX}/bin/*"]. +                          map { |fn| File.basename fn }. +                          select { |bn| File.exist? "/usr/bin/#{bn}" } + +              if conflicts.size > 0 +                out = inject_file_list conflicts, <<-EOS.undent +                /usr/bin occurs before #{HOMEBREW_PREFIX}/bin +                This means that system-provided programs will be used instead of those +                provided by Homebrew. The following tools exist at both paths: +                EOS + +                out += <<-EOS.undent + +                Consider setting your PATH so that #{HOMEBREW_PREFIX}/bin +                occurs before /usr/bin. Here is a one-liner: +                    echo 'export PATH="#{HOMEBREW_PREFIX}/bin:$PATH"' >> #{shell_profile} +              EOS +              end +            end +          when "#{HOMEBREW_PREFIX}/bin" +            $seen_prefix_bin = true +          when "#{HOMEBREW_PREFIX}/sbin" +            $seen_prefix_sbin = true +          end +        end +        out +      end + +      def check_user_path_2 +        unless $seen_prefix_bin +          <<-EOS.undent +          Homebrew's bin was not found in your PATH. +          Consider setting the PATH for example like so +              echo 'export PATH="#{HOMEBREW_PREFIX}/bin:$PATH"' >> #{shell_profile} +        EOS +        end +      end + +      def check_user_path_3 +        # Don't complain about sbin not being in the path if it doesn't exist +        sbin = (HOMEBREW_PREFIX+"sbin") +        if sbin.directory? && sbin.children.length > 0 +          unless $seen_prefix_sbin +            <<-EOS.undent +            Homebrew's sbin was not found in your PATH but you have installed +            formulae that put executables in #{HOMEBREW_PREFIX}/sbin. +            Consider setting the PATH for example like so +                echo 'export PATH="#{HOMEBREW_PREFIX}/sbin:$PATH"' >> #{shell_profile} +          EOS +          end +        end +      end + +      def check_for_bad_curl +        if MacOS.version <= "10.8" && !Formula["curl"].installed? then <<-EOS.undent +          The system curl on 10.8 and below is often incapable of supporting +          modern secure connections & will fail on fetching formulae. + +          We recommend you: +            brew install curl +          EOS +        end +      end + +      def check_user_curlrc +        if %w[CURL_HOME HOME].any? { |key| ENV[key] && File.exist?("#{ENV[key]}/.curlrc") } then <<-EOS.undent +        You have a curlrc file +        If you have trouble downloading packages with Homebrew, then maybe this +        is the problem? If the following command doesn't work, then try removing +        your curlrc: +          curl https://github.com +        EOS +        end +      end + +      def check_for_unsupported_curl_vars +        # Support for SSL_CERT_DIR seemed to be removed in the 10.10.5 update. +        if MacOS.version >= :yosemite && !ENV["SSL_CERT_DIR"].nil? then <<-EOS.undent +          SSL_CERT_DIR support was removed from Apple's curl. +          If fetching formulae fails you should: +            unset SSL_CERT_DIR +          and remove it from #{shell_profile} if present. +          EOS +        end +      end + +      def check_which_pkg_config +        binary = which "pkg-config" +        return if binary.nil? + +        mono_config = Pathname.new("/usr/bin/pkg-config") +        if mono_config.exist? && mono_config.realpath.to_s.include?("Mono.framework") then <<-EOS.undent +        You have a non-Homebrew 'pkg-config' in your PATH: +          /usr/bin/pkg-config => #{mono_config.realpath} + +        This was most likely created by the Mono installer. `./configure` may +        have problems finding brew-installed packages using this other pkg-config. + +        Mono no longer installs this file as of 3.0.4. You should +        `sudo rm /usr/bin/pkg-config` and upgrade to the latest version of Mono. +        EOS +        elsif binary.to_s != "#{HOMEBREW_PREFIX}/bin/pkg-config" then <<-EOS.undent +        You have a non-Homebrew 'pkg-config' in your PATH: +          #{binary} + +        `./configure` may have problems finding brew-installed packages using +        this other pkg-config. +        EOS +        end +      end + +      def check_for_gettext +        find_relative_paths("lib/libgettextlib.dylib", +                            "lib/libintl.dylib", +                            "include/libintl.h") + +        return if @found.empty? + +        # Our gettext formula will be caught by check_linked_keg_only_brews +        f = Formulary.factory("gettext") rescue nil +        return if f && f.linked_keg.directory? && @found.all? do |path| +          Pathname.new(path).realpath.to_s.start_with? "#{HOMEBREW_CELLAR}/gettext" +        end + +        s = <<-EOS.undent_________________________________________________________72 +          gettext files detected at a system prefix +          These files can cause compilation and link failures, especially if they +          are compiled with improper architectures. Consider removing these files: +          EOS +        inject_file_list(@found, s) +      end + +      def check_for_iconv +        unless find_relative_paths("lib/libiconv.dylib", "include/iconv.h").empty? +          if (f = Formulary.factory("libiconv") rescue nil) && f.linked_keg.directory? +            unless f.keg_only? then <<-EOS.undent +            A libiconv formula is installed and linked +            This will break stuff. For serious. Unlink it. +            EOS +            end +          else +            s = <<-EOS.undent_________________________________________________________72 +              libiconv files detected at a system prefix other than /usr +              Homebrew doesn't provide a libiconv formula, and expects to link against +              the system version in /usr. libiconv in other prefixes can cause +              compile or link failure, especially if compiled with improper +              architectures. OS X itself never installs anything to /usr/local so +              it was either installed by a user or some other third party software. + +              tl;dr: delete these files: +              EOS +            inject_file_list(@found, s) +          end +        end +      end + +      def check_for_config_scripts +        return unless HOMEBREW_CELLAR.exist? +        real_cellar = HOMEBREW_CELLAR.realpath + +        scripts = [] + +        whitelist = %W[ +          /usr/bin /usr/sbin +          /usr/X11/bin /usr/X11R6/bin /opt/X11/bin +          #{HOMEBREW_PREFIX}/bin #{HOMEBREW_PREFIX}/sbin +          /Applications/Server.app/Contents/ServerRoot/usr/bin +          /Applications/Server.app/Contents/ServerRoot/usr/sbin +        ].map(&:downcase) + +        paths.each do |p| +          next if whitelist.include?(p.downcase) || !File.directory?(p) + +          realpath = Pathname.new(p).realpath.to_s +          next if realpath.start_with?(real_cellar.to_s, HOMEBREW_CELLAR.to_s) + +          scripts += Dir.chdir(p) { Dir["*-config"] }.map { |c| File.join(p, c) } +        end + +        return if scripts.empty? +        inject_file_list scripts, <<-EOS.undent +          "config" scripts exist outside your system or Homebrew directories. +          `./configure` scripts often look for *-config scripts to determine if +          software packages are installed, and what additional flags to use when +          compiling and linking. + +          Having additional scripts in your path can confuse software installed via +          Homebrew if the config script overrides a system or Homebrew provided +          script of the same name. We found the following "config" scripts: +        EOS +      end + +      def check_DYLD_vars +        found = ENV.keys.grep(/^DYLD_/) +        return if found.empty? +        s = inject_file_list found.map { |e| "#{e}: #{ENV.fetch(e)}" }, <<-EOS.undent +          Setting DYLD_* vars can break dynamic linking. +          Set variables: +        EOS +        if found.include? "DYLD_INSERT_LIBRARIES" +          s += <<-EOS.undent + +          Setting DYLD_INSERT_LIBRARIES can cause Go builds to fail. +          Having this set is common if you use this software: +            http://asepsis.binaryage.com/ +          EOS +        end +        s +      end + +      def check_for_symlinked_cellar +        return unless HOMEBREW_CELLAR.exist? +        if HOMEBREW_CELLAR.symlink? +          <<-EOS.undent +          Symlinked Cellars can cause problems. +          Your Homebrew Cellar is a symlink: #{HOMEBREW_CELLAR} +                          which resolves to: #{HOMEBREW_CELLAR.realpath} + +          The recommended Homebrew installations are either: +          (A) Have Cellar be a real directory inside of your HOMEBREW_PREFIX +          (B) Symlink "bin/brew" into your prefix, but don't symlink "Cellar". + +          Older installations of Homebrew may have created a symlinked Cellar, but this can +          cause problems when two formula install to locations that are mapped on top of each +          other during the linking step. +        EOS +        end +      end + +      def check_for_multiple_volumes +        return unless HOMEBREW_CELLAR.exist? +        volumes = Volumes.new + +        # Find the volumes for the TMP folder & HOMEBREW_CELLAR +        real_cellar = HOMEBREW_CELLAR.realpath + +        tmp = Pathname.new(Dir.mktmpdir("doctor", HOMEBREW_TEMP)) +        real_temp = tmp.realpath.parent + +        where_cellar = volumes.which real_cellar +        where_temp = volumes.which real_temp + +        Dir.delete tmp + +        unless where_cellar == where_temp then <<-EOS.undent +        Your Cellar and TEMP directories are on different volumes. +        OS X won't move relative symlinks across volumes unless the target file already +        exists. Brews known to be affected by this are Git and Narwhal. + +        You should set the "HOMEBREW_TEMP" environmental variable to a suitable +        directory on the same volume as your Cellar. +        EOS +        end +      end + +      def check_filesystem_case_sensitive +        volumes = Volumes.new +        case_sensitive_vols = [HOMEBREW_PREFIX, HOMEBREW_REPOSITORY, HOMEBREW_CELLAR, HOMEBREW_TEMP].select do |dir| +          # We select the dir as being case-sensitive if either the UPCASED or the +          # downcased variant is missing. +          # Of course, on a case-insensitive fs, both exist because the os reports so. +          # In the rare situation when the user has indeed a downcased and an upcased +          # dir (e.g. /TMP and /tmp) this check falsely thinks it is case-insensitive +          # but we don't care beacuse: 1. there is more than one dir checked, 2. the +          # check is not vital and 3. we would have to touch files otherwise. +          upcased = Pathname.new(dir.to_s.upcase) +          downcased = Pathname.new(dir.to_s.downcase) +          dir.exist? && !(upcased.exist? && downcased.exist?) +        end.map { |case_sensitive_dir| volumes.get_mounts(case_sensitive_dir) }.uniq +        return if case_sensitive_vols.empty? +        <<-EOS.undent +        The filesystem on #{case_sensitive_vols.join(",")} appears to be case-sensitive. +        The default OS X filesystem is case-insensitive. Please report any apparent problems. +      EOS +      end + +      def __check_git_version +        # https://help.github.com/articles/https-cloning-errors +        `git --version`.chomp =~ /git version ((?:\d+\.?)+)/ + +        if $1 && Version.new($1) < Version.new("1.7.10") +          git_upgrade_cmd = Formula["git"].any_version_installed? ? "upgrade" : "install" + +          <<-EOS.undent +            An outdated version of Git was detected in your PATH. +            Git 1.7.10 or newer is required to perform checkouts over HTTPS from GitHub. +            Please upgrade: brew #{git_upgrade_cmd} git +          EOS +        end +      end + +      def check_for_git +        if Utils.git_available? +          __check_git_version +        else <<-EOS.undent +        Git could not be found in your PATH. +        Homebrew uses Git for several internal functions, and some formulae use Git +        checkouts instead of stable tarballs. You may want to install Git: +          brew install git +        EOS +        end +      end + +      def check_git_newline_settings +        return unless Utils.git_available? + +        autocrlf = `git config --get core.autocrlf`.chomp + +        if autocrlf == "true" then <<-EOS.undent +        Suspicious Git newline settings found. + +        The detected Git newline settings will cause checkout problems: +          core.autocrlf = #{autocrlf} + +        If you are not routinely dealing with Windows-based projects, +        consider removing these by running: +        `git config --global core.autocrlf input` +        EOS +        end +      end + +      def check_git_origin +        return if !Utils.git_available? || !(HOMEBREW_REPOSITORY/".git").exist? + +        origin = Homebrew.git_origin + +        if origin.nil? then <<-EOS.undent +          Missing git origin remote. + +          Without a correctly configured origin, Homebrew won't update +          properly. You can solve this by adding the Homebrew remote: +            cd #{HOMEBREW_REPOSITORY} +            git remote add origin https://github.com/Homebrew/homebrew.git +          EOS +        elsif origin !~ /(mxcl|Homebrew)\/homebrew(\.git)?$/ then <<-EOS.undent +          Suspicious git origin remote found. + +          With a non-standard origin, Homebrew won't pull updates from +          the main repository. The current git origin is: +            #{origin} + +          Unless you have compelling reasons, consider setting the +          origin remote to point at the main repository, located at: +            https://github.com/Homebrew/homebrew.git +          EOS +        end +      end + +      def check_for_autoconf +        return unless MacOS::Xcode.provides_autotools? + +        autoconf = which("autoconf") +        safe_autoconfs = %w[/usr/bin/autoconf /Developer/usr/bin/autoconf] +        unless autoconf.nil? || safe_autoconfs.include?(autoconf.to_s) then <<-EOS.undent +        An "autoconf" in your path blocks the Xcode-provided version at: +          #{autoconf} + +        This custom autoconf may cause some Homebrew formulae to fail to compile. +        EOS +        end +      end + +      def __check_linked_brew(f) +        f.installed_prefixes.each do |prefix| +          prefix.find do |src| +            next if src == prefix +            dst = HOMEBREW_PREFIX + src.relative_path_from(prefix) +            return true if dst.symlink? && src == dst.resolved_path +          end +        end + +        false +      end + +      def check_for_linked_keg_only_brews +        return unless HOMEBREW_CELLAR.exist? + +        linked = Formula.installed.select do |f| +          f.keg_only? && __check_linked_brew(f) +        end + +        return if linked.empty? +        inject_file_list linked.map(&:full_name), <<-EOS.undent +        Some keg-only formula are linked into the Cellar. +        Linking a keg-only formula, such as gettext, into the cellar with +        `brew link <formula>` will cause other formulae to detect them during +        the `./configure` step. This may cause problems when compiling those +        other formulae. + +        Binaries provided by keg-only formulae may override system binaries +        with other strange results. + +        You may wish to `brew unlink` these brews: +        EOS +      end + +      def check_for_other_frameworks +        # Other frameworks that are known to cause problems when present +        %w[expat.framework libexpat.framework libcurl.framework]. +          map { |frmwrk| "/Library/Frameworks/#{frmwrk}" }. +          select { |frmwrk| File.exist? frmwrk }. +          map do |frmwrk| +            <<-EOS.undent +          #{frmwrk} detected +          This can be picked up by CMake's build system and likely cause the build to +          fail. You may need to move this file out of the way to compile CMake. +          EOS +          end.join +      end + +      def check_tmpdir +        tmpdir = ENV["TMPDIR"] +        "TMPDIR #{tmpdir.inspect} doesn't exist." unless tmpdir.nil? || File.directory?(tmpdir) +      end + +      def check_missing_deps +        return unless HOMEBREW_CELLAR.exist? +        missing = Set.new +        Homebrew::Diagnostic.missing_deps(Formula.installed).each_value do |deps| +          missing.merge(deps) +        end + +        if missing.any? then <<-EOS.undent +          Some installed formula are missing dependencies. +          You should `brew install` the missing dependencies: + +              brew install #{missing.sort_by(&:full_name) * " "} + +          Run `brew missing` for more details. +        EOS +        end +      end + +      def check_git_status +        return unless Utils.git_available? +        HOMEBREW_REPOSITORY.cd do +          unless `git status --untracked-files=all --porcelain -- Library/Homebrew/ 2>/dev/null`.chomp.empty? +            <<-EOS.undent_________________________________________________________72 +          You have uncommitted modifications to Homebrew +          If this is a surprise to you, then you should stash these modifications. +          Stashing returns Homebrew to a pristine state but can be undone +          should you later need to do so for some reason. +              cd #{HOMEBREW_LIBRARY} && git stash && git clean -d -f +          EOS +          end +        end +      end + +      def check_for_enthought_python +        if which "enpkg" then <<-EOS.undent +        Enthought Python was found in your PATH. +        This can cause build problems, as this software installs its own +        copies of iconv and libxml2 into directories that are picked up by +        other build systems. +        EOS +        end +      end + +      def check_for_library_python +        if File.exist?("/Library/Frameworks/Python.framework") then <<-EOS.undent +        Python is installed at /Library/Frameworks/Python.framework + +        Homebrew only supports building against the System-provided Python or a +        brewed Python. In particular, Pythons installed to /Library can interfere +        with other software installs. +        EOS +        end +      end + +      def check_for_old_homebrew_share_python_in_path +        s = "" +        ["", "3"].map do |suffix| +          if paths.include?((HOMEBREW_PREFIX/"share/python#{suffix}").to_s) +            s += "#{HOMEBREW_PREFIX}/share/python#{suffix} is not needed in PATH.\n" +          end +        end +        unless s.empty? +          s += <<-EOS.undent +          Formerly homebrew put Python scripts you installed via `pip` or `pip3` +          (or `easy_install`) into that directory above but now it can be removed +          from your PATH variable. +          Python scripts will now install into #{HOMEBREW_PREFIX}/bin. +          You can delete anything, except 'Extras', from the #{HOMEBREW_PREFIX}/share/python +          (and #{HOMEBREW_PREFIX}/share/python3) dir and install affected Python packages +          anew with `pip install --upgrade`. +        EOS +        end +      end + +      def check_for_bad_python_symlink +        return unless which "python" +        `python -V 2>&1` =~ /Python (\d+)\./ +        # This won't be the right warning if we matched nothing at all +        return if $1.nil? +        unless $1 == "2" then <<-EOS.undent +        python is symlinked to python#{$1} +        This will confuse build scripts and in general lead to subtle breakage. +        EOS +        end +      end + +      def check_for_non_prefixed_coreutils +        gnubin = "#{Formulary.factory("coreutils").prefix}/libexec/gnubin" +        if paths.include? gnubin then <<-EOS.undent +          Putting non-prefixed coreutils in your path can cause gmp builds to fail. +          EOS +        end +      end + +      def check_for_non_prefixed_findutils +        default_names = Tab.for_name("findutils").with? "default-names" +        if default_names then <<-EOS.undent +          Putting non-prefixed findutils in your path can cause python builds to fail. +          EOS +        end +      end + +      def check_for_pydistutils_cfg_in_home +        if File.exist? "#{ENV["HOME"]}/.pydistutils.cfg" then <<-EOS.undent +        A .pydistutils.cfg file was found in $HOME, which may cause Python +        builds to fail. See: +          https://bugs.python.org/issue6138 +          https://bugs.python.org/issue4655 +        EOS +        end +      end + +      def check_for_outdated_homebrew +        return unless Utils.git_available? +        HOMEBREW_REPOSITORY.cd do +          if File.directory? ".git" +            local = `git rev-parse -q --verify refs/remotes/origin/master`.chomp +            remote = /^([a-f0-9]{40})/.match(`git ls-remote origin refs/heads/master 2>/dev/null`) +            if remote.nil? || local == remote[0] +              return +            end +          end + +          timestamp = if File.directory? ".git" +            `git log -1 --format="%ct" HEAD`.to_i +          else +            HOMEBREW_LIBRARY.mtime.to_i +          end + +          if Time.now.to_i - timestamp > 60 * 60 * 24 then <<-EOS.undent +          Your Homebrew is outdated. +          You haven't updated for at least 24 hours. This is a long time in brewland! +          To update Homebrew, run `brew update`. +          EOS +          end +        end +      end + +      def check_for_unlinked_but_not_keg_only +        unlinked = Formula.racks.reject do |rack| +          if !(HOMEBREW_REPOSITORY/"Library/LinkedKegs"/rack.basename).directory? +            begin +              Formulary.from_rack(rack).keg_only? +            rescue FormulaUnavailableError, TapFormulaAmbiguityError, TapFormulaWithOldnameAmbiguityError +              false +            end +          else +            true +          end +        end.map(&:basename) + +        return if unlinked.empty? +        inject_file_list unlinked, <<-EOS.undent +        You have unlinked kegs in your Cellar +        Leaving kegs unlinked can lead to build-trouble and cause brews that depend on +        those kegs to fail to run properly once built. Run `brew link` on these: +        EOS +      end + +      def check_xcode_license_approved +        # If the user installs Xcode-only, they have to approve the +        # license or no "xc*" tool will work. +        if `/usr/bin/xcrun clang 2>&1` =~ /license/ && !$?.success? then <<-EOS.undent +          You have not agreed to the Xcode license. +          Builds will fail! Agree to the license by opening Xcode.app or running: +              sudo xcodebuild -license +          EOS +        end +      end + +      def check_for_latest_xquartz +        return unless MacOS::XQuartz.version +        return if MacOS::XQuartz.provided_by_apple? + +        installed_version = Version.new(MacOS::XQuartz.version) +        latest_version    = Version.new(MacOS::XQuartz.latest_version) + +        return if installed_version >= latest_version + +        <<-EOS.undent +          Your XQuartz (#{installed_version}) is outdated +          Please install XQuartz #{latest_version}: +            https://xquartz.macosforge.org +        EOS +      end + +      def check_for_old_env_vars +        if ENV["HOMEBREW_KEEP_INFO"] +          <<-EOS.undent +          `HOMEBREW_KEEP_INFO` is no longer used +          info files are no longer deleted by default; you may +          remove this environment variable. +          EOS +        end +      end + +      def check_for_pth_support +        homebrew_site_packages = Language::Python.homebrew_site_packages +        return unless homebrew_site_packages.directory? +        return if Language::Python.reads_brewed_pth_files?("python") != false +        return unless Language::Python.in_sys_path?("python", homebrew_site_packages) +        user_site_packages = Language::Python.user_site_packages "python" +        <<-EOS.undent +          Your default Python does not recognize the Homebrew site-packages +          directory as a special site-packages directory, which means that .pth +          files will not be followed. This means you will not be able to import +          some modules after installing them with Homebrew, like wxpython. To fix +          this for the current user, you can run: + +            mkdir -p #{user_site_packages} +            echo 'import site; site.addsitedir("#{homebrew_site_packages}")' >> #{user_site_packages}/homebrew.pth +        EOS +      end + +      def check_for_external_cmd_name_conflict +        cmds = paths.flat_map { |p| Dir["#{p}/brew-*"] }.uniq +        cmds = cmds.select { |cmd| File.file?(cmd) && File.executable?(cmd) } +        cmd_map = {} +        cmds.each do |cmd| +          cmd_name = File.basename(cmd, ".rb") +          cmd_map[cmd_name] ||= [] +          cmd_map[cmd_name] << cmd +        end +        cmd_map.reject! { |_cmd_name, cmd_paths| cmd_paths.size == 1 } +        return if cmd_map.empty? +        s = "You have external commands with conflicting names.\n" +        cmd_map.each do |cmd_name, cmd_paths| +          s += inject_file_list cmd_paths, <<-EOS.undent + +            Found command `#{cmd_name}` in following places: +          EOS +        end +        s +      end + +      def all +        methods.map(&:to_s).grep(/^check_/) +      end +    end # end class Checks +  end +end diff --git a/Library/Homebrew/exceptions.rb b/Library/Homebrew/exceptions.rb index ad4e600f6..7e6103a39 100644 --- a/Library/Homebrew/exceptions.rb +++ b/Library/Homebrew/exceptions.rb @@ -265,8 +265,8 @@ class BuildError < RuntimeError        puts issues.map { |i| "#{i["title"]} #{i["html_url"]}" }.join("\n")      end -    require "cmd/doctor" -    unsupported_osx = Checks.new.check_for_unsupported_osx +    require "diagnostic" +    unsupported_osx = Diagnostic::Checks.new.check_for_unsupported_osx      opoo unsupported_osx if unsupported_osx    end  end diff --git a/Library/Homebrew/test/test_integration_cmds.rb b/Library/Homebrew/test/test_integration_cmds.rb index 41cc9ac05..64ce9f76a 100644 --- a/Library/Homebrew/test/test_integration_cmds.rb +++ b/Library/Homebrew/test/test_integration_cmds.rb @@ -1,6 +1,7 @@  require "bundler"  require "testing_env"  require "core_formula_repository" +require "fileutils"  class IntegrationCommandTests < Homebrew::TestCase    def cmd_output(*args) @@ -152,7 +153,7 @@ class IntegrationCommandTests < Homebrew::TestCase      cmd("readall", "--aliases", "--syntax")      cmd("readall", "Homebrew/homebrew")    ensure -    formula_file.unlink +    formula_file.unlink unless formula_file.nil?      repo.alias_dir.rmtree    end @@ -181,4 +182,58 @@ class IntegrationCommandTests < Homebrew::TestCase    ensure      Tap::TAP_DIRECTORY.rmtree    end + +  def test_missing +    url = "file://#{File.expand_path("..", __FILE__)}/tarballs/testball-0.1.tbz" +    sha256 = "1dfb13ce0f6143fe675b525fc9e168adb2215c5d5965c9f57306bb993170914f" +    repo = CoreFormulaRepository.new +    foo_file = repo.formula_dir/"foo.rb" +    foo_file.write <<-EOS.undent +      class Foo < Formula +        url "#{url}" +        sha256 "#{sha256}" +      end +    EOS + +    bar_file = repo.formula_dir/"bar.rb" +    bar_file.write <<-EOS.undent +      class Bar < Formula +        url "#{url}" +        sha256 "#{sha256}" +        depends_on "foo" +      end +    EOS + +    cmd("install", "bar") +    cmd("uninstall", "foo") +    assert_match "foo", cmd("missing") +  ensure +    cmd("uninstall", "--force", "foo", "bar") +    cmd("cleanup", "--force", "--prune=all") +    foo_file.unlink unless foo_file.nil? +    bar_file.unlink unless bar_file.nil? +  end + +  def test_doctor_check_path_for_trailing_slashes +    assert_match "Some directories in your path end in a slash", +      cmd_fail("doctor", "check_path_for_trailing_slashes", +               {"PATH" => ENV["PATH"] + File::PATH_SEPARATOR + "/foo/bar/"}) +  end + +  def test_doctor_check_for_anaconda +    mktmpdir do |path| +      anaconda = "#{path}/anaconda" +      python = "#{path}/python" +      FileUtils.touch anaconda +      File.open(python, "w") do |file| +        file.write("#! #{`which bash`}\necho -n '#{python}'\n") +      end +      FileUtils.chmod 0777, anaconda +      FileUtils.chmod 0777, python + +      assert_match "Anaconda", +        cmd_fail("doctor", "check_for_anaconda", +                 {"PATH" => path + File::PATH_SEPARATOR + ENV["PATH"]}) +    end +  end  end | 
