2d00cf06d7398ce0154bb4ddd7999210fae0f86e
[platform/upstream/nodejs.git] / tools / install.py
1 #!/usr/bin/env python
2
3 import errno
4
5 try:
6   import json
7 except ImportError:
8   import simplejson as json
9
10 import os
11 import re
12 import shutil
13 import sys
14
15 # set at init time
16 node_prefix = '/usr/local' # PREFIX variable from Makefile
17 install_path = None # base target directory (DESTDIR + PREFIX from Makefile)
18 target_defaults = None
19 variables = None
20
21 def abspath(*args):
22   path = os.path.join(*args)
23   return os.path.abspath(path)
24
25 def load_config():
26   s = open('config.gypi').read()
27   s = re.sub(r'#.*?\n', '', s) # strip comments
28   s = re.sub(r'\'', '"', s) # convert quotes
29   return json.loads(s)
30
31 def try_unlink(path):
32   try:
33     os.unlink(path)
34   except OSError, e:
35     if e.errno != errno.ENOENT: raise
36
37 def try_symlink(source_path, link_path):
38   print 'symlinking %s -> %s' % (source_path, link_path)
39   try_unlink(link_path)
40   os.symlink(source_path, link_path)
41
42 def try_mkdir_r(path):
43   try:
44     os.makedirs(path)
45   except OSError, e:
46     if e.errno != errno.EEXIST: raise
47
48 def try_rmdir_r(path):
49   path = abspath(path)
50   while path.startswith(install_path):
51     try:
52       os.rmdir(path)
53     except OSError, e:
54       if e.errno == errno.ENOTEMPTY: return
55       if e.errno == errno.ENOENT: return
56       raise
57     path = abspath(path, '..')
58
59 def mkpaths(path, dst):
60   if dst.endswith('/'):
61     target_path = abspath(install_path, dst, os.path.basename(path))
62   else:
63     target_path = abspath(install_path, dst)
64   return path, target_path
65
66 def try_copy(path, dst):
67   source_path, target_path = mkpaths(path, dst)
68   print 'installing %s' % target_path
69   try_mkdir_r(os.path.dirname(target_path))
70   try_unlink(target_path) # prevent ETXTBSY errors
71   return shutil.copy2(source_path, target_path)
72
73 def try_remove(path, dst):
74   source_path, target_path = mkpaths(path, dst)
75   print 'removing %s' % target_path
76   try_unlink(target_path)
77   try_rmdir_r(os.path.dirname(target_path))
78
79 def install(paths, dst): map(lambda path: try_copy(path, dst), paths)
80 def uninstall(paths, dst): map(lambda path: try_remove(path, dst), paths)
81
82 def update_shebang(path, shebang):
83   print 'updating shebang of %s to %s' % (path, shebang)
84   s = open(path, 'r').read()
85   s = re.sub(r'#!.*\n', '#!' + shebang + '\n', s)
86   open(path, 'w').write(s)
87
88 def npm_files(action):
89   target_path = 'lib/node_modules/npm/'
90
91   # don't install npm if the target path is a symlink, it probably means
92   # that a dev version of npm is installed there
93   if os.path.islink(abspath(install_path, target_path)): return
94
95   # npm has a *lot* of files and it'd be a pain to maintain a fixed list here
96   # so we walk its source directory instead...
97   for dirname, subdirs, basenames in os.walk('deps/npm', topdown=True):
98     subdirs[:] = filter('test'.__ne__, subdirs) # skip test suites
99     paths = [os.path.join(dirname, basename) for basename in basenames]
100     action(paths, target_path + dirname[9:] + '/')
101
102   # create/remove symlink
103   link_path = abspath(install_path, 'bin/npm')
104   if action == uninstall:
105     action([link_path], 'bin/npm')
106   elif action == install:
107     try_symlink('../lib/node_modules/npm/bin/npm-cli.js', link_path)
108     if os.environ.get('PORTABLE'):
109       # This crazy hack is necessary to make the shebang execute the copy
110       # of node relative to the same directory as the npm script. The precompiled
111       # binary tarballs use a prefix of "/" which gets translated to "/bin/iojs"
112       # in the regular shebang modifying logic, which is incorrect since the
113       # precompiled bundle should be able to be extracted anywhere and "just work"
114       shebang = '/bin/sh\n// 2>/dev/null; exec "`dirname "$0"`/iojs" "$0" "$@"'
115     else:
116       shebang = os.path.join(node_prefix or '/', 'bin/iojs')
117     update_shebang(link_path, shebang)
118   else:
119     assert(0) # unhandled action type
120
121 def subdir_files(path, dest, action):
122   ret = {}
123   for dirpath, dirnames, filenames in os.walk(path):
124     files = [dirpath + '/' + f for f in filenames if f.endswith('.h')]
125     ret[dest + dirpath.replace(path, '')] = files
126   for subdir, files in ret.items():
127     action(files, subdir + '/')
128
129 def files(action):
130   is_windows = sys.platform == 'win32'
131
132   exeext = '.exe' if is_windows else ''
133   action(['out/Release/iojs' + exeext], 'bin/iojs' + exeext)
134
135   if not is_windows:
136     # Install iojs -> node compatibility symlink.
137     link_target = 'bin/node'
138     link_path = abspath(install_path, link_target)
139     if action == uninstall:
140       action([link_path], link_target)
141     elif action == install:
142       try_symlink('iojs', link_path)
143     else:
144       assert(0)  # Unhandled action type.
145
146   if 'true' == variables.get('node_use_dtrace'):
147     action(['out/Release/node.d'], 'lib/dtrace/node.d')
148
149   # behave similarly for systemtap
150   action(['src/node.stp'], 'share/systemtap/tapset/')
151
152   if 'freebsd' in sys.platform or 'openbsd' in sys.platform:
153     action(['doc/iojs.1'], 'man/man1/')
154   else:
155     action(['doc/iojs.1'], 'share/man/man1/')
156
157   if 'true' == variables.get('node_install_npm'): npm_files(action)
158
159   action([
160     'common.gypi',
161     'config.gypi',
162     'src/node.h',
163     'src/node_buffer.h',
164     'src/node_internals.h',
165     'src/node_object_wrap.h',
166     'src/node_version.h',
167     'src/smalloc.h',
168   ], 'include/node/')
169
170   subdir_files('deps/cares/include', 'include/node/', action)
171
172   if 'false' == variables.get('node_shared_libuv'):
173     subdir_files('deps/uv/include', 'include/node/', action)
174
175   if 'false' == variables.get('node_shared_openssl'):
176     subdir_files('deps/openssl/openssl/include/openssl', 'include/node/openssl/', action)
177     action(['deps/openssl/config/opensslconf.h'], 'include/node/openssl/')
178
179   if 'false' == variables.get('node_shared_v8'):
180     subdir_files('deps/v8/include', 'include/node/', action)
181
182   if 'false' == variables.get('node_shared_zlib'):
183     action([
184       'deps/zlib/zconf.h',
185       'deps/zlib/zlib.h',
186     ], 'include/node/')
187
188 def run(args):
189   global node_prefix, install_path, target_defaults, variables
190
191   # chdir to the project's top-level directory
192   os.chdir(abspath(os.path.dirname(__file__), '..'))
193
194   conf = load_config()
195   variables = conf['variables']
196   target_defaults = conf['target_defaults']
197
198   # argv[2] is a custom install prefix for packagers (think DESTDIR)
199   # argv[3] is a custom install prefix (think PREFIX)
200   # Difference is that dst_dir won't be included in shebang lines etc.
201   if len(args) > 2:
202     dst_dir = args[2]
203   if len(args) > 3:
204     node_prefix = args[3]
205
206   # install_path thus becomes the base target directory.
207   install_path = dst_dir + node_prefix + '/'
208
209   cmd = args[1] if len(args) > 1 else 'install'
210   if cmd == 'install': return files(install)
211   if cmd == 'uninstall': return files(uninstall)
212   raise RuntimeError('Bad command: %s\n' % cmd)
213
214 if __name__ == '__main__':
215   run(sys.argv[:])