Module: GCPT::Executable

Defined in:
lib/gcpt/utils/executable.rb

Defined Under Namespace

Classes: Indenter

Class Method Summary collapse

Instance Method Summary collapse

Class Method Details

.capture_command(executable, command, capture: :merge, env: {}, **kwargs) ⇒ (String, Process::Status)

Runs the given command, capturing the desired output.

Parameters:

  • executable (String)

    The binary to use.

  • command (Array<#to_s>)

    The command to send to the binary.

  • capture (Symbol) (defaults to: :merge)

    Whether it should raise if the command fails.

  • env (Hash) (defaults to: {})

    Environment variables to be set for the command.

Returns:

  • ((String, Process::Status))

    The desired captured output from the command, and the status from running the command.

Raises:

  • If the executable could not be located.



125
126
127
128
129
130
131
132
133
134
135
136
137
# File 'lib/gcpt/utils/executable.rb', line 125

def self.capture_command(executable, command, capture: :merge, env: {}, **kwargs)
  bin = which!(executable)

  require 'open3'
  command = command.map(&:to_s)
  case capture
  when :merge then Open3.capture2e(env, [bin, bin], *command, **kwargs)
  when :both then Open3.capture3(env, [bin, bin], *command, **kwargs)
  when :out then Open3.capture3(env, [bin, bin], *command, **kwargs).values_at(0, -1)
  when :err then Open3.capture3(env, [bin, bin], *command, **kwargs).drop(1)
  when :none then Open3.capture3(env, [bin, bin], *command, **kwargs).last
  end
end

.capture_command!(executable, command, **kwargs) ⇒ (String, Process::Status)

Runs the given command, capturing the desired output.

Parameters:

  • executable (String)

    The binary to use.

  • command (Array<#to_s>)

    The command to send to the binary.

  • capture (Symbol)

    Whether it should raise if the command fails.

  • env (Hash)

    Environment variables to be set for the command.

Returns:

  • ((String, Process::Status))

    The desired captured output from the command, and the status from running the command.

Raises:

  • If the executable could not be located.

  • If running the command fails



143
144
145
146
147
148
149
150
151
152
# File 'lib/gcpt/utils/executable.rb', line 143

def self.capture_command!(executable, command, **kwargs)
  capture_command(executable, command, **kwargs).tap do |result|
    result = Array(result)
    status = result.last
    unless status.success?
      output = result[0..-2].join
      raise Informative, "#{executable} #{command.join(' ')}\n\n#{output}".strip
    end
  end
end

.execute_command(executable, command, raise_on_failure = true) ⇒ String

Executes the given command displaying it if in verbose mode.

Parameters:

  • executable (String)

    The binary to use.

  • command (Array<#to_s>)

    The command to send to the binary.

  • raise_on_failure (Bool) (defaults to: true)

    Whether it should raise if the command fails.

Returns:

  • (String)

    the output of the command (STDOUT and STDERR).

Raises:

  • If the executable could not be located.

  • If the command fails and the ‘raise_on_failure` is set to true.



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
# File 'lib/gcpt/utils/executable.rb', line 37

def self.execute_command(executable, command, raise_on_failure = true)
  bin = which!(executable)

  command = command.map(&:to_s)
  if File.basename(bin) == 'tar.exe'
    # Tar on Windows needs --force-local
    command.push('--force-local')
  end
  full_command = "#{bin} #{command.join(' ')}"

  stdout = Indenter.new([])
  stderr = Indenter.new([])

  status = popen3(bin, command, stdout, stderr)
  stdout = stdout.join
  stderr = stderr.join
  output = stdout + stderr
  unless status.success?
    if raise_on_failure
      raise Informative, "#{full_command}\n\n#{output}"
    else
      puts("[!] Failed: #{full_command}".red)
    end
  end

  output
end

.which(program) ⇒ String, Nil

Returns the absolute path to the binary with the given name on the current ‘PATH`, or `nil` if none is found.

Parameters:

  • program (String)

    The name of the program being searched for.

Returns:

  • (String, Nil)

    The absolute path to the given program, or ‘nil` if it wasn’t found in the current ‘PATH`.



74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
# File 'lib/gcpt/utils/executable.rb', line 74

def self.which(program)
  program = program.to_s
  paths = ENV.fetch('PATH') { '' }.split(File::PATH_SEPARATOR)
  paths.unshift('./')
  paths.uniq!
  paths.each do |path|
    bin = File.expand_path(program, path)
    if Gem.win_platform?
      bin += '.exe'
    end
    if File.file?(bin) && File.executable?(bin)
      return bin
    end
  end
  nil
end

.which!(program) ⇒ String

Returns the absolute path to the binary with the given name on the current ‘PATH`, or raises if none is found.

Parameters:

  • program (String)

    The name of the program being searched for.

Returns:

  • (String)

    The absolute path to the given program.



99
100
101
102
103
# File 'lib/gcpt/utils/executable.rb', line 99

def self.which!(program)
  which(program).tap do |bin|
    raise Informative, "Unable to locate the executable `#{program}`" unless bin
  end
end

Instance Method Details

#executable(name) ⇒ void

This method returns an undefined value.

Creates the methods for the executable with the given name.

Parameters:

  • name (Symbol)

    the name of the executable.



10
11
12
13
14
15
16
17
18
# File 'lib/gcpt/utils/executable.rb', line 10

def executable(name)
  define_method(name) do |*command|
    Executable.execute_command(name, Array(command).flatten, false)
  end

  define_method(name.to_s + '!') do |*command|
    Executable.execute_command(name, Array(command).flatten, true)
  end
end