Skip to content

Commit

Permalink
change to ninja generator
Browse files Browse the repository at this point in the history
This program was a feature-lacking build tool. So I decided to convert it
to a ninja generator that can track file additions and deletions.
  • Loading branch information
tylorr committed Nov 13, 2013
1 parent 66ba5d1 commit aa71fc2
Show file tree
Hide file tree
Showing 4 changed files with 81 additions and 191 deletions.
236 changes: 56 additions & 180 deletions assetman.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,41 +14,14 @@ var path = require('path'),
fs = require('fs'),
util = require('util'),
exec = require('child_process').exec,
mkdirp = require('mkdirp'),
minimatch = require('minimatch'),
Glob = require("glob").Glob,
glob = require('glob'),
ninja = require('ninja-build-gen')(),
checkHelp,
showHelp,
help,
parse;

var currentDir = process.cwd(),
previousDir, assetConfig,
configPath;

// Search for Asset Config file starting with working directory and working
// up the hierarchy
// curr and prev will be the same when root is reached
while (currentDir != previousDir) {
configPath = path.join(currentDir, 'assets.json');
if (fs.existsSync(configPath)) {
assetConfig = require(configPath);

// resolve paths
assetConfig.boar_repo = path.resolve(currentDir, assetConfig.boar_repo);
assetConfig.target_dir = path.resolve(currentDir, assetConfig.target_dir);
break;
}

previousDir = currentDir;
currentDir = path.join(currentDir, '..');
}

if (!assetConfig) {
console.error('ERROR: Could not find assets.json file');
process.exit(1);
}

// check argv for -h or --help
checkHelp = function(argv) {
var len = argv.length,
Expand Down Expand Up @@ -116,180 +89,83 @@ parse = function(settings, argv) {
}
};

var convert,
convertAll,
convertRecent,
convertOne,
getConverter,
filterConverters;

// convert one file with the provided converter
convert = function(input, converter) {
var filename,
indir, outdir,
absInput, absFilename,
re, i, ext;

// replace extension
// output = input.replace(re, converter.ext);
filename = input.slice(0, input.lastIndexOf(path.extname(input)));

indir = assetConfig.boar_repo;
outdir = assetConfig.target_dir;

absInput = path.join(indir, input);
absFilename = path.join(outdir, filename);
// make directories if they don't exist
mkdirp(path.dirname(absFilename), function(err) {
if (err) {
throw err;
}

// var commands = con
// If a string, turn into array: [string]
var commands = ([]).concat(converter.commands);

commands.forEach(function(command) {
// Populate convert command
command = command.replace('%i', absInput).replace('%n', absFilename);

exec(command,
function(error, stdout, stderr) {
if (error) throw err;
console.info('Processed: ' + input);
});
});
});
};

