Module: Utils

Defined in:
brew/Library/Homebrew/utils/git.rb,
brew/Library/Homebrew/utils/svn.rb,
brew/Library/Homebrew/utils/fork.rb,
brew/Library/Homebrew/utils/link.rb,
brew/Library/Homebrew/utils/popen.rb,
brew/Library/Homebrew/utils/shell.rb,
brew/Library/Homebrew/utils/bottles.rb,
brew/Library/Homebrew/utils/analytics.rb,
brew/Library/Homebrew/utils/inreplace.rb,
brew/Library/Homebrew/extend/os/mac/utils/bottles.rb,
brew/Library/Homebrew/extend/os/mac/utils/analytics.rb

Defined Under Namespace

Modules: Analytics, Inreplace, Link, Shell Classes: Bottles, InreplaceError

Class Method Summary collapse

Class Method Details

.clear_git_available_cacheObject



94
95
96
97
98
# File 'brew/Library/Homebrew/utils/git.rb', line 94

def self.clear_git_available_cache
  @git_available = nil
  @git_path = nil
  @git_version = nil
end

.clear_svn_version_cacheObject



4
5
6
# File 'brew/Library/Homebrew/utils/svn.rb', line 4

def self.clear_svn_version_cache
  remove_instance_variable(:@svn) if instance_variable_defined?(:@svn)
end

.ensure_git_installed!Object



78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
# File 'brew/Library/Homebrew/utils/git.rb', line 78

def self.ensure_git_installed!
  return if git_available?

  # we cannot install brewed git if homebrew/core is unavailable.
  if CoreTap.instance.installed?
    begin
      oh1 "Installing #{Formatter.identifier("git")}"
      safe_system HOMEBREW_BREW_FILE, "install", "git"
    rescue
      raise "Git is unavailable"
    end
  end

  raise "Git is unavailable" unless git_available?
end

.git_available?Boolean

Returns:

  • (Boolean)


58
59
60
# File 'brew/Library/Homebrew/utils/git.rb', line 58

def self.git_available?
  @git_available ||= quiet_system HOMEBREW_SHIMS_PATH/"scm/git", "--version"
end

.git_pathObject



62
63
64
65
66
67
68
# File 'brew/Library/Homebrew/utils/git.rb', line 62

def self.git_path
  return unless git_available?

  @git_path ||= Utils.popen_read(
    HOMEBREW_SHIMS_PATH/"scm/git", "--homebrew=print-path"
  ).chomp.presence
end

.git_remote_exists?(url) ⇒ Boolean

Returns:

  • (Boolean)


100
101
102
103
104
# File 'brew/Library/Homebrew/utils/git.rb', line 100

def self.git_remote_exists?(url)
  return true unless git_available?

  quiet_system "git", "ls-remote", url
end

.git_versionObject



70
71
72
73
74
75
76
# File 'brew/Library/Homebrew/utils/git.rb', line 70

def self.git_version
  return unless git_available?

  @git_version ||= Utils.popen_read(
    HOMEBREW_SHIMS_PATH/"scm/git", "--version"
  ).chomp[/git version (\d+(?:\.\d+)*)/, 1]
end

.popen(args, mode, options = {}) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
# File 'brew/Library/Homebrew/utils/popen.rb', line 26

def self.popen(args, mode, options = {})
  IO.popen("-", mode) do |pipe|
    if pipe
      return pipe.read unless block_given?

      yield pipe
    else
      options[:err] ||= :close unless ENV["HOMEBREW_STDERR"]
      begin
        exec(*args, options)
      rescue Errno::ENOENT
        $stderr.puts "brew: command not found: #{args[0]}" unless options[:err] == :close
        exit! 127
      rescue SystemCallError
        $stderr.puts "brew: exec failed: #{args[0]}" unless options[:err] == :close
        exit! 1
      end
    end
  end
end

.popen_read(*args, **options, &block) ⇒ Object



4
5
6
# File 'brew/Library/Homebrew/utils/popen.rb', line 4

def self.popen_read(*args, **options, &block)
  popen(args, "rb", options, &block)
end

.popen_write(*args, **options, &block) ⇒ Object



15
16
17
# File 'brew/Library/Homebrew/utils/popen.rb', line 15

def self.popen_write(*args, **options, &block)
  popen(args, "wb", options, &block)
end

.rewrite_child_error(child_error) ⇒ Object



7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# File 'brew/Library/Homebrew/utils/fork.rb', line 7

