[Home] Build node.js on OpenEmbedded (natively, not cross-compiled, no bitbake)

Node.js on OpenEmbeded

The successful build instructions in a nutshell:

opkg install python python-modules python-distutils python-misc
opkg install task-sdk-native
opkg install openssl openssl-dev
wget http://nodejs.org/dist/node-v0.1.102.tar.gz
tar xf node-v0.1.102.tar.gz
cd node-v0.1.102
export CC='gcc -march=armv7-a -mtune=cortex-a8' # -march=armv5te works for older systems
export CXX='g++ -march=armv7-a -mtune=cortex-a8'
./configure
make
make install

Tracing Errors

There are a number of DIY kits for embedded development which support OpenEmbedded. BeagleBoard and Gumstix are two that have a growing community and a good name for themselves.

Over the weekend I had the pleasure of borrowing a Gumstix Overo Earth with the Tobi expansion board to see if I could get Node.js running on it before I bought my own. I'm going to give you the red-green step-through starting with a bitbake build of a console-image using the default gumstix opkg repository (I believe this is a clone of the Angstrom repo - the same as BeagleBoard, etc - with a few tweaks), but assuming you have the OpenEmbedded environment of your choice up and running, the instructions should be pretty much the same.

Prerequisits:

Note on Rolling your own

Rolling your own is always optional and is done on your host system (the x86 or x86-84 box you cross-compile from, not the OpenEmbedded device). You will need to find the ipk files in a sub-directory of ./tmp/deploy/glibc/ipk/, copy them over with scp, and install them with opkg install _package-name_.ipk

Red

cd ~/
wget http://nodejs.org/dist/node-v0.1.102.tar.gz
tar xf node-v0.1.102.tar.gz
cd node-v0.1.102
./configure
make

And we see that we don't have gcc...

task-sdk-native: The native arm compiler

Rolling your own: several hours

bitbake task-sdk-native

On some Linuxes (Ubuntu pre-10.04, for example) you will get [an error][xz-error]

/bin/sh: xz: command not found 
tar: This does not look like a tar archive 
tar: Exiting with failure status due to previous errors 
NOTE: Task failed: 
ERROR: TaskFailed event exception, aborting 
ERROR: Build of /home/mark/overo-oe/org.openembedded.dev/recipes/grep/grep_2.6.3.bb do_unpack failed 
ERROR: Task 1727 (/home/mark/overo-oe/org.openembedded.dev/recipes/grep/grep_2.6.3.bb, do_unpack) failed 
NOTE: Tasks Summary: Attempted 2437 tasks of which 2411 didn't need to be rerun and 1 failed. 
ERROR: '/home/mark/overo-oe/org.openembedded.dev/recipes/grep/grep_2.6.3.bb' failed 

This is because lzma has been replaced by xz-utils. You can run apt-get install xz-utils and safely ignore the warning that by uninstalling lzma that You are about to do something potentially harmful. and enter Yes, do as I say!.

Installing from the repo: several minutes

opkg install task-sdk-native

Now let's try again.

cd ~/node-v0.1.102
make

And we see that we don't have python

python: runs the WAF compiler tools

This first error is that python isn't installed, that seems intuitive enough to fix

./configure 
/usr/bin/env: python: No such file or directory
opkg install python

Next is a BOM enocding error. This has to do with utf-8 / unicode / ISOxxxx stuff.

./configure
  File "/home/root/node-v0.1.102/tools/waf-light", line 2
SyntaxError: encoding problem: with BOM    
# edit /home/root/node-v0.1.102/tools/waf-light
# change `# encoding: ISO8859-1` to `# encoding: utf-8`

shutil is missing. After some hunting, I found it.

./configure 
Traceback (most recent call last):
  File "/home/root/node-v0.1.102/tools/waf-light", line 157, in <module>
    import Scripting
  File "/home/root/node-v0.1.102/tools/wafadmin/Scripting.py", line 7, in <module>
    import os, sys, shutil, traceback, datetime, inspect, errno
ImportError: No module named shutil
opkg install python-modules

The next error is quite interesting. You'd think string would be part of python

./configure
ImportError: No module named string
opkg install python-distutils

We find that the gcc compiler isn't installed, which almost seems like we're getting out of the python errors... but not quite

./configure 
Checking for program g++ or c++          : not found 
Checking for program icpc                : not found 
Checking for program c++                 : not found 
/home/root/node-v0.1.102/wscript:130: error: could not configure a cxx compiler!
opkg install task-sdk-native

Finally our last python error - python doesn't know which platform we're on. I would like to note that misc, util, modules, and tools are terrible names for packages...

./configure
/home/root/node-v0.1.102/deps/c-ares/wscript: error: Traceback (most recent call last):
  File "/home/root/node-v0.1.102/tools/wafadmin/Utils.py", line 274, in load_module
    exec(compile(code, file_path, 'exec'), module.__dict__)
  File "/home/root/node-v0.1.102/deps/c-ares/wscript", line 2, in <module>
    import platform
ImportError: No module named platform
opkg install python-misc

And now on to compiler errors...

v8: the engine behind node

I knew that the OMAP3530 has thumb support, so this was confusing

/home/root/node-v0.1.101/deps/v8/src/arm/macro-assembler-arm.cc:59:3: error: #error "For thumb inter-working we require an architecture which supports blx"
scons: *** [obj/release/arm/macro-assembler-arm.o] Error 1
scons: building terminated because of errors.
Waf: Leaving directory `/home/root/node-v0.1.101/build'
Build failed:  -> task failed (err #2): 
        {task: libv8.a SConstruct -> libv8.a}
make: *** [all] Error 1

At first I thought the best course of action was to fake CAN_USE_THUMB_INSTRUCTIONS by manually defining it in the code

g++ -o obj/release/platform-linux.o -c -Wall -W -Wno-unused-parameter -Wnon-virtual-dtor -pedantic -O3 -fomit-frame-pointer -fdata-sections -ffunction-sections -ansi -fno-rtti -fno-exceptions -Wall -W -Wno-unused-parameterc
/tmp/ccNcgrdW.s: Assembler messages:
/tmp/ccNcgrdW.s:150: Error: bad instruction `int $3'
scons: *** [obj/release/platform-linux.o] Error 1
scons: building terminated because of errors.
Waf: Leaving directory `/home/root/node-v0.1.101/build'
Build failed:  -> task failed (err #2): 
        {task: libv8.a SConstruct -> libv8.a}
make: *** [all] Error 1

But that wasn't enough so I figured ARM_V7_A should be faked too

/tmp/ccxCSjUb.s: Assembler messages:
/tmp/ccxCSjUb.s:47: Error: selected processor does not support `bkpt 0'
scons: *** [obj/release/arm/cpu-arm.o] Error 1
scons: building terminated because of errors.
Waf: Leaving directory `/home/root/node-v0.1.101/build'
Build failed:  -> task failed (err #2): 
  {task: libv8.a SConstruct -> libv8.a}
make: *** [all] Error 1

Further investigation revealed that the compiler was faking (and forcing) the platform to appear as an ARMv4:

touch foo.cc
g++ foo.cc -dM -E | grep ARM
#define __ARMEL__ 1
#define __ARM_ARCH_4T__ 1
#define __ARM_EABI__ 1

I found that the way to fix this is to use -march, -mtune, and -mcpu flags:

touch foo.cc
g++ foo.cc -march=armv7-a -mtune=cortex-a8 -dM -E | grep ARM
#define __ARMEL__ 1
#define __ARM_ARCH_7A__ 1
#define __ARM_EABI__ 1

I couldn't figure out how to pass compiler arguments directly to WAF

./configure -march=armv7-a
waf-light: error: no such option: -march

I did find that WAF respects environment variables.

export CC='gcc -march=armv7-a'
export CXX='g++ -march=armv7-a'

Viola!

make
#... lots of successful output
Waf: Leaving directory `/home/root/node-v0.1.101/build'
'build' finished successfully (40m36.030s)

Other Notes

production: node.js - without python and gcc

I believe the binary node is the only thing necessary to be able to run node, assuming that the shared libraries that it is linked against are present - those being openssl and optionally v8, libev, libio.

You shouldn't need python, or g++. However, you will need libcstd++

overo-oe/tmp/work/armv7a-angstrom-linux-gnueabi/gcc-cross-4.3.3-r12.1/staging-pkg/deploy/glibc/ipk/armv7a/libstdc++6_4.3.3-r12.1.5_armv7a.ipk

openssl support

opkg install openssl
opkg install openssl-dev

running on other distros

librt.so.1 => /lib/librt.so.1 (0x40026000)
libssl.so.0.9.8 => /usr/lib/libssl.so.0.9.8 (0x40035000)
libcrypto.so.0.9.8 => /usr/lib/libcrypto.so.0.9.8 (0x4007c000)
libdl.so.2 => /lib/libdl.so.2 (0x401a3000)
libm.so.6 => /lib/libm.so.6 (0x401ae000)
libgcc_s.so.1 => /lib/libgcc_s.so.1 (0x40221000)
libpthread.so.0 => /lib/libpthread.so.0 (0x40233000)
libc.so.6 => /lib/libc.so.6 (0x40252000)
/lib/ld-linux.so.3 (0x40000000)

