[gnome-ostree] subtask.js: Redo of task.js API



commit 629a3c973127ffdbe124fc249186af755d0817ac
Author: Colin Walters <walters verbum org>
Date:   Mon Jan 21 16:31:55 2013 -0500

    subtask.js: Redo of task.js API
    
    This is a bit cleaner.

 Makefile-ostbuild.am                    |    2 +-
 src/ostbuild/js/builtins/autobuilder.js |   86 +++++++++++++------------------
 src/ostbuild/js/builtins/build.js       |   44 +++++++++-------
 src/ostbuild/js/jsonutil.js             |    8 ++-
 src/ostbuild/js/{task.js => subtask.js} |   70 ++++++++++++++++---------
 5 files changed, 113 insertions(+), 97 deletions(-)
---
diff --git a/Makefile-ostbuild.am b/Makefile-ostbuild.am
index 6ccf5be..5d5c2bf 100644
--- a/Makefile-ostbuild.am
+++ b/Makefile-ostbuild.am
@@ -52,7 +52,7 @@ jsostbuild_DATA= \
 	src/ostbuild/js/procutil.js \
 	src/ostbuild/js/snapshot.js \
 	src/ostbuild/js/streamutil.js \
-	src/ostbuild/js/task.js \
+	src/ostbuild/js/subtask.js \
 	src/ostbuild/js/vcs.js \
 	$(NULL)
 
diff --git a/src/ostbuild/js/builtins/autobuilder.js b/src/ostbuild/js/builtins/autobuilder.js
index f178c71..6ad6f33 100644
--- a/src/ostbuild/js/builtins/autobuilder.js
+++ b/src/ostbuild/js/builtins/autobuilder.js
@@ -22,7 +22,7 @@ const Format = imports.format;
 
 const GSystem = imports.gi.GSystem;
 
-const Task = imports.task;
+const SubTask = imports.subtask;
 const JsonDB = imports.jsondb;
 const ProcUtil = imports.procutil;
 const JsonUtil = imports.jsonutil;
