X-Git-Url: http://git.roojs.org/?p=app.Builder.js;a=blobdiff_plain;f=src%2FSpawn.vala;h=d3b9428b9f96ef5d841aed9c3892683eb9bdfc26;hp=f6ed088ff0e569a4b1373256ae2ff40e41c5cae6;hb=7e857d8072408ca9f7a3384f2bff241200b5991c;hpb=50dc320eb6e8565e6b39beba3222fb6900edc576 diff --git a/src/Spawn.vala b/src/Spawn.vala index f6ed088ff..d3b9428b9 100644 --- a/src/Spawn.vala +++ b/src/Spawn.vala @@ -1,29 +1,29 @@ -/// # valac --pkg gio-2.0 --pkg gtk+-3.0 --pkg posix Spawn.vala -o /tmp/Spawn +/// # valac --pkg gio-2.0 --pkg posix Spawn.vala -o /tmp/Spawn using GLib; -using Gtk; + /** * Revised version? * - * x = new Spawn( working dir, args) + * x = new Spawn( "/tmp", {"ls", "-l" }) + * + * // these are optionall.. * x.env = ..... (if you need to set one... - * x.output_line.connect((string str) => { ... }); + * x.output_line.connect((string str) => { + * if ( Gtk.events_pending()) { Gtk.main_iteration(); } + * }); * x.input_line.connect(() => { return string }); - * x.finish.connect((int res, string output, string stderr) => { ... }); - * x.run(); + * + * x.run((int res, string output, string stderr) => { ... }); + * * */ - - - -} - public errordomain SpawnError { NO_ARGS, WRITE_ERROR, @@ -33,32 +33,45 @@ public errordomain SpawnError { /** * @class Spawn - * @param cfg {SpawnConfig} settings - see properties. + * @param cwd {String} working directory. (defaults to home directory) + * @param args {Array} arguments eg. [ 'ls', '-l' ] * - * @arg cwd {String} working directory. (defaults to home directory) - * @arg args {Array} arguments eg. [ 'ls', '-l' ] - * @arg listeners {Object} (optional) handlers for output, stderr, input - * stderr/output both receive output line as argument - * input should return any standard input - * finish recieves result as argument. + * @arg env {Array} enviroment eg. [ 'GITDIR=/home/test' ] - * @arg async {Boolean} (optional)return instantly, or wait for exit. (default no) - * @arg exceptions {Boolean} throw exception on failure (default no) - * @arg debug {Boolean} print out what's going on.. (default no) + * @arg is_async {Boolean} (optional)return instantly, or wait for exit. (default no) + * @arg trhow_exceptions {Boolean} throw exception on failure (default no) + * */ - - + public class Spawn : Object { - public signal string input(); + /** + * @signal input called at start to send input when process starts? + * @return the string or null + */ + public signal string? input(); + /** + * @signal complete called at when the command has completed. + * + */ + public signal void complete(int res, string str, string stderr); + /** + * @signal output_line called when a line is recieved from the process. + * Note you may want to connect this and run + * if ( Gtk.events_pending()) { Gtk.main_iteration(); } + * + * @param {string} str + */ public signal void output_line(string str); - public signal void finish(int res, string str, string stderr); - + public string cwd; public string[] args; public string[] env; - public bool debug = false; + + public bool is_async = true; + public bool throw_exceptions = false; + public bool detach = false; public Spawn(string cwd, string[] args) throws Error { @@ -93,7 +106,7 @@ public class Spawn : Object /** * @property result {Number} execution result. */ - int result= 0; + public int result= 0; /** * @property pid {Number} pid of child process (of false if it's not running) */ @@ -127,65 +140,78 @@ public class Spawn : Object * result is applied to object properties (eg. '?' or 'stderr') * @returns {Object} self. */ - public void run() throws SpawnError, GLib.SpawnError, GLib.IOChannelError - { - - - err_src = -1; - out_src = -1; - int standard_input; - int standard_output; - int standard_error; + public void run( ) throws SpawnError, GLib.SpawnError, GLib.IOChannelError + { + + + err_src = -1; + out_src = -1; + int standard_input; + int standard_output; + int standard_error; - - Glib.debug("cd %s; %s" , this.cfg.cwd , string.joinv(" ", this.cfg.args)); - - - Process.spawn_async_with_pipes ( - this.cfg.cwd, - this.cfg.args, - this.cfg.env, - SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD, - null, - out this.pid, - out standard_input, - out standard_output, - out standard_error); + + GLib.debug("cd %s; %s" , this.cwd , string.joinv(" ", this.args)); + + if (this.detach) { + Process.spawn_async_with_pipes ( + this.cwd, + this.args, + this.env.length > 0 ? this.env : null, + SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD, + null, + out this.pid); + ChildWatch.add (this.pid, (pid, status) => { + // Triggered when the child indicated by child_pid exits + Process.close_pid (pid); + + }); + + return; + + } + Process.spawn_async_with_pipes ( + this.cwd, + this.args, + this.env.length > 0 ? this.env : null, + SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD, + null, + out this.pid, + out standard_input, + out standard_output, + out standard_error); // stdout: - - - //print(JSON.stringify(gret)); - - if (this.cfg.debug) { - - stdout.printf("PID: %d" ,this.pid); - } - - this.in_ch = new GLib.IOChannel.unix_new(standard_input); - this.out_ch = new GLib.IOChannel.unix_new(standard_output); - this.err_ch = new GLib.IOChannel.unix_new(standard_error); - - // make everything non-blocking! - - - - // using NONBLOCKING only works if io_add_watch - //returns true/false in right conditions - this.in_ch.set_flags (GLib.IOFlags.NONBLOCK); - this.out_ch.set_flags (GLib.IOFlags.NONBLOCK); - this.err_ch.set_flags (GLib.IOFlags.NONBLOCK); - - + + + //print(JSON.stringify(gret)); + + GLib.debug("PID: %d" ,this.pid); + + + this.in_ch = new GLib.IOChannel.unix_new(standard_input); + this.out_ch = new GLib.IOChannel.unix_new(standard_output); + this.err_ch = new GLib.IOChannel.unix_new(standard_error); + + // make everything non-blocking! + + + + // using NONBLOCKING only works if io_add_watch + //returns true/false in right conditions + this.in_ch.set_flags (GLib.IOFlags.NONBLOCK); + this.out_ch.set_flags (GLib.IOFlags.NONBLOCK); + this.err_ch.set_flags (GLib.IOFlags.NONBLOCK); + + ChildWatch.add (this.pid, (w_pid, result) => { this.result = result; - if (this.cfg.debug) { - stdout.printf("child_watch_add : result:%d ", result); - } + GLib.debug("child_watch_add : result:%d ", result); + this.read(this.out_ch); this.read(this.err_ch); @@ -199,9 +225,9 @@ public class Spawn : Object } this.tidyup(); //print("DONE TIDYUP"); - if (this.cfg.finish != null) { - this.cfg.finish(this.result); - } + + this.complete(this.result, this.output, this.stderr); + }); @@ -228,10 +254,11 @@ public class Spawn : Object // call input.. if (this.pid > -1) { // child can exit before we get this far.. - if (this.cfg.input != null) { - if (this.cfg.debug) print("Trying to call listeners"); + var input = this.input(); + if (input != null) { + try { - this.write(this.cfg.input()); + this.write(input); // this probably needs to be a bit smarter... //but... let's close input now.. this.in_ch.shutdown(true); @@ -249,26 +276,26 @@ public class Spawn : Object } // async - if running - return.. - if (this.cfg.async && this.pid > -1) { + if (this.is_async && this.pid > -1) { return; } // start mainloop if not async.. if (this.pid > -1) { - print("starting main loop"); + GLib.debug("starting main loop"); //if (this.cfg.debug) { // // } this.ctx = new MainLoop (); this.ctx.run(); // wait fore exit? - print("main_loop done!"); + GLib.debug("main_loop done!"); } else { this.tidyup(); // tidyup get's called in main loop. } - if (this.cfg.exceptions && this.result != 0) { + if (this.throw_exceptions && this.result != 0) { throw new SpawnError.EXECUTE_ERROR(this.stderr); //this.toString = function() { return this.stderr; }; @@ -344,7 +371,7 @@ public class Spawn : Object */ private bool read(IOChannel ch) { - string prop = (ch == this.out_ch) ? "output" : "stderr"; + // string prop = (ch == this.out_ch) ? "output" : "stderr"; // print("prop: " + prop); @@ -374,28 +401,28 @@ public class Spawn : Object //} if (ch == this.out_ch) { this.output += buffer; - if (this.cfg.output != null) { - this.cfg.output( buffer); - } + this.output_line( buffer); + } else { this.stderr += buffer; + this.output_line( buffer); } //_this[prop] += x.str_return; //if (this.cfg.debug) { - stdout.printf("%s : %s", prop , buffer); + //GLib.debug("%s : %s", prop , buffer); //} - if (this.cfg.async) { + if (this.is_async) { - if ( Gtk.events_pending()) { - Gtk.main_iteration(); - } + //if ( Gtk.events_pending()) { + // Gtk.main_iteration(); + //} } //this.ctx.iteration(true); continue; case GLib.IOStatus.AGAIN: - //print("Should be called again.. waiting for more data.."); + //print("Should be called again.. waiting for more data.."); return true; //break; case GLib.IOStatus.ERROR: @@ -412,24 +439,25 @@ public class Spawn : Object } } - /* -// test -try { - Seed.print(run({ - args: ['ls', '/tmp'], - debug : true - })); -} catch (e) { print(JSON.stringify(e)); } +/* -var secs = (new Date()).getSeconds() - -try { -Seed.print(run({ - args: ['/bin/touch', '/tmp/spawntest-' + secs ], - debug : true -})); -} catch (e) { print( 'Error: ' + JSON.stringify(e)); } +int main (string[] args) { + GLib.Log.set_handler(null, + GLib.LogLevelFlags.LEVEL_DEBUG | GLib.LogLevelFlags.LEVEL_WARNING, + (dom, lvl, msg) => { + print("%s: %s\n", dom, msg); + }); - + var ctx = new GLib.MainLoop (); + var a = new Spawn("", { "ls" , "-l"}); + a.run((res, str, stderr) => { + print(str); + ctx.quit(); + }); + + + ctx.run(); // wait for exit? + + return 0; +} */ -