X-Git-Url: http://git.roojs.org/?p=app.Builder.js;a=blobdiff_plain;f=src%2FSpawn.vala;h=d3b9428b9f96ef5d841aed9c3892683eb9bdfc26;hp=e927c4f2ac4012b48e99d6a37c7cc48dcbb16b46;hb=700f970c58ea880664ac6d459472b93dab1c41f4;hpb=21e157d6e41e3ece95866b44532e9b49be3fac2f diff --git a/src/Spawn.vala b/src/Spawn.vala index e927c4f2a..d3b9428b9 100644 --- a/src/Spawn.vala +++ b/src/Spawn.vala @@ -1,115 +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; -// compile valac - - - -///using Gee; // for array list? -/* -static int main (string[] args) { - // A reference to our file - - var cfg = new SpawnConfig("", { "ls" } , { "" }); - cfg.setHandlers( - (line) => { - stdout.printf("%s\n", line); - }, - null,null,null ); - cfg.setOptions( - false, // async - false, // exceptions?? needed?? - false // debug??? - ); - try { - new Spawn(cfg); - - } catch (Error e) { - stdout.printf("Error %s", e.message); - } - - return 0; - -} -*/ -//var Gio = imports.gi.Gio; -//var GLib = imports.gi.GLib; - + /** -* @namespace Spawn -* -* Library to wrap GLib.spawn_async_with_pipes -* -* usage: -* v -* -*var output = new Spawn( SpawnConfig() { - cwd = "/home", // empty string to default to homedirectory. - args = {"ls", "-l" }, - evn = {}, - ouput = (line) => { stdout.printf("%d\n", line); } - stderr = (line) => { stdout.printf("%d\n", line); } - input = () => { return "xxx"; } -}; -* -* -*/ -public delegate void SpawnOutput(string line); -public delegate void SpawnErr(string line); -public delegate string SpawnInput(); -public delegate void SpawnFinish(int result); - + * Revised version? + * + * x = new Spawn( "/tmp", {"ls", "-l" }) + * + * // these are optionall.. + * x.env = ..... (if you need to set one... + * x.output_line.connect((string str) => { + * if ( Gtk.events_pending()) { Gtk.main_iteration(); } + * }); + * x.input_line.connect(() => { return string }); + * + * x.run((int res, string output, string stderr) => { ... }); -public class SpawnConfig { - public string cwd; - public string[] args; - public string[] env; - public bool async; - public bool exceptions; // fire exceptions. - public bool debug; // fire exceptions. - - public SpawnOutput output; - public SpawnErr stderr; - public SpawnInput input; - public SpawnFinish finish; - // defaults.. - public SpawnConfig(string cwd, - string[] args, - string[] env - ) { - this.cwd = cwd; - this.args = args; - this.env = env; - - async = false; - exceptions = true; - debug = false; - - output = null; - stderr = null; - input = null; - - } + * + * + */ - - public void setHandlers( - SpawnOutput? output, - SpawnErr? stderr, - SpawnInput? input, - SpawnFinish? finish - ) { - this.output = output; - this.stderr = stderr; - this.input = input; - this.finish = finish; - } - - -} + public errordomain SpawnError { NO_ARGS, WRITE_ERROR, @@ -119,40 +33,62 @@ 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 { + /** + * @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 string cwd; + public string[] args; + public string[] env; + + public bool is_async = true; + public bool throw_exceptions = false; + public bool detach = false; - SpawnConfig cfg; - - public Spawn(SpawnConfig cfg) throws Error + public Spawn(string cwd, string[] args) throws Error { - this.cfg = cfg; + this.cwd = cwd; + this.args = args; + this.env = {}; + this.output = ""; this.stderr = ""; - this.cfg.cwd = this.cfg.cwd.length < 1 ? GLib.Environment.get_home_dir() : this.cfg.cwd; - if (this.cfg.args.length < 0) { + this.cwd = this.cwd.length < 1 ? GLib.Environment.get_home_dir() : this.cwd; + if (this.args.length < 0) { throw new SpawnError.NO_ARGS("No arguments"); } - this.run(); + } @@ -170,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) */ @@ -204,83 +140,95 @@ 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; - - if (this.cfg.debug) { - stdout.printf("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); - } - - this.read(this.out_ch); - this.read(this.err_ch); - - - Process.close_pid(this.pid); - this.pid = -1; - if (this.ctx != null) { - this.ctx.quit(); - this.ctx = null; - } - this.tidyup(); - //print("DONE TIDYUP"); - if (this.cfg.finish != null) { - this.cfg.finish(this.result); - } - }); + ChildWatch.add (this.pid, (w_pid, result) => { + + this.result = result; + GLib.debug("child_watch_add : result:%d ", result); + + + this.read(this.out_ch); + this.read(this.err_ch); + + + Process.close_pid(this.pid); + this.pid = -1; + if (this.ctx != null) { + this.ctx.quit(); + this.ctx = null; + } + this.tidyup(); + //print("DONE TIDYUP"); + + this.complete(this.result, this.output, this.stderr); + + }); @@ -306,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); @@ -326,27 +275,27 @@ public class Spawn : Object } } - // async - if running - return.. - if (this.cfg.async && this.pid > -1) { + // async - if running - return.. + 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; }; @@ -422,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); @@ -452,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: @@ -490,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() +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); + }); -try { -Seed.print(run({ - args: ['/bin/touch', '/tmp/spawntest-' + secs ], - debug : true -})); -} catch (e) { print( 'Error: ' + JSON.stringify(e)); } - - + 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; +} */ -