Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

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.
  • Loading branch information...
commit 50e00de92a7563f39ff50f9a53c7e2ed15e556c6 1 parent 34c750d
@bnoordhuis bnoordhuis authored
Showing with 216 additions and 160 deletions.
  1. +2 −2 Makefile
  2. +214 −0 tools/install.py
  3. +0 −158 tools/installer.js
View
4 Makefile
@@ -39,10 +39,10 @@ out/Makefile: common.gypi deps/uv/uv.gyp deps/http_parser/http_parser.gyp deps/z
$(PYTHON) tools/gyp_node -f make
install: all
- out/Release/node tools/installer.js install $(DESTDIR)
+ $(PYTHON) tools/install.py $@ $(DESTDIR)
uninstall:
- out/Release/node tools/installer.js uninstall
+ $(PYTHON) tools/install.py $@ $(DESTDIR)
clean:
-rm -rf out/Makefile node node_g out/$(BUILDTYPE)/node blog.html email.md
View
214 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[:])
View
158 tools/installer.js
@@ -1,158 +0,0 @@
-var fs = require('fs'),
- path = require('path'),
- exec = require('child_process').exec,
- cmd = process.argv[2],
- dest_dir = process.argv[3] || '';
-
-if (cmd !== 'install' && cmd !== 'uninstall') {
- console.error('Unknown command: ' + cmd);
- process.exit(1);
-}
-
-// Use the built-in config reported by the current process
-var variables = process.config.variables,
- node_prefix = variables.node_prefix || '/usr/local';
-
-// Execution queue
-var queue = [],
- dirs = [];
-
-// Copy file from src to dst
-function copy(src, dst, callback) {
- // If src is array - copy each file separately
- if (Array.isArray(src)) {
- src.forEach(function(src) {
- copy(src, dst, callback);
- });
- return;
- }
-
- dst = path.join(dest_dir, node_prefix, dst);
- var dir = dst.replace(/\/[^\/]*$/, '/');
-
- // Create directory if hasn't done this yet
- if (dirs.indexOf(dir) === -1) {
- dirs.push(dir);
- queue.push('mkdir -p ' + dir);
- }
-
- // Queue file/dir copy
- queue.push('cp -rf ' + src + ' ' + dst);
-}
-
-// Remove files
-function remove(files) {
- files.forEach(function(file) {
- file = path.join(dest_dir, node_prefix, file);
- queue.push('rm -rf ' + file);
- });
-}
-
-// Add/update shebang (#!) line so that npm uses
-// the newly installed node, rather than the first in PATH.
-function shebang(line, npmDir) {
- var script = JSON.stringify(path.join(npmDir, 'scripts/relocate.sh'));
- var bin = JSON.stringify(path.join(npmDir, 'bin/npm-cli.js'));
- queue.push('/bin/sh ' + script + ' ' + line);
-}
-
-// Run every command in queue, one-by-one
-function run() {
- var cmd = queue.shift();
- if (!cmd) return;
-
- if (Array.isArray(cmd) && cmd[0] instanceof Function) {
- var func = cmd[0];
- var args = cmd.slice(1);
- console.log.apply(null, [func.name].concat(args));
- func.apply(null, args);
- run();
- } else {
- console.log(cmd);
- exec(cmd, function(err, stdout, stderr) {
- if (stderr) console.error(stderr);
- if (err) process.exit(1);
-
- run();
- });
- }
-}
-
-if (cmd === 'install') {
- // Copy includes
- copy([
- // Node
- 'src/node.h', 'src/node_buffer.h', 'src/node_object_wrap.h',
- 'src/node_version.h', 'src/ev-emul.h', 'src/eio-emul.h',
- // v8
- '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',
- // uv
- 'deps/uv/include/uv.h'
- ], 'include/node/');
-
- // man page
- copy(['doc/node.1'], 'share/man/man1/');
-
- // dtrace
- if (!process.platform.match(/^linux/)) {
- copy(['src/node.d'], 'lib/dtrace/');
- }
-
- // Private uv headers
- copy([
- '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/');
-
- copy([
- 'deps/uv/include/ares.h',
- 'deps/uv/include/ares_version.h'
- ], 'include/node/');
-
- // Copy binary file
- copy('out/Release/node', 'bin/node');
-
- // Install node-waf
- if (variables.node_install_waf) {
- copy('tools/wafadmin', 'lib/node/');
- copy('tools/node-waf', 'bin/node-waf');
- }
-
- // Install npm (eventually)
- if (variables.node_install_npm) {
- // Frequently, in development, the installed npm is a symbolic
- // link to the development folder, and so installing this is
- // a bit annoying. If it's a symlink, skip it.
- var isSymlink = false;
- var exists = true;
- var npmDir = path.resolve(node_prefix, 'lib/node_modules/npm');
- try {
- var st = fs.lstatSync(npmDir);
- isSymlink = st.isSymbolicLink();
- } catch (e) {
- exists = true;
- }
-
- if (!isSymlink) {
- if (exists) queue.push('rm -rf ' + npmDir);
- copy('deps/npm', 'lib/node_modules/npm');
- queue.push('ln -sf ../lib/node_modules/npm/bin/npm-cli.js ' +
- path.join(dest_dir, node_prefix, 'bin/npm'));
- shebang(path.join(node_prefix, 'bin/node'),
- path.join(dest_dir, node_prefix, 'lib/node_modules/npm'));
- }
- }
-} else {
- remove([
- 'bin/node', 'bin/npm', 'bin/node-waf',
- 'include/node/*', 'lib/node_modules', 'lib/node',
- 'lib/dtrace/node.d', 'share/man/man1/node.1'
- ]);
-}
-
-run();
Please sign in to comment.
Something went wrong with that request. Please try again.