def self.rewrite_child_error(child_error)
  error = if child_error.inner_class == ErrorDuringExecution
    ErrorDuringExecution.new(child_error.inner["cmd"],
                             status: child_error.inner["status"],
                             output: child_error.inner["output"])
  elsif child_error.inner_class == BuildError
    # We fill `BuildError#formula` and `BuildError#options` in later,
    # when we rescue this in `FormulaInstaller#build`.
    BuildError.new(nil, child_error.inner["cmd"],
                   child_error.inner["args"], child_error.inner["env"])
  elsif child_error.inner_class == Interrupt
    Interrupt.new
  else
    # Everything other error in the child just becomes a RuntimeError.
    RuntimeError.new(child_error.message)
  end

  error.set_backtrace child_error.backtrace

  error
end

.safe_fork(&_block) ⇒ Object



29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
# File 'brew/Library/Homebrew/utils/fork.rb', line 29

def self.safe_fork(&_block)
  Dir.mktmpdir("homebrew", HOMEBREW_TEMP) do |tmpdir|
    UNIXServer.open("#{tmpdir}/socket") do |server|
      read, write = IO.pipe

      pid = fork do
        begin
          ENV["HOMEBREW_ERROR_PIPE"] = server.path
          server.close
          read.close
          write.fcntl(Fcntl::F_SETFD, Fcntl::FD_CLOEXEC)
          yield
        rescue Exception => e # rubocop:disable Lint/RescueException
          error_hash = JSON.parse e.to_json

          # Special case: We need to recreate ErrorDuringExecutions
          # for proper error messages and because other code expects
          # to rescue them further down.
          if e.is_a?(ErrorDuringExecution)
            error_hash["cmd"] = e.cmd
            error_hash["status"] = e.status.exitstatus
            error_hash["output"] = e.output
          end

          write.puts error_hash.to_json
          write.close

          exit!
        else
          exit!(true)
        end
      end

      ignore_interrupts(:quietly) do # the child will receive the interrupt and marshal it back
        begin
          socket = server.accept_nonblock
        rescue Errno::EAGAIN, Errno::EWOULDBLOCK, Errno::ECONNABORTED, Errno::EPROTO, Errno::EINTR
          retry unless Process.waitpid(pid, Process::WNOHANG)
        else
          socket.send_io(write)
          socket.close
        end
        write.close
        data = read.read
        read.close
        Process.wait(pid) unless socket.nil?

        # 130 is the exit status for a process interrupted via Ctrl-C.
        # We handle it here because of the possibility of an interrupted process terminating
        # without writing its Interrupt exception to the error pipe.
        raise Interrupt if $CHILD_STATUS.exitstatus == 130

        if data && !data.empty?
          error_hash = JSON.parse(data.lines.first)

          e = ChildProcessError.new(error_hash)

          raise rewrite_child_error(e)
        end

        raise "Forked child process failed: #{$CHILD_STATUS}" unless $CHILD_STATUS.success?
      end
    end
  end
end

.safe_popen_read(*args, **options, &block) ⇒ Object



8
9
10
11
12
13
# File 'brew/Library/Homebrew/utils/popen.rb', line 8

def self.safe_popen_read(*args, **options, &block)
  output = popen_read(*args, **options, &block)
  return output if $CHILD_STATUS.success?

  raise ErrorDuringExecution.new(args, status: $CHILD_STATUS, output: [[:stdout, output]])
end

.safe_popen_write(*args, **options, &block) ⇒ Object



19
20
21
22
23
24
# File 'brew/Library/Homebrew/utils/popen.rb', line 19

def self.safe_popen_write(*args, **options, &block)
  output = popen_write(*args, **options, &block)
  return output if $CHILD_STATUS.success?

  raise ErrorDuringExecution.new(args, status: $CHILD_STATUS, output: [[:stdout, output]])
end

.svn_available?Boolean

Returns:

  • (Boolean)


8
9
10
11
12
# File 'brew/Library/Homebrew/utils/svn.rb', line 8

def self.svn_available?
  return @svn if instance_variable_defined?(:@svn)

  @svn = quiet_system HOMEBREW_SHIMS_PATH/"scm/svn", "--version"
end

.svn_remote_exists?(url) ⇒ Boolean

Returns:

  • (Boolean)


14
15
16
17
18
19
20
21
# File 'brew/Library/Homebrew/utils/svn.rb', line 14

def self.svn_remote_exists?(url)
  return true unless svn_available?

  # OK to unconditionally trust here because we're just checking if
  # a URL exists.
  quiet_system "svn", "ls", url, "--depth", "empty",
               "--non-interactive", "--trust-server-cert"
end