[gnome-ostree] task: Add the concept of "TaskAfter"



commit 0ced95e2910a298bfd2d78563f9ad16282753587
Author: Colin Walters <walters verbum org>
Date:   Sun Feb 24 13:06:13 2013 -0500

    task: Add the concept of "TaskAfter"
    
    This way we can automatically run build when resolve completes, both
    bdiff and builddisks when build completes, and smoketest when
    builddisks completes.

 src/js/builtins/autobuilder.js  |  100 ++++++++++++---------------------------
 src/js/builtins/make.js         |   17 +++++--
 src/js/builtins/run_task.js     |    4 +-
 src/js/jsondb.js                |    7 +++
 src/js/task.js                  |   88 +++++++++++++++++++++++++++-------
 src/js/tasks/task-bdiff.js      |    5 +-
 src/js/tasks/task-build.js      |    5 +-
 src/js/tasks/task-builddisks.js |    5 +-
 src/js/tasks/task-resolve.js    |   18 +++++--
 src/js/tasks/task-smoketest.js  |    3 +-
 10 files changed, 142 insertions(+), 110 deletions(-)
---
diff --git a/src/js/builtins/autobuilder.js b/src/js/builtins/autobuilder.js
index b22710b..4230572 100644
--- a/src/js/builtins/autobuilder.js
+++ b/src/js/builtins/autobuilder.js
@@ -50,9 +50,8 @@ const Autobuilder = new Lang.Class({
         this.parser.addArgument('--autoupdate-self', { action: 'storeTrue' });
         this.parser.addArgument('--stage');
 
-       this._stages = ['resolve', 'build', 'builddisks', 'smoke'];
-
        this._buildNeeded = true;
+       this._initialResolveNeeded = true;
        this._fullResolveNeeded = true;
        this._resolveTimeout = 0;
        this._sourceSnapshotPath = null;
@@ -64,17 +63,6 @@ const Autobuilder = new Lang.Class({
        this._initSnapshot(null, null, cancellable);
 
        this._autoupdate_self = args.autoupdate_self;
-       if (!args.stage)
-           args.stage = 'build';
-       this._stageIndex = this._stages.indexOf(args.stage);
-       if (this._stageIndex < 0)
-           throw new Error("Unknown stage " + args.stage);
-       this._do_builddisks = this._stageIndex >= this._stages.indexOf('builddisks');
-       this._do_smoke = this._stageIndex >= this._stages.indexOf('smoke');
-
-       this._resolveTaskName = 'resolve'
-       this._buildTaskName = 'build'
-       this._bdiffTaskName = 'bdiff';
 
        this._manifestPath = Gio.File.new_for_path('manifest.json');
 
@@ -90,15 +78,18 @@ const Autobuilder = new Lang.Class({
 
        this._taskmaster = new Task.TaskMaster(this.workdir.get_child('tasks'),
                                                  { onEmpty: Lang.bind(this, this._onTasksComplete) });
+       this._taskmaster.connect('task-executing', Lang.bind(this, this._onTaskExecuting));
        this._taskmaster.connect('task-complete', Lang.bind(this, this._onTaskCompleted));
 
        this._sourceSnapshotPath = this._src_db.getLatestPath();
 
+       /* Start an initial, non-fetching resolve */
+       this._runResolve();
+       /* Flag immediately that we need a full resolve */
+       this._fullResolveNeeded = true;
+       /* And set a timeout for 10 minutes for the next full resolve */
        this._resolveTimeout = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT,
                                                         60 * 10, Lang.bind(this, this._triggerFullResolve));
-       this._runResolve();
-       if (this._sourceSnapshotPath != null)
-           this._runBuild();
 
        this._updateStatus();
 
@@ -108,11 +99,20 @@ const Autobuilder = new Lang.Class({
     _onTasksComplete: function() {
     },
 
+    _onTaskExecuting: function(taskmaster, task) {
+       let workdir = task._workdir;
+       print("Task " + task.name + " executing in " + workdir.get_path());
+       this._updateStatus();
+    },
+
     _onTaskCompleted: function(taskmaster, task, success, error) {
-       if (task.name == this._resolveTaskName) {
-           this._onResolveExited(task, success, error);
-       } else if (task.name == this._buildTaskName) {
-           this._onBuildExited(task, success, error);
+       if (task.name == 'resolve')
+           this._runResolve();
+       if (success) {
+           print("Task " + task.name + " complete: " + task._workdir.get_path());
+       } else {
+           this._failed = true;
+           print("Task " + task.name + " failed: " + task._workdir.get_path());
        }
        this._updateStatus();
     },
@@ -161,68 +161,28 @@ const Autobuilder = new Lang.Class({
     _runResolve: function() {
        let cancellable = null;
        
-       if (!(this._queuedForceResolve.length > 0 || this._fullResolveNeeded))
+       if (!(this._initialResolveNeeded ||
+             this._queuedForceResolve.length > 0 ||
+             this._fullResolveNeeded))
            return;
 
-       if (this._taskmaster.isTaskQueued(this._resolveTaskName))
+       if (this._taskmaster.isTaskQueued('resolve'))
            return;
 
        if (this._autoupdate_self)
            ProcUtil.runSync(['git', 'pull', '-r'], cancellable)
 
-       if (this._fullResolveNeeded) {
+       if (this._initialResolveNeeded) {
+           this._initialResolveNeeded = false;
+           this._taskmaster.pushTask('resolve', { });
+       } else if (this._fullResolveNeeded) {
            this._fullResolveNeeded = false;
-           this._taskmaster.pushTask(this._resolveTaskName,
-                                     { fetchAll: true });
+           this._taskmaster.pushTask('resolve', { fetchAll: true });
        } else {
-           this._taskmaster.pushTask(this._resolveTaskName,
-                                     { fetchComponents: this._queuedForceResolve });
+           this._taskmaster.pushTask('resolve', { fetchComponents: this._queuedForceResolve });
        }
        this._queuedForceResolve = [];
 
        this._updateStatus();
-    },
-
-    _onResolveExited: function(resolveTask, success, msg) {
-       print(Format.vprintf("resolve exited; success=%s msg=%s", [success, msg]))
-       this._prevSourceSnapshotPath = this._sourceSnapshotPath;
-       this._sourceSnapshotPath = this._src_db.getLatestPath();
-       let changed = (this._prevSourceSnapshotPath == null ||
-                      !this._prevSourceSnapshotPath.equal(this._sourceSnapshotPath));
-        if (changed)
-            print(Format.vprintf("New version is %s", [this._sourceSnapshotPath.get_path()]))
-       if (!this._buildNeeded)
-           this._buildNeeded = changed;
-       this._runBuild();
-       this._runResolve();
-       this._updateStatus();
-    },
-
-    _onBuildExited: function(buildTaskset, success, msg) {
-       print(Format.vprintf("build exited; success=%s msg=%s", [success, msg]))
-       if (this._buildNeeded)
-           this._runBuild()
-       
-       this._updateStatus();
-    },
-    
-    _runBuild: function() {
-       let cancellable = null;
-       if (this._taskmaster.isTaskQueued(this._buildTaskName))
-           return;
-       if (!this._buildNeeded)
-           return;
-
-       this._buildNeeded = false;
-       this._taskmaster.pushTask(this._buildTaskName);
-       this._updateStatus();
-    },
-
-    _runBdiff: function() {
-       if (this._taskmaster.isTaskQueued(this._bdiffTaskName))
-           return;
-
-       this._taskmaster.pushTask(this._bdiffTaskName);
-       this._updateStatus();
     }
 });
diff --git a/src/js/builtins/make.js b/src/js/builtins/make.js
index 93dcfae..3f91b2f 100644
--- a/src/js/builtins/make.js
+++ b/src/js/builtins/make.js
@@ -40,6 +40,8 @@ const Make = new Lang.Class({
 
     _init: function() {
        this.parent();
+       this.parser.addArgument(['-n', '--only'], { action: 'storeTrue',
+                                                   help: "Don't process tasks after this" });
        this.parser.addArgument('taskname');
        this.parser.addArgument('parameters', { nargs: '*' });
     },
@@ -48,8 +50,10 @@ const Make = new Lang.Class({
        this._initWorkdir(null, cancellable);
        this._loop = loop;
        this._failed = false;
+       this._oneOnly = args.only;
        let taskmaster = new Task.TaskMaster(this.workdir.get_child('tasks'),
-                                            { onEmpty: Lang.bind(this, this._onTasksComplete) });
+                                            { onEmpty: Lang.bind(this, this._onTasksComplete),
+                                              processAfter: !args.only });
        this._taskmaster = taskmaster;
        taskmaster.connect('task-executing', Lang.bind(this, this._onTaskExecuting));
        taskmaster.connect('task-complete', Lang.bind(this, this._onTaskCompleted));
@@ -74,13 +78,16 @@ const Make = new Lang.Class({
        let workdir = task._workdir;
        print("Task " + task.name + " executing in " + workdir.get_path());
        let output = workdir.get_child('output.txt');
-       let context = new GSystem.SubprocessContext({ argv: ['tail', '-f', output.get_path() ] });
-       this._tail = new GSystem.Subprocess({ context: context });
-       this._tail.init(null);
+       if (this._oneOnly) {
+           let context = new GSystem.SubprocessContext({ argv: ['tail', '-f', output.get_path() ] });
+           this._tail = new GSystem.Subprocess({ context: context });
+           this._tail.init(null);
+       }
     },
 
     _onTaskCompleted: function(taskmaster, task, success, error) {
-       this._tail.request_exit();
+       if (this._oneOnly)
+           this._tail.request_exit();
        if (success) {
            print("Task " + task.name + " complete: " + task._workdir.get_path());
        } else {
diff --git a/src/js/builtins/run_task.js b/src/js/builtins/run_task.js
index d76168f..d37ce40 100644
--- a/src/js/builtins/run_task.js
+++ b/src/js/builtins/run_task.js
@@ -46,9 +46,9 @@ const RunTask = new Lang.Class({
 
     execute: function(args, loop, cancellable) {
        let taskset = Task.TaskSet.prototype.getInstance();
-       let [taskDef, vars] = taskset.getTask(args.taskName);
+       let taskDef = taskset.getTask(args.taskName);
        let params = JSON.parse(args.parameters);
-       let instance = new taskDef(null, args.taskName, vars, params);
+       let instance = new taskDef(null, args.taskName, [], params);
        instance.execute(cancellable);
     }
 });
diff --git a/src/js/jsondb.js b/src/js/jsondb.js
index ece07d5..71013da 100644
--- a/src/js/jsondb.js
+++ b/src/js/jsondb.js
@@ -76,6 +76,13 @@ const JsonDB = new Lang.Class({
        return this._path.get_child(all[0][3]);
     },
 
+    getLatestVersion: function() {
+       let path = this.getLatestPath();
+       if (path == null)
+           return null;
+       return this.parseVersionStr(path.get_basename());
+    },
+
     getPreviousPath: function(path) {
         let name = path.get_basename();
        let [target_major, target_minor] = this._parseVersion(name);
diff --git a/src/js/task.js b/src/js/task.js
index 757e139..d16516c 100644
--- a/src/js/task.js
+++ b/src/js/task.js
@@ -62,22 +62,31 @@ const TaskSet = new Lang.Class({
        params = Params.parse(params, { allowNone: false })
        for (let i = 0; i < this._tasks.length; i++) {
            let taskDef = this._tasks[i];
-            let pattern = taskDef.prototype.TaskPattern;
-            let re = pattern[0];
-            let match = re.exec(taskName);
-            if (!match)
-               continue;
-            let vars = {};
-            for (let i = 1; i < pattern.length; i++) {
-               vars[pattern[i]] = match[i];
-            }
-           return [taskDef, vars];
+            let curName = taskDef.prototype.TaskName
+           if (curName == taskName)
+               return taskDef;
        }
        if (!params.allowNone)
            throw new Error("No task definition matches " + taskName);
        return null;
     },
 
+    getTasksAfter: function(taskName) {
+       let ret = [];
+       for (let i = 0; i < this._tasks.length; i++) {
+           let taskDef = this._tasks[i];
+           let after = taskDef.prototype.TaskAfter;
+           for (let j = 0; j < after.length; j++) {
+               let a = after[j];
+               if (a == taskName) {
+                   ret.push(taskDef);
+                   break;
+               }
+           }
+       }
+       return ret;
+    },
+
     getInstance: function() {
        if (!_tasksetInstance)
            _tasksetInstance = new TaskSet();
@@ -89,8 +98,10 @@ const TaskMaster = new Lang.Class({
     Name: 'TaskMaster',
 
     _init: function(path, params) {
-       params = Params.parse(params, {onEmpty: null});
+        params = Params.parse(params, { onEmpty: null, 
+                                       processAfter: true });
        this.path = path;
+       this._processAfter = params.processAfter;
        this.maxConcurrent = GLib.get_num_processors();
        this._onEmpty = params.onEmpty;
        this.cancellable = null;
@@ -102,23 +113,36 @@ const TaskMaster = new Lang.Class({
        this._caughtError = false;
 
        this._taskset = TaskSet.prototype.getInstance();
+
+       this._taskVersions = {};
+    },
+
+    _pushTaskDef: function(taskDef, parameters) {
+       let name = taskDef.prototype.TaskName;
+       if (!this._isTaskPending(name)) {
+           let instance = new taskDef(this, name, [], parameters);
+           instance.onComplete = Lang.bind(this, this._onComplete, instance);
+           this._pendingTasksList.push(instance);
+           this._queueRecalculate();
+       }
     },
 
     pushTask: function(taskName, parameters) {
-       let [taskDef, vars] = this._taskset.getTask(taskName);
-       let instance = new taskDef(this, taskName, vars, parameters);
-       instance.onComplete = Lang.bind(this, this._onComplete, instance);
-       this._pendingTasksList.push(instance);
-       this._queueRecalculate();
+       let taskDef = this._taskset.getTask(taskName);
+       this._pushTaskDef(taskDef, parameters);
     },
 
-    isTaskQueued: function(taskName) {
+    _isTaskPending: function(taskName) {
        for (let i = 0; i < this._pendingTasksList.length; i++) {
            let pending = this._pendingTasksList[i];
            if (pending.name == taskName)
                return true;
        }
-       return this.isTaskExecuting(taskName);
+       return false;
+    },
+
+    isTaskQueued: function(taskName) {
+       return this._isTaskPending(taskName) || this.isTaskExecuting(taskName);
     },
 
     isTaskExecuting: function(taskName) {
@@ -162,7 +186,6 @@ const TaskMaster = new Lang.Class({
     },
 
     _onComplete: function(success, error, task) {
-       this.emit('task-complete', task, success, error);
        let idx = -1;
        for (let i = 0; i < this._executing.length; i++) {
            let executingTask = this._executing[i];
@@ -174,6 +197,24 @@ const TaskMaster = new Lang.Class({
        if (idx == -1)
            throw new Error("TaskMaster: Internal error - Failed to find completed task:" + task.TaskName);
        this._executing.splice(idx, 1);
+       this.emit('task-complete', task, success, error);
+       if (this._processAfter) {
+           let changed = true;
+           let version = task.queryVersion();
+           if (version !== null) {
+               let oldVersion = this._taskVersions[task.name];
+               if (oldVersion == version)
+                   changed = false;
+               else if (oldVersion != null)
+                   print("task " + task.name + " new version: " + version);
+           }
+           if (changed) {
+               let tasksAfter = this._taskset.getTasksAfter(task.name);
+               for (let i = 0; i < tasksAfter.length; i++) {
+                   this._pushTaskDef(tasksAfter[i], {});
+               }
+           }
+       }
        this._queueRecalculate();
     },
 
@@ -182,6 +223,10 @@ const TaskMaster = new Lang.Class({
               this._pendingTasksList.length > 0 &&
               !this.isTaskExecuting(this._pendingTasksList[0].name)) {
            let task = this._pendingTasksList.shift();
+           let version = task.queryVersion();
+           if (version !== null) {
+               this._taskVersions[task.name] = version;
+           }
            task._executeInSubprocessInternal(this.cancellable);
            this.emit('task-executing', task);
            this._executing.push(task);
@@ -194,6 +239,7 @@ const TaskDef = new Lang.Class({
     Name: 'TaskDef',
 
     TaskPattern: null,
+    TaskAfter: [],
 
     PreserveStdout: true,
     RetainFailed: 1,
@@ -260,6 +306,10 @@ const TaskDef = new Lang.Class({
        }
     },
 
+    queryVersion: function() {
+       return null;
+    },
+
     execute: function(cancellable) {
        throw new Error("Not implemented");
     },
diff --git a/src/js/tasks/task-bdiff.js b/src/js/tasks/task-bdiff.js
index 91993e3..a42303c 100644
--- a/src/js/tasks/task-bdiff.js
+++ b/src/js/tasks/task-bdiff.js
@@ -37,9 +37,8 @@ const TaskBdiff = new Lang.Class({
     Name: "TaskBdiff",
     Extends: Task.TaskDef,
 
-    TaskPattern: [/bdiff$/],
-
-    TaskAfterPrefix: '/build/',
+    TaskName: "bdiff",
+    TaskAfter: ['build'],
 
     _gitLogToJson: function(repoDir, specification) {
        let log = ProcUtil.runSyncGetOutputLines(['git', 'log', '--format=email', specification],
diff --git a/src/js/tasks/task-build.js b/src/js/tasks/task-build.js
index 44c14b5..f7f6b54 100644
--- a/src/js/tasks/task-build.js
+++ b/src/js/tasks/task-build.js
@@ -46,9 +46,8 @@ const TaskBuild = new Lang.Class({
     Name: "TaskBuild",
     Extends: Task.TaskDef,
 
-    TaskPattern: [/build$/],
-
-    TaskAfterPrefix: '/resolve/',
+    TaskName: "build",
+    TaskAfter: ['resolve'],
 
     _resolveRefs: function(refs) {
         if (refs.length == 0)
diff --git a/src/js/tasks/task-builddisks.js b/src/js/tasks/task-builddisks.js
index 39e2330..661907a 100644
--- a/src/js/tasks/task-builddisks.js
+++ b/src/js/tasks/task-builddisks.js
@@ -39,9 +39,8 @@ const TaskBuildDisks = new Lang.Class({
     Name: 'TaskBuildDisks',
     Extends: Task.TaskDef,
 
-    TaskPattern: [/builddisks$/],
-
-    TaskAfterPrefix: '/build/',
+    TaskName: "builddisks",
+    TaskAfter: ['build'],
 
     // Legacy
     _VERSION_RE: /^(\d+)\.(\d+)$/,
diff --git a/src/js/tasks/task-resolve.js b/src/js/tasks/task-resolve.js
index 7c06d69..f43ac46 100644
--- a/src/js/tasks/task-resolve.js
+++ b/src/js/tasks/task-resolve.js
@@ -36,12 +36,24 @@ const TaskResolve = new Lang.Class({
     Name: "TaskResolve",
     Extends: Task.TaskDef,
 
-    TaskPattern: [/resolve$/],
+    TaskName: "resolve",
 
     DefaultParameters: {fetchAll: false,
                        fetchComponents: [],
                        timeoutSec: 10},
 
+    _getDb: function() {
+       if (this._db == null) {
+           let snapshotdir = this.workdir.get_child('snapshots');
+           this._db = new JsonDB.JsonDB(snapshotdir);
+       }
+       return this._db;
+    },
+
+    queryVersion: function() {
+       return this._getDb().getLatestVersion();
+    },
+
     execute: function(cancellable) {
         let manifestPath = this.workdir.get_child('manifest.json');
        let data = JsonUtil.loadJson(manifestPath, cancellable);
@@ -70,9 +82,7 @@ const TaskResolve = new Lang.Class({
             component['revision'] = revision;
        }
 
-       let snapshotdir = this.workdir.get_child('snapshots');
-       this._src_db = new JsonDB.JsonDB(snapshotdir);
-        let [path, modified] = this._src_db.store(this._snapshot.data, cancellable);
+        let [path, modified] = this._getDb().store(this._snapshot.data, cancellable);
         if (modified) {
             print("New source snapshot: " + path.get_path());
         } else {
diff --git a/src/js/tasks/task-smoketest.js b/src/js/tasks/task-smoketest.js
index 525178c..1a15c8a 100644
--- a/src/js/tasks/task-smoketest.js
+++ b/src/js/tasks/task-smoketest.js
@@ -220,7 +220,8 @@ const TaskSmoketest = new Lang.Class({
     Name: 'TaskSmoketest',
     Extends: Task.TaskDef,
 
-    TaskPattern: [/smoketest$/],
+    TaskName: "smoketest",
+    TaskAfter: ['builddisks'],
 
     execute: function(cancellable) {
              let imageDir = this.workdir.get_child('images');


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