diff options
author | Ben Noordhuis <info@bnoordhuis.nl> | 2012-08-02 01:06:31 +0200 |
---|---|---|
committer | Ben Noordhuis <info@bnoordhuis.nl> | 2012-08-02 13:51:35 +0200 |
commit | 50e00de92a7563f39ff50f9a53c7e2ed15e556c6 (patch) | |
tree | a7e24658fa54fac5353da46e9dcf926684f9365f /tools/install.py | |
parent | 34c750d7a96ad29c023d117f55239b94e90d23d5 (diff) | |
download | node-new-50e00de92a7563f39ff50f9a53c7e2ed15e556c6.tar.gz |
installer: fix cross-compile installs
The old installer was a JS script, which didn't work if node had been
cross-compiled for another architecture. Replace it with a python script.
Fixes #3807.
Diffstat (limited to 'tools/install.py')
-rwxr-xr-x | tools/install.py | 214 |
1 files changed, 214 insertions, 0 deletions
diff --git a/tools/install.py b/tools/install.py new file mode 100755 index 0000000000..66e51d0ae3 --- /dev/null +++ b/tools/install.py @@ -0,0 +1,214 @@ +#!/usr/bin/env python + +import errno +import json +import os +import re +import shutil +import sys + +# set at init time +dst_dir = None +node_prefix = None # dst_dir without DESTDIR prefix +target_defaults = None +variables = None + +def abspath(*args): + path = os.path.join(*args) + return os.path.abspath(path) + +def load_config(): + s = open('config.gypi').read() + s = re.sub(r'#.*?\n', '', s) # strip comments + s = re.sub(r'\'', '"', s) # convert quotes + return json.loads(s) + +def try_unlink(path): + try: + os.unlink(path) + except OSError, e: + if e.errno != errno.ENOENT: raise + +def try_symlink(source_path, link_path): + print 'symlinking %s -> %s' % (source_path, link_path) + try_unlink(link_path) + os.symlink(source_path, link_path) + +def try_mkdir_r(path): + try: + os.makedirs(path) + except OSError, e: + if e.errno != errno.EEXIST: raise + +def try_rmdir_r(path): + path = abspath(path) + while path.startswith(dst_dir): + try: + os.rmdir(path) + except OSError, e: + if e.errno == errno.ENOTEMPTY: return + if e.errno == errno.ENOENT: return + raise + path = abspath(path, '..') + +def mkpaths(path, dst): + if dst.endswith('/'): + target_path = abspath(dst_dir, dst, os.path.basename(path)) + else: + target_path = abspath(dst_dir, dst) + return path, target_path + +def try_copy(path, dst): + source_path, target_path = mkpaths(path, dst) + print 'installing %s' % target_path + try_mkdir_r(os.path.dirname(target_path)) + return shutil.copy2(source_path, target_path) + +def try_remove(path, dst): + source_path, target_path = mkpaths(path, dst) + print 'removing %s' % target_path + try_unlink(target_path) + try_rmdir_r(os.path.dirname(target_path)) + +def install(paths, dst): map(lambda path: try_copy(path, dst), paths) +def uninstall(paths, dst): map(lambda path: try_remove(path, dst), paths) + +def waf_files(action): + action(['tools/node-waf'], 'bin/node-waf') + action(['tools/wafadmin/ansiterm.py', + 'tools/wafadmin/Build.py', + 'tools/wafadmin/Configure.py', + 'tools/wafadmin/Constants.py', + 'tools/wafadmin/Environment.py', + 'tools/wafadmin/__init__.py', + 'tools/wafadmin/Logs.py', + 'tools/wafadmin/Node.py', + 'tools/wafadmin/Options.py', + 'tools/wafadmin/pproc.py', + 'tools/wafadmin/py3kfixes.py', + 'tools/wafadmin/Runner.py', + 'tools/wafadmin/Scripting.py', + 'tools/wafadmin/TaskGen.py', + 'tools/wafadmin/Task.py', + 'tools/wafadmin/Tools/ar.py', + 'tools/wafadmin/Tools/cc.py', + 'tools/wafadmin/Tools/ccroot.py', + 'tools/wafadmin/Tools/compiler_cc.py', + 'tools/wafadmin/Tools/compiler_cxx.py', + 'tools/wafadmin/Tools/compiler_d.py', + 'tools/wafadmin/Tools/config_c.py', + 'tools/wafadmin/Tools/cxx.py', + 'tools/wafadmin/Tools/dmd.py', + 'tools/wafadmin/Tools/d.py', + 'tools/wafadmin/Tools/gas.py', + 'tools/wafadmin/Tools/gcc.py', + 'tools/wafadmin/Tools/gdc.py', + 'tools/wafadmin/Tools/gnu_dirs.py', + 'tools/wafadmin/Tools/gob2.py', + 'tools/wafadmin/Tools/gxx.py', + 'tools/wafadmin/Tools/icc.py', + 'tools/wafadmin/Tools/icpc.py', + 'tools/wafadmin/Tools/__init__.py', + 'tools/wafadmin/Tools/intltool.py', + 'tools/wafadmin/Tools/libtool.py', + 'tools/wafadmin/Tools/misc.py', + 'tools/wafadmin/Tools/nasm.py', + 'tools/wafadmin/Tools/node_addon.py', + 'tools/wafadmin/Tools/osx.py', + 'tools/wafadmin/Tools/preproc.py', + 'tools/wafadmin/Tools/python.py', + 'tools/wafadmin/Tools/suncc.py', + 'tools/wafadmin/Tools/suncxx.py', + 'tools/wafadmin/Tools/unittestw.py', + 'tools/wafadmin/Tools/winres.py', + 'tools/wafadmin/Tools/xlc.py', + 'tools/wafadmin/Tools/xlcxx.py', + 'tools/wafadmin/Utils.py'], + 'lib/node/') + +def update_shebang(path, shebang): + print 'updating shebang of %s' % path + s = open(path, 'r').read() + s = re.sub(r'#!.*\n', '#!' + shebang + '\n', s) + open(path, 'w').write(s) + +def npm_files(action): + target_path = 'lib/node_modules/npm/' + + # don't install npm if the target path is a symlink, it probably means + # that a dev version of npm is installed there + if os.path.islink(abspath(dst_dir, target_path)): return + + # npm has a *lot* of files and it'd be a pain to maintain a fixed list here + # so we walk its source directory instead... + for dirname, subdirs, basenames in os.walk('deps/npm', topdown=True): + subdirs[:] = filter('test'.__ne__, subdirs) # skip test suites + paths = [os.path.join(dirname, basename) for basename in basenames] + action(paths, target_path + dirname[9:] + '/') + + # create/remove symlink + link_path = abspath(dst_dir, 'bin/npm') + if action == uninstall: + action([link_path], 'bin/npm') + elif action == install: + try_symlink('../lib/node_modules/npm/bin/npm-cli.js', link_path) + update_shebang(link_path, node_prefix + '/bin/node') + else: + assert(0) # unhandled action type + +def files(action): + action(['deps/uv/include/ares.h', + 'deps/uv/include/ares_version.h', + 'deps/uv/include/uv.h', + 'deps/v8/include/v8-debug.h', + 'deps/v8/include/v8-preparser.h', + 'deps/v8/include/v8-profiler.h', + 'deps/v8/include/v8-testing.h', + 'deps/v8/include/v8.h', + 'deps/v8/include/v8stdint.h', + 'src/eio-emul.h', + 'src/ev-emul.h', + 'src/node.h', + 'src/node_buffer.h', + 'src/node_object_wrap.h', + 'src/node_version.h'], + 'include/node/') + action(['deps/uv/include/uv-private/eio.h', + 'deps/uv/include/uv-private/ev.h', + 'deps/uv/include/uv-private/ngx-queue.h', + 'deps/uv/include/uv-private/tree.h', + 'deps/uv/include/uv-private/uv-unix.h', + 'deps/uv/include/uv-private/uv-win.h'], + 'include/node/uv-private/') + action(['doc/node.1'], 'share/man/man1/') + action(['out/Release/node'], 'bin/node') + + # install unconditionally, checking if the platform supports dtrace doesn't + # work when cross-compiling and besides, there's at least one linux flavor + # with dtrace support now (oracle's "unbreakable" linux) + action(['src/node.d'], 'lib/dtrace/') + + if variables.get('node_install_waf'): waf_files(action) + if variables.get('node_install_npm'): npm_files(action) + +def run(args): + global dst_dir, node_prefix, target_defaults, variables + + # chdir to the project's top-level directory + os.chdir(abspath(os.path.dirname(__file__), '..')) + + conf = load_config() + variables = conf['variables'] + target_defaults = conf['target_defaults'] + + # argv[2] is a custom install prefix for packagers (think DESTDIR) + dst_dir = node_prefix = variables.get('node_prefix', '/usr/local') + if len(args) > 2: dst_dir = abspath(args[2] + '/' + dst_dir) + + cmd = args[1] if len(args) > 1 else 'install' + if cmd == 'install': return files(install) + if cmd == 'uninstall': return files(uninstall) + raise RuntimeError('Bad command: %s\n' % cmd) + +if __name__ == '__main__': + run(sys.argv[:]) |