Module: Albacore::CrossPlatformCmd

Overview

module for normalising slashes across operating systems and running commands

Defined Under Namespace

Classes: PseudoStatus

Constant Summary collapse

KILL_TIMEOUT =

seconds

2

Instance Attribute Summary collapse

Instance Method Summary collapse

Methods included from Logging

#debug, #err, #error, #fatal, #info, #puts, #trace, #warn

Instance Attribute Details

#pidObject (readonly)

Returns the value of attribute pid.



41
42
43
# File 'lib/albacore/cross_platform_cmd.rb', line 41

def pid
  @pid
end

Instance Method Details

#chdir(wd, &block) ⇒ Object



219
220
221
222
223
224
225
226
227
# File 'lib/albacore/cross_platform_cmd.rb', line 219

def chdir wd, &block
  return block.call if wd.nil?
  Dir.chdir wd do
    debug { "pushd #{wd} [cross_platform_cmd #chdir]" }
    res = block.call
    debug { "popd #{wd} [cross_platform_cmd #chdir]" }
    return res
  end
end

#initializeObject



45
46
47
48
# File 'lib/albacore/cross_platform_cmd.rb', line 45

def initialize
  pid = Process.pid
  at_exit { stop if Process.pid == pid }
end

#make_commandObject

create a new command string



182
183
184
# File 'lib/albacore/cross_platform_cmd.rb', line 182

def make_command
  ::Albacore::Paths.make_command @executable, @parameters
end

#normalise_slashes(path) ⇒ Object



177
178
179
# File 'lib/albacore/cross_platform_cmd.rb', line 177

def normalise_slashes path
  ::Albacore::Paths.normalise_slashes path
end

#sh(*cmd, &block) ⇒ Object

run in shell

Raises:

  • (ArgumentError)


140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
# File 'lib/albacore/cross_platform_cmd.rb', line 140

def sh *cmd, &block
  raise ArgumentError, "cmd is nil" if cmd.nil? # don't allow nothing to be passed
  opts = Map.options((Hash === cmd.last) ? cmd.pop : {}) # same arg parsing as rake

  cmd = cmd.join(' ') # shell needs a single string
  block = handler_with_message cmd unless block_given?

  chdir opts.get(:work_dir) do

    trace { "#sh( ...,  options: #{opts.to_s}) [cross_platform_cmd #sh]" }
    puts cmd unless opts.getopt :silent, false # log cmd verbatim

    lines = ''
    handle_not_found block do
      IO.popen(cmd, 'r') do |io|
        io.each do |line|
          lines << line
          puts line if opts.getopt(:output, true) or not opts.getopt(:silent, false)
        end
      end
    end

    return block.call($? == 0 && lines, $?, lines)
  end
end

#shie(*cmd, &block) ⇒ Object

shell ignore exit code returns:

[ok, status]
where status:
  #exitstatus : Int
  #pid      : Int


172
173
174
175
# File 'lib/albacore/cross_platform_cmd.rb', line 172

def shie *cmd, &block
  block = lambda { |ok, status, output| ok } unless block_given?
  sh *cmd, &block
end

#stopObject



115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
# File 'lib/albacore/cross_platform_cmd.rb', line 115

def stop
  if pid
    begin
      Process.kill('TERM', pid)

      begin
        Timeout.timeout(KILL_TIMEOUT) { Process.wait(pid) }
      rescue Timeout::Error
        Process.kill('KILL', pid)
        Process.wait(pid)
      end
    rescue Errno::ESRCH, Errno::ECHILD
      # Zed's dead, baby
    end

    @out_thread.kill
    @pid = nil
  end
end

#system(*cmd, &block) ⇒ Object

run executable

system(cmd, [args array], Hash(opts), block|ok,status|)

ok => false if bad exit code, or the output otherwise

options are passed as the last argument

options:

work_dir: a file path (default '.')
silent:   whether to supress all output or not (default false)
output:   whether to supress the command's output (default false)
out:      output pipe
err:      error pipe
clr_command:
          whether to include 'mono' in front of the things to run
          if the command is a clr command

Raises:

  • (ArgumentError)


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
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/albacore/cross_platform_cmd.rb', line 67

def system *cmd, &block
  raise ArgumentError, "cmd is nil" if cmd.nil? # don't allow nothing to be passed
  opts = Map.options((Hash === cmd.last) ? cmd.pop : {}). # same arg parsing as rake
    apply({
      :silent => false,
      :output => true,
      :work_dir => FileUtils.pwd,
      :out => Albacore.application.output,
      :err => Albacore.application.output_err,
      :clr_command => false })

  exe, pars, printable, block = prepare_command cmd, (opts.get('clr_command')), &block

  # TODO: figure out how to interleave output and error streams
  out, _, inmem = opts.get(:out), opts.get(:err), StringIO.new

  trace { "system( exe=#{exe}, pars=[#{pars.join(', ')}], options=#{opts.to_s}), in directory: #{opts.getopt(:work_dir, '<<current>>')} [cross_platform_cmd #system]" }

  puts printable unless opts.get :silent, false # log cmd verbatim

  handle_not_found block do
    # create a pipe for the process to work with
    read, write = IO.pipe
    eread, ewrite = IO.pipe

    # this thread chews through the output
    @out_thread = Thread.new {
      while !read.eof?
        data = read.readpartial(1024)
        out.write data
        inmem.write data # to give the block at the end
      end
    }

    debug 'execute the new process, letting it write to the write FD (file descriptor)'
    @pid = Process.spawn(*[exe, *pars],
      { :out => write,
        #:err => ewrite,
        :chdir => opts.get(:work_dir) })

    debug 'waiting for process completion'
    _, status = Process.wait2 @pid

    ret_str = inmem.string.encode 'utf-8', invalid: :replace, undef: :replace, replace: ''
    return block.call(status.success? && ret_str, status, ret_str)
  end
end

#which(executable) ⇒ Object



186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/albacore/cross_platform_cmd.rb', line 186

def which executable
  raise ArgumentError, "executable to #which is nil" unless executable

  dir = File.dirname executable
  file = File.basename executable

  cmd = ::Rake::Win32.windows? ? 'where' : 'which'
  parameters = []
  pattern = if dir == '.' then file
  elsif ::Rake::Win32.windows? then "#{dir}:#{file}"
  else executable
  end
  parameters << Paths.normalise_slashes("\"#{pattern}\"")
  parameters << '2> nul' if ::Rake::Win32.windows?
  cmd, parameters = Paths.normalise cmd, parameters
  cmd = "#{cmd} #{parameters.join(' ')}"

  trace { "#{cmd} [cross_platform_cmd #which]" }

  res = IO.popen(cmd) do |io|
    io.read.chomp
  end

  unless $? == 0
    nil
  else
    res
  end
rescue Errno::ENOENT => e
  trace "which/where returned #{$?}: #{e} [cross_platform_cmd #which]"
  nil
end