mist
Version:
Mist build system
472 lines (426 loc) • 12.4 kB
JavaScript
var Globber, Hasher, MistResolver, path;
path = require('path');
Globber = require('./globber');
Hasher = require('./hasher');
module.exports = MistResolver = (function() {
function MistResolver(rootDir, rootMist) {
this.rootDir = rootDir;
this.rootMist = rootMist;
this.groupRefs = {};
this.compileCommands();
this.setupTargets();
this.generateTemplates();
this.generateTargets();
}
/*
* Compiles all commands
*/
MistResolver.prototype.compileCommands = function() {
var j, len1, ref, results1, rule;
ref = this.rootMist.rules;
results1 = [];
for (j = 0, len1 = ref.length; j < len1; j++) {
rule = ref[j];
results1.push(rule.command = {
hash: Hasher.hash(rule.src.command),
command: MistResolver.delimitCommand(rule.src.command)
});
}
return results1;
};
/*
* Creates a targets object for each rule
*/
MistResolver.prototype.setupTargets = function() {
var j, len1, ref, results1, rule;
ref = this.rootMist.rules;
results1 = [];
for (j = 0, len1 = ref.length; j < len1; j++) {
rule = ref[j];
results1.push(rule.targets = {});
}
return results1;
};
/*
* Generates templates for dependencies and outputs
*/
MistResolver.prototype.generateTemplates = function() {
var j, len1, mktm, ref, results1, rule;
mktm = (function(_this) {
return function(i) {
return _this.makeTemplate(i);
};
})(this);
ref = this.rootMist.rules;
results1 = [];
for (j = 0, len1 = ref.length; j < len1; j++) {
rule = ref[j];
results1.push(rule.templates = {
dependencies: rule.src.dependencies.map(mktm),
orderDependencies: rule.src.orderDependencies.map(mktm),
outputs: rule.src.outputs.map(mktm),
auxOutputs: rule.src.auxOutputs.map(mktm)
});
}
return results1;
};
/*
* Transforms a source input into a template function
*
* input:
* The input pair generated from the parser
* rule:
* The rule for this input
*/
MistResolver.prototype.makeTemplate = function(input, rule) {
switch (input.type) {
case 'glob':
return (function(_this) {
return function(path, group) {
if (group != null) {
[];
}
path = MistResolver.delimitPath(path, input.value);
return Globber.performGlob(path, _this.rootDir);
};
})(this);
case 'group':
return this.groupRefs[input.value] = this.groupRefs[input.value] || [];
case 'simple':
return function(path, group) {
if (group != null) {
[];
}
return MistResolver.delimitPath(path, input.value);
};
default:
throw "unknown template type: " + input.type;
}
};
/*
* Iterates all inputs and generates target outputs,
* supplying groups with targets as well
*/
MistResolver.prototype.generateTargets = function() {
var groupSubs, input, j, l, len1, len2, len3, n, ref, ref1, ref2, result, results, results1, rule;
groupSubs = {};
ref = this.rootMist.rules;
for (j = 0, len1 = ref.length; j < len1; j++) {
rule = ref[j];
ref1 = rule.src.inputs;
for (l = 0, len2 = ref1.length; l < len2; l++) {
input = ref1[l];
if (input.type === 'group') {
(groupSubs[input.value] = groupSubs[input.value] || []).push(rule);
}
}
}
ref2 = this.rootMist.rules;
results1 = [];
for (n = 0, len3 = ref2.length; n < len3; n++) {
rule = ref2[n];
results1.push((function() {
var len4, o, ref3, results2;
ref3 = rule.src.inputs;
results2 = [];
for (o = 0, len4 = ref3.length; o < len4; o++) {
input = ref3[o];
switch (input.type) {
case 'glob':
results = Globber.performGlob(input.value, this.rootDir);
results2.push((function() {
var len5, q, results3;
results3 = [];
for (q = 0, len5 = results.length; q < len5; q++) {
result = results[q];
results3.push(this.processInput(rule, result, null, groupSubs));
}
return results3;
}).call(this));
break;
case 'group':
break;
default:
throw "unknown input type: " + input.type;
}
}
return results2;
}).call(this));
}
return results1;
};
/*
* Gives a single input to a rule for processing
*
* rule:
* The rule for which to create a target based on the input
* input:
* The input for which to create a target
* group:
* A group, if any, that triggered the input
* groupSubs:
* A dictionary of group=>rules that are subscribed to receive
* target outputs as inputs to process
*/
MistResolver.prototype.processInput = function(rule, input, group, groupSubs) {
var a, j, k, l, len1, len2, len3, n, output, processor, ref, ref1, ref2, ref3;
if (groupSubs == null) {
groupSubs = {};
}
if (input in rule.targets) {
return;
}
processor = function(fn) {
if (fn instanceof Function) {
return fn(input, group);
} else {
return fn;
}
};
rule.targets[input] = {
dependencies: rule.templates.dependencies.map(processor),
orderDependencies: rule.templates.orderDependencies.map(processor),
outputs: rule.templates.outputs.map(processor),
auxOutputs: rule.templates.auxOutputs.map(processor)
};
ref = rule.targets[input];
for (k in ref) {
a = ref[k];
rule.targets[input][k] = a.flatten();
}
ref1 = rule.src.groups;
for (j = 0, len1 = ref1.length; j < len1; j++) {
group = ref1[j];
ref2 = rule.targets[input].outputs;
for (l = 0, len2 = ref2.length; l < len2; l++) {
output = ref2[l];
(this.groupRefs[group] = this.groupRefs[group] || []).push(output);
if (group in groupSubs) {
ref3 = groupSubs[group];
for (n = 0, len3 = ref3.length; n < len3; n++) {
rule = ref3[n];
this.processInput(rule, output, group, groupSubs);
}
}
}
}
return null;
};
/*
* Compiles the resolved tree into a raw target list
*/
MistResolver.prototype.compile = function() {
var input, inputs, j, k, len1, outputs, ref, ref1, result, rule, target, v;
result = {
targets: [],
commands: {}
};
ref = this.rootMist.rules;
for (j = 0, len1 = ref.length; j < len1; j++) {
rule = ref[j];
result.commands[rule.command.hash] = {
command: rule.command.command
};
if (rule.src.foreach) {
ref1 = rule.targets;
for (input in ref1) {
target = ref1[input];
outputs = target.outputs.compile();
inputs = [input];
result.targets.push({
command: {
name: rule.command.hash,
vars: MistResolver.compileVars(inputs, outputs)
},
inputs: inputs,
dependencies: target.dependencies.compile(),
orderDependencies: target.orderDependencies.compile(),
outputs: outputs,
auxOutputs: target.auxOutputs.compile()
});
}
} else {
inputs = ((function() {
var results1;
results1 = [];
for (k in rule.targets) {
results1.push(k);
}
return results1;
})()).compile();
outputs = ((function() {
var ref2, results1;
ref2 = rule.targets;
results1 = [];
for (k in ref2) {
v = ref2[k];
results1.push(v.outputs);
}
return results1;
})()).compile();
result.targets.push({
command: {
name: rule.command.hash,
vars: MistResolver.compileVars(inputs, outputs)
},
inputs: inputs,
dependencies: ((function() {
var ref2, results1;
ref2 = rule.targets;
results1 = [];
for (k in ref2) {
v = ref2[k];
results1.push(v.dependencies);
}
return results1;
})()).compile(),
orderDependencies: ((function() {
var ref2, results1;
ref2 = rule.targets;
results1 = [];
for (k in ref2) {
v = ref2[k];
results1.push(v.orderDependencies);
}
return results1;
})()).compile(),
outputs: outputs,
auxOutputs: ((function() {
var ref2, results1;
ref2 = rule.targets;
results1 = [];
for (k in ref2) {
v = ref2[k];
results1.push(v.auxOutputs);
}
return results1;
})()).compile()
});
}
}
return result;
};
return MistResolver;
})();
/*
* Make sure to always include `$1` in the replacement
*/
MistResolver.delimiterPattern = /%(-?\d+-?(?:,-?\d+-?)*(?=d))?([%fFbBoOd])/g;
/*
* Returns whether or not a string has filename delimiters present
*
* str:
* The string to check
*/
MistResolver.hasDelimiters = function(str) {
return !!(str.match(MistResolver.delimiterPattern));
};
/*
* Delimits a template given a pathname
*
* pathname:
* The pathname to use when expanding the templates
* template:
* A delimited template
*/
MistResolver.delimitPath = function(pathname, template) {
var dict;
dict = MistResolver.generateDict(pathname);
return template.replace(MistResolver.delimiterPattern, function(m, p, c) {
c = (p || '') + c;
if (c in dict) {
return dict[c];
} else {
throw "unknown file delimiter: " + c;
}
});
};
/*
* Generates a delimiter dictionary for a given pathname.
*
* Implemented with memoization for slightly better performance.
*
* pathname;
* The pathname for which to build up a dictionary
*/
MistResolver.generateDict = function(pathname) {
var dict, i, ii, j, leaf, leafs, len, len1, ni, pathJoinArray;
dict = {};
if (pathname in this) {
dict = this[pathname];
} else {
dict['%'] = '%';
dict['x'] = path.extname(pathname);
dict['b'] = path.basename(pathname);
dict['X'] = dict['b'].replace(/.+?(\..+)$/, '$1');
dict['B'] = path.basename(dict['b'], dict['x']);
dict['f'] = pathname;
dict['F'] = path.basename(dict['f'], dict['x']);
dict['o'] = '%o';
dict['O'] = '%O';
leafs = path.dirname(pathname).split(path.sep);
len = leafs.length;
pathJoinArray = function(arr) {
return path.join.apply(path, arr);
};
for (i = j = 0, len1 = leafs.length; j < len1; i = ++j) {
leaf = leafs[i];
ii = i + 1;
ni = len - ii;
dict[ii + "d"] = leaf;
dict[ii + "-d"] = pathJoinArray(leafs.slice(i));
dict["-" + ii + "d"] = leafs[ni];
dict["-" + ii + "-d"] = pathJoinArray(leafs.slice(ni));
}
}
return dict;
};
MistResolver.generateDict = MistResolver.generateDict.bind({});
/*
* Delimits a command
*
* command:
* The command to delimit
*/
MistResolver.delimitCommand = function(command) {
return command.replace(MistResolver.delimiterPattern, function(m, c, d) {
if (d === '%') {
'%';
}
c = (c || '').replace('-', '_');
return "${D_" + c + d + "}";
});
};
MistResolver.compileVars = function(inputs, outputs) {
var input, j, k, len1, outputDirs, ref, result, v;
result = {};
outputDirs = outputs.map(path.dirname);
ref = inputs.map(MistResolver.generateDict);
for (j = 0, len1 = ref.length; j < len1; j++) {
input = ref[j];
for (k in input) {
v = input[k];
if (k === '%') {
continue;
}
if (k === 'o') {
v = outputs;
}
if (k === 'O') {
v = outputDirs;
}
k = k.replace('-', '_');
k = "D_" + k;
if (!(k in result)) {
result[k] = [];
}
result[k] = result[k].concat(v);
}
}
for (k in result) {
v = result[k];
result[k] = v.unique().linearize();
}
return result;
};
//# sourceMappingURL=mist-resolver.js.map