uglify crashing after running child_process.execFile
Asked Answered
S

1

10

EDIT 2

I "solved" the problem, but I don't want to post it as an answer b/c it doesn't explain what actually happened. In the code for the .NET resourceReader.exe I use

Console.OutputEncoding = System.Text.Encoding.UTF8;

to output the internationalized resources to stdout in unicode. If I reset the encoding at the end of my program with

Console.OutputEncoding = System.Text.Encoding.Default;

then I don't get any errors in Node. If I don't reset it, I get the error described in the original question. It seems that .NET is somehow messing up some of the output encoding settings on the cmd.exe and causing the subsequent node run to fail!

EDIT

I narrowed down the error to being caused by resourceReader.exe. It's a.NET program which reads some resource streams out of the .NET assembly and prints them to the stdout using Console.WriteLine. I added Console.OutputEncoding = System.Text.Encoding.UTF8 to resourceReader.exe because some of the resources are in non ASCII letters and that's whats causing the crash in grunt!

If I take that line out, the task doesn't crash, but the resources show up in non printable ASCII characters! Also, the crash only happens if I actually print non-ASCII to sdtout. If I don't print them, it doesn't error.

ORIGINAL

I added a step to my Gruntfile which uses child_process.execFile to run an read some data from an external program and uses it in the build. Now whenever I run my build, it runs fine the first time, but crashes the second time!

Here's the output from the crash (this is during the uglify task):

File build/Scripts/NDB.contacts.min.js created: 16.85 kBevents.js:85
  throw er; // Unhandled 'error' event
        ^
Error: This socket is closed.
  at WriteStream.Socket._writeGeneric (net.js:656:19)
  at WriteStream.Socket._write (net.js:709:8)
  at doWrite (_stream_writable.js:301:12)
  at writeOrBuffer (_stream_writable.js:288:5)
  at WriteStream.Writable.write (_stream_writable.js:217:11)
  at WriteStream.Socket.write (net.js:634:40)
  at Log._write (C:\...\node_modules\grunt\node_modules\grunt-legacy-log\index.js:161:26)
  at Log.wrapper [as _write] (C:\...\node_modules\grunt\node_modules\grunt-legacy-log\node_modules\lodash\index.js:3095:19)
  at Log._writeln (C:\...\node_modules\grunt\node_modules\grunt-legacy-log\index.js:166:8)
  at Log.wrapper [as _writeln] (C:\...\node_modules\grunt\node_modules\grunt-legacy-log\node_modules\lodash\index.js:3095:19)
  at Log.writeln (C:\...\node_modules\grunt\node_modules\grunt-legacy-log\index.js:177:8)
  at Log.wrapper (C:\...\node_modules\grunt\node_modules\grunt-legacy-log\node_modules\lodash\index.js:3095:19)
  at writeln (C:\...\node_modules\grunt\lib\grunt\fail.js:30:13)
  at Object.fail.fatal (C:\...\node_modules\grunt\lib\grunt\fail.js:46:3)
  at process.uncaughtHandler (C:\...\node_modules\grunt\lib\grunt.js:121:10)
  at process.emit (events.js:129:20)
  at process._fatalException (node.js:236:26)
  at Task.runTaskFn (C:\...\node_modules\grunt\lib\util\task.js:250:7)
  at Task.<anonymous> (C:\...\node_modules\grunt\lib\util\task.js:293:12)
  at C:\...\node_modules\grunt\lib\util\task.js:220:11
  at process._tickCallback (node.js:355:11)

Here's the code for the task which uses child_process.

    function readAllCultures() {
        var readDeferred = q.defer();

        childProc.execFile("../tools/resourceReader.exe", function (err, stdout, stderr) {
            if (err) throw new Error(err);

            var cultures = JSON.parse(stdout);
            readDeferred.resolve(cultures);
        });

        return readDeferred.promise;
    }

Here's some things I discovered debugging that might be helpful

  1. If I redirect the output of grunt (using either > filename or | process) it runs fine
  2. When I redirect output, I never see the message from uglify that it created the main output only that it created the source map.
  3. If I close and reopen my command prompt (cmd.exe) it works fine
  4. I added a listener to the exit and close events of the child process using rdr.on("close", function() { console.log("close"); }); and the same for exit. Both events fire as expected in the first run.
  5. Using Process Explorer, I can see node.exe open under the command prompt and close again when my command finishes running. There are no processes visibly "left open" under the command prompt.
Slavery answered 3/5, 2016 at 9:45 Comment(8)
I think your program is still running in background after your first Grunt call, use htop to confirm.Bratcher
@RaNdoM_PoWneD I'm on Windows. I don't see it running in process manager.Slavery
Well sorry I have no further idea :/ I feel like something is not terminated well (like an open pipe, a socket...) that block the new instance to run. Good luckBratcher
BTW you should use rather return readDeferred.reject() than throwing from callback handler.Crosscountry
@just.another.programmer: The fact that redirects affect this makes sense when you take a look at what grunt-legacy-log is doing. If you take a look here: github.com/gruntjs/grunt-legacy-log/blob/master/index.js#L161 (as found in the stacktrace) the "socket" trying to be written seems to be stdoutElmoelmore
Seeing as I didn't really do anything, not submitting this as an answer, but pretty sure your problem has something to do with this: #24357303Elmoelmore
@Elmoelmore I switched to cross-spawn to see if it would help. I still get the exact same error on second run!Slavery
@Elmoelmore See my update. Seems to be the .NET program is somehow "breaking" the environment.Slavery
R
0

The fact that the stack trace is ultimately showing a socket.write error is interesting. Nothing in the code you've provided suggests you are trying to write to a socket.

If we follow the stack down, we can see that it's actually grunt that is trying to write to a socket, as it's attempting to log an uncaught exception.

So, first your grunt task throws a currently unknown error, and then grunt itself errors because it can't tell you about it.

I would first try and log the error that occurs in your child process. Currently if there is an error present, you simply throw it without finding out what it is. It's likely that this is what grunt is trying and failing to log.

Replace

if (err) throw new Error(err);

With

if (err) console.error(err);

This will hopefully avoid the socket.write problem and give you specific information about what error is happening on the child process. What do you see?

Second, I would try using child_process.exec instead of child_process.execFile. This will spawn a shell and run resourceReader.exe inside of it (instead of running it directly). This might help avoid any problems you are encountering with the command still running/failing in the background, which could be the cause of the socket.write error.

Receiptor answered 11/5, 2016 at 9:22 Comment(1)
My read culture task runs after the uglify task. I tried your suggestion and it did not make any change to the output.Slavery

© 2022 - 2024 — McMap. All rights reserved.