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



214
215
216
217
218
219
220
221
222
# File 'lib/albacore/cross_platform_cmd.rb', line 214

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



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

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

#normalise_slashes(path) ⇒ Object



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

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

#sh(*cmd, &block) ⇒ Object

run in shell

Raises:

  • (ArgumentError)


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

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


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

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

#stopObject



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

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
# 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

    return block.call(status.success? && inmem.string, status, inmem.string)
  end
end

#which(executable) ⇒ Object



185
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
# File 'lib/albacore/cross_platform_cmd.rb', line 185

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 = []
  parameters << Paths.normalise_slashes(file) if dir == '.'
  parameters << Paths.normalise_slashes("#{dir}:#{file}") unless dir == '.'
  cmd, parameters = Paths.normalise cmd, parameters

  trace { "#{cmd} #{parameters.join(' ')} [cross_platform_cmd #which]" }

  null = ::Rake::Win32.windows? ? "NUL" : "/dev/null"
  res = IO.popen([cmd, *parameters]) 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