3
votes

I made a small app with node webkit. Pre-packaging, it works fine. But after I zipped it and added it to Contents/Resources in node-webkit.app I get an error when I run the app. It opens fine, but the task it does involves child processes, and I get this error:

Uncaught node.js Error Error: spawn ENOENT.

I'm guessing it might be something related to the issue raised in this question: Node-Webkit Child Process Exec

because my child processes are calling pdftk, a separate command line program. Ultimately, I'd love to install pdftk as part of my app - I have not been able to figure out how to do this. I tried including it as one of the things to be zipped with the rest of the app, but that caused the app to crash immediately after launch (it would open a window with the correct title but no contents, which would immediately close).

So, main question is, how do I install pdftk as part of a packaged node-webkit app, so that the app can be launched simply by double clicking the icon rather than using the command line? Thanks for any help.

2
Perhaps showing the piece of code where you execute the command would help. And to answer the last question, if pdftk can run as a standalone, then you only need to keep the entire application in the package and then execute its relative path. - E_net4
Sure, the code executing that command is: pdftk = spawn('pdftk', [inputFile, 'cat', extract, 'output', outputFile, 'dont_ask']); pdftk.on('exit', function (code) { ... } }); Putting the entire application in the package causes the app to not even launch - it crashes immediately. I assume I must be doing something wrong in the packaging step, but can't figure out what it is. - lpappone
Well, try executing the packaged version of pdftk on a command line. This should be enough to know whether the problem is in the Node.js application or this other piece of software. Also, what platform are you in? - E_net4
I'm in OSX. I haven't been able to execute the packaged version of pdftk from the command line - using the whole path to its location within the package I get an error that it's not a directory. (Trying to do the exact same thing with a copy of pdftk that's not inside a zipped and packaged file works fine, however.) - lpappone
please show the command you're using from the command line to try to start it. Sounds like a path problem. - bonez

2 Answers

4
votes

I am assuming your code in question is executed via the node-main entry point of node-webkit: https://github.com/rogerwang/node-webkit/wiki/Node-main

If any exception happens (there) which is not catched in your application will crash.

Sadly at the moment the breakpad feature for getting crashdumps is not working on OSX: https://github.com/rogerwang/node-webkit/issues/2518

How to prevent Node-Webkit from crashing immediately

Wrap the code in try/catches to prevent the crash and get information why the crash occurs.

try {

    the_child_process = child_process.spawn(pathToBin, args);

} catch (err) {

    global.console.log( "Error while trying to start child process: " + JSON.stringify(err) );
}

This is a general advice for a situation like you are experiencing to track down the real cause for the issue.

How to include a binary with your node-webkit app

There are a few things involved.

  1. Including the binaries inside your app.nw

    This should be self explanatory - but there is one caveat which caused me some trouble: Make sure the binary is marked as executable via chmod 755. If you are using grunt you might like grunt-chmod. Now your binaries are part of your app's package and you can execute them by knowing the absolute path.

  2. Resolve the path to the binary at runtime even when packaged. The following piece of code is my solution for selecting the right binary for the current platform assuming your tool is multi platform. Also it assumes your binaries are ordered in a certain folder structure. Alternatively select the right binary in your build process and use always the same path.

    var arch = process.arch;
    var platform = process.platform;
    // this will return the root path of your app-package at runtime
    var rootDir = process.cwd(); 
    var isWin = false;
    
    var execPath = rootDir;
    
    // some base path is appended
    execPath = path.join(execPath, 'path', 'to', 'bin');
    
    // select folder for current platform
    switch (platform) {
        case 'darwin':
            execPath = path.join(execPath, 'mac');
            break;
        case 'linux':
            execPath = path.join(execPath, 'lin');
            break;
        case 'win32':
            execPath = path.join(execPath, 'win');
            isWin = true;
            break;
        default:
            global.console.log("unsupported platform: " + platform);
            return null;
    }
    
    // select folder for current processor architecture
    switch (arch) {
        case 'ia32':
            execPath = path.join(execPath, 'x86');
            break;
        case 'x64':
            execPath = path.join(execPath, 'x64');
            break;
        default:
            global.console.log("unsupported architecture: " + arch);
            return null;
    }
    
    // add executable filename
    execPath = path.join(execPath, 'node');
    
    if (isWin) {
        execPath = execPath + ".exe";
    }
    
    global.console.log("Path to your binary: " + execPath);
    
    return execPath;
    
  3. Resolve the paths which are fed to your binary as arguments eventually. This was also a bit confusing because all paths were treated as relative to the app's package root path. My node-main file resides in a folder in my app package so I thought I should reference files relative from there, but this was not the case.

    app package root
    |--- package.json     <- node-webkit package.json
    |
    |--- client           <- here my sources for the frontend reside
    |
    |--- server           
    |----|--- node_modules  <- server runtime dependencies
    |----|--- src           <- server source code
    |----|----|--- server.js  <- this is my node server file to execute via node
    |
    |--- node-webkit      <- node webkit code and dependencies
    |----|--- bin           <- a directory with my deployed binaries
    |----|--- node-main.js  <- this is my node main file
    

To invoke a node binary with my server file the following line led to success:

    child_process.spawn(absPathToNodeBin, "server/src/server.js");
0
votes

This is my experience:

var child_process = nw.require('child_process');
//exec
var exec = child_process.exec;
var ret = exec('cat *');

//spawn
var sp = child_process.spawn;
var sr = sp('cat *');