1#!/usr/bin/env python 2 3from __future__ import print_function 4 5import ast 6import errno 7import os 8import shutil 9import sys 10 11# set at init time 12node_prefix = '/usr/local' # PREFIX variable from Makefile 13install_path = '' # base target directory (DESTDIR + PREFIX from Makefile) 14target_defaults = None 15variables = None 16 17def abspath(*args): 18 path = os.path.join(*args) 19 return os.path.abspath(path) 20 21def load_config(): 22 s = open('config.gypi').read() 23 return ast.literal_eval(s) 24 25def try_unlink(path): 26 try: 27 os.unlink(path) 28 except OSError as e: 29 if e.errno != errno.ENOENT: raise 30 31def try_symlink(source_path, link_path): 32 print('symlinking %s -> %s' % (source_path, link_path)) 33 try_unlink(link_path) 34 try_mkdir_r(os.path.dirname(link_path)) 35 os.symlink(source_path, link_path) 36 37def try_mkdir_r(path): 38 try: 39 os.makedirs(path) 40 except OSError as e: 41 if e.errno != errno.EEXIST: raise 42 43def try_rmdir_r(path): 44 path = abspath(path) 45 while path.startswith(install_path): 46 try: 47 os.rmdir(path) 48 except OSError as e: 49 if e.errno == errno.ENOTEMPTY: return 50 if e.errno == errno.ENOENT: return 51 raise 52 path = abspath(path, '..') 53 54def mkpaths(path, dst): 55 if dst.endswith('/'): 56 target_path = abspath(install_path, dst, os.path.basename(path)) 57 else: 58 target_path = abspath(install_path, dst) 59 return path, target_path 60 61def try_copy(path, dst): 62 source_path, target_path = mkpaths(path, dst) 63 print('installing %s' % target_path) 64 try_mkdir_r(os.path.dirname(target_path)) 65 try_unlink(target_path) # prevent ETXTBSY errors 66 return shutil.copy2(source_path, target_path) 67 68def try_remove(path, dst): 69 source_path, target_path = mkpaths(path, dst) 70 print('removing %s' % target_path) 71 try_unlink(target_path) 72 try_rmdir_r(os.path.dirname(target_path)) 73 74def install(paths, dst): 75 for path in paths: 76 try_copy(path, dst) 77 78def uninstall(paths, dst): 79 for path in paths: 80 try_remove(path, dst) 81 82def npm_files(action): 83 target_path = 'lib/node_modules/npm/' 84 85 # don't install npm if the target path is a symlink, it probably means 86 # that a dev version of npm is installed there 87 if os.path.islink(abspath(install_path, target_path)): return 88 89 # npm has a *lot* of files and it'd be a pain to maintain a fixed list here 90 # so we walk its source directory instead... 91 for dirname, subdirs, basenames in os.walk('deps/npm', topdown=True): 92 subdirs[:] = [subdir for subdir in subdirs if subdir != 'test'] 93 paths = [os.path.join(dirname, basename) for basename in basenames] 94 action(paths, target_path + dirname[9:] + '/') 95 96 # create/remove symlink 97 link_path = abspath(install_path, 'bin/npm') 98 if action == uninstall: 99 action([link_path], 'bin/npm') 100 elif action == install: 101 try_symlink('../lib/node_modules/npm/bin/npm-cli.js', link_path) 102 else: 103 assert 0 # unhandled action type 104 105 # create/remove symlink 106 link_path = abspath(install_path, 'bin/npx') 107 if action == uninstall: 108 action([link_path], 'bin/npx') 109 elif action == install: 110 try_symlink('../lib/node_modules/npm/bin/npx-cli.js', link_path) 111 else: 112 assert 0 # unhandled action type 113 114def subdir_files(path, dest, action): 115 ret = {} 116 for dirpath, dirnames, filenames in os.walk(path): 117 files_in_path = [dirpath + '/' + f for f in filenames if f.endswith('.h')] 118 ret[dest + dirpath.replace(path, '')] = files_in_path 119 for subdir, files_in_path in ret.items(): 120 action(files_in_path, subdir + '/') 121 122def files(action): 123 is_windows = sys.platform == 'win32' 124 output_file = 'node' 125 output_prefix = 'out/Release/' 126 127 if 'false' == variables.get('node_shared'): 128 if is_windows: 129 output_file += '.exe' 130 else: 131 if is_windows: 132 output_file += '.dll' 133 else: 134 output_file = 'lib' + output_file + '.' + variables.get('shlib_suffix') 135 # GYP will output to lib.target except on OS X, this is hardcoded 136 # in its source - see the _InstallableTargetInstallPath function. 137 if sys.platform != 'darwin': 138 output_prefix += 'lib.target/' 139 140 if 'false' == variables.get('node_shared'): 141 action([output_prefix + output_file], 'bin/' + output_file) 142 else: 143 action([output_prefix + output_file], 'lib/' + output_file) 144 145 if 'true' == variables.get('node_use_dtrace'): 146 action(['out/Release/node.d'], 'lib/dtrace/node.d') 147 148 # behave similarly for systemtap 149 action(['src/node.stp'], 'share/systemtap/tapset/') 150 151 action(['deps/v8/tools/gdbinit'], 'share/doc/node/') 152 action(['deps/v8/tools/lldb_commands.py'], 'share/doc/node/') 153 154 if 'freebsd' in sys.platform or 'openbsd' in sys.platform: 155 action(['doc/node.1'], 'man/man1/') 156 else: 157 action(['doc/node.1'], 'share/man/man1/') 158 159 if 'true' == variables.get('node_install_npm'): npm_files(action) 160 161 headers(action) 162 163def headers(action): 164 def ignore_inspector_headers(files_arg, dest): 165 inspector_headers = [ 166 'deps/v8/include/v8-inspector.h', 167 'deps/v8/include/v8-inspector-protocol.h' 168 ] 169 files_arg = [name for name in files_arg if name not in inspector_headers] 170 action(files_arg, dest) 171 172 action([ 173 'common.gypi', 174 'config.gypi', 175 'src/node.h', 176 'src/node_api.h', 177 'src/js_native_api.h', 178 'src/js_native_api_types.h', 179 'src/node_api_types.h', 180 'src/node_buffer.h', 181 'src/node_object_wrap.h', 182 'src/node_version.h', 183 ], 'include/node/') 184 185 # Add the expfile that is created on AIX 186 if sys.platform.startswith('aix'): 187 action(['out/Release/node.exp'], 'include/node/') 188 189 subdir_files('deps/v8/include', 'include/node/', ignore_inspector_headers) 190 191 if 'false' == variables.get('node_shared_libuv'): 192 subdir_files('deps/uv/include', 'include/node/', action) 193 194 if 'true' == variables.get('node_use_openssl') and \ 195 'false' == variables.get('node_shared_openssl'): 196 subdir_files('deps/openssl/openssl/include/openssl', 'include/node/openssl/', action) 197 subdir_files('deps/openssl/config/archs', 'include/node/openssl/archs', action) 198 subdir_files('deps/openssl/config', 'include/node/openssl', action) 199 200 if 'false' == variables.get('node_shared_zlib'): 201 action([ 202 'deps/zlib/zconf.h', 203 'deps/zlib/zlib.h', 204 ], 'include/node/') 205 206def run(args): 207 global node_prefix, install_path, target_defaults, variables 208 209 # chdir to the project's top-level directory 210 os.chdir(abspath(os.path.dirname(__file__), '..')) 211 212 conf = load_config() 213 variables = conf['variables'] 214 target_defaults = conf['target_defaults'] 215 216 # argv[2] is a custom install prefix for packagers (think DESTDIR) 217 # argv[3] is a custom install prefix (think PREFIX) 218 # Difference is that dst_dir won't be included in shebang lines etc. 219 dst_dir = args[2] if len(args) > 2 else '' 220 221 if len(args) > 3: 222 node_prefix = args[3] 223 224 # install_path thus becomes the base target directory. 225 install_path = dst_dir + node_prefix + '/' 226 227 cmd = args[1] if len(args) > 1 else 'install' 228 229 if os.environ.get('HEADERS_ONLY'): 230 if cmd == 'install': return headers(install) 231 if cmd == 'uninstall': return headers(uninstall) 232 else: 233 if cmd == 'install': return files(install) 234 if cmd == 'uninstall': return files(uninstall) 235 236 raise RuntimeError('Bad command: %s\n' % cmd) 237 238if __name__ == '__main__': 239 run(sys.argv[:]) 240