summaryrefslogtreecommitdiff
path: root/node_modules/jake/lib/rule.js
diff options
context:
space:
mode:
Diffstat (limited to 'node_modules/jake/lib/rule.js')
-rw-r--r--node_modules/jake/lib/rule.js311
1 files changed, 311 insertions, 0 deletions
diff --git a/node_modules/jake/lib/rule.js b/node_modules/jake/lib/rule.js
new file mode 100644
index 0000000..25f51ae
--- /dev/null
+++ b/node_modules/jake/lib/rule.js
@@ -0,0 +1,311 @@
+let path = require('path');
+let fs = require('fs');
+let Task = require('./task/task').Task;
+
+// Split a task to two parts, name space and task name.
+// For example, given 'foo:bin/a%.c', return an object with
+// - 'ns' : foo
+// - 'name' : bin/a%.c
+function splitNs(task) {
+ let parts = task.split(':');
+ let name = parts.pop();
+ let ns = resolveNs(parts);
+ return {
+ 'name' : name,
+ 'ns' : ns
+ };
+}
+
+// Return the namespace based on an array of names.
+// For example, given ['foo', 'baz' ], return the namespace
+//
+// default -> foo -> baz
+//
+// where default is the global root namespace
+// and -> means child namespace.
+function resolveNs(parts) {
+ let ns = jake.defaultNamespace;
+ for(let i = 0, l = parts.length; ns && i < l; i++) {
+ ns = ns.childNamespaces[parts[i]];
+ }
+ return ns;
+}
+
+// Given a pattern p, say 'foo:bin/a%.c'
+// Return an object with
+// - 'ns' : foo
+// - 'dir' : bin
+// - 'prefix' : a
+// - 'suffix' : .c
+function resolve(p) {
+ let task = splitNs(p);
+ let name = task.name;
+ let ns = task.ns;
+ let split = path.basename(name).split('%');
+ return {
+ ns: ns,
+ dir: path.dirname(name),
+ prefix: split[0],
+ suffix: split[1]
+ };
+}
+
+// Test whether string a is a suffix of string b
+function stringEndWith(a, b) {
+ let l;
+ return (l = b.lastIndexOf(a)) == -1 ? false : l + a.length == b.length;
+}
+
+// Replace the suffix a of the string s with b.
+// Note that, it is assumed a is a suffix of s.
+function stringReplaceSuffix(s, a, b) {
+ return s.slice(0, s.lastIndexOf(a)) + b;
+}
+
+class Rule {
+ constructor(opts) {
+ this.pattern = opts.pattern;
+ this.source = opts.source;
+ this.prereqs = opts.prereqs;
+ this.action = opts.action;
+ this.opts = opts.opts;
+ this.desc = opts.desc;
+ this.ns = opts.ns;
+ }
+
+ // Create a file task based on this rule for the specified
+ // task-name
+ // ======
+ // FIXME: Right now this just throws away any passed-in args
+ // for the synthsized task (taskArgs param)
+ // ======
+ createTask(fullName, level) {
+ let self = this;
+ let pattern;
+ let source;
+ let action;
+ let opts;
+ let prereqs;
+ let valid;
+ let src;
+ let tNs;
+ let createdTask;
+ let name = Task.getBaseTaskName(fullName);
+ let nsPath = Task.getBaseNamespacePath(fullName);
+ let ns = this.ns.resolveNamespace(nsPath);
+
+ pattern = this.pattern;
+ source = this.source;
+
+ if (typeof source == 'string') {
+ src = Rule.getSource(name, pattern, source);
+ }
+ else {
+ src = source(name);
+ }
+
+ // TODO: Write a utility function that appends a
+ // taskname to a namespace path
+ src = nsPath.split(':').filter(function (item) {
+ return !!item;
+ }).concat(src).join(':');
+
+ // Generate the prerequisite for the matching task.
+ // It is the original prerequisites plus the prerequisite
+ // representing source file, i.e.,
+ //
+ // rule( '%.o', '%.c', ['some.h'] ...
+ //
+ // If the objective is main.o, then new task should be
+ //
+ // file( 'main.o', ['main.c', 'some.h' ] ...
+ prereqs = this.prereqs.slice(); // Get a copy to work with
+ prereqs.unshift(src);
+
+ // Prereq should be:
+ // 1. an existing task
+ // 2. an existing file on disk
+ // 3. a valid rule (i.e., not at too deep a level)
+ valid = prereqs.some(function (p) {
+ let ns = self.ns;
+ return ns.resolveTask(p) ||
+ fs.existsSync(Task.getBaseTaskName(p)) ||
+ jake.attemptRule(p, ns, level + 1);
+ });
+
+ // If any of the prereqs aren't valid, the rule isn't valid
+ if (!valid) {
+ return null;
+ }
+ // Otherwise, hunky-dory, finish creating the task for the rule
+ else {
+ // Create the action for the task
+ action = function () {
+ let task = this;
+ self.action.apply(task);
+ };
+
+ opts = this.opts;
+
+ // Insert the file task into Jake
+ //
+ // Since createTask function stores the task as a child task
+ // of currentNamespace. Here we temporariliy switch the namespace.
+ // FIXME: Should allow optional ns passed in instead of this hack
+ tNs = jake.currentNamespace;
+ jake.currentNamespace = ns;
+ createdTask = jake.createTask('file', name, prereqs, action, opts);
+ createdTask.source = src.split(':').pop();
+ jake.currentNamespace = tNs;
+
+ return createdTask;
+ }
+ }
+
+ match(name) {
+ return Rule.match(this.pattern, name);
+ }
+
+ // Test wether the a prerequisite matchs the pattern.
+ // The arg 'pattern' does not have namespace as prefix.
+ // For example, the following tests are true
+ //
+ // pattern | name
+ // bin/%.o | bin/main.o
+ // bin/%.o | foo:bin/main.o
+ //
+ // The following tests are false (trivally)
+ //
+ // pattern | name
+ // bin/%.o | foobin/main.o
+ // bin/%.o | bin/main.oo
+ static match(pattern, name) {
+ let p;
+ let task;
+ let obj;
+ let filename;
+
+ if (pattern instanceof RegExp) {
+ return pattern.test(name);
+ }
+ else if (pattern.indexOf('%') == -1) {
+ // No Pattern. No Folder. No Namespace.
+ // A Simple Suffix Rule. Just test suffix
+ return stringEndWith(pattern, name);
+ }
+ else {
+ // Resolve the dir, prefix and suffix of pattern
+ p = resolve(pattern);
+
+ // Resolve the namespace and task-name
+ task = splitNs(name);
+ name = task.name;
+
+ // Set the objective as the task-name
+ obj = name;
+
+ // Namespace is already matched.
+
+ // Check dir
+ if (path.dirname(obj) != p.dir) {
+ return false;
+ }
+
+ filename = path.basename(obj);
+
+ // Check file name length
+ if ((p.prefix.length + p.suffix.length + 1) > filename.length) {
+ // Length does not match.
+ return false;
+ }
+
+ // Check prefix
+ if (filename.indexOf(p.prefix) !== 0) {
+ return false;
+ }
+
+ // Check suffix
+ if (!stringEndWith(p.suffix, filename)) {
+ return false;
+ }
+
+ // OK. Find a match.
+ return true;
+ }
+ }
+
+ // Generate the source based on
+ // - name name for the synthesized task
+ // - pattern pattern for the objective
+ // - source pattern for the source
+ //
+ // Return the source with properties
+ // - dep the prerequisite of source
+ // (with the namespace)
+ //
+ // - file the file name of source
+ // (without the namespace)
+ //
+ // For example, given
+ //
+ // - name foo:bin/main.o
+ // - pattern bin/%.o
+ // - source src/%.c
+ //
+ // return 'foo:src/main.c',
+ //
+ static getSource(name, pattern, source) {
+ let dep;
+ let pat;
+ let match;
+ let file;
+ let src;
+
+ // Regex pattern -- use to look up the extension
+ if (pattern instanceof RegExp) {
+ match = pattern.exec(name);
+ if (match) {
+ if (typeof source == 'function') {
+ src = source(name);
+ }
+ else {
+ src = stringReplaceSuffix(name, match[0], source);
+ }
+ }
+ }
+ // Assume string
+ else {
+ // Simple string suffix replacement
+ if (pattern.indexOf('%') == -1) {
+ if (typeof source == 'function') {
+ src = source(name);
+ }
+ else {
+ src = stringReplaceSuffix(name, pattern, source);
+ }
+ }
+ // Percent-based substitution
+ else {
+ pat = pattern.replace('%', '(.*?)');
+ pat = new RegExp(pat);
+ match = pat.exec(name);
+ if (match) {
+ if (typeof source == 'function') {
+ src = source(name);
+ }
+ else {
+ file = match[1];
+ file = source.replace('%', file);
+ dep = match[0];
+ src = name.replace(dep, file);
+ }
+ }
+ }
+ }
+
+ return src;
+ }
+}
+
+
+exports.Rule = Rule;