From 7da080b8c32d20b62dfe3893334878d78f5c83e0 Mon Sep 17 00:00:00 2001 From: Sven Slootweg Date: Mon, 3 Jul 2017 18:22:23 +0200 Subject: [PATCH] Initial commit --- .gitignore | 2 ++ .npmignore | 1 + README.md | 29 ++++++++++++++++++ gulpfile.js | 32 ++++++++++++++++++++ index.js | 3 ++ package.json | 29 ++++++++++++++++++ src/drv.pegjs | 30 ++++++++++++++++++ src/index.js | 84 +++++++++++++++++++++++++++++++++++++++++++++++++++ test.js | 12 ++++++++ 9 files changed, 222 insertions(+) create mode 100644 .gitignore create mode 100644 .npmignore create mode 100644 README.md create mode 100644 gulpfile.js create mode 100644 index.js create mode 100644 package.json create mode 100644 src/drv.pegjs create mode 100644 src/index.js create mode 100644 test.js diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..8f028e4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,2 @@ +/node_modules/ +/lib/ diff --git a/.npmignore b/.npmignore new file mode 100644 index 0000000..2ccbe46 --- /dev/null +++ b/.npmignore @@ -0,0 +1 @@ +/node_modules/ diff --git a/README.md b/README.md new file mode 100644 index 0000000..7603810 --- /dev/null +++ b/README.md @@ -0,0 +1,29 @@ +# drv + +A Nix `.drv` file parser. + +## License + +[WTFPL](http://www.wtfpl.net/txt/copying/) or [CC0](https://creativecommons.org/publicdomain/zero/1.0/), whichever you prefer. A donation and/or attribution are appreciated, but not required. + +## Donate + +Maintaining open-source projects takes a lot of time, and the more donations I receive, the more time I can dedicate to open-source. If this module is useful to you, consider [making a donation](http://cryto.net/~joepie91/donate.html)! + +You can donate using Bitcoin, PayPal, Flattr, cash-in-mail, SEPA transfers, and pretty much anything else. Thank you! + +## Contributing + +Pull requests welcome. Please make sure your modifications are in line with the overall code style, and ensure that you're editing the files in `src/`, not those in `lib/`. + +Build tool of choice is `gulp`; simply run `gulp` while developing, and it will watch for changes. + +Be aware that by making a pull request, you agree to release your modifications under the licenses stated above. + +## Usage + +TODO + +## API + +TODO diff --git a/gulpfile.js b/gulpfile.js new file mode 100644 index 0000000..f3143c9 --- /dev/null +++ b/gulpfile.js @@ -0,0 +1,32 @@ +const gulp = require('gulp'); + +const presetES2015 = require("@joepie91/gulp-preset-es2015"); +const presetPegjs = require("@joepie91/gulp-preset-pegjs") + +let sources = { + babel: ["src/**/*.js"], + pegjs: ["src/**/*.pegjs"] +} + +gulp.task('babel', function() { + return gulp.src(sources.babel) + .pipe(presetES2015({ + basePath: __dirname + })) + .pipe(gulp.dest("lib/")); +}); + +gulp.task('pegjs', function() { + return gulp.src(sources.pegjs) + .pipe(presetPegjs({ + basePath: __dirname + })) + .pipe(gulp.dest("lib/")); +}) + +gulp.task('watch', function () { + gulp.watch(sources.babel, ['babel']); + gulp.watch(sources.pegjs, ['pegjs']); +}); + +gulp.task('default', ['pegjs', 'babel', 'watch']); diff --git a/index.js b/index.js new file mode 100644 index 0000000..507257b --- /dev/null +++ b/index.js @@ -0,0 +1,3 @@ +'use strict'; + +module.exports = require("./lib"); \ No newline at end of file diff --git a/package.json b/package.json new file mode 100644 index 0000000..4ce1fde --- /dev/null +++ b/package.json @@ -0,0 +1,29 @@ +{ + "name": "drv", + "version": "1.0.0", + "description": "Nix .drv file parser", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "http://git.cryto.net/joepie91/node-drv.git" + }, + "keywords": [ + "nix", + "nixos", + "drv", + "derive", + "derivation", + "parser" + ], + "author": "Sven Slootweg", + "license": "WTFPL", + "devDependencies": { + "@joepie91/gulp-preset-es2015": "^1.0.1", + "@joepie91/gulp-preset-pegjs": "^1.0.0", + "babel-preset-es2015": "^6.6.0", + "gulp": "^3.9.1" + } +} diff --git a/src/drv.pegjs b/src/drv.pegjs new file mode 100644 index 0000000..6dc18cd --- /dev/null +++ b/src/drv.pegjs @@ -0,0 +1,30 @@ +start + = deriveTag + +value + = deriveTag + / array + / tuple + / string + +multipleItems + = item:value subsequentItems:("," value)* { return [item].concat(subsequentItems.map(subsequentItem => subsequentItem[1])); } + +deriveTag + = "Derive(" items:multipleItems ")" { return {type: "deriveTag", items: items}; } + +array + = "[" items:multipleItems "]" { return {type: "array", items: items}; } + +tuple + = "(" items:multipleItems ")" { return {type: "tuple", items: items}; } + +string + = '"' chars:stringCharacter* '"' { return chars.join(""); } + +stringCharacter + = !('"' / "\\") char:. { return char; } + / stringEscapedQuote { return '"'; } + +stringEscapedQuote + = '\\"' diff --git a/src/index.js b/src/index.js new file mode 100644 index 0000000..f881e07 --- /dev/null +++ b/src/index.js @@ -0,0 +1,84 @@ +'use strict'; + +const drvParser = require("./drv"); + +function valueType(value) { + if (typeof value === "string") { + return "string"; + } else { + return value.type; + } +} + +function assertType(value, expectedType) { + if (valueType(value) == null) { + throw new Error(`Expected a '${expectedType}', got nothing`); + } else if (valueType(value) !== expectedType) { + throw new Error(`Expected a '${expectedType}', got '${valueType(value)}'`); + } else { + return true; + } +} + +function assertedValue(type, value) { + if (assertType(value, type)) { + return value; + } +} + +function mapArray(array, callback) { + return assertedValue("array", array).items.map(callback); +} + +function mapTuple(tuple, fields, callback) { + return assertedValue("tuple", tuple).items.reduce((result, item, i) => { + if (fields[i] != null) { + let value; + + if (callback != null) { + value = callback(item, i); + } else { + value = item; + } + + result[fields[i]] = value; + return result; + } else { + throw new Error(`Attempted to map field ${i}, but only ${fields.length} field names were specified`); + } + }, {}); +} + +function parseDerivation(text) { + let root = parseAST(text); + + return { + outputs: mapArray(root.items[0], (item) => { + return mapTuple(item, ["name", "path", "hashAlgorithm", "hash"]); + }), + inputDerivations: mapArray(root.items[1], (item) => { + return mapTuple(item, ["path", "inputDerivations"], (value, i) => { + if (i === 1) { + /* An `array` of inputDerivations */ + return value.items; + } else { + return value; + } + }); + }), + inputSources: assertedValue("array", root.items[2]).items, + platform: assertedValue("string", root.items[3]), + builder: assertedValue("string", root.items[4]), + builderArguments: assertedValue("array", root.items[5]).items, + environmentVariables: mapArray(root.items[6], (item) => { + return mapTuple(item, ["name", "value"]); + }) + } +}; + +function parseAST(text) { + return drvParser.parse(text); +} + +parseDerivation.ast = parseAST; +module.exports = parseDerivation; diff --git a/test.js b/test.js new file mode 100644 index 0000000..00c6934 --- /dev/null +++ b/test.js @@ -0,0 +1,12 @@ +'use strict'; + +const util = require("util"); +const parse = require("./lib"); + +let testDerivation = 'Derive([("dev","/nix/store/lpwm4gcsda53g26jp6rxz4x13l8lywzg-libXdmcp-1.1.2-dev","",""),("doc","/nix/store/y8925a01zwzrw0sakpv066bc4m6b2qj5-libXdmcp-1.1.2-doc","",""),("out","/nix/store/3iq0cd1ll0c7hss8xxgalxqn0jm0b2mi-libXdmcp-1.1.2","","")],[("/nix/store/3l3gnqvlwrmiqdma5vg3zibkabv4d6wh-pkg-config-0.29.drv",["out"]),("/nix/store/6qrlismj26hncj3809k302z6d3ycfwhh-stdenv.drv",["out"]),("/nix/store/flyb4cyxz0s48iq8qcdxdf2bcmgcc4cq-bash-4.3-p48.drv",["out"]),("/nix/store/pm59fc4bwblygsfxbq5dr72lkg7wxwsi-libXdmcp-1.1.2.tar.bz2.drv",["out"]),("/nix/store/w3qvvackybwaq6d2sw58yacsrbbrxa4b-xproto-7.0.29.drv",["out"])],["/nix/store/zsi9kk33hj7kvjfw0zahpdsiggg58nn4-builder.sh"],"x86_64-linux","/nix/store/gabjbkwga2dhhp2wzyaxl83r8hjjfc37-bash-4.3-p48/bin/bash",["-e","/nix/store/zsi9kk33hj7kvjfw0zahpdsiggg58nn4-builder.sh"],[("buildInputs",""),("builder","/nix/store/gabjbkwga2dhhp2wzyaxl83r8hjjfc37-bash-4.3-p48/bin/bash"),("dev","/nix/store/lpwm4gcsda53g26jp6rxz4x13l8lywzg-libXdmcp-1.1.2-dev"),("doc","/nix/store/y8925a01zwzrw0sakpv066bc4m6b2qj5-libXdmcp-1.1.2-doc"),("hardeningDisable","bindnow relro"),("name","libXdmcp-1.1.2"),("nativeBuildInputs","/nix/store/y419xb805psvlia6rl2jjrxmfsin5v63-pkg-config-0.29 /nix/store/cpy3mign235h3bjx63jz4baiislqznb9-xproto-7.0.29"),("out","/nix/store/3iq0cd1ll0c7hss8xxgalxqn0jm0b2mi-libXdmcp-1.1.2"),("outputs","out dev doc"),("propagatedBuildInputs",""),("propagatedNativeBuildInputs",""),("src","/nix/store/djf171kmvws6891q2nzd2icdjd5bgj4r-libXdmcp-1.1.2.tar.bz2"),("stdenv","/nix/store/985d95clq0216a6pcp3qzw4igp84ajvr-stdenv"),("system","x86_64-linux")])'; + +try { + console.log(util.inspect(parse(testDerivation), {colors: true, depth: null})); +} catch (err) { + console.error(util.inspect(err, {colors: true})); +}