Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Compiling JavaScript With Node.JS Source #1301

Closed
JacobTDC opened this issue May 27, 2018 · 41 comments
Closed

Compiling JavaScript With Node.JS Source #1301

JacobTDC opened this issue May 27, 2018 · 41 comments

Comments

@JacobTDC
Copy link

JacobTDC commented May 27, 2018

How would I go about compiling Node.JS code using the source files and GCC/G++? I've figured out how to use tools/js2c.py to get C/C++, but how do I include all relevant header files to GCC without doing it manually?


Makefile:

default: js2c compile

compile: config
  @echo Compiling...
  @g++ main.cpp '-DNODE_WANT_INTERNALS=1' -I"./node/src" -I"./node/deps/v8/include" -I"./node/deps/uv/include" -I"./node/tools/msvs/genfiles" -I"./node/deps/cares/include" -std=gnu++1y -stdlib=libc++

config:
  @./tools/configure.sh $(nodeV)

js2c: config
  @echo Converting JavaScript to C++...
  @python2 ./node/tools/js2c.py main.cpp main.js

tools/configure.sh:

#!/bin/bash

echo "Checking for Node.js source..."
if [ ! -d "node" ]; then
  echo "Node.js source not found."
  if [ -n "$1" ]; then
    nodeV="v${1/#v}"
    echo "Getting Node.js $nodeV"
    nodeTab=$(wget "https://nodejs.org/dist/index.tab" -qO -)
    if [ -z "$(echo $nodeTab|grep "$nodeV\s")" ]; then
      echo "Node.js version \"$nodeV\" does not exist!"
      exit 1
    fi
    nodeUrl="$nodeV/node-$nodeV.tar.gz"
  else
    echo "No version specified, getting latest."
    nodeUrl="node-latest.tar.gz"
  fi
  tmpDir=$(mktemp -d "./tmp-XXXXXX")
  trap "{ rm -rf $tmpDir; }" EXIT
  echo "Downloading..."
  wget "https://nodejs.org/dist/$nodeUrl" --show-progress --progress=bar -qO -|tar xpz -C $tmpDir
  mv $tmpDir/* node
  rm -rf $tmpDir
else
  echo "Node.js source found."
fi
@gireeshpunathil
Copy link
Member

  • if you are looking for direct building Node.js from source, concrete steps are mentioned in https://github.com/nodejs/node/blob/master/BUILDING.md
  • if you are wanting some customization to the build, ./configure --help provides the necessary options
  • if you are wanting to build some custom C++ application that makes use of the object codes of the node.js's javascript source, you can declare the symbols you want as externs and link the source with node_javascript.o. I am not sure the thousands of symbols in the object file is declared in any header for consumption - probably such an app development model is not expected, I may be wrong.

let me know which is your case.

@JacobTDC
Copy link
Author

JacobTDC commented May 28, 2018

I'm trying to build/compile standalone JavaScript apps with only official Node.JS source/resources. I have two reasons to not use 3rd party apps. One, they typically don't work on aarch64/Android, and two, I just want to learn.

@gireeshpunathil
Copy link
Member

fair points, thanks. Still trying to figure out the exact requirement:

  • are you trying to build Node.js from source? basically building node executable?
  • are you trying to build custom JS code into C++ object code using js2c.py ?
  • are you trying to build a custom node executable with custom JS code in it?

@gireeshpunathil
Copy link
Member

/cc @nodejs/build @nodejs/core

@benjamingr
Copy link
Member

I don’t think tagging 86 people (core) in a help request waiting for author input is great use of core’s time.

@gireeshpunathil
Copy link
Member

@benjamingr - point taken. Apologies to 86 people whose time was potentially wasted.

@JacobTDC
Copy link
Author

@gireeshpunathil option 2, using js2c.py, and then compile that C++ code. I can't figure out how to compile it. If it's impossible, that's no big deal, I'm just doing this for fun/educational purposes.

@devsnek
Copy link
Member

devsnek commented May 29, 2018

@benjamingr @gireeshpunathil I would argue those teams exist to be pinged.

@JacobTDC the output of js2c.py is just the source of the JavaScript but in char array form. it's interpreted as a string by node.js at runtime and evaluated. it cannot run by itself.

@benjamingr
Copy link
Member

benjamingr commented May 29, 2018

@devsnek those teams exist to be pinged at core issues relating to collaborators.

This is a help request from a new user - important, but not important enough to encourage collaborator overcommitment IMO. I think this is a problem we have and should get better at openjs-foundation/summit#64 . Sorry for the interruption :)

@JacobTDC
Copy link
Author

@devsnek, yes, but by including the Node.JS and V8 source, shouldn't it be possible to compile that into a standalone?

@devsnek
Copy link
Member

devsnek commented May 29, 2018

@JacobTDC yes, that's what the final binary node is. i'm not quite sure what your question is now

@JacobTDC
Copy link
Author

JacobTDC commented May 29, 2018

@devsnek, see EDIT above. That should clarify things.

@gireeshpunathil
Copy link
Member

@JacobTDC - can you try this?

g++  '-DNODE_WANT_INTERNALS=1'  -I../src  -I../deps/v8/include  -std=gnu++1y -stdlib=libc++  -c main.o main.cc

this is not backed up by any documentation, instead through following Node's build procedure for the JS files, and reducing the command line to a bare minimum that is required for most common cases.

@richardlau
Copy link
Member

Have you considered building Node.js from source using --link-module and then manually looking at the generated makefile after the configure step?

@JacobTDC
Copy link
Author

JacobTDC commented May 30, 2018

@gireeshpunathil I'm getting a new error I haven't seen before, but all the other errors are gone:

In file included from main.cpp:3:
In file included from ./node/src/node_javascript.h:27:
In file included from ./node/src/node_internals.h:28:
./node/src/node_mutex.h:5:10: fatal error: 'uv.h' file not found
#include "uv.h"
         ^~~~~~
1 error generated.
make: *** [Makefile:3: compile] Error 1

@JacobTDC
Copy link
Author

I included does/uv/include to get this error:

main.cpp:43:10: error: use of undeclared identifier
      'internal_bootstrap_loaders_value'
  return internal_bootstrap_loaders_value.ToStringChecked(env->isolate());
         ^
main.cpp:47:10: error: use of undeclared identifier
      'internal_bootstrap_node_value'
  return internal_bootstrap_node_value.ToStringChecked(env->isolate());
         ^
2 errors generated.
make: *** [Makefile:3: compile] Error 1

@devsnek
Copy link
Member

devsnek commented May 30, 2018

node depends on some specific files already existing (internal/bootstrap/node.js and internal/bootstrap/loaders.js)

@JacobTDC
Copy link
Author

Also, @gireeshpunathil, where exactly did you find that?

@JacobTDC
Copy link
Author

@devsnek, how do I include those two files?

@gireeshpunathil
Copy link
Member

my approach was simple:

  1. build node (./configure && make)
  2. touch one of the '.js' files (say ./lib/net.js)
  3. build again this time with verbose on (make -C out BUILDTYPE=Release V=1)

the commands generated and output are precisely the ones that are required for compiling and linking the JS files.

internal_bootstrap_loaders_value is a static structure inside the generated node_javascript.cc . I am assuming that you will eventually be linking with node_javascript.o as well. If so, you could declare internal_bootstrap_loaders_value as extern in your source, and the linker will find it when you link the objects together.

@JacobTDC
Copy link
Author

JacobTDC commented May 31, 2018

@gireeshpunathil, I'm still new to C++ and compilation; can I get an English translation for that last part? XD
By that I mean, can you walk me through the process of including those files?

@gireeshpunathil
Copy link
Member

sure - can you tell me how are you producing your main.cpp ? i.e., the arguments you passed to tools/js2c.py ?

@JacobTDC
Copy link
Author

JacobTDC commented May 31, 2018

I haven't passed any args except main.js and main.cpp, @gireeshpunathil. Sorry it took me two hours to respond...
I actually have a makefile for this to make things easier:

default: js2c compile

compile: config
  @echo Compiling...
  @g++ main.cpp '-DNODE_WANT_INTERNALS=1' -I"./node/src" -I"./node/deps/v8/include" -I"./node/deps/uv/include" -I"./node/tools/msvs/genfiles" -I"./node/deps/cares/include" -std=gnu++1y -stdlib=libc++

config:
  @./tools/configure.sh $(nodeV)

js2c: config
  @echo Converting JavaScript to C++...
  @python2 ./node/tools/js2c.py main.cpp main.js

Where ./tools/configure.sh just makes sure Node.js source is downloaded.


My ./tools/configure.sh looks like this:

#!/bin/bash

echo "Checking for Node.js source..."
if [ ! -d "node" ]; then
  echo "Node.js source not found."
  if [ -n "$1" ]; then
    nodeV="v${1/#v}"
    echo "Getting Node.js $nodeV"
    nodeTab=$(wget "https://nodejs.org/dist/index.tab" -qO -)
    if [ -z "$(echo $nodeTab|grep "$nodeV\s")" ]; then
      echo "Node.js version \"$nodeV\" does not exist!"
      exit 1
    fi
    nodeUrl="$nodeV/node-$nodeV.tar.gz"
  else
    echo "No version specified, getting latest."
    nodeUrl="node-latest.tar.gz"
  fi
  tmpDir=$(mktemp -d "./tmp-XXXXXX")
  trap "{ rm -rf $tmpDir; }" EXIT
  echo "Downloading..."
  wget "https://nodejs.org/dist/$nodeUrl" --show-progress --progress=bar -qO -|tar xpz -C $tmpDir
  mv $tmpDir/* node
  rm -rf $tmpDir
else
  echo "Node.js source found."
fi

@gireeshpunathil
Copy link
Member

@JacobTDC - the missing symbol error (internal_bootstrap_loaders_value) will be resolved if you add lib/internal/bootstrap/loaders.js along with the main.js in your js2c: config target.

However, I fear you may get new errors based on additional symbols being missed.

So 2 options:

  1. try the above option, if it goes well fair enough. for every missing symbol, identify the JS module by replacing their underscore with a slash, and strip the last token, and add it to the list for js2c.py

  2. Follow @richardlau 's suggestion:

$ ./configure --link-module main.js
$ make

I did not try this myself (will try), but from the documentation this looks like the right path.

@JacobTDC
Copy link
Author

JacobTDC commented May 31, 2018

@gireeshpunathil it worked (though I did have to change return internal_bootstrap_loaders_value to return node_lib_internal_bootstrap_loaders_value in main.cpp), but now I get the error undefined reference to main from the linker. I know this is fixable with C++, but I'm not sure exactly how. I'm also not too excited to build ALL OF NODE FROM SOURCE on my phone, and you might guess why. XD

@JacobTDC
Copy link
Author

Actually, I'm getting a lot of undefined reference errors from the linker.

@gireeshpunathil
Copy link
Member

@JacobTDC - good to know you are progressing.
which of my suggested method you followed? 1 or 2? I guess 1?

I'm also not too excited to build ALL OF NODE FROM SOURCE on my phone and you might guess why.

No, I am unable to guess. Why? memory? lack of ABI support? or something else?

But nevertheless, that question hints at your intention as to convert JS code into C++ and run them standalone executable? as opposed to having a Node.js runtime? That will be tough, if not impossible:

  • the generated C++ source is still a piece of code that makes use of V8 APIs heavilly, so it needs a v8 instance to run the code, not directly on the C++ runtime
  • arbitrary JS code that makes use of Node.js API abstractions cannot be built and run without Node runtime.

@JacobTDC
Copy link
Author

JacobTDC commented Jun 1, 2018

@gireeshpunathil, I waited for an hour, and still didn't fully build Node. My phone doesn't have enough processing power to build ALL of Node in a practical amount of time.
Also, Node compiles the JavaScript at runtime, so it should be possible to compile it using Node.js source by including the necessary components from V8 and Node.js.

@JacobTDC
Copy link
Author

JacobTDC commented Jun 1, 2018

I used option 1, BTW.

@gireeshpunathil
Copy link
Member

@JacobTDC - thanks

so it should be possible to compile it using Node.js source by including the necessary components from V8 and Node.js.

agreed, that is what we are attempting. but not having the infrastructure to do that is where we are.

  • if you just want to run a piece of pure JS code, you could use v8 JS engine to run it
  • if you want to run piece of JS code that uses Node.js APIs, you could use Node.js binary installation.
  • if you want to compile your JS code into C++ objects and bundle into an executable, you need entire Node.js source (that contain v8 source too) to be available for building, and the computation power to support the building. Depending on your use case however, we could customize the number of files that come up for compilation. For example, have a look at ./configure --help and see what optional things exists and what you wont be needing in the compilation unit. The 2 heavyweight things I can think of are icu and ssl . If you are not looking for wider ICU coverage, apply --with-intl=none . If you can share the opensll library from the system, apply --without-ssl . This may make your device bit more fast.

@JacobTDC
Copy link
Author

JacobTDC commented Jun 1, 2018

@gireeshpunathil, using g++ -c main.cpp [includes] I got it to output main.o. Where do I go from here?

@gireeshpunathil
Copy link
Member

you need to link it with node.o (+ all other object files from node and v8) to make an executable. If you do make V=1 on a regular system (such as linux) the linking command will be printed towards the end. Pay attention to the command that has -o node as the target, that will be your command.

@JacobTDC
Copy link
Author

JacobTDC commented Jun 1, 2018

@gireeshpunathil Where is node.o? I can't find it...

@JacobTDC
Copy link
Author

JacobTDC commented Jun 1, 2018

Wait, NVM, just realized. Your response just clicked for me...

@JacobTDC
Copy link
Author

JacobTDC commented Jun 1, 2018

I assume node.o is the Node.js executable?

@gireeshpunathil
Copy link
Member

@JacobTDC -

  • if you don't have node.o that means you haven't built any node sources.
  • if you don't build node sources, you have to manage the v8 orchestration by yourself!
  • adding a simple main method in your .cpp file will satisfy the linker
  • but that will not take you anywhere - from the entry point, you need path that leading to the v8 engine, and some code to manage the lifecycle of your applications (that is exactly node)

At one point I assumed that you were trying to build a virtual machine by hand, but from further interactions including the last one, I realized that you are experimenting and trying to learn. That is absolutely fine, but the direction you are wanting to take is neither supported, recommended nor tested. To add to that, you seem to be wanting to have this on a platform that inhibit these experiments.

I recommend that

  • do your experiments in a regular systems (such as linux on x64)
  • first build and use node from source (https://github.com/nodejs/node/blob/master/BUILDING.md)
  • understand (and try) different configure options (./configure --help)
  • understand snapshot, and building JS into C++ (follow the build command output from V=1)
  • with this level of understanding, try experimenting with that on the mobile.

hope this helps.

@JacobTDC
Copy link
Author

JacobTDC commented Jun 2, 2018

Believe it or not, my PHONE has more processing power than my computer (XD), and I just like to experiment on it. However, @gireeshpunathil, I will try using a Linux VM. Maybe that will work better.
My phone is also my only non-virtual Linux machine.

@gireeshpunathil
Copy link
Member

@JacobTDC - wonderful, thanks. Let me know if you have got all the info that you wanted.

@gireeshpunathil
Copy link
Member

@JacobTDC - how is it going? are we all well and close this?

@JacobTDC
Copy link
Author

@gireeshpunathil I'm closing the issue now. Sorry, I forgot to close it... XD

@JacobTDC
Copy link
Author

JacobTDC commented Jun 22, 2018

I know I just closed this, but for anyone interested, I found patches that fix Node for android here: https://github.com/termux/termux-packages/tree/master/packages/nodejs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants