3

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.

Community
  • 1
  • 1
lpappone
  • 2,755
  • 2
  • 15
  • 15
  • 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 Nov 18 '14 at 01:20
  • 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 Nov 19 '14 at 21:22
  • 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 Nov 23 '14 at 00:50
  • 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 Nov 24 '14 at 05:25
  • please show the command you're using from the command line to try to start it. Sounds like a path problem. – bonez Nov 24 '14 at 21:10
  • Hi bonez. It now runs from double clicking on it, but I get the spawn ENOENT error. I've narrowed the process down to where pdftk gets called by a spawned child process in node. I've included pdftk as part of the packaged app, and I think something is wrong with the path I use to call it in the child process, but I don't know what it is. I've tried just 'pdftk,' which worked in the original version, and the relative path in the package from the js file to the pdftk executable, but neither work. Any ideas? – lpappone Nov 26 '14 at 12:30
  • I should mention it runs fine if I start it from the command line (because I assume in that case it's using my PATH environment variable, which it's not when I double click on the icon). – lpappone Nov 26 '14 at 12:36

2 Answers2

4

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");
steinharts
  • 324
  • 2
  • 8
  • Thanks - this is awesome! I don't have the time anymore right now to devote to investigating whether it will solve my problems, but I hope to do so soon. Thank you! – lpappone Jan 16 '15 at 03:48
  • I'm new to js, node and node webkit. How do you access the global console log? I'm running nw.exe from a command prompt but never see my exception messages. I know I'm getting an error because I can see it using alert(). – Christopher Painter Jan 17 '17 at 15:05
  • What if I don't want to ship the binary with my app. I just want to see if the user has the binary installed. It works when I run it from the command line because I'm running it under my user env, but then when I run it as a Mac OS X app it doesn't work. Any ideas? – shieldstroy Sep 14 '17 at 01:07
0

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 *');
Verdigrass
  • 1,203
  • 12
  • 14