wepoll/tools/combine.js
2018-01-05 18:03:39 +01:00

150 lines
3.7 KiB
JavaScript

// This is a mess. I know.
const path = require('path');
const fs = require('fs');
// Parse command line options.
const files = [];
const includeDirs = [];
let stripGuardsEnabled = false;
process.argv
.slice(2)
.forEach((arg) => {
let match;
if ((match = /^-I(.*)$/.exec(arg)))
includeDirs.push(match[1]);
else if (arg === '--strip-guards')
stripGuardsEnabled = true;
else
files.push(arg);
});
const included = {};
function readFileWithPath(fileName, dirs) {
if (/[/\\]/.test(fileName))
return fs.readFileSync(fileName, 'utf8');
for (let i = 0; i < dirs.length; i++) {
const filePath = path.resolve(dirs[i], fileName);
try {
return fs.readFileSync(filePath, 'utf8');
} catch (e) {
// Ignore.
}
}
const err = new Error('file not found: ' + fileName);
err.code = 'ENOENT';
throw err;
}
function strip_guards(filename, source) {
const lead_comments_re = /^(\s*\/\*((?!\*\/)[\s\S])*\*\/)*\s*/;
const trail_comments_re = /(\s*\/\*((?!\*\/)[\s\S])*\*\/)*\s*$/;
const lead_guards_re = /^#ifndef\s+(\w+)\s+#define\s+(\w+)\s+/;
const trail_guards_re = /#endif$/;
// Strip leading and trailing comments and whitespace.
source = source.replace(lead_comments_re, '');
source = source.replace(trail_comments_re, '');
// Find include guards.
const lead_guards = lead_guards_re.exec(source);
const trail_guards = trail_guards_re.exec(source);
// Remove include guards, if found.
if (lead_guards && trail_guards && lead_guards[1] == lead_guards[2]) {
console.error('Stripping include guards: ' + filename);
source = source.replace(lead_guards_re, '');
source = source.replace(trail_guards_re, '');
}
// Add back a trailing newline.
source += '\n';
return source;
}
function lines(filename, strip) {
let source = readFileWithPath(filename, ['.'].concat(includeDirs));
if (strip) source = strip_guards(filename, source);
return source.split(/\r?\n/g);
}
function include(line, filename) {
const key = path.basename(filename).toLowerCase();
if (included[key])
return ''; // Included earlier.
console.error('Including: ' + key);
included[key] = true;
return lines(filename, true);
}
function add(filename) {
const key = path.basename(filename).toLowerCase();
console.error('Adding: ' + key);
included[key] = true;
return lines(filename, stripGuardsEnabled);
}
const sys_included = {};
function include_sys(line, filename) {
const key = path.basename(filename).toLowerCase();
if (sys_included[key])
return ''; // Included earlier.
sys_included[key] = true;
}
let source = [];
source = source.concat('/*')
.concat(fs.readFileSync('LICENSE', 'utf8')
.replace(/^\s+|\s+$/g, '')
.split(/\r?\n/g)
.map(function(s) {
return ' * ' + s;
})
.map(function(s) {
return s.replace(/\s+$/, '');
}))
.concat(' */')
.concat('');
for (let i = 0; i < files.length; i++) {
const filename = files[i];
source = source.concat(add(filename));
}
const patterns = [
{ re: /^\s*#include\s*"([^"]*)".*$/, fn: include },
{ re: /^\s*#include\s*<([^"]*)>.*$/, fn: include_sys }
];
restart: for (let lno = 0; lno < source.length;) {
for (const i in patterns) {
const line = source[lno];
const pattern = patterns[i];
const match = pattern.re.exec(line);
if (match) {
const repl = pattern.fn.apply(null, match);
if (repl != null && repl !== line) {
source.splice.apply(source, [lno, 1].concat(repl));
continue restart;
}
}
}
lno++;
}
source = source
.map((line) => line.replace(/\s+$/, ''))
.join('\n')
.replace(/\n{3,}/g, '\n\n')
.replace(/\n*$/, '\n');
process.stdout.write(source);