kripken / xml.js

Port of libxml to JavaScript using Emscripten
Other
174 stars 67 forks source link

Exits Meteor 1.0 #11

Open CTimmerman opened 8 years ago

CTimmerman commented 8 years ago

Using Meteor 1.0, CoffeeScript, and https://github.com/meteorhacks/npm this works on the server:

xmllint = Meteor.npmRequire 'xmllint'
result = xmllint.validateXML
    xml: xml
    schema: xsd

However, as soon as the function that code is in is done, Meteor restarts with "=> Exited with code: 0", where 0 is the exit code of xmllint, as when it found errors in xml or xsd, the code was 3 or 5.

sterpe commented 8 years ago

I'm probably not familiar enough with Meteor to be of much assistance, it looks like you are calling correctly to me. But if you are on the server anyway, I'd propose calling out to the real C lib xmllint vis-a-vis node child_process or whatever equivalent Meteor provides...It's a much more capable implementation.

CTimmerman commented 8 years ago

The site hosting the Windows binaries of xmllint is down, and XMLStarlet doesn't provide details, so i hacked up a Node wrapper for xmllint.js:

fs = require('fs')
// process.argv = [ 'node', 'path\\xmllint.js.app', 'the.xml', 'the.xsd' ]
xml = fs.readFileSync(process.argv[2])
xsd = fs.readFileSync(process.argv[3])
eval(''+fs.readFileSync(__dirname+'/xmllint.js.lib'))  // http://stackoverflow.com/a/5809968/819417
result = xmllint.validateXML({xml: xml, schema: xsd})
if(result.errors) console.log('XML ERR', result.errors)
else console.log('XML OK')

Calling it using non-.js names to exclude it from Meteor:

exec = Meteor.wrapAsync(Npm.require('child_process').exec)
#command = process.cwd().split('.meteor')[0] + "server\\xml.exe val --xsd #{app_path}server/company.xsd #{xmlfile}"
command = "\"C:/Program Files (x86)/nodejs/node.exe\" #{app_path}server/xmllint.js.app #{xmlfile} #{app_path}server/company.xsd"
#console.log command
console.log exec(command)

...which does weird things on error, so i use a Future instead:

Future = Npm.require 'fibers/future'
fut = new Future()
exec(command, (error, stdout, stderr)->
    #console.log('error', error)    # Non-zero exit, error.code
    #console.log('stdout', stdout)  # Errors are here.
    #console.log('stderr', stderr)  # Not even when there's an error.
    fut.return(stdout)
)
console.log fut.wait()
sterpe commented 8 years ago

It's very strange. The main file is set up to export as a node_module. I wonder if it's some issue with Meteor or, alternatively, what node -v are you running? It could be the emscripten compiled piece isn't working as well under new(er) node. I believe that when we compiled this it was node 0.10.x.

sterpe commented 8 years ago

Or something with Windows. It would be interesting to run your project in a Linux VM just to see what if anything was different.

CTimmerman commented 8 years ago

Meteor's wrapAsync is certainly broken, as linked. project\.meteor\local\build\.node_version.txt says v0.10.40. process.version in xmllint.js.app says v0.12.6.

To repro, run this:

meteor create test
cd test
meteor add meteorhacks:npm
meteor