@@ -45,15 +45,14 @@ const AutoBuilder = new Lang.Class({
     Name: 'AutoBuilder',
     
     _init: function() {
-	this._resolve_proc = null;
-	this._build_proc = null;
-
 	this.config = Config.get();
 	this.workdir = Gio.File.new_for_path(this.config.getGlobal('workdir'));
 	this.prefix = this.config.getPrefix();
 	this._snapshot_dir = this.workdir.get_child('snapshots');
 	this._status_path = this.workdir.get_child('autobuilder-' + this.prefix + '.json');
 
+	this._manifestPath = Gio.File.new_for_path('manifest.json');
+
 	this._build_needed = true;
 	this._full_resolve_needed = true;
 	this._queued_force_builds = [];
@@ -70,9 +69,9 @@ const AutoBuilder = new Lang.Class({
 	GSystem.file_ensure_directory(snapshotdir, true, null);
 	this._src_db = new JsonDB.JsonDB(snapshotdir, this.prefix + '-src-snapshot');
 
-	let taskdir = new Task.TaskDir(this.workdir.get_child('tasks'));
-	this._resolve_taskset = taskdir.get(this.prefix + '-resolve');
-	this._build_taskset = taskdir.get(this.prefix + '-build');
+	let taskdir = this.workdir.get_child('tasks');
+	this._resolve_taskset = new SubTask.TaskSet(taskdir.get_child(this.prefix + '-resolve'));
+	this._build_taskset = new SubTask.TaskSet(taskdir.get_child(this.prefix + '-build'));
 
 	this._source_snapshot_path = this._src_db.getLatestPath();
 
@@ -89,12 +88,12 @@ const AutoBuilder = new Lang.Class({
 
     _updateStatus: function() {
 	let newStatus = "";
-	if (this._resolve_proc == null && this._build_proc == null) {
+	if (!this._resolve_taskset.isRunning() && !this._build_taskset.isRunning()) {
 	    newStatus = "idle";
 	} else {
-	    if (this._resolve_proc != null)
+	    if (this._resolve_taskset.isRunning())
 		newStatus += "[resolving] ";
-	    if (this._build_proc != null)
+	    if (this._build_taskset.isRunning())
 		newStatus += "[building] ";
 	}
 	if (newStatus != this._status) {
@@ -121,22 +120,19 @@ const AutoBuilder = new Lang.Class({
     queueResolve: function(components) {
 	this._queued_force_resolve.push.apply(this._queued_force_resolve, components);
 	print("queued resolves: " + this._queued_force_resolve);
-	if (this._resolve_proc == null)
+	if (!this._resolve_taskset.isRunning())
 	    this._fetch();
     },
     
     _fetchAll: function() {
 	this._full_resolve_needed = true;
-	if (this._resolve_proc == null)
+	if (!this._resolve_taskset.isRunning())
 	    this._fetch();
 	return true;
     },
 
     _fetch: function() {
 	let cancellable = null;
-	if (this._resolve_proc != null) throw new Error("Attempted multiple fetch");
-	let t = this._resolve_taskset.start();
-	let taskWorkdir = t.path;
 
 	if (this._autoupdate_self)
 	    ProcUtil.runSync(['git', 'pull', '-r'], cancellable)
@@ -155,25 +151,22 @@ const AutoBuilder = new Lang.Class({
 	}
 	this._queued_force_resolve = [];
 	let context = new GSystem.SubprocessContext({ argv: args });
-	context.set_stdout_file_path(t.logfile_path.get_path());
-	context.set_stderr_disposition(GSystem.SubprocessStreamDisposition.STDERR_MERGE);
-	this._resolve_proc = new GSystem.Subprocess({context: context});
-	this._resolve_proc.init(null);
-	print(Format.vprintf("Resolve task %s.%s started (%s), pid=%s", [t.major, t.minor,
-									 isFull ? "full" : "incremental",
-									 this._resolve_proc.get_pid()]));
-	this._resolve_proc.wait(null, Lang.bind(this, this._onResolveExited));
+	let workdir = this._resolve_taskset.prepare();
+	let tmpManifest = workdir.get_child(this._manifestPath.get_basename());
+	GSystem.file_linkcopy(this._manifestPath, tmpManifest, Gio.FileCopyFlags.OVERWRITE, cancellable);	
+	let t = this._resolve_taskset.start(context,
+					    cancellable,
+					    Lang.bind(this, this._onResolveExited));
+	print(Format.vprintf("Resolve task %s.%s started (%s)", [t.major, t.minor,
+								 isFull ? "full" : "incremental"]));
 
 	this._updateStatus();
 
 	return false;
     },
 
-    _onResolveExited: function(process, result) {
-	this._resolve_proc = null;
-	let [success, msg] = ProcUtil.asyncWaitCheckFinish(process, result);
+    _onResolveExited: function(resolveTask, success, msg) {
 	print(Format.vprintf("resolve exited; success=%s msg=%s", [success, msg]))
-	this._resolve_taskset.finish(success);
 	this._prev_source_snapshot_path = this._source_snapshot_path;
 	this._source_snapshot_path = this._src_db.getLatestPath();
 	let changed = (this._prev_source_snapshot_path == null ||
@@ -182,7 +175,7 @@ const AutoBuilder = new Lang.Class({
             print(Format.vprintf("New version is %s", [this._source_snapshot_path.get_path()]))
 	if (!this._build_needed)
 	    this._build_needed = changed;
-	if (this._build_needed && this._build_proc == null)
+	if (this._build_needed && !this._build_taskset.isRunning())
 	    this._run_build();
 
 	if (this._full_resolve_needed || this._queued_force_resolve.length > 0) {
@@ -194,42 +187,33 @@ const AutoBuilder = new Lang.Class({
     
     _run_build: function() {
 	let cancellable = null;
-	if (this._build_proc != null) throw new Error();
+	if (this._build_taskset.isRunning()) throw new Error();
 	if (!this._build_needed) throw new Error();
 
 	this._build_needed = false;
 
-	let task = this._build_taskset.start();
-	let workdir = task.path;
-	let transientSnapshotPath = workdir.get_child(this._source_snapshot_path.get_basename());
-	GSystem.file_linkcopy(this._source_snapshot_path, transientSnapshotPath, Gio.FileCopyFlags.OVERWRITE, null);
-	let args = ['ostbuild', 'build', '--snapshot=' + transientSnapshotPath.get_path()];
+	let snapshotName = this._source_snapshot_path.get_basename();
+
+	let workdir = this._build_taskset.prepare();
+	let tmpSnapshotPath = workdir.get_child(snapshotName);
+	GSystem.file_linkcopy(this._source_snapshot_path, tmpSnapshotPath,
+			      Gio.FileCopyFlags.OVERWRITE, cancellable);	
+	
+	let args = ['ostbuild', 'build', '--snapshot=' + snapshotName];
 	args.push.apply(args, this._queued_force_builds);
 	this._queued_force_builds = [];
 
-	let version = this._src_db.parseVersionStr(this._source_snapshot_path.get_basename());
-	let meta = {'version': version,
-		    'version-path': this._snapshot_dir.get_relative_path(this._source_snapshot_path)};
-	let metaPath = workdir.get_child('meta.json');
-	JsonUtil.writeJsonFileAtomic(metaPath, meta, cancellable);
-
 	let context = new GSystem.SubprocessContext({ argv: args });
-	context.set_stdout_file_path(task.logfile_path.get_path());
-	context.set_stderr_disposition(GSystem.SubprocessStreamDisposition.STDERR_MERGE);
-	this._build_proc = new GSystem.Subprocess({context: context});
-	this._build_proc.init(null);
-	print(Format.vprintf("Build task %s.%s started, pid=%s", [task.major, task.minor, this._build_proc.get_pid()]));
-	this._build_proc.wait(null, Lang.bind(this, this._onBuildExited));
+	let task = this._build_taskset.start(context,
+					     cancellable,
+					     Lang.bind(this, this._onBuildExited));
+	print(Format.vprintf("Build task %s.%s started", [task.major, task.minor]));
 
 	this._updateStatus();
     },
 
-    _onBuildExited: function(process, result) {
-	if (this._build_proc == null) throw new Error();
-	this._build_proc = null;
-	let [success, msg] = ProcUtil.asyncWaitCheckFinish(process, result);
+    _onBuildExited: function(buildTaskset, success, msg) {
 	print(Format.vprintf("build exited; success=%s msg=%s", [success, msg]))
-	this._build_taskset.finish(success);
 	if (this._build_needed)
 	    this._run_build()
 	
diff --git a/src/ostbuild/js/builtins/build.js b/src/ostbuild/js/builtins/build.js
index 70cc2f6..fa707ae 100644
--- a/src/ostbuild/js/builtins/build.js
+++ b/src/ostbuild/js/builtins/build.js
@@ -22,7 +22,7 @@ const Format = imports.format;
 
 const GSystem = imports.gi.GSystem;
 
-const Task = imports.task;
+const SubTask = imports.subtask;
 const JsonDB = imports.jsondb;
 const ProcUtil = imports.procutil;
 const StreamUtil = imports.streamutil;
@@ -277,6 +277,12 @@ const Build = new Lang.Class({
         return cachedata['ostree'];
     },
 
+    _onBuildComplete: function(taskset, success, msg, loop) {
+	this._currentBuildSucceded = success;
+	this._currentBuildSuccessMsg = msg;
+	loop.quit();
+    },
+
     _buildOneComponent: function(component, architecture, cancellable) {
         let basename = component['name'];
 
@@ -358,17 +364,15 @@ const Build = new Lang.Class({
 	    }
 	}
 
-        let taskdir = new Task.TaskDir(this.workdir.get_child('tasks'));
-        let buildTaskset = taskdir.get(buildname);
-        let t = buildTaskset.start()
-        let workdir = t.path;
+        let taskdir = this.workdir.get_child('tasks');
+        let buildTaskset = new SubTask.TaskSet(taskdir.get_child(buildname));
+
+	let workdir = buildTaskset.prepare();
 
         let tempMetadataPath = workdir.get_child('_ostbuild-meta.json');
         JsonUtil.writeJsonFileAtomic(tempMetadataPath, expandedComponent, cancellable);
 
-        let checkoutdir = this.workdir.get_child('checkouts');
-        let componentSrc = checkoutdir.get_child(buildname);
-        GSystem.file_ensure_directory(componentSrc.get_parent(), true, cancellable);
+        let componentSrc = workdir.get_child(basename);
         let childArgs = ['ostbuild', 'checkout', '--snapshot=' + this._snapshot.path.get_path(),
 			 '--checkoutdir=' + componentSrc.get_path(),
 			 '--metadata-path=' + tempMetadataPath.get_path(),
@@ -417,16 +421,22 @@ const Build = new Lang.Class({
         envCopy['CXXFLAGS'] = OPT_COMMON_CFLAGS[architecture];
 
 	let context = new GSystem.SubprocessContext({ argv: childArgs });
-	context.set_stdout_file_path(t.logfile_path.get_path());
-	context.set_stderr_disposition(GSystem.SubprocessStreamDisposition.STDERR_MERGE);
 	context.set_environment(ProcUtil.objectToEnvironment(envCopy));
-	let proc = new GSystem.Subprocess({ context: context });
-	proc.init(cancellable);
-	print(Format.vprintf("Started child process %s: pid=%s", [JSON.stringify(proc.context.argv), proc.get_pid()]));
-	let [res, estatus] = proc.wait_sync(cancellable);
-	let [buildSuccess, msg] = ProcUtil.getExitStatusAndString(estatus);
+
+	let mainContext = new GLib.MainContext();
+	mainContext.push_thread_default();
+	let loop = GLib.MainLoop.new(mainContext, true);
+	let t;
+	try {
+	    t = buildTaskset.start(context, cancellable, Lang.bind(this, this._onBuildComplete, loop));
+	    loop.run();
+	} finally {
+	    mainContext.pop_thread_default();
+	}
+	let buildSuccess = this._currentBuildSucceded;
+	let msg = this._currentBuildSuccessMsg;
+
         if (!buildSuccess) {
-            buildTaskset.finish(false);
             this._analyzeBuildFailure(t, architecture, component, componentSrc,
                                       currentVcsVersion, previousVcsVersion, cancellable);
 	    throw new Error("Build failure in component " + buildname + " : " + msg);
@@ -463,8 +473,6 @@ const Build = new Lang.Class({
 
         let ostreeRevision = this._saveComponentBuild(buildname, expandedComponent, cancellable);
 
-        buildTaskset.finish(true);
-
         return ostreeRevision;
     },
 
diff --git a/src/ostbuild/js/jsonutil.js b/src/ostbuild/js/jsonutil.js
index 03431a0..04a82ca 100644
--- a/src/ostbuild/js/jsonutil.js
+++ b/src/ostbuild/js/jsonutil.js
@@ -22,10 +22,14 @@ const Gio = imports.gi.Gio;
  * Read/write JSON to/from GFile paths, very inefficiently.
  */
 
-function writeJsonFileAtomic(path, data, cancellable) {
+function writeJsonToStream(stream, data, cancellable) {
     let buf = JSON.stringify(data, null, "  ");
+    stream.write_bytes(new GLib.Bytes(buf), cancellable);
+}
+
+function writeJsonFileAtomic(path, data, cancellable) {
     let s = path.replace(null, false, Gio.FileCreateFlags.REPLACE_DESTINATION, cancellable);
-    s.write_bytes(new GLib.Bytes(buf), cancellable);
+    writeJsonToStream(s, data, cancellable);
     s.close(cancellable);
 }
 
diff --git a/src/ostbuild/js/task.js b/src/ostbuild/js/subtask.js
similarity index 78%
rename from src/ostbuild/js/task.js
rename to src/ostbuild/js/subtask.js
index c374a8f..d97968c 100644
--- a/src/ostbuild/js/task.js
+++ b/src/ostbuild/js/subtask.js
@@ -22,22 +22,9 @@ const Lang = imports.lang;
 
 const GSystem = imports.gi.GSystem;
 
-const VERSION_RE = /(\d+)\.(\d+)/;
-
-const TaskDir = new Lang.Class({
-    Name: 'TaskDir',
+const ProcUtil = imports.procutil;
 
-    _init: function(path) {
-	this.path = path;
-    },
-
-    get: function(name) {
-	let child = this.path.get_child(name);
-	GSystem.file_ensure_directory(child, true, null);
-
-	return new TaskSet(child);
-    }
-});
+const VERSION_RE = /(\d+)\.(\d+)/;
 
 const TaskHistoryEntry = new Lang.Class({
     Name: 'TaskHistoryEntry',
@@ -91,11 +78,13 @@ const TaskHistoryEntry = new Lang.Class({
 const TaskSet = new Lang.Class({
     Name: 'TaskSet',
 
-    _init: function(path, prefix) {
+    _init: function(path) {
 	this.path = path;
+	GSystem.file_ensure_directory(this.path, true, null);
 
 	this._history = [];
 	this._running = false;
+	this._prepared = false;
 	this._running_version = null;
 	this._maxVersions = 10;
 	
@@ -126,9 +115,25 @@ const TaskSet = new Lang.Class({
 	this._cleanOldEntries();
     },
 
-    start: function() {
+    _onProcessComplete: function(proc, result) {
+	if (!this._running) throw new Error();
+
+	let [success, msg] = ProcUtil.asyncWaitCheckFinish(proc, result);
+
+	let last = this._history[this._history.length-1];
+	last.finish(success);
+	this._running = false;
+	this._process = null;
+
+	this._cleanOldEntries();
+
+	this._processCallback(this, success, msg);
+    },
+
+    prepare: function() {
 	if (this._running) throw new Error();
-	this._running = true;
+	if (this._prepared) throw new Error();
+	this._prepared = true;
 	let yearver = new Date().getFullYear();
 	let lastversion = -1;
 	if (this._history.length > 0) {
@@ -140,18 +145,33 @@ const TaskSet = new Lang.Class({
 	}
 	let historyPath = this.path.get_child(format.vprintf('%d.%d', [yearver, lastversion + 1]));
 	GSystem.file_ensure_directory(historyPath, true, null);
+
 	let entry = new TaskHistoryEntry(historyPath, 'running');
-	this._history.push(entry);
 	entry.logfile_path = historyPath.get_child('log');
-	return entry;
+	this._history.push(entry);
+	
+	return historyPath;
     },
 
-    finish: function(success) {
-	if (!this._running) throw new Error();
+    start: function(processContext, cancellable, callback) {
+	if (this._running) throw new Error();
+	if (!this._prepared)
+	    this.prepare();
+	this._running = true;
+	this._prepared = false;
 	let last = this._history[this._history.length-1];
-	last.finish(success);
-	this._running = false;
-	this._cleanOldEntries();
+	processContext.set_cwd(last.path.get_path());
+	processContext.set_stdout_file_path(last.logfile_path.get_path());
+	processContext.set_stderr_disposition(GSystem.SubprocessStreamDisposition.STDERR_MERGE);
+	this._process = new GSystem.Subprocess({ context: processContext });
+	this._processCallback = callback;
+	this._process.init(cancellable);
+	this._process.wait(cancellable, Lang.bind(this, this._onProcessComplete));
+	return last;
+    },
+
+    isRunning: function() {
+	return this._running;
     },
 
     getHistory: function() {



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]