diff options
| author | Markus Reiter | 2017-05-03 01:00:03 +0200 |
|---|---|---|
| committer | GitHub | 2017-05-03 01:00:03 +0200 |
| commit | 77b9ef84ee0025c85faa78c252291ebddc896313 (patch) | |
| tree | 26cc08d02398f5e5149377d69144ff62c2c349ee /Library | |
| parent | 7a0e5d123c42e2fb907c0ad80cfe17125d8c9d56 (diff) | |
| parent | 1be5eeec26000b881c2ec8ff53333266eedd9fff (diff) | |
| download | brew-77b9ef84ee0025c85faa78c252291ebddc896313.tar.bz2 | |
Merge pull request #2560 from reitermarkus/PATH-refactoring
Refactor PATH generation.
Diffstat (limited to 'Library')
| -rw-r--r-- | Library/Homebrew/PATH.rb | 74 | ||||
| -rw-r--r-- | Library/Homebrew/brew.rb | 12 | ||||
| -rw-r--r-- | Library/Homebrew/cmd/sh.rb | 2 | ||||
| -rw-r--r-- | Library/Homebrew/diagnostic.rb | 3 | ||||
| -rw-r--r-- | Library/Homebrew/extend/ENV/shared.rb | 40 | ||||
| -rw-r--r-- | Library/Homebrew/extend/ENV/std.rb | 12 | ||||
| -rw-r--r-- | Library/Homebrew/extend/ENV/super.rb | 84 | ||||
| -rw-r--r-- | Library/Homebrew/global.rb | 3 | ||||
| -rw-r--r-- | Library/Homebrew/requirement.rb | 6 | ||||
| -rw-r--r-- | Library/Homebrew/test/PATH_spec.rb | 115 | ||||
| -rw-r--r-- | Library/Homebrew/utils.rb | 14 |
11 files changed, 277 insertions, 88 deletions
diff --git a/Library/Homebrew/PATH.rb b/Library/Homebrew/PATH.rb new file mode 100644 index 000000000..de7167eb4 --- /dev/null +++ b/Library/Homebrew/PATH.rb @@ -0,0 +1,74 @@ +class PATH + include Enumerable + extend Forwardable + + def_delegator :@paths, :each + + def initialize(*paths) + @paths = parse(*paths) + end + + def prepend(*paths) + @paths = parse(*paths, *@paths) + self + end + + def append(*paths) + @paths = parse(*@paths, *paths) + self + end + + def insert(index, *paths) + @paths = parse(*@paths.insert(index, *paths)) + self + end + + def select(&block) + self.class.new(@paths.select(&block)) + end + + def reject(&block) + self.class.new(@paths.reject(&block)) + end + + def to_ary + @paths + end + alias to_a to_ary + + def to_str + @paths.join(File::PATH_SEPARATOR) + end + alias to_s to_str + + def ==(other) + if other.respond_to?(:to_ary) + return true if to_ary == other.to_ary + end + + if other.respond_to?(:to_str) + return true if to_str == other.to_str + end + + false + end + + def empty? + @paths.empty? + end + + def existing + existing_path = select(&File.method(:directory?)) + # return nil instead of empty PATH, to unset environment variables + existing_path unless existing_path.empty? + end + + private + + def parse(*paths) + paths.flatten + .compact + .flat_map { |p| Pathname.new(p).to_path.split(File::PATH_SEPARATOR) } + .uniq + end +end diff --git a/Library/Homebrew/brew.rb b/Library/Homebrew/brew.rb index 62cfd79c3..f2e723114 100644 --- a/Library/Homebrew/brew.rb +++ b/Library/Homebrew/brew.rb @@ -12,9 +12,9 @@ require "pathname" HOMEBREW_LIBRARY_PATH = Pathname.new(__FILE__).realpath.parent $:.unshift(HOMEBREW_LIBRARY_PATH.to_s) require "global" +require "tap" if ARGV == %w[--version] || ARGV == %w[-v] - require "tap" puts "Homebrew #{HOMEBREW_VERSION}" puts "Homebrew/homebrew-core #{CoreTap.instance.version_string}" exit 0 @@ -47,13 +47,15 @@ begin end end + path = PATH.new(ENV["PATH"]) + # Add contributed commands to PATH before checking. - Dir["#{HOMEBREW_LIBRARY}/Taps/*/*/cmd"].each do |tap_cmd_dir| - ENV["PATH"] += "#{File::PATH_SEPARATOR}#{tap_cmd_dir}" - end + path.append(Pathname.glob(Tap::TAP_DIRECTORY/"*/*/cmd")) # Add SCM wrappers. - ENV["PATH"] += "#{File::PATH_SEPARATOR}#{HOMEBREW_SHIMS_PATH}/scm" + path.append(HOMEBREW_SHIMS_PATH/"scm") + + ENV["PATH"] = path if cmd internal_cmd = require? HOMEBREW_LIBRARY_PATH.join("cmd", cmd) diff --git a/Library/Homebrew/cmd/sh.rb b/Library/Homebrew/cmd/sh.rb index 249753355..69f329cb3 100644 --- a/Library/Homebrew/cmd/sh.rb +++ b/Library/Homebrew/cmd/sh.rb @@ -23,7 +23,7 @@ module Homebrew ENV.setup_build_environment if superenv? # superenv stopped adding brew's bin but generally users will want it - ENV["PATH"] = ENV["PATH"].split(File::PATH_SEPARATOR).insert(1, "#{HOMEBREW_PREFIX}/bin").join(File::PATH_SEPARATOR) + ENV["PATH"] = PATH.new(ENV["PATH"]).insert(1, HOMEBREW_PREFIX/"bin") end ENV["PS1"] = 'brew \[\033[1;32m\]\w\[\033[0m\]$ ' ENV["VERBOSE"] = "1" diff --git a/Library/Homebrew/diagnostic.rb b/Library/Homebrew/diagnostic.rb index 8cca1ba91..1544e6765 100644 --- a/Library/Homebrew/diagnostic.rb +++ b/Library/Homebrew/diagnostic.rb @@ -100,8 +100,7 @@ module Homebrew # See https://github.com/Homebrew/legacy-homebrew/pull/9986 def check_path_for_trailing_slashes - all_paths = ENV["PATH"].split(File::PATH_SEPARATOR) - bad_paths = all_paths.select { |p| p[-1..-1] == "/" } + bad_paths = PATH.new(ENV["PATH"]).select { |p| p.end_with?("/") } return if bad_paths.empty? inject_file_list bad_paths, <<-EOS.undent diff --git a/Library/Homebrew/extend/ENV/shared.rb b/Library/Homebrew/extend/ENV/shared.rb index 2cdc2f83a..b51ade48b 100644 --- a/Library/Homebrew/extend/ENV/shared.rb +++ b/Library/Homebrew/extend/ENV/shared.rb @@ -1,6 +1,7 @@ require "formula" require "compilers" require "development_tools" +require "PATH" # Homebrew extends Ruby's `ENV` to make our code more readable. # Implemented in {SharedEnvExtension} and either {Superenv} or @@ -80,7 +81,7 @@ module SharedEnvExtension end def append_path(key, path) - append key, path, File::PATH_SEPARATOR if File.directory? path + self[key] = PATH.new(self[key]).append(path) end # Prepends a directory to `PATH`. @@ -92,7 +93,7 @@ module SharedEnvExtension # (e.g. <pre>ENV.prepend_path "PATH", which("emacs").dirname</pre>) def prepend_path(key, path) return if %w[/usr/bin /bin /usr/sbin /sbin].include? path.to_s - prepend key, path, File::PATH_SEPARATOR if File.directory? path + self[key] = PATH.new(self[key]).prepend(path) end def prepend_create_path(key, path) @@ -196,22 +197,23 @@ module SharedEnvExtension # @private def userpaths! - paths = self["PATH"].split(File::PATH_SEPARATOR) - # put Superenv.bin and opt path at the first - new_paths = paths.select { |p| p.start_with?("#{HOMEBREW_REPOSITORY}/Library/ENV", "#{HOMEBREW_PREFIX}/opt") } - # XXX hot fix to prefer brewed stuff (e.g. python) over /usr/bin. - new_paths << "#{HOMEBREW_PREFIX}/bin" - # reset of self["PATH"] - new_paths += paths - # user paths - new_paths += ORIGINAL_PATHS.map do |p| - begin - p.realpath.to_s - rescue - nil - end - end - %w[/usr/X11/bin /opt/X11/bin] - self["PATH"] = new_paths.uniq.join(File::PATH_SEPARATOR) + path = PATH.new(self["PATH"]).select do |p| + # put Superenv.bin and opt path at the first + p.start_with?("#{HOMEBREW_REPOSITORY}/Library/ENV", "#{HOMEBREW_PREFIX}/opt") + end + path.append(HOMEBREW_PREFIX/"bin") # XXX hot fix to prefer brewed stuff (e.g. python) over /usr/bin. + path.append(self["PATH"]) # reset of self["PATH"] + path.append( + # user paths + ORIGINAL_PATHS.map do |p| + begin + p.realpath.to_s + rescue + nil + end + end - %w[/usr/X11/bin /opt/X11/bin], + ) + self["PATH"] = path end def fortran @@ -244,7 +246,7 @@ module SharedEnvExtension else if (gfortran = which("gfortran", (HOMEBREW_PREFIX/"bin").to_s)) ohai "Using Homebrew-provided fortran compiler." - elsif (gfortran = which("gfortran", ORIGINAL_PATHS.join(File::PATH_SEPARATOR))) + elsif (gfortran = which("gfortran", PATH.new(ORIGINAL_PATHS))) ohai "Using a fortran compiler found at #{gfortran}." end if gfortran diff --git a/Library/Homebrew/extend/ENV/std.rb b/Library/Homebrew/extend/ENV/std.rb index aafc0a451..c4cc0985f 100644 --- a/Library/Homebrew/extend/ENV/std.rb +++ b/Library/Homebrew/extend/ENV/std.rb @@ -57,12 +57,12 @@ module Stdenv end def determine_pkg_config_libdir - paths = [] - paths << "#{HOMEBREW_PREFIX}/lib/pkgconfig" - paths << "#{HOMEBREW_PREFIX}/share/pkgconfig" - paths += homebrew_extra_pkg_config_paths - paths << "/usr/lib/pkgconfig" - paths.select { |d| File.directory? d }.join(File::PATH_SEPARATOR) + PATH.new( + HOMEBREW_PREFIX/"lib/pkgconfig", + HOMEBREW_PREFIX/"share/pkgconfig", + homebrew_extra_pkg_config_paths, + "/usr/lib/pkgconfig", + ).existing end # Removes the MAKEFLAGS environment variable, causing make to use a single job. diff --git a/Library/Homebrew/extend/ENV/super.rb b/Library/Homebrew/extend/ENV/super.rb index 4d6d96ad3..ef41161af 100644 --- a/Library/Homebrew/extend/ENV/super.rb +++ b/Library/Homebrew/extend/ENV/super.rb @@ -101,29 +101,28 @@ module Superenv end def determine_path - paths = [Superenv.bin] + path = PATH.new(Superenv.bin) # Formula dependencies can override standard tools. - paths += deps.map { |d| d.opt_bin.to_s } - - paths += homebrew_extra_paths - paths += %w[/usr/bin /bin /usr/sbin /sbin] + path.append(deps.map(&:opt_bin)) + path.append(homebrew_extra_paths) + path.append("/usr/bin", "/bin", "/usr/sbin", "/sbin") # Homebrew's apple-gcc42 will be outside the PATH in superenv, # so xcrun may not be able to find it begin case homebrew_cc when "gcc-4.2" - paths << Formulary.factory("apple-gcc42").opt_bin + path.append(Formulary.factory("apple-gcc42").opt_bin) when GNU_GCC_REGEXP - paths << gcc_version_formula($&).opt_bin + path.append(gcc_version_formula($&).opt_bin) end rescue FormulaUnavailableError # Don't fail and don't add these formulae to the path if they don't exist. nil end - paths.to_path_s + path.existing end def homebrew_extra_pkg_config_paths @@ -131,15 +130,17 @@ module Superenv end def determine_pkg_config_path - paths = deps.map { |d| "#{d.opt_lib}/pkgconfig" } - paths += deps.map { |d| "#{d.opt_share}/pkgconfig" } - paths.to_path_s + PATH.new( + deps.map { |d| d.opt_lib/"pkgconfig" }, + deps.map { |d| d.opt_share/"pkgconfig" }, + ).existing end def determine_pkg_config_libdir - paths = %w[/usr/lib/pkgconfig] - paths += homebrew_extra_pkg_config_paths - paths.to_path_s + PATH.new( + "/usr/lib/pkgconfig", + homebrew_extra_pkg_config_paths, + ).existing end def homebrew_extra_aclocal_paths @@ -147,10 +148,11 @@ module Superenv end def determine_aclocal_path - paths = keg_only_deps.map { |d| "#{d.opt_share}/aclocal" } - paths << "#{HOMEBREW_PREFIX}/share/aclocal" - paths += homebrew_extra_aclocal_paths - paths.to_path_s + PATH.new( + keg_only_deps.map { |d| d.opt_share/"aclocal" }, + HOMEBREW_PREFIX/"share/aclocal", + homebrew_extra_aclocal_paths, + ).existing end def homebrew_extra_isystem_paths @@ -158,13 +160,14 @@ module Superenv end def determine_isystem_paths - paths = ["#{HOMEBREW_PREFIX}/include"] - paths += homebrew_extra_isystem_paths - paths.to_path_s + PATH.new( + HOMEBREW_PREFIX/"include", + homebrew_extra_isystem_paths, + ).existing end def determine_include_paths - keg_only_deps.map { |d| d.opt_include.to_s }.to_path_s + PATH.new(keg_only_deps.map(&:opt_include)).existing end def homebrew_extra_library_paths @@ -172,10 +175,11 @@ module Superenv end def determine_library_paths - paths = keg_only_deps.map { |d| d.opt_lib.to_s } - paths << "#{HOMEBREW_PREFIX}/lib" - paths += homebrew_extra_library_paths - paths.to_path_s + PATH.new( + keg_only_deps.map(&:opt_lib), + HOMEBREW_PREFIX/"lib", + homebrew_extra_library_paths, + ).existing end def determine_dependencies @@ -183,9 +187,10 @@ module Superenv end def determine_cmake_prefix_path - paths = keg_only_deps.map { |d| d.opt_prefix.to_s } - paths << HOMEBREW_PREFIX.to_s - paths.to_path_s + PATH.new( + keg_only_deps.map(&:opt_prefix), + HOMEBREW_PREFIX.to_s, + ).existing end def homebrew_extra_cmake_include_paths @@ -193,9 +198,7 @@ module Superenv end def determine_cmake_include_path - paths = [] - paths += homebrew_extra_cmake_include_paths - paths.to_path_s + PATH.new(homebrew_extra_cmake_include_paths).existing end def homebrew_extra_cmake_library_paths @@ -203,9 +206,7 @@ module Superenv end def determine_cmake_library_path - paths = [] - paths += homebrew_extra_cmake_library_paths - paths.to_path_s + PATH.new(homebrew_extra_cmake_library_paths).existing end def homebrew_extra_cmake_frameworks_paths @@ -213,9 +214,10 @@ module Superenv end def determine_cmake_frameworks_path - paths = deps.map { |d| d.opt_frameworks.to_s } - paths += homebrew_extra_cmake_frameworks_paths - paths.to_path_s + PATH.new( + deps.map(&:opt_frameworks), + homebrew_extra_cmake_frameworks_paths, + ).existing end def determine_make_jobs @@ -330,10 +332,4 @@ module Superenv end end -class Array - def to_path_s - map(&:to_s).uniq.select { |s| File.directory? s }.join(File::PATH_SEPARATOR).chuzzle - end -end - require "extend/os/extend/ENV/super" diff --git a/Library/Homebrew/global.rb b/Library/Homebrew/global.rb index 108ca0cb7..877253072 100644 --- a/Library/Homebrew/global.rb +++ b/Library/Homebrew/global.rb @@ -3,6 +3,7 @@ require "extend/fileutils" require "extend/pathname" require "extend/git_repository" require "extend/ARGV" +require "PATH" require "extend/string" require "os" require "utils" @@ -55,7 +56,7 @@ HOMEBREW_PULL_OR_COMMIT_URL_REGEX = %r[https://github\.com/([\w-]+)/([\w-]+)?/(? require "compat" unless ARGV.include?("--no-compat") || ENV["HOMEBREW_NO_COMPAT"] ENV["HOMEBREW_PATH"] ||= ENV["PATH"] -ORIGINAL_PATHS = ENV["HOMEBREW_PATH"].split(File::PATH_SEPARATOR).map do |p| +ORIGINAL_PATHS = PATH.new(ENV["HOMEBREW_PATH"]).map do |p| begin Pathname.new(p).expand_path rescue diff --git a/Library/Homebrew/requirement.rb b/Library/Homebrew/requirement.rb index a4bdabdd1..6c20e7917 100644 --- a/Library/Homebrew/requirement.rb +++ b/Library/Homebrew/requirement.rb @@ -96,7 +96,7 @@ class Requirement # PATH. parent = satisfied_result_parent return unless parent - return if ENV["PATH"].split(File::PATH_SEPARATOR).include?(parent.to_s) + return if PATH.new(ENV["PATH"]).include?(parent.to_s) ENV.append_path("PATH", parent) end @@ -151,11 +151,11 @@ class Requirement end def which(cmd) - super(cmd, ORIGINAL_PATHS.join(File::PATH_SEPARATOR)) + super(cmd, PATH.new(ORIGINAL_PATHS)) end def which_all(cmd) - super(cmd, ORIGINAL_PATHS.join(File::PATH_SEPARATOR)) + super(cmd, PATH.new(ORIGINAL_PATHS)) end class << self diff --git a/Library/Homebrew/test/PATH_spec.rb b/Library/Homebrew/test/PATH_spec.rb new file mode 100644 index 000000000..68233c23c --- /dev/null +++ b/Library/Homebrew/test/PATH_spec.rb @@ -0,0 +1,115 @@ +require "PATH" + +describe PATH do + describe "#initialize" do + it "can take multiple arguments" do + expect(described_class.new("/path1", "/path2")).to eq("/path1:/path2") + end + + it "can parse a mix of arrays and arguments" do + expect(described_class.new(["/path1", "/path2"], "/path3")).to eq("/path1:/path2:/path3") + end + + it "splits an existing PATH" do + expect(described_class.new("/path1:/path2")).to eq(["/path1", "/path2"]) + end + + it "removes duplicates" do + expect(described_class.new("/path1", "/path1")).to eq("/path1") + end + end + + describe "#to_ary" do + it "returns a PATH array" do + expect(described_class.new("/path1", "/path2").to_ary).to eq(["/path1", "/path2"]) + end + end + + describe "#to_str" do + it "returns a PATH string" do + expect(described_class.new("/path1", "/path2").to_str).to eq("/path1:/path2") + end + end + + describe "#prepend" do + it "prepends a path to a PATH" do + expect(described_class.new("/path1").prepend("/path2").to_str).to eq("/path2:/path1") + end + + it "removes duplicates" do + expect(described_class.new("/path1").prepend("/path1").to_str).to eq("/path1") + end + end + + describe "#append" do + it "prepends a path to a PATH" do + expect(described_class.new("/path1").append("/path2").to_str).to eq("/path1:/path2") + end + + it "removes duplicates" do + expect(described_class.new("/path1").append("/path1").to_str).to eq("/path1") + end + end + + describe "#insert" do + it "inserts a path at a given index" do + expect(described_class.new("/path1").insert(0, "/path2").to_str).to eq("/path2:/path1") + end + + it "can insert multiple paths" do + expect(described_class.new("/path1").insert(0, "/path2", "/path3")).to eq("/path2:/path3:/path1") + end + end + + describe "#include?" do + it "returns true if a path is included" do + path = described_class.new("/path1", "/path2") + expect(path).to include("/path1") + expect(path).to include("/path2") + end + + it "returns false if a path is not included" do + expect(described_class.new("/path1")).not_to include("/path2") + end + + it "returns false if the given string contains a separator" do + expect(described_class.new("/path1", "/path2")).not_to include("/path1:") + end + end + + describe "#each" do + it "loops through each path" do + enum = described_class.new("/path1", "/path2").each + + expect(enum.next).to eq("/path1") + expect(enum.next).to eq("/path2") + end + end + + describe "#select" do + it "returns an object of the same class instead of an Array" do + expect(described_class.new.select { true }).to be_a(described_class) + end + end + + describe "#reject" do + it "returns an object of the same class instead of an Array" do + expect(described_class.new.reject { true }).to be_a(described_class) + end + end + + describe "#existing" do + it "returns a new PATH without non-existent paths" do + allow(File).to receive(:directory?).with("/path1").and_return(true) + allow(File).to receive(:directory?).with("/path2").and_return(false) + + path = described_class.new("/path1", "/path2") + expect(path.existing.to_ary).to eq(["/path1"]) + expect(path.to_ary).to eq(["/path1", "/path2"]) + end + + it "returns nil instead of an empty #{described_class}" do + expect(described_class.new.existing).to be nil + end + end +end diff --git a/Library/Homebrew/utils.rb b/Library/Homebrew/utils.rb index edc540c45..83b8ba342 100644 --- a/Library/Homebrew/utils.rb +++ b/Library/Homebrew/utils.rb @@ -190,10 +190,10 @@ module Homebrew Gem::Specification.reset # Add Gem binary directory and (if missing) Ruby binary directory to PATH. - path = ENV["PATH"].split(File::PATH_SEPARATOR) - path.unshift(RUBY_BIN) if which("ruby") != RUBY_PATH - path.unshift(Gem.bindir) - ENV["PATH"] = path.join(File::PATH_SEPARATOR) + path = PATH.new(ENV["PATH"]) + path.prepend(RUBY_BIN) if which("ruby") != RUBY_PATH + path.prepend(Gem.bindir) + ENV["PATH"] = path.existing if Gem::Specification.find_all_by_name(name, version).empty? ohai "Installing or updating '#{name}' gem" @@ -293,7 +293,7 @@ def quiet_system(cmd, *args) end def which(cmd, path = ENV["PATH"]) - path.split(File::PATH_SEPARATOR).each do |p| + PATH.new(path).each do |p| begin pcmd = File.expand_path(cmd, p) rescue ArgumentError @@ -307,7 +307,7 @@ def which(cmd, path = ENV["PATH"]) end def which_all(cmd, path = ENV["PATH"]) - path.to_s.split(File::PATH_SEPARATOR).map do |p| + PATH.new(path).map do |p| begin pcmd = File.expand_path(cmd, p) rescue ArgumentError @@ -416,7 +416,7 @@ def nostdout end def paths(env_path = ENV["PATH"]) - @paths ||= env_path.split(File::PATH_SEPARATOR).collect do |p| + @paths ||= PATH.new(env_path).collect do |p| begin File.expand_path(p).chomp("/") rescue ArgumentError |
