Module: SubSpawn::Internal
- Defined in:
- lib/subspawn/fd_parse.rb,
lib/subspawn/fd_types.rb,
lib/subspawn/graph_helper.rb
Defined Under Namespace
Classes: BiNode, Bigraph, FdSource
Class Method Summary collapse
-
.graph_order(fds) ⇒ Object
convert an unordered list of FdSource’s into an ordered list with appropriate temporaries for a correct swap.
-
.guess_mode(d) ⇒ Object
mode conversion.
-
.parse_fd(fd, allow_pty = false) ⇒ Object
argument value to int (or :tty).
-
.parse_fd_opts(fds, &settty) ⇒ Object
make FdSource objects of each redirection.
-
.which(cmd, env) ⇒ Object
I’d love to use this method, but it doesn’t accept env def self.which_jruby(cmd) require ‘jruby’ org.jruby.util.ShellLauncher.findPathExecutable(JRuby.runtime, cmd)&.absolute_path end.
Class Method Details
.graph_order(fds) ⇒ Object
convert an unordered list of FdSource’s into an ordered list with appropriate temporaries for a correct swap
96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
# File 'lib/subspawn/fd_parse.rb', line 96 def self.graph_order(fds) # Note that we keep all dests together. It may be smarter to break dests apart to avoid # temporaries, but that is left as an excercise to the reader next_temp = [fds.map(&:max).max || 2, 2].max + 1 graph = Bigraph.new # build graphs fds.each do |fd| from = fd.heads || [Object.new] to = fd.tails || [Object.new] from.zip(to) do |h, t| graph.insert(h, t, fd) end end #puts graph.to_dot # find and break cycles while point = graph.find_cycle # we probably could find the least-cost fd, but this will do for now items = graph.delete_outgoing(point) #p items temp = FdSource::Temp.new([next_temp], point.to_i) close = FdSource::Close.new([next_temp]) items = [temp, *items.map{|x|x.temp_source(next_temp)}, close] #p items # reinsert items.each do |fd| from = fd.heads || [Object.new] to = fd.tails || [Object.new] from.zip(to) do |h, t| graph.insert(h, t, fd) end end #puts graph.to_dot end #puts "removed all cycles, hopeuflly" elts = graph.ordered_kahn.reverse # execute in the opposite order of dependencies #puts graph.to_dot elts end |
.guess_mode(d) ⇒ Object
mode conversion
27 28 29 30 31 32 33 34 35 36 37 38 39 |
# File 'lib/subspawn/fd_parse.rb', line 27 def self.guess_mode(d) read = d.include? 0 # stdin write = (d.include? 1 or d.include? 2) # stdout if read && write raise ArgumentError, "Invalid FD source specification: ambiguious mode (r & w)" elsif read :read elsif write :write else raise ArgumentError, "Invalid FD source specification: ambiguious mode (stdio missing)" end end |
.parse_fd(fd, allow_pty = false) ⇒ Object
argument value to int (or :tty)
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/subspawn/fd_parse.rb', line 7 def self.parse_fd(fd, allow_pty=false) # fd = if fd.respond_to? :to_path # fd = if fd.respond_to? :to_file # fd = if fd.respond_to? :to_file case fd when Integer then fd when IO then fd.fileno when :in, :input, :stdin then 0 when :out, :output, :stdout then 1 when :err, :error, :stderr then 2 else if allow_pty and %i{pty tty}.include? fd :tty else raise ArgumentError, "Unknown FD type: #{fd.inspect}" end end end |
.parse_fd_opts(fds, &settty) ⇒ Object
make FdSource objects of each redirection
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 |
# File 'lib/subspawn/fd_parse.rb', line 42 def self.parse_fd_opts(fds, &settty) child_lookup = {} fds.map do |dests, src| d = dests.map{|x| parse_fd(x, true)} # TODO: configurable src = case src when Array case src.first when String # file #raise ArgumentError, "Invalid :child FD source specification" unless src.length == 2 # TODO: validate FdSource::File.new(d, *src) when :child raise ArgumentError, "Invalid :child FD source specification" unless src.length == 2 # {a => c, b => [child, a]} is the same as {[a, b] => c} # so we can transform the former into the latter newfd = parse_fd(src.last) # TODO: this isn't an error, create a new one raise ArgumentError, "Invalid :child FD source specification" unless child_lookup[newfd] child_lookup[newfd].tap{|child| # add our destinations to the source's redirection d.each { |di| child.dests << di } } else raise ArgumentError, "Invalid FD source specification" end when String FdSource::File.new d, src, ({read: IO::RDONLY, write: IO::WRONLY | IO::CREAT | IO::TRUNC}[guess_mode(d)]) when :close FdSource::Close.new d when :pty, :tty FdSource::PTY.new d when :pipe FdSource::Pipe.new d, guess_mode(d) when :pipe_r FdSource::Pipe.new d, :read # TODO: ensure pipe direction is sensical when :pipe_w FdSource::Pipe.new d, :read else if d.include? :tty and src.is_a? File # TODO: is this redundant? settty.call(src.path) d.delete(:tty) end FdSource::Basic.new d, parse_fd(src) end # save redirected fds so we can sneak a child reference in src.tap{|x| d.each{|c| raise ArgumentError, "Invalid FD source specification: duplicate FDs" if child_lookup[c] child_lookup[c] = x } } end.reject(&:nil?) end |
.which(cmd, env) ⇒ Object
I’d love to use this method, but it doesn’t accept env def self.which_jruby(cmd) require ‘jruby’ org.jruby.util.ShellLauncher.findPathExecutable(JRuby.runtime, cmd)&.absolute_path end
141 142 143 144 145 146 147 |
# File 'lib/subspawn/fd_parse.rb', line 141 def self.which(cmd, env) return nil if cmd.nil? or cmd.to_str == "" SubSpawn::Platform. (cmd, env). lazy. find {|x|!File.directory? x and File.executable? x} end |