Then add private/xmllint.js, private/xmllintwrapper.js using my wrapper code (even though they're in private/ instead of server/, Meteor still restarts when modifying a .js file there), replace the content of packages.json with {"xmllint": "0.1.0"} and replace the content of test.js with this:

if(Meteor.isClient){
    Session.setDefault('counter', 0)

    Template.hello.helpers({
        counter: function(){
            return Session.get('counter')
        }
    })

    Template.hello.events({
        'click button': function(){
            Session.set('counter', Session.get('counter') + 1)
            Meteor.call('validate')
        }
    })
}

if(Meteor.isServer){
    Meteor.methods({
        validate: function(){
            var command = "\"C:/Program Files (x86)/nodejs/node.exe\" assets/app/xmllintwrapper.js assets/app/any.xml "+process.cwd().split('.meteor')[0]+"private/any.xsd"
            console.log("COMMAND", command)
            try{
                // WORKS but FAILS as it restarts Meteor! (METEOR@1.2.1 according to result. 1.0 according to Windows 10 Control Panel\All Control Panel Items\Programs and Features) - Caused by change in project folder?
                var xmllint = Meteor.npmRequire('xmllint')
                var fs = Npm.require('fs')
                var xml = fs.readFileSync(process.cwd().split('.meteor')[0] + 'private/any.xml', 'utf8')
                var xsd = fs.readFileSync(process.cwd().split('.meteor')[0] + 'private/any.xsd', 'utf8')
                result = xmllint.validateXML({xml: xml, schema: xsd})
                // FAILS:
                /*
                var exec = Meteor.wrapAsync(Npm.require('child_process').exec)
                console.log(exec(command))
                */
                // WORKS:
                /*
                var exec = Npm.require('child_process').exec
                Future = Npm.require('fibers/future')
                fut = new Future()
                exec(command, function(error, stdout, stderr){
                    console.log('error:', error)    // Non-zero exit, error.code
                    console.log('stdout:', stdout)  // Errors are here.
                    console.log('stderr:', stderr)  // Not even when there's an error.
                    fut.return(stdout)
                })
                result = fut.wait()
                */
            }catch(er){console.log(er.stack)}
        }
    })
    Meteor.startup(function(){})
}
CTimmerman commented 8 years ago

In case github doesn't send edit notifications: I've added the case of my first post.

sterpe commented 8 years ago

You should probably also file a similar issue to the meteor team. Xmllint is fully self contained and valid js so running it should not force a meteor restart of the container. I'd bet they are aware of issues that can cause this when running js.

sterpe commented 8 years ago

Also is 'result' being assigned to without first declaring it as 'var'?

CTimmerman commented 8 years ago

I've linked this issue to the error handling issue and CoffeeScript autogenerates var. (To make a global in CoffeeScript, use @name.) JavaScript only requires var when "use strict" is active.

jakedetels commented 8 years ago

I'm experiencing the same issue, but in a Node.js (4.2.2) + Windows 10 environment. The validateXML function works perfectly, yet after it has finished running, Node.js exits without any explanation in the console.

I see this ticket has been open for a few months now. @sterpe: Is there any possibility of finding a resolution soon?

sterpe commented 8 years ago

@jakedetels, are you experiencing the same issue with Meteor or apart from Meteor?

sterpe commented 8 years ago

@jakedetels I'm not able to reproduce this issue with node itself using any node@4.4.2/5.x/6.x

Here is my rather trivial test

var fs = require('fs')
, xmllint = require('xmllint')
, xml = fs.readFileSync('./test/test.xml').toString()
, schema = fs.readFileSync('./test/test.xsd').toString()
;

console.log(xmllint.validateXML({
    xml: [xml, xml],
    schema: [schema, schema]
}));
while (1) {
}

The test files are the same in ./test.

Can you provide a reproduction?

abernix commented 8 years ago

@sterpe Your reproduction looks fair. I'm not sure what's happening with @jakedetels – though it definitely sounds familiar. I reproduced @CTimmerman's issue on Meteor with node-inspector and got this stack-trace from xmllint. Looks like it's getting a No such file or directory tmp:


xmllint.validateXML.MEMFS.node_ops.lookup (xmllint.js:11)
xmllint.validateXML.FS.lookup (xmllint.js:11)
xmllint.validateXML.FS.lookupNode (xmllint.js:11)
xmllint.validateXML.FS.mayCreate (xmllint.js:11)
xmllint.validateXML.FS.mknod (xmllint.js:11)
xmllint.validateXML.FS.mkdir (xmllint.js:11)
xmllint.validateXML.FS.createDefaultDirectories (xmllint.js:11)
xmllint.validateXML.FS.staticInit (xmllint.js:11)
xmllint.validateXML (xmllint.js:11)
(anonymous function) (test.js:9)
(anonymous function) (test.js:25)
(anonymous function) (boot.js:242)
_.each._.forEach (underscore.js:79)
(anonymous function) (boot.js:137)

It looks similar to kripken/emscripten#2047 . Meteor server runs synchronously versus Node's normal async and usually wrapping in a Fiber will fix things but I don't quite understand what's going on in this case. Any ideas?

sterpe commented 8 years ago

@abernix This might be too deep in the weeds for my knowledge of emscripten's FS. @kripken, I don't know if you have any thought on this?

kripken commented 8 years ago

Hard to tell from the stack trace. But /tmp/ should be created when the emscripten FS starts up, in createDefaultDirectories. Perhaps putting some logging there to verify it runs before you do operations on the FS would be helpful (the FS may be initialized asynchronously, if it's waiting on some other operation like an asynchronous load of files).

Beat-YT commented 2 years ago

You can use my fork https://github.com/Beat-YT/xml.js