Browse Source

Separation of concerns between builder and cjdns-specific build stuff

Caleb James DeLisle 3 years ago
parent
commit
6a29c552ab

+ 1 - 1
node_build/Codestyle.js

@@ -15,7 +15,7 @@
 'use strict';
 var Fs = require('fs');
 var nThen = require('nthen');
-var Semaphore = require('../tools/lib/Semaphore');
+var Semaphore = require('saferphore');
 var Child = require('child_process');
 
 var headerLines = [

+ 1 - 1
node_build/Cp.js

@@ -1,6 +1,6 @@
 'use strict';
 var Fs = require("fs");
-var Semaphore = require('../tools/lib/Semaphore');
+var Semaphore = require('saferphore');
 var nThen = require('nthen');
 
 var sema = Semaphore.create(64);

+ 0 - 36
node_build/HasFunction.js

@@ -1,36 +0,0 @@
-'use strict';
-var nThen = require("nthen");
-var Fs = require("fs");
-
-module.exports.check = function (builder, func, ldflags, callback) {
-
-    var file = builder.tmpFile();
-    var outputFile = builder.tmpFile();
-
-    nThen(function (waitFor) {
-
-        Fs.writeFile(file, "int main() { " + func + "(); }", waitFor(function (err, ret) {
-            if (err) {
-                waitFor.abort();
-                callback(err);
-            }
-        }));
-
-    }).nThen(function (waitFor) {
-
-        var flags = [];
-        flags.push.apply(flags, ["-x", "c", "-o", outputFile, file]);
-        flags.push.apply(flags, ldflags);
-
-        builder.cc(flags, waitFor(function (ret, out, err) {
-            if (ret && /undefined reference/.test(err)) {
-                callback(undefined, false);
-            } else if (ret) {
-                callback(new Error(err));
-            } else {
-                callback(undefined, true);
-            }
-        }));
-
-    });
-};

+ 172 - 198
node_build/builder.js

@@ -19,35 +19,85 @@ const Fs = require('fs');
 const Spawn = require('child_process').spawn;
 const nThen = require('nthen');
 const Crypto = require('crypto');
-const Semaphore = require('../tools/lib/Semaphore');
-const GetVersion = require('./GetVersion');
+const Saferphore = require('saferphore');
 
-/*
- * Why hello dear packager,
- *
- * I suppose you have found this place as you are trying to figure out how to work this into your
- * build system. You're probably faced with a decision between getting node.js into your build and
- * "fixing" this build process so it doesn't need such a silly thing. A 500 line script is certainly
- * not unapproachable, right?
- * The reason why I am speaking to you now is because I care about you. I want you to be happy
- * and live a carefree life, and because you are standing on the precipice of a cavern so dark and
- * deep that while you may well make it out alive, your personal pride and innocence almost
- * certainly will not. Imagine yourself after months of sleepless nights wallowing in the quicksand,
- * forever trying to slay the dragon which is always so close yet and so far away. Imagine the deep
- * hatred you will have for humanity, code, and the creator of this doomsday machine. I beg you to
- * turn back now while there is still hope. You need not die here, your life is important, and
- * whether you close this file now or not, in the end you will still end up including node.js in
- * your build.
- *
- * The Creator
- */
+/*::
+export type Builder_File_t = {|
+    includes: string[],
+    links: string[],
+    cflags: string[],
+    ldflags: string[],
+    mtime: number,
+|};
+export type Builder_Hfile_t = {|
+    mtime: number,
+|};
+export type Builder_Compiler_t = {|
+    isLLVM: bool,
+    isClang: bool,
+    isGCC: bool,
+    version: string
+|};
+export type Builder_State_t = {|
+    rebuildIfChanges: string[],
+    rebuildIfChangesHash: string,
+    compilerType: Builder_Compiler_t,
+    cFiles: { [string]: Builder_File_t },
+    hFiles: { [string]: Builder_Hfile_t },
+|};
+export type Builder_Linter_t = (string, string, (string, bool)=>void)=>void;
+export type Builder_TestRunnerCb_t = (string, bool)=>void;
+export type Builder_t = {|
+    cc: (string[], (number, string, string)=>void)=>void,
+    buildExecutable: (string, ?string)=>void,
+    buildTest: (string)=>string,
+    runTest: (string, (string, Builder_TestRunnerCb_t)=>void)=>void,
+    lintFiles: (Builder_Linter_t)=>void,
+    config: Builder_Config_t,
+    tmpFile: (?string)=>string,
+    compilerType: () => Builder_Compiler_t
+|};
+export type Builder_BaseConfig_t = {|
+    systemName?: ?string,
+    gcc?: ?string,
+    buildDir?: ?string,
+|};
+export type Builder_Config_t = {
+    systemName: string,
+    gcc: string,
+    buildDir: string,
+    includeDirs: string[],
+    cflags: string[],
+    ldflags: string[],
+    libs: string[],
+    jobs: number,
+    fileCflags: {[string]: string[]},
+} & {[string]:any};
+import type { Nthen_WaitFor_t } from 'nthen';
+import type { Saferphore_t } from 'saferphore';
+export type Builder_Stage_t = (Builder_t, Nthen_WaitFor_t)=>void;
+export type Builder_CompileJob_t = { cFile: string, outputFile: ?string };
+export type Builder_PreCtx_t = {
+    buildStage: Builder_Stage_t,
+    testStage: Builder_Stage_t,
+    packStage: Builder_Stage_t,
+    failureStage: Builder_Stage_t,
+    successStage: Builder_Stage_t,
+    completeStage: Builder_Stage_t,
 
-// Since many of the compile operations are short, the best
-// performance seems to be when running 1.25x the number of jobs as
-// cpu cores. On BSD and iphone systems, os.cpus() is not reliable so
-// if it returns undefined let's just assume 1
-const cpus = Os.cpus(); // workaround, nodejs seems to be broken on openbsd (undefined result after second call)
-const PROCESSORS = Math.floor((typeof cpus === 'undefined' ? 1 : cpus.length) * 1.25);
+    failure: bool,
+    linters: Builder_Linter_t[],
+    executables: Array<Builder_CompileJob_t>,
+    tests: Array<(Builder_TestRunnerCb_t)=>void>,
+    toCompile: { [string]: Builder_File_t },
+    config: Builder_Config_t,
+    sema: Saferphore_t,
+};
+export type Builder_Ctx_t = Builder_PreCtx_t & {
+    builder: Builder_t,
+    state: Builder_State_t,
+};
+*/
 
 const error = function (message) /*:Error*/ {
     try {
@@ -57,12 +107,6 @@ const error = function (message) /*:Error*/ {
     }
 };
 
-const throwIfErr = function(err) {
-    if (err) {
-        throw new Error(err);
-    }
-};
-
 const expandArgs = function (args) {
     const out = [];
     for (let i = 0; i < args.length; i++) {
@@ -79,25 +123,24 @@ const expandArgs = function (args) {
     return out;
 };
 
-const sema = Semaphore.create(PROCESSORS);
 const compiler = function (
-    compilerPath /*:string*/,
+    ctx /*:Builder_Ctx_t*/,
     args /*:string[]*/,
     callback /*:(number, string, string)=>bool|void*/,
     content /*:string*/
 ) {
     let stop = false;
     args = expandArgs(args);
-    sema.take(function (returnAfter) {
+    ctx.sema.take(function (returnAfter) {
         if (stop) {
-            return returnAfter(function (ret) {
+            return void returnAfter(function (ret) {
                 callback(1, '', 'interrupted');
             });
         }
         if (process.env.VERBOSE) {
-            console.log(compilerPath + ' ' + args.join(' '));
+            console.log(ctx.config.gcc + ' ' + args.join(' '));
         }
-        const gcc = Spawn(compilerPath, args);
+        const gcc = Spawn(ctx.config.gcc, args);
         let err = '';
         let out = '';
 
@@ -109,10 +152,10 @@ const compiler = function (
 
         gcc.on('error', function (err) {
             if (err.code === 'ENOENT') {
-                console.error('\x1b[1;31mError: ' + compilerPath + ' is required!\x1b[0m');
+                console.error('\x1b[1;31mError: ' + ctx.config.gcc + ' is required!\x1b[0m');
             } else {
                 console.error(
-                    '\x1b[1;31mFail run ' + process.cwd() + ': ' + compilerPath + ' '
+                    '\x1b[1;31mFail run ' + process.cwd() + ': ' + ctx.config.gcc + ' '
                     + args.join(' ') + '\x1b[0m'
                 );
                 console.error('Message:' + err);
@@ -132,12 +175,12 @@ const compiler = function (
 };
 
 const cc = function (
-    gcc /*:string*/,
+    ctx /*:Builder_Ctx_t*/,
     args /*:string[]*/,
     callback /*:(?Error, ?string)=>bool|void*/,
     content /*:string*/
 ) {
-    compiler(gcc, args, function (ret, out, err) {
+    compiler(ctx, args, function (ret, out, err) {
         if (ret) {
             return callback(error("gcc " + args.map(String).join(' ') + "\n\n" + err));
         }
@@ -150,90 +193,6 @@ const cc = function (
     }, content);
 };
 
-/*::
-export type Builder_File_t = {|
-    includes: string[],
-    links: string[],
-    cflags: string[],
-    ldflags: string[],
-    mtime: number,
-|};
-export type Builder_Hfile_t = {|
-    mtime: number,
-|};
-export type Builder_Compiler_t = {|
-    isLLVM: bool,
-    isClang: bool,
-    isGCC: bool,
-    version: string
-|};
-export type Builder_State_t = {|
-    rebuildIfChanges: string[],
-    rebuildIfChangesHash: string,
-    compilerType: Builder_Compiler_t,
-    cFiles: { [string]: Builder_File_t },
-    hFiles: { [string]: Builder_Hfile_t },
-|};
-export type Builder_Linter_t = (string, string, (string, bool)=>void)=>void;
-export type Builder_TestRunnerCb_t = (string, bool)=>void;
-export type Builder_t = {|
-    cc: (string[], (number, string, string)=>void)=>void,
-    buildExecutable: (string, ?string)=>void,
-    buildTest: (string)=>string,
-    runTest: (string, (string, Builder_TestRunnerCb_t)=>void)=>void,
-    lintFiles: (Builder_Linter_t)=>void,
-    config: Builder_Config_t,
-    tmpFile: (?string)=>string,
-    compilerType: () => Builder_Compiler_t,
-    processors: number,
-|};
-export type Builder_BaseConfig_t = {|
-    systemName: string,
-    crossCompiling: bool,
-    gcc: string,
-    tempDir: string,
-    optimizeLevel: string,
-    logLevel: string,
-    buildDir?: string,
-|};
-export type Builder_Config_t = {
-    systemName: string,
-    crossCompiling: bool,
-    gcc: string,
-    buildDir: string,
-    tempDir: string,
-    optimizeLevel: string,
-    logLevel: string,
-    includeDirs: string[],
-    cflags: string[],
-    ldflags: string[],
-    libs: string[],
-    version: string,
-    fileCflags: {[string]: string[]},
-} & {[string]:any};
-import type { Nthen_WaitFor_t } from 'nthen';
-export type Builder_Stage_t = (Builder_t, Nthen_WaitFor_t)=>void;
-export type Builder_CompileJob_t = { cFile: string, outputFile: ?string };
-export type Builder_PreCtx_t = {
-    buildStage: Builder_Stage_t,
-    testStage: Builder_Stage_t,
-    packStage: Builder_Stage_t,
-    failureStage: Builder_Stage_t,
-    successStage: Builder_Stage_t,
-    completeStage: Builder_Stage_t,
-
-    failure: bool,
-    linters: Builder_Linter_t[],
-    executables: Array<Builder_CompileJob_t>,
-    tests: Array<(Builder_TestRunnerCb_t)=>void>,
-    toCompile: { [string]: Builder_File_t },
-    config: Builder_Config_t,
-};
-export type Builder_Ctx_t = Builder_PreCtx_t & {
-    builder: Builder_t,
-    state: Builder_State_t,
-};
-*/
 const getStatePrototype = function () /*:Builder_State_t*/ {
     return {
         rebuildIfChanges: [],
@@ -253,7 +212,7 @@ const getStatePrototype = function () /*:Builder_State_t*/ {
 
 const tmpFile = function (ctx /*:Builder_Ctx_t*/, name) {
     name = name || '';
-    return ctx.config.tempDir + '/jsmake-' + name + Crypto.pseudoRandomBytes(10).toString('hex');
+    return ctx.config.buildDir + '/tmp/' + name + Crypto.pseudoRandomBytes(10).toString('hex');
 };
 
 const finalizeCtx = function (
@@ -264,7 +223,7 @@ const finalizeCtx = function (
     ctx.state = state;
     ctx.builder = (Object.freeze({
         cc: function (args, callback) {
-            compiler(ctx.builder.config.gcc, args, callback, '');
+            compiler(ctx, args, callback, '');
         },
 
         buildExecutable: function (cFile, outputFile) {
@@ -293,8 +252,6 @@ const finalizeCtx = function (
 
         compilerType: () => JSON.parse(JSON.stringify(ctx.state.compilerType)),
 
-        // Concurrency...
-        processors: PROCESSORS
     }) /*:Builder_t*/);
     return ctx;
 };
@@ -500,7 +457,7 @@ const preprocessFile = function (cFile, ctx, callback)
 
     nThen((w) => {
         //debug("CPP");
-        cc(ctx.config.gcc, ['-E', ...cflags, cFile], w(function (err, output) {
+        cc(ctx, ['-E', ...cflags, cFile], w(function (err, output) {
             if (err) { throw err; }
             fileContent = output;
             return false;
@@ -635,25 +592,12 @@ const makeTime = function () {
 
 const link = function (cFile, callback, ctx /*:Builder_Ctx_t*/) {
     const state = ctx.state;
-    let tempDir;
-
     const temp = getTempExe(ctx, cFile);
-
-    nThen(function (waitFor) {
-
-        tempDir = tmpFile(ctx);
-        Fs.mkdir(tempDir, {}, waitFor(function (err) {
-            if (err) { throw err; }
-        }));
-        // TODO delete all other tmp files
-
-    }).nThen(function (waitFor) {
-
+    nThen((waitFor) => {
         const linkOrder = getLinkOrder(cFile, state.cFiles);
         for (let i = 0; i < linkOrder.length; i++) {
             linkOrder[i] = getOFile(ctx, linkOrder[i]);
         }
-
         const fileObj = state.cFiles[cFile];
         const ldArgs = []
             .concat(ctx.config.ldflags)
@@ -662,37 +606,11 @@ const link = function (cFile, callback, ctx /*:Builder_Ctx_t*/) {
             .concat(linkOrder)
             .concat(ctx.config.libs);
         debug('\x1b[1;31mLinking C executable ' + cFile + '\x1b[0m');
-
-        cc(ctx.config.gcc, ldArgs, waitFor(function (err, ret) {
+        cc(ctx, ldArgs, waitFor(function (err, ret) {
             if (err) { throw err; }
             return false;
         }), '');
-
-    }).nThen(function (waitFor) {
-
-        Fs.readdir(tempDir, waitFor(function (err, files) {
-            if (err) { throw err; }
-
-            files.forEach(function (file) {
-                Fs.unlink(tempDir + '/' + file, waitFor(function (err) {
-                    if (err) { throw err; }
-                }));
-            });
-        }));
-
-    }).nThen(function (waitFor) {
-
-        Fs.rmdir(tempDir, waitFor(function (err) {
-            if (err) { throw err; }
-        }));
-
-    }).nThen(function (waitFor) {
-
-        if (callback) {
-            callback();
-        }
-
-    });
+    }).nThen((_) => callback());
 };
 
 const compile = function (ctx, cFile, done) {
@@ -700,7 +618,7 @@ const compile = function (ctx, cFile, done) {
     const file = ctx.state.cFiles[cFile];
     const oFile = getOFile(ctx, cFile);
     const iFile = getIFile(ctx, cFile);
-    cc(ctx.config.gcc, ['-c', '-x', 'cpp-output', '-o', oFile, ...file.cflags, iFile], (err) => {
+    cc(ctx, ['-c', '-x', 'cpp-output', '-o', oFile, ...file.cflags, iFile], (err) => {
         done(err);
         return typeof(err) !== 'undefined';
     }, '');
@@ -753,7 +671,7 @@ const probeCompiler = function (ctx /*:Builder_Ctx_t*/, callback) {
             isGCC: false,
             version: ''
         };
-        compiler(ctx.config.gcc, ['-v'], waitFor(function (ret, out, err) {
+        compiler(ctx, ['-v'], waitFor(function (ret, out, err) {
             // TODO(cjd): afl-clang-fast errors when called with -v
             //if (ret !== 0) { throw new Error("Failed to probe compiler ret[" + ret + "]\n" + err); }
             if (/Apple LLVM version /.test(err)) {
@@ -788,7 +706,7 @@ const probeCompiler = function (ctx /*:Builder_Ctx_t*/, callback) {
                 compilerType.isGCC = true;
                 compilerType.version = err.match(/gcc version ([^ ]+) /)[1];
             }
-            console.log(JSON.stringify(compilerType));
+            //console.log(JSON.stringify(compilerType));
         }), '');
     }).nThen(callback);
 };
@@ -804,6 +722,34 @@ const deepFreeze = (obj) => {
     }
 };
 
+const sweep = (path, done) => {
+    let files = [];
+    nThen((w) => {
+        Fs.readdir(path, w((err, fls) => {
+            if (err) { throw err; }
+            files = fls;
+        }));
+    }).nThen((w) => {
+        files.forEach((f) => {
+            const file = path + '/' + f;
+            Fs.stat(file, w((err, st) => {
+                if (err) { throw err; }
+                if (st.isDirectory()) {
+                    sweep(file, w(() => {
+                        Fs.rmdir(file, w((err) => {
+                            if (err) { throw err; }
+                        }));
+                    }));
+                } else {
+                    Fs.unlink(file, w((err) => {
+                        if (err) { throw err; }
+                    }));
+                }
+            }));
+        });
+    }).nThen((_) => done());
+};
+
 module.exports.configure = function (
     params /*:Builder_BaseConfig_t*/,
     configFunc /*:(Builder_t, Nthen_WaitFor_t)=>void*/
@@ -813,11 +759,27 @@ module.exports.configure = function (
     const time = makeTime();
     time();
 
-    if (typeof(params.systemName) !== 'string') {
-        throw new Error("system not specified");
+    const systemName = params.systemName || process.platform;
+    const buildDir = params.buildDir || 'build_' + systemName;
+
+    let gcc;
+    if (params.gcc) {
+        gcc = params.gcc;
+    } else if (systemName === 'openbsd') {
+        gcc = 'egcc';
+    } else if (systemName === 'freebsd') {
+        gcc = 'clang';
+    } else {
+        gcc = 'gcc';
     }
 
-    const buildDir = params.buildDir = params.buildDir || 'build_' + params.systemName;
+    // Since many of the compile operations are short, the best
+    // performance seems to be when running 1.25x the number of jobs as
+    // cpu cores. On BSD and iphone systems, os.cpus() is not reliable so
+    // if it returns undefined let's just assume 1
+    // workaround, nodejs seems to be broken on openbsd (undefined result after second call)
+    const cpus = Os.cpus();
+    const jobs = Math.floor((typeof cpus === 'undefined' ? 1 : cpus.length) * 1.25);
 
     const pctx /*:Builder_PreCtx_t*/ = {
         buildStage: (_x,_y)=>{},
@@ -832,15 +794,12 @@ module.exports.configure = function (
         executables: [],
         tests: [],
         toCompile: {},
+        sema: Saferphore.create(1),
 
         config: {
-            crossCompiling: params.crossCompiling,
             buildDir,
-            gcc: params.gcc,
-            logLevel: params.logLevel,
-            optimizeLevel: params.optimizeLevel,
-            systemName: params.systemName,
-            tempDir: params.tempDir,
+            gcc,
+            systemName,
 
             version: '',
             includeDirs: ['.'],
@@ -848,6 +807,7 @@ module.exports.configure = function (
             ldflags: [],
             libs: [],
             fileCflags: {},
+            jobs,
         },
     };
     let state = getStatePrototype();
@@ -865,6 +825,18 @@ module.exports.configure = function (
             }));
         }));
 
+    }).nThen(function (waitFor) {
+
+        Fs.exists(buildDir + '/tmp', waitFor(function (exists) {
+            if (exists) {
+                sweep(buildDir + '/tmp', waitFor());
+            } else {
+                Fs.mkdir(buildDir + '/tmp', {}, waitFor(function (err) {
+                    if (err) { throw err; }
+                }));
+            }
+        }));
+
     }).nThen(function (waitFor) {
 
         // read out the state if it exists
@@ -905,19 +877,21 @@ module.exports.configure = function (
         //if (!ctx.builder) { throw new Error(); }
         configFunc(ctx.builder, waitFor);
 
-    }).nThen(function (waitFor) {
+    }).nThen(function (_) {
+        ctx.sema = Saferphore.create(ctx.config.jobs);
 
-        if (!ctx.config.version) {
-            GetVersion(waitFor(function(err, data) {
-                if (err === null) {
-                    ctx.config.version = ('' + data).replace(/(\r\n|\n|\r)/gm, "");
-                } else {
-                    ctx.config.version = 'unknown';
-                }
-            }));
+        if (ctx.config.systemName !== systemName) {
+            throw new Error("systemName cannot be changed in configure phase " +
+                "it must be specified in the initial configuration " +
+                `initial systemName = ${systemName}, changed to ${ctx.config.systemName}`);
+        }
+
+        if (ctx.config.gcc !== gcc) {
+            throw new Error("gcc cannot be changed in configure phase " +
+                "it must be specified in the initial configuration " +
+                `initial gcc = ${gcc}, changed to ${ctx.config.gcc}`);
         }
 
-    }).nThen(function (_) {
         deepFreeze(ctx.config);
 
         debug("Configure " + time() + "ms");
@@ -1070,7 +1044,6 @@ const postConfigure = (ctx /*:Builder_Ctx_t*/, time) => {
             deepFreeze(state);
         }));
     }).nThen(function (w) {
-
         Object.keys(ctx.toCompile).forEach((cFile) => {
             compile(ctx, cFile, w((err) => {
                 if (err) {
@@ -1105,7 +1078,7 @@ const postConfigure = (ctx /*:Builder_Ctx_t*/, time) => {
 
         debug("Checking codestyle");
 
-        const sema = Semaphore.create(64);
+        const sema = Saferphore.create(64);
 
         Object.keys(ctx.toCompile).forEach(function (cFile) {
             sema.take(waitFor(function (returnAfter) {
@@ -1177,5 +1150,6 @@ const postConfigure = (ctx /*:Builder_Ctx_t*/, time) => {
 
         ctx.completeStage(ctx.builder, waitFor);
 
+        sweep(ctx.config.buildDir + '/tmp', waitFor());
     });
 };

+ 29 - 53
node_build/make.js

@@ -19,40 +19,24 @@ var nThen = require('nthen');
 var Codestyle = require('./Codestyle');
 var Cp = require('./Cp');
 var Spawn = require('child_process').spawn;
-var Os = require('os');
 var FindPython = require('./FindPython');
-var CanCompile = require('./CanCompile');
 var Builder = require('./builder');
 var TestRunner = require('./TestRunner');
 const CjdnsTest = require('./CjdnsTest');
+const GetVersion = require('./GetVersion');
 
-// ['linux','darwin','sunos','win32','freebsd','openbsd','netbsd']
-var SYSTEM = process.env['SYSTEM'] || process.platform;
-var GCC = process.env['CC'];
 var CFLAGS = process.env['CFLAGS'];
 var LDFLAGS = process.env['LDFLAGS'];
-
 var NO_MARCH_FLAG = ['arm', 'ppc', 'ppc64'];
 
-if (GCC) {
-    // Already specified.
-} else if (SYSTEM === 'openbsd') {
-    GCC = 'egcc';
-} else if (SYSTEM === 'freebsd') {
-    GCC = 'clang';
-} else {
-    GCC = 'gcc';
-}
-
 Builder.configure({
-    systemName:     SYSTEM,
-    crossCompiling: process.env['CROSS'] !== undefined,
-    gcc:            GCC,
-    tempDir:        process.env['CJDNS_BUILD_TMPDIR'] || '/tmp',
-    optimizeLevel:  '-O3',
-    logLevel:       process.env['Log_LEVEL'] || 'DEBUG'
+    systemName:     process.env['SYSTEM'] || process.platform,
+    gcc:            process.env['CC'],
 }, function (builder, waitFor) {
 
+    builder.config.crossCompiling = process.env['CROSS'] !== undefined;
+    let optimizeLevel = '-O3';
+
     builder.config.cflags.push(
         '-std=c99',
         '-Wall',
@@ -62,11 +46,10 @@ Builder.configure({
         '-Wmissing-prototypes',
         '-pedantic',
         '-D', builder.config.systemName + '=1',
-        '-D', 'CJD_PACKAGE_VERSION="' + builder.config.version + '"',
         '-Wno-unused-parameter',
         '-fomit-frame-pointer',
 
-        '-D', 'Log_' + builder.config.logLevel,
+        '-D', 'Log_' + (process.env['Log_LEVEL'] || 'DEBUG'),
 
         '-g',
 
@@ -164,18 +147,17 @@ Builder.configure({
         var cflags = CFLAGS.split(' ');
         cflags.forEach(function(flag) {
              if (/^\-O[^02s]$/.test(flag)) {
-                console.log("Skipping " + flag + ", assuming " +
-                            builder.config.optimizeLevel + " instead.");
+                console.log("Skipping " + flag + ", assuming " + optimizeLevel + " instead.");
             } else if (/^\-O[02s]$/.test(flag)) {
-                builder.config.optimizeLevel = flag;
+                optimizeLevel = flag;
             } else {
                 [].push.apply(builder.config.cflags, cflags);
             }
         });
     }
 
-    builder.config.cflags.push(builder.config.optimizeLevel);
-    if (!/^\-O0$/.test(builder.config.optimizeLevel)) {
+    builder.config.cflags.push(optimizeLevel);
+    if (!/^\-O0$/.test(optimizeLevel)) {
         builder.config.cflags.push('-D_FORTIFY_SOURCE=2');
     }
 
@@ -189,25 +171,6 @@ Builder.configure({
         builder.config.cflags.push('-Dandroid=1');
     }
 
-    if (process.env.NO_LTO) {
-        console.log("Link time optimization disabled");
-    } else {
-        CanCompile.check(builder,
-                        'int main() { return 0; }\n',
-                        [ builder.config.cflags, '-flto', '-x', 'c' ],
-                        waitFor(function (err, can) {
-            if (can) {
-                console.log("Compiler supports link time optimization");
-                builder.config.ldflags.push(
-                    '-flto',
-                    builder.config.optimizeLevel
-                );
-            } else {
-                console.log("Link time optimization not supported [" + err + "]");
-            }
-        }));
-    }
-
     var uclibc = process.env['UCLIBC'] == '1';
     var libssp;
     switch (process.env['SSP_SUPPORT']) {
@@ -257,6 +220,19 @@ Builder.configure({
         CjdnsTest.generate(builder, process.env['SUBNODE'] !== '', waitFor());
     }
 
+    nThen((w) => {
+        if (builder.config.version) { return; }
+        GetVersion(w(function(err, data) {
+            if (!err) {
+                builder.config.version = ('' + data).replace(/(\r\n|\n|\r)/gm, "");
+            } else {
+                builder.config.version = 'unknown';
+            }
+        }));
+    }).nThen((w) => {
+        builder.config.cflags.push('-D', 'CJD_PACKAGE_VERSION="' + builder.config.version + '"');
+    }).nThen(waitFor());
+
     var dependencyDir = builder.config.buildDir + '/dependencies';
     var libuvLib = dependencyDir + '/libuv/out/Release/libuv.a';
     if (['win32', 'netbsd'].indexOf(builder.config.systemName) >= 0) {//this might be needed for other BSDs
@@ -295,9 +271,9 @@ Builder.configure({
                     args.unshift('-fPIC');
                 }
 
-                args.unshift(builder.config.optimizeLevel, '-fomit-frame-pointer');
+                args.unshift(optimizeLevel, '-fomit-frame-pointer');
 
-                if (!/^\-O0$/.test(builder.config.optimizeLevel)) {
+                if (!/^\-O0$/.test(optimizeLevel)) {
                     args.unshift('-D_FORTIFY_SOURCE=2');
                 }
 
@@ -408,16 +384,16 @@ Builder.configure({
             });
             gyp.on('close', waitFor(function () {
                 var args = [
-                    '-j', ''+builder.processors,
+                    '-j', ''+builder.config.jobs,
                     '-C', 'out',
                     'BUILDTYPE=Release',
                     'CC=' + builder.config.gcc,
                     'CXX=' + builder.config.gcc,
                     'V=1'
                 ];
-                var cflags = [builder.config.optimizeLevel, '-DNO_EMFILE_TRICK=1'];
+                var cflags = [optimizeLevel, '-DNO_EMFILE_TRICK=1'];
 
-                if (!/^\-O0$/.test(builder.config.optimizeLevel)) {
+                if (!/^\-O0$/.test(optimizeLevel)) {
                     cflags.push('-D_FORTIFY_SOURCE=2');
                 }
 

+ 7 - 5
node_modules/nthen/package.json

@@ -1,4 +1,10 @@
 {
+  "_args": [
+    [
+      "nthen@0.2.1",
+      "/Users/user/wrk/cjdns"
+    ]
+  ],
   "_from": "nthen@0.2.1",
   "_id": "nthen@0.2.1",
   "_inBundle": false,
@@ -16,12 +22,10 @@
     "fetchSpec": "0.2.1"
   },
   "_requiredBy": [
-    "#USER",
     "/"
   ],
   "_resolved": "https://registry.npmjs.org/nthen/-/nthen-0.2.1.tgz",
-  "_shasum": "cf665cada4afd773f2443d77c5d77e79a965531f",
-  "_spec": "nthen@0.2.1",
+  "_spec": "0.2.1",
   "_where": "/Users/user/wrk/cjdns",
   "author": {
     "name": "Caleb James DeLisle"
@@ -29,8 +33,6 @@
   "bugs": {
     "url": "https://github.com/cjdelisle/nthen/issues"
   },
-  "bundleDependencies": false,
-  "deprecated": false,
   "description": "Simple intuitive asynchronous control flow.",
   "devDependencies": {
     "flow-bin": "^0.106.2",

+ 7 - 0
node_modules/saferphore/.flowconfig

@@ -0,0 +1,7 @@
+[ignore]
+
+[include]
+
+[libs]
+
+[options]

+ 1 - 0
node_modules/saferphore/.jshintignore

@@ -0,0 +1 @@
+node_modules/

+ 16 - 0
node_modules/saferphore/.jshintrc

@@ -0,0 +1,16 @@
+{
+    "laxcomma": true,
+    "laxbreak": true,
+    "node": true,
+    "browser": true,
+    "sub": true,
+    "curly": true,
+    "eqeqeq": true,
+    "iterator": true,
+    "latedef": true,
+    "nocomma": true,
+    "notypeof": true,
+    "shadow": false,
+    "undef": true,
+    "unused": false
+}

+ 12 - 0
node_modules/saferphore/.travis.yml

@@ -0,0 +1,12 @@
+language: node_js
+
+node_js:
+  - '6'
+  - '5'
+  - '4'
+  - '0.12'
+
+script:
+  - npm run-script test
+  - npm run-script lint
+  - npm run-script flow

+ 50 - 0
node_modules/saferphore/index.js

@@ -0,0 +1,50 @@
+/*@flow*/
+/*::
+export type Saferphore_ReturnAfter_t = ((...any)=>void)=>(...any)=>void;
+export type Saferphore_t = {
+    take: ((Saferphore_ReturnAfter_t)=>void)=>void,
+};
+*/
+;(function () {
+"use strict";
+var create = function (resourceCount /*:number*/) /*:Saferphore_t*/ {
+    var queue = [];
+    var check;
+    var mkRa = function () /*:Saferphore_ReturnAfter_t*/ {
+        var outerCalled = 0;
+        return function (func) {
+            if (outerCalled++) { throw new Error("returnAfter() called multiple times"); }
+            var called = 0;
+            return function () {
+                if (called++) {
+                    throw new Error("returnAfter wrapped callback called multiple times");
+                }
+                if (func) { func.apply(null, arguments); }
+                resourceCount++;
+                check();
+            };
+        };
+    };
+    check = function () {
+        if (resourceCount < 0) { throw new Error("(resourceCount < 0) should never happen"); }
+        if (resourceCount === 0 || queue.length === 0) { return; }
+        resourceCount--;
+        queue.shift()(mkRa());
+    };
+    return {
+        take: function (func) {
+            queue.push(func);
+            check();
+        }
+    };
+};
+if (typeof(window) === 'object') {
+    if (window.define && window.define.amd) {
+        window.define({ create: create });
+    } else {
+        window.Saferphore = { create: create };
+    }
+} else if (typeof(module) === 'object' && module.exports) {
+    module.exports.create = create;
+}
+}());

+ 51 - 0
node_modules/saferphore/package.json

@@ -0,0 +1,51 @@
+{
+  "_args": [
+    [
+      "saferphore@0.0.2",
+      "/Users/user/wrk/cjdns"
+    ]
+  ],
+  "_from": "saferphore@0.0.2",
+  "_id": "saferphore@0.0.2",
+  "_inBundle": false,
+  "_integrity": "sha512-gjdCGXxrSKi1oSWJH0vx99WtB2wkjbXPQf6ALHEskLo0e4Vgd57txhracaeLlPt2r4rUiJBe+thriuxS85q7JA==",
+  "_location": "/saferphore",
+  "_phantomChildren": {},
+  "_requested": {
+    "type": "version",
+    "registry": true,
+    "raw": "saferphore@0.0.2",
+    "name": "saferphore",
+    "escapedName": "saferphore",
+    "rawSpec": "0.0.2",
+    "saveSpec": null,
+    "fetchSpec": "0.0.2"
+  },
+  "_requiredBy": [
+    "/"
+  ],
+  "_resolved": "https://registry.npmjs.org/saferphore/-/saferphore-0.0.2.tgz",
+  "_spec": "0.0.2",
+  "_where": "/Users/user/wrk/cjdns",
+  "bugs": {
+    "url": "https://github.com/cjdelisle/saferphore/issues"
+  },
+  "description": "Node Semaphore which has protection against double-returning",
+  "devDependencies": {
+    "flow-bin": "^0.115.0",
+    "jshint": "~2.9.1"
+  },
+  "homepage": "https://github.com/cjdelisle/saferphore#readme",
+  "license": "MIT",
+  "name": "saferphore",
+  "repository": {
+    "type": "git",
+    "url": "git+https://github.com/cjdelisle/saferphore.git"
+  },
+  "scripts": {
+    "flow": "flow",
+    "lint": "jshint --verbose --config .jshintrc --exclude-path .jshintignore .",
+    "test": "node test"
+  },
+  "version": "0.0.2"
+}

+ 30 - 0
node_modules/saferphore/readme.md

@@ -0,0 +1,30 @@
+# Saferphore - Node semaphore with protection against double-returning
+
+    # node
+    npm install --save saferphore
+
+    # Browser
+    bower install --save saferphore
+
+
+```javascript
+const Saferphore = require('saferphore');
+
+var sem = Saferphore.create(4);
+for (var i = 0; i < 10000; i++) {
+    sem.take(function (returnAfter) {
+        Fs.writeFile('file_' + i, 'hi', returnAfter(function (err) {
+            if (err) { throw err; }
+        });
+    });
+}
+```
+
+You can only return what you take, if you try to call returnAfter() twice then it will throw a
+clear error instead of creating a leaky semaphore.
+
+```javascript
+sem.take(function (returnAfter) {
+    stream.on('data', returnAfter(processData)); // BZZZZZZZT error when it's called more than once
+});
+```

+ 54 - 0
node_modules/saferphore/test.js

@@ -0,0 +1,54 @@
+'use strict';
+var Saferphore = require('./index');
+var assert = function (x) { if (!x) { throw new Error(); } };
+
+var basicTest = function () {
+    var sem = Saferphore.create(4);
+    var out = [];
+    (new Array(10)).join().split(',').forEach(function (x,y) {
+        sem.take(function (returnAfter) {
+            out.push(y);
+            setTimeout(returnAfter(function () {
+                if (out.length === 10) {
+                    if (out.join() !== "0,1,2,3,4,5,6,7,8,9") { throw new Error(); }
+                }
+            }));
+        });
+    });
+    if (out.join() !== "0,1,2,3") {
+        throw new Error(out.join());
+    }
+};
+
+var doubleCallTest = function () {
+    var sem = Saferphore.create(4);
+    var catcher = setTimeout(function () {
+        throw new Error();
+    }, 1000);
+    sem.take(function (returnAfter) {
+        setTimeout(returnAfter(function () {
+            setTimeout(function () {
+                clearTimeout(catcher);
+                try { returnAfter()(); } catch (e) { return; }
+                throw new Error();
+            });
+        }));
+    });
+};
+
+var doubleCallbackTest = function () {
+    var sem = Saferphore.create(4);
+    var catcher = setTimeout(function () {
+        throw new Error();
+    }, 1000);
+    sem.take(function (returnAfter) {
+        var wrapped = returnAfter();
+        wrapped();
+        try { wrapped(); } catch (e) { clearTimeout(catcher); return; }
+        throw new Error();
+    });
+};
+
+basicTest();
+doubleCallTest();
+doubleCallbackTest();

+ 5 - 220
package-lock.json

@@ -1,233 +1,18 @@
 {
     "name": "cjdns",
-    "version": "0.17.2",
+    "version": "0.17.3",
     "lockfileVersion": 1,
     "requires": true,
     "dependencies": {
-        "balanced-match": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
-            "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
-        },
-        "brace-expansion": {
-            "version": "1.1.11",
-            "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
-            "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-            "requires": {
-                "balanced-match": "^1.0.0",
-                "concat-map": "0.0.1"
-            }
-        },
-        "cli": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/cli/-/cli-1.0.1.tgz",
-            "integrity": "sha1-IoF1NPJL+klQw01TLUjsvGIbjBQ=",
-            "requires": {
-                "exit": "0.1.2",
-                "glob": "^7.1.1"
-            }
-        },
-        "concat-map": {
-            "version": "0.0.1",
-            "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-            "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
-        },
-        "console-browserify": {
-            "version": "1.1.0",
-            "resolved": "https://registry.npmjs.org/console-browserify/-/console-browserify-1.1.0.tgz",
-            "integrity": "sha1-8CQcRXMKn8YyOyBtvzjtx0HQuxA=",
-            "requires": {
-                "date-now": "^0.1.4"
-            }
-        },
-        "core-util-is": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz",
-            "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac="
-        },
-        "date-now": {
-            "version": "0.1.4",
-            "resolved": "https://registry.npmjs.org/date-now/-/date-now-0.1.4.tgz",
-            "integrity": "sha1-6vQ5/U1ISK105cx9vvIAZyueNFs="
-        },
-        "dom-serializer": {
-            "version": "0.2.2",
-            "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-0.2.2.tgz",
-            "integrity": "sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==",
-            "requires": {
-                "domelementtype": "^2.0.1",
-                "entities": "^2.0.0"
-            },
-            "dependencies": {
-                "domelementtype": {
-                    "version": "2.0.2",
-                    "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.0.2.tgz",
-                    "integrity": "sha512-wFwTwCVebUrMgGeAwRL/NhZtHAUyT9n9yg4IMDwf10+6iCMxSkVq9MGCVEH+QZWo1nNidy8kNvwmv4zWHDTqvA=="
-                },
-                "entities": {
-                    "version": "2.0.3",
-                    "resolved": "https://registry.npmjs.org/entities/-/entities-2.0.3.tgz",
-                    "integrity": "sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ=="
-                }
-            }
-        },
-        "domelementtype": {
-            "version": "1.3.1",
-            "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-1.3.1.tgz",
-            "integrity": "sha512-BSKB+TSpMpFI/HOxCNr1O8aMOTZ8hT3pM3GQ0w/mWRmkhEDSFJkkyzz4XQsBV44BChwGkrDfMyjVD0eA2aFV3w=="
-        },
-        "domhandler": {
-            "version": "2.3.0",
-            "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-2.3.0.tgz",
-            "integrity": "sha1-LeWaCCLVAn+r/28DLCsloqir5zg=",
-            "requires": {
-                "domelementtype": "1"
-            }
-        },
-        "domutils": {
-            "version": "1.5.1",
-            "resolved": "https://registry.npmjs.org/domutils/-/domutils-1.5.1.tgz",
-            "integrity": "sha1-3NhIiib1Y9YQeeSMn3t+Mjc2gs8=",
-            "requires": {
-                "dom-serializer": "0",
-                "domelementtype": "1"
-            }
-        },
-        "entities": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/entities/-/entities-1.0.0.tgz",
-            "integrity": "sha1-sph6o4ITR/zeZCsk/fyeT7cSvyY="
-        },
-        "exit": {
-            "version": "0.1.2",
-            "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
-            "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw="
-        },
-        "fs.realpath": {
-            "version": "1.0.0",
-            "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-            "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
-        },
-        "glob": {
-            "version": "7.1.6",
-            "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz",
-            "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==",
-            "requires": {
-                "fs.realpath": "^1.0.0",
-                "inflight": "^1.0.4",
-                "inherits": "2",
-                "minimatch": "^3.0.4",
-                "once": "^1.3.0",
-                "path-is-absolute": "^1.0.0"
-            }
-        },
-        "htmlparser2": {
-            "version": "3.8.3",
-            "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-3.8.3.tgz",
-            "integrity": "sha1-mWwosZFRaovoZQGn15dX5ccMEGg=",
-            "requires": {
-                "domelementtype": "1",
-                "domhandler": "2.3",
-                "domutils": "1.5",
-                "entities": "1.0",
-                "readable-stream": "1.1"
-            }
-        },
-        "inflight": {
-            "version": "1.0.6",
-            "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
-            "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
-            "requires": {
-                "once": "^1.3.0",
-                "wrappy": "1"
-            }
-        },
-        "inherits": {
-            "version": "2.0.4",
-            "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-            "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
-        },
-        "isarray": {
-            "version": "0.0.1",
-            "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
-            "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
-        },
-        "jshint": {
-            "version": "2.12.0",
-            "resolved": "https://registry.npmjs.org/jshint/-/jshint-2.12.0.tgz",
-            "integrity": "sha512-TwuuaUDmra0JMkuqvqy+WGo2xGHSNjv1BA1nTIgtH2K5z1jHuAEeAgp7laaR+hLRmajRjcrM71+vByBDanCyYA==",
-            "requires": {
-                "cli": "~1.0.0",
-                "console-browserify": "1.1.x",
-                "exit": "0.1.x",
-                "htmlparser2": "3.8.x",
-                "lodash": "~4.17.19",
-                "minimatch": "~3.0.2",
-                "shelljs": "0.3.x",
-                "strip-json-comments": "1.0.x"
-            }
-        },
-        "lodash": {
-            "version": "4.17.20",
-            "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz",
-            "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA=="
-        },
-        "minimatch": {
-            "version": "3.0.4",
-            "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz",
-            "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==",
-            "requires": {
-                "brace-expansion": "^1.1.7"
-            }
-        },
         "nthen": {
             "version": "0.2.1",
             "resolved": "https://registry.npmjs.org/nthen/-/nthen-0.2.1.tgz",
             "integrity": "sha512-QW3p6gxOaHHLFM+2HcytugGUiji4pNpLkNAtPQi2gX2+hSmndnNSDYbDSUdIEy7vGJ+LkocZ3EvgwxeKfToZGg=="
         },
-        "once": {
-            "version": "1.4.0",
-            "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
-            "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=",
-            "requires": {
-                "wrappy": "1"
-            }
-        },
-        "path-is-absolute": {
-            "version": "1.0.1",
-            "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
-            "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18="
-        },
-        "readable-stream": {
-            "version": "1.1.14",
-            "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz",
-            "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=",
-            "requires": {
-                "core-util-is": "~1.0.0",
-                "inherits": "~2.0.1",
-                "isarray": "0.0.1",
-                "string_decoder": "~0.10.x"
-            }
-        },
-        "shelljs": {
-            "version": "0.3.0",
-            "resolved": "https://registry.npmjs.org/shelljs/-/shelljs-0.3.0.tgz",
-            "integrity": "sha1-NZbmMHp4FUT1kfN9phg2DzHbV7E="
-        },
-        "string_decoder": {
-            "version": "0.10.31",
-            "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz",
-            "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ="
-        },
-        "strip-json-comments": {
-            "version": "1.0.4",
-            "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-1.0.4.tgz",
-            "integrity": "sha1-HhX7ysl9Pumb8tc7TGVrCCu6+5E="
-        },
-        "wrappy": {
-            "version": "1.0.2",
-            "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
-            "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8="
+        "saferphore": {
+            "version": "0.0.2",
+            "resolved": "https://registry.npmjs.org/saferphore/-/saferphore-0.0.2.tgz",
+            "integrity": "sha512-gjdCGXxrSKi1oSWJH0vx99WtB2wkjbXPQf6ALHEskLo0e4Vgd57txhracaeLlPt2r4rUiJBe+thriuxS85q7JA=="
         }
     }
 }

+ 3 - 3
package.json

@@ -1,8 +1,8 @@
 {
     "name": "cjdns",
-    "version": "0.17.2",
+    "version": "0.17.3",
     "dependencies": {
-        "jshint": "^2.12.0",
-        "nthen": "^0.2.1"
+        "nthen": "^0.2.1",
+        "saferphore": "0.0.2"
     }
 }

+ 48 - 21
tools/lib/Semaphore.js

@@ -1,24 +1,51 @@
-module.exports.create = function (resourceCount) {
-    var queue = [];
-    var returnAfter = function (func) {
-        var called = 0;
-        return function () {
-            if (called++) { throw new Error("Function called multiple times"); }
-            if (func) { func.apply(null, arguments); }
-            resourceCount++;
-            check();
+/*@flow*/
+/*::
+export type Saferphore_ReturnAfter_t = ((...any)=>void)=>(...any)=>void;
+export type Saferphore_t = {
+    take: ((Saferphore_ReturnAfter_t)=>void)=>void,
+};
+*/
+;(function () {
+    "use strict";
+    var create = function (resourceCount /*:number*/) /*:Saferphore_t*/ {
+        var queue = [];
+        var check;
+        var mkRa = function () /*:Saferphore_ReturnAfter_t*/ {
+            var outerCalled = 0;
+            return function (func) {
+                if (outerCalled++) { throw new Error("returnAfter() called multiple times"); }
+                var called = 0;
+                return function () {
+                    if (called++) {
+                        throw new Error("returnAfter wrapped callback called multiple times");
+                    }
+                    if (func) { func.apply(null, arguments); }
+                    resourceCount++;
+                    check();
+                };
+            };
+        };
+        check = function () {
+            if (resourceCount < 0) { throw new Error("(resourceCount < 0) should never happen"); }
+            if (resourceCount === 0 || queue.length === 0) { return; }
+            resourceCount--;
+            queue.shift()(mkRa());
+        };
+        return {
+            take: function (func) {
+                queue.push(func);
+                check();
+            }
         };
     };
-    var check = function () {
-        if (resourceCount < 0) { throw new Error("(resourceCount < 0) should never happen"); }
-        if (resourceCount === 0 || queue.length === 0) { return; }
-        resourceCount--;
-        queue.shift()(returnAfter);
-    };
-    return {
-        take: function (func) {
-            queue.push(func);
-            check();
+    if (typeof(window) === 'object') {
+        if (window.define && window.define.amd) {
+            window.define({ create: create });
+        } else {
+            window.Saferphore = { create: create };
         }
-    };
-};
+    } else if (typeof(module) === 'object' && module.exports) {
+        module.exports.create = create;
+    }
+    }());
+    

+ 1 - 1
tools/lib/cjdnsadmin/cjdnsadmin.js

@@ -18,7 +18,7 @@ var Bencode = require('./bencode');
 var Crypto = require('crypto');
 var Fs = require('fs');
 var nThen = require('nthen');
-var Semaphore = require('../Semaphore.js');
+var Semaphore = require('saferphore');
 
 var TIMEOUT_MILLISECONDS = 10000;