// Get converter from asset config and check input file against filters
getConverter = function(file, converters) {
var len = converters.length,
i, converter;

for (i = 0; i < len; ++i) {
converter = converters[i];
var getConfig = function(srcPath) {
var configPath = path.join(srcPath, 'assets.json');

if (minimatch(file, converter.pattern)) {
return converter;
}
if (fs.existsSync(configPath)) {
return require(configPath);
} else {
console.error('ERROR: The source directory "' + srcPath + '" does not appear to contain assets.json.');
process.exit(1);
}

return null;
};

// Get converters with the right tags
filterConverters = function(filters) {
// filters may be 'arguments' which isn't a proper array
filters = Array.prototype.slice.call(filters);
var generate = function(srcPath) {
srcPath = srcPath || '';
srcPath = path.resolve(process.cwd(), srcPath);

var converters;
if (filters.length !== 0) {
converters = assetConfig.converters.filter(function(converter) {
return !converter.tag || filters.indexOf(converter.tag) !== -1;
});
} else {
converters = assetConfig.converters;
}
var buildPath = process.cwd(),
assetConfig = getConfig(srcPath);

return converters;
};
var srcRelPath = path.relative(buildPath, srcPath);

// Converts all files in the boar repo
convertAll = function() {
var indir = assetConfig.boar_repo,
converters = filterConverters(arguments);
var compareEchoPath = path.join(__dirname, 'compare_echo.js'),
command = 'node ' + compareEchoPath + ' "$glob" $out ' + srcRelPath;
ninja.rule('compare_echo')
.restat(true)
.description('Update file list')
.run(command);

var glob, cache = null;
converters.forEach(function(converter) {
glob = new Glob(converter.pattern, { cache: cache, cwd: indir });
cache = glob.cache;
ninja.rule('rebuild')
.generator(true)
.description('Generate build.ninja')
.run('assetman.cmd ' + srcRelPath);

glob.on("match", function(file) {
convert(file, converter);
})
});
};
for (var rule in assetConfig.rules) {
ninja.rule(rule).run(assetConfig.rules[rule]);
}

// Converts all files that were recently modified in the boar repo
convertRecent = function() {
var infoPath = path.join(assetConfig.boar_repo, '.boar/info'),
converters = filterConverters(arguments);
var patterns = [];
var assets = [];
for (var pattern in assetConfig.files) {
patterns.push(pattern);
var files = glob.sync(pattern, {cwd: srcPath});

fs.readFile(infoPath, function(err, boarJSON) {
if (err) {
console.error("ERROR: " + assetConfig.boar_repo + " is not a boar repo");
process.exit(1);
}
var len = files.length, i;
for (i = 0; i < len; ++i) {
var target = path.relative(buildPath, path.join(srcPath, files[i]));
var inRelPath = files[i];
var relDir = path.dirname(inRelPath);

var boarInfo = JSON.parse(boarJSON),
revision = boarInfo.session_id,
repoPath = boarInfo.repo_path,
boarLog = util.format('boar --repo=%s log -vr %d', repoPath, revision);
var filename = path.basename(inRelPath, path.extname(inRelPath));

// run boar log
exec(boarLog, function(error, stdout, stderr) {
if (error || stderr) {
console.error("ERROR: Error executing boar log: " + (error || stderr));
process.exit(1);
}
var outPatterns = assetConfig.files[pattern];
for (var outPattern in outPatterns) {
var rule = outPatterns[outPattern];
var outName = outPattern.replace(/\$filename/, filename);
var outRelPath = path.join(relDir, outName);

// Scan for new or modified files
// A new file
// M modified file
var modified = stdout.match(/^[AM]\s*(.+)/m),
len = modified.length,
i, file,
converter;

// convert each modified/new file
for (i = 1; i < len; i++) {
file = modified[i];
converter = getConverter(file, converters);
if (converter) {
convert(file, converter);
} else {
console.log("WARN: No converter for: " + file);
}
ninja.edge(outRelPath).from(target).using(rule);
assets.push(outRelPath);
}
});
});
};

// converts all files that meets provided pattern
convertOne = function(pattern) {
if (!pattern) {
console.error("ERROR: Pattern must be supplied to convert command.");
process.exit(1);
}
}

var indir = assetConfig.boar_repo;
var globs = patterns.join(' ');
ninja.edge('.dirty');
ninja.edge('.files').from('.dirty').using('compare_echo').assign('glob', globs);

var glob = new Glob(pattern, { cwd: indir });
var assetConfigPath = path.join(srcRelPath, 'assets.json');
ninja.edge('build.ninja').from(['.files', assetConfigPath]).using('rebuild');

glob.on('match', function(file) {
var converter = getConverter(file, assetConfig.converters);
ninja.edge('assets').from(assets);
ninja.byDefault('assets');

if (converter) {
convert(file, converter);
} else {
console.warn("WARN: No converter for: " + file);
}
});
ninja.save('build.ninja');
};

var commandSettings = {
commands: {
all: {
action: convertAll
},
recent: {
action: convertRecent
},
convert: {
action: convertOne
}
}
action: generate
};

var argv = process.argv.slice(2);
Expand Down
19 changes: 19 additions & 0 deletions compare_echo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
var glob = require('glob'),
fs = require('fs'),
args = process.argv.slice(2),
cwd = args[2] || process.cwd();

// construct search patterns
var patterns = args[0].split(' '),
pattern = patterns.length > 1 ? '{' + patterns.join(',') + '}' : patterns[0];

// collect files that match pattern
var files = glob.sync(pattern, {cwd: cwd}).join('\n');

// update file list in output if they don't match
var outpath = args[1];
fs.readFile(outpath, {encoding: 'utf8'}, function(_, oldfiles) {
if (oldfiles !== files) {
fs.writeFileSync(outpath, files);
}
});
13 changes: 4 additions & 9 deletions example-assets.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,6 @@
{
"boar_repo": "RawAssets",
"target_dir": "Assets",
"converters" : [
{
"pattern": "**/*.psd",
"tag": "images",
"commands": "convert \"%i[0]\" \"%n.png\""
}
]
"**/*.psd": {
"$filename.png": "convert \"$in[0]\" -resize 50% \"$out\"",
"[email protected]": "convert \"$in[0]\" \"$out\""
}
}
4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "assetman",
"description": "Boar-to-DVCS asset manager",
"version": "0.4.1",
"version": "1.0.0",
"homepage": "https://github.com/miningold/assetman",
"author": {
"name": "Tylor Reynolds",
Expand Down Expand Up @@ -30,7 +30,7 @@
"dependencies": {
"glob": "~3.2.6",
"minimatch": "~0.2.12",
"mkdirp": "~0.3.5"
"ninja-build-gen": "~0.1.1"
},
"preferGlobal": "true",
"bin": {
Expand Down

0 comments on commit aa71fc2

Please sign in to comment.