Leftover warnings

I'm pretty sure that sync_file_range is a fairly new operation that isn't implemented in all file io libraries yet.

default/deps/libeio/eio_1.o: In function `eio__sync_file_range':
/home/root/node-v0.1.102/build/../deps/libeio/eio.c:872: warning: warning: sync_file_range is not implemented and will always fail

creating a static binary

And the solution is to add conf.env["FULLSTATIC"] = True to the wscript

TODO: create a patch for this and post it to the mailing list

vim wscript

  opt.add_option( '--shared-libev-libpath'
                , action='store'
                , default=False
                , help='A directory to search for the shared libev DLL'
                , dest='shared_libev_libpath'
                )

+  opt.add_option( '--static'
+                , action='store'
+                , default=False
+                , help='Whether to compile node statically'
+                , dest='full_static'
+                )

def configure(conf):
  conf.check_tool('compiler_cxx')
  if not conf.env.CXX: conf.fatal('c++ compiler not found')
  conf.check_tool('compiler_cc')
  if not conf.env.CC: conf.fatal('c compiler not found')

  o = Options.options

  conf.env["USE_DEBUG"] = o.debug

  # TODO --static = o.static
+  conf.env["FULLSTATIC"] = o.full_static
  conf.env["USE_SHARED_V8"] = o.shared_v8 or o.shared_v8_includes or o.shared_v8_libpath or o.shared_v8_libname
  conf.env["USE_SHARED_CARES"] = o.shared_cares or o.shared_cares_includes or o.shared_cares_libpath
  conf.env["USE_SHARED_LIBEV"] = o.shared_libev or o.shared_libev_includes or o.shared_libev_libpath

You can't link static libraries against dynamic libraries, so your friendly error might look like:

/arm-angstrom-linux-gnueabi/bin/ld: cannot find -lssl
opkg install openssl-static
make

Leftover warnings

I'm not sure if these matter or not, but they're the warnings I got

root/node-v0.1.102/build/default/node -pthread -rdynamic /home/root/node-v0.1.102/build/default/libv8.a -static -lrt -lssl -lcrypto -ldl
default/src/node_4.o: In function `node::DLOpen(v8::Arguments const&)':
/home/root/node-v0.1.102/build/../src/node.cc:1271: warning: Using 'dlopen' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
default/src/node_4.o: In function `node::SetGid(v8::Arguments const&)':
/home/root/node-v0.1.102/build/../src/node.cc:1099: warning: Using 'getgrnam_r' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
default/src/node_4.o: In function `node::SetUid(v8::Arguments const&)':
/home/root/node-v0.1.102/build/../src/node.cc:1134: warning: Using 'getpwnam_r' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
default/src/node_net_4.o: In function `node::Resolve(eio_req*)':
/home/root/node-v0.1.102/build/../src/node_net.cc:1165: warning: Using 'getaddrinfo' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking
default/deps/c-ares/ares_getnameinfo_1.o: In function `lookup_service':
/home/root/node-v0.1.102/build/../deps/c-ares/ares_getnameinfo.c:310: warning: Using 'getservbyport_r' in statically linked applications requires at runtime the shared libraries from the glibc version used for linking

Other Resources

hicolor-icon-theme

There's this rather annoying (but benign) error that pops up on console systems. For some reason consolekit depends on hicolor-icon-theme, which isn't correctly installed on the console system.

Configuring hicolor-icon-theme.
//usr/lib/opkg/info/hicolor-icon-theme.postinst: line 7: can't create /etc/gtk-2.0/gdk-pixbuf.loaders: nonexistent directory
//usr/lib/opkg/info/hicolor-icon-theme.postinst: line 7: gdk-pixbuf-query-loaders: not found
//usr/lib/opkg/info/hicolor-icon-theme.postinst: line 13: gtk-update-icon-cache: not found
Configuring openssl-dev.
Collected errors:
 * pkg_run_script: postinst script returned status 127.
 * opkg_configure: hicolor-icon-theme.postinst returned 127.
opkg remove --force-depends hicolor-icon-theme
Removing package hicolor-icon-theme from root...
//usr/lib/opkg/info/hicolor-icon-theme.postrm: line 6: gtk-update-icon-cache: not found
Collected errors:
 * pkg_run_script: postrm script returned status 127.

The last error is also meaningless.

opkg remove --force-depends hicolor-icon-theme
No packages removed.
Updated at 2010-07-26
blog comments powered by Disqus