net: `Server.listen`, `Server.close` and `Socket.connect` return `this`
[platform/upstream/nodejs.git] / wscript
1 #!/usr/bin/env python
2
3 # Copyright Joyent, Inc. and other Node contributors.
4 #
5 # Permission is hereby granted, free of charge, to any person obtaining a
6 # copy of this software and associated documentation files (the
7 # "Software"), to deal in the Software without restriction, including
8 # without limitation the rights to use, copy, modify, merge, publish,
9 # distribute, sublicense, and/or sell copies of the Software, and to permit
10 # persons to whom the Software is furnished to do so, subject to the
11 # following conditions:
12 #
13 # The above copyright notice and this permission notice shall be included
14 # in all copies or substantial portions of the Software.
15 #
16 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
17 # OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
19 # NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
20 # DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
21 # OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
22 # USE OR OTHER DEALINGS IN THE SOFTWARE.
23
24 import re
25 import Options
26 import sys, os, shutil, glob
27 import Utils
28 from Utils import cmd_output
29 from os.path import join, dirname, abspath, normpath
30 from logging import fatal
31
32 cwd = os.getcwd()
33 APPNAME="node.js"
34
35 # Use the directory that this file is found in to find the tools
36 # directory where the js2c.py file can be found.
37 sys.path.append(sys.argv[0] + '/tools');
38 import js2c
39
40 if sys.platform.startswith("cygwin"):
41   print "cygwin not supported"
42   sys.exit(1)
43
44 srcdir = '.'
45 blddir = 'out'
46 supported_archs = ('arm', 'ia32', 'x64') # 'mips' supported by v8, but not node
47
48 jobs=1
49 if os.environ.has_key('JOBS'):
50   jobs = int(os.environ['JOBS'])
51
52 def safe_path(path):
53   return path.replace("\\", "/")
54
55 def canonical_cpu_type(arch):
56   m = {'x86': 'ia32', 'i386':'ia32', 'x86_64':'x64', 'amd64':'x64'}
57   if arch in m: arch = m[arch]
58   if not arch in supported_archs:
59     raise Exception("supported architectures are "+', '.join(supported_archs)+\
60                     " but NOT '" + arch + "'.")
61   return arch
62
63 def set_options(opt):
64   # the gcc module provides a --debug-level option
65   opt.tool_options('compiler_cxx')
66   opt.tool_options('compiler_cc')
67   opt.tool_options('misc')
68   opt.add_option( '--libdir'
69                 , action='store'
70                 , type='string'
71                 , default=False
72                 , help='Install into this libdir [Release: ${PREFIX}/lib]'
73                 )
74   opt.add_option( '--debug'
75                 , action='store_true'
76                 , default=False
77                 , help='Build debug variant [Release: False]'
78                 , dest='debug'
79                 )
80   opt.add_option( '--profile'
81                 , action='store_true'
82                 , default=False
83                 , help='Enable profiling [Release: False]'
84                 , dest='profile'
85                 )
86   opt.add_option( '--efence'
87                 , action='store_true'
88                 , default=False
89                 , help='Build with -lefence for debugging [Release: False]'
90                 , dest='efence'
91                 )
92
93   opt.add_option( '--without-snapshot'
94                 , action='store_true'
95                 , default=False
96                 , help='Build without snapshotting V8 libraries. You might want to set this for cross-compiling. [Release: False]'
97                 , dest='without_snapshot'
98                 )
99
100   opt.add_option( '--without-ssl'
101                 , action='store_true'
102                 , default=False
103                 , help='Build without SSL'
104                 , dest='without_ssl'
105                 )
106
107
108   opt.add_option('--shared-v8'
109                 , action='store_true'
110                 , default=False
111                 , help='Link to a shared V8 DLL instead of static linking'
112                 , dest='shared_v8'
113                 )
114
115   opt.add_option( '--shared-v8-includes'
116                 , action='store'
117                 , default=False
118                 , help='Directory containing V8 header files'
119                 , dest='shared_v8_includes'
120                 )
121
122   opt.add_option( '--shared-v8-libpath'
123                 , action='store'
124                 , default=False
125                 , help='A directory to search for the shared V8 DLL'
126                 , dest='shared_v8_libpath'
127                 )
128
129   opt.add_option( '--shared-v8-libname'
130                 , action='store'
131                 , default=False
132                 , help="Alternative lib name to link to (default: 'v8')"
133                 , dest='shared_v8_libname'
134                 )
135
136   opt.add_option( '--openssl-includes'
137                 , action='store'
138                 , default=False
139                 , help='A directory to search for the OpenSSL includes'
140                 , dest='openssl_includes'
141                 )
142
143   opt.add_option( '--openssl-libpath'
144                 , action='store'
145                 , default=False
146                 , help="A directory to search for the OpenSSL libraries"
147                 , dest='openssl_libpath'
148                 )
149
150   opt.add_option( '--no-ssl2'
151                 , action='store_true'
152                 , default=False
153                 , help="Disable OpenSSL v2"
154                 , dest='openssl_nov2'
155                 )
156
157   opt.add_option( '--gdb'
158                 , action='store_true'
159                 , default=False
160                 , help="add gdb support"
161                 , dest='use_gdbjit'
162                 )
163
164
165   opt.add_option( '--shared-zlib'
166                 , action='store_true'
167                 , default=False
168                 , help='Link to a shared zlib DLL instead of static linking'
169                 , dest='shared_zlib'
170                 )
171
172   opt.add_option( '--shared-zlib-includes'
173                 , action='store'
174                 , default=False
175                 , help='Directory containing zlib header files'
176                 , dest='shared_zlib_includes'
177                 )
178
179   opt.add_option( '--shared-zlib-libpath'
180                 , action='store'
181                 , default=False
182                 , help='A directory to search for the shared zlib DLL'
183                 , dest='shared_zlib_libpath'
184                 )
185
186
187   opt.add_option( '--shared-cares'
188                 , action='store_true'
189                 , default=False
190                 , help='Link to a shared C-Ares DLL instead of static linking'
191                 , dest='shared_cares'
192                 )
193
194   opt.add_option( '--shared-cares-includes'
195                 , action='store'
196                 , default=False
197                 , help='Directory containing C-Ares header files'
198                 , dest='shared_cares_includes'
199                 )
200
201   opt.add_option( '--shared-cares-libpath'
202                 , action='store'
203                 , default=False
204                 , help='A directory to search for the shared C-Ares DLL'
205                 , dest='shared_cares_libpath'
206                 )
207
208
209   opt.add_option( '--with-dtrace'
210                 , action='store_true'
211                 , default=False
212                 , help='Build with DTrace (experimental)'
213                 , dest='dtrace'
214                 )
215  
216
217   opt.add_option( '--product-type'
218                 , action='store'
219                 , default='program'
220                 , help='What kind of product to produce (program, cstaticlib '\
221                        'or cshlib) [default: %default]'
222                 , dest='product_type'
223                 )
224
225   opt.add_option( '--dest-cpu'
226                 , action='store'
227                 , default=None
228                 , help='CPU architecture to build for. Valid values are: '+\
229                        ', '.join(supported_archs)
230                 , dest='dest_cpu'
231                 )
232
233 def get_node_version():
234   def get_define_value(lines, define):
235     for line in lines:
236       if define in line:
237         return line.split()[-1] #define <NAME> <VALUE>
238
239   lines = open("src/node_version.h").readlines()
240   node_major_version = get_define_value(lines, 'NODE_MAJOR_VERSION')
241   node_minor_version = get_define_value(lines, 'NODE_MINOR_VERSION')
242   node_patch_version = get_define_value(lines, 'NODE_PATCH_VERSION')
243   node_is_release    = get_define_value(lines, 'NODE_VERSION_IS_RELEASE')
244
245   return "%s.%s.%s%s" % ( node_major_version,
246                            node_minor_version,
247                            node_patch_version,
248                            node_is_release == "0" and "-pre" or ""
249                          )
250
251
252
253 def configure(conf):
254   conf.check_tool('compiler_cxx')
255   if not conf.env.CXX: conf.fatal('c++ compiler not found')
256   conf.check_tool('compiler_cc')
257   if not conf.env.CC: conf.fatal('c compiler not found')
258
259   o = Options.options
260
261   if o.libdir:
262     conf.env['LIBDIR'] = o.libdir
263   else:
264     conf.env['LIBDIR'] = conf.env['PREFIX'] + '/lib'
265
266   conf.env["USE_DEBUG"] = o.debug
267   # Snapshot building does noet seem to work on mingw32
268   conf.env["SNAPSHOT_V8"] = not o.without_snapshot and not sys.platform.startswith("win32")
269   if sys.platform.startswith("sunos"):
270     conf.env["SNAPSHOT_V8"] = False
271   conf.env["USE_PROFILING"] = o.profile
272
273   conf.env["USE_SHARED_V8"] = o.shared_v8 or o.shared_v8_includes or o.shared_v8_libpath or o.shared_v8_libname
274   conf.env["USE_SHARED_CARES"] = o.shared_cares or o.shared_cares_includes or o.shared_cares_libpath
275   conf.env["USE_SHARED_ZLIB"] = o.shared_zlib or o.shared_zlib_includes or o.shared_zlib_libpath
276
277   conf.env["USE_GDBJIT"] = o.use_gdbjit
278
279   if not conf.env["USE_SHARED_ZLIB"] and not sys.platform.startswith("win32"):
280     conf.env.append_value("LINKFLAGS", "-lz")
281
282   conf.check(lib='dl', uselib_store='DL')
283   if not sys.platform.startswith("sunos") and not sys.platform.startswith("win32"):
284     conf.env.append_value("CCFLAGS", "-rdynamic")
285     conf.env.append_value("LINKFLAGS_DL", "-rdynamic")
286
287   if sys.platform.startswith("freebsd") or sys.platform.startswith("openbsd"):
288     conf.check(lib='kvm', uselib_store='KVM')
289
290   #if Options.options.debug:
291   #  conf.check(lib='profiler', uselib_store='PROFILER')
292
293   if Options.options.dtrace:
294     if not sys.platform.startswith("sunos"):
295       conf.fatal('DTrace support only currently available on Solaris')
296
297     conf.find_program('dtrace', var='DTRACE', mandatory=True)
298     conf.env["USE_DTRACE"] = True
299     conf.env.append_value("CXXFLAGS", "-DHAVE_DTRACE=1")
300
301   if Options.options.efence:
302     conf.check(lib='efence', libpath=['/usr/lib', '/usr/local/lib'], uselib_store='EFENCE')
303
304   if sys.platform.startswith("freebsd"):
305      if not conf.check(lib="execinfo",
306                        includes=['/usr/include', '/usr/local/include'],
307                        libpath=['/usr/lib', '/usr/local/lib'],
308                        uselib_store="EXECINFO"):
309        conf.fatal("Install the libexecinfo port from /usr/ports/devel/libexecinfo.")
310
311   if not Options.options.without_ssl:
312     # Don't override explicitly supplied openssl paths with pkg-config results.
313     explicit_openssl = o.openssl_includes or o.openssl_libpath
314
315     # Disable ssl v2 methods
316     if o.openssl_nov2:
317       conf.env.append_value("CPPFLAGS", "-DOPENSSL_NO_SSL2=1")
318
319     if not explicit_openssl and conf.check_cfg(package='openssl',
320                                                args='--cflags --libs',
321                                                uselib_store='OPENSSL'):
322       Options.options.use_openssl = conf.env["USE_OPENSSL"] = True
323       conf.env.append_value("CPPFLAGS", "-DHAVE_OPENSSL=1")
324     else:
325       if o.openssl_libpath: 
326         openssl_libpath = [o.openssl_libpath]
327       elif not sys.platform.startswith('win32'):
328         openssl_libpath = ['/usr/lib', '/usr/local/lib', '/opt/local/lib', '/usr/sfw/lib']
329       else:
330         openssl_libpath = [normpath(join(cwd, '../openssl'))]
331
332       if o.openssl_includes: 
333         openssl_includes = [o.openssl_includes]
334       elif not sys.platform.startswith('win32'):
335         openssl_includes = [];
336       else:
337         openssl_includes = [normpath(join(cwd, '../openssl/include'))];
338
339       openssl_lib_names = ['ssl', 'crypto']
340       if sys.platform.startswith('win32'):
341         openssl_lib_names += ['ws2_32', 'gdi32']
342
343       libssl = conf.check_cc(lib=openssl_lib_names,
344                              header_name='openssl/ssl.h',
345                              function_name='SSL_library_init',
346                              includes=openssl_includes,
347                              libpath=openssl_libpath,
348                              uselib_store='OPENSSL')
349
350       libcrypto = conf.check_cc(lib='crypto',
351                                 header_name='openssl/crypto.h',
352                                 includes=openssl_includes,
353                                 libpath=openssl_libpath,
354                                 uselib_store='OPENSSL')
355
356       if libcrypto and libssl:
357         conf.env["USE_OPENSSL"] = Options.options.use_openssl = True
358         conf.env.append_value("CPPFLAGS", "-DHAVE_OPENSSL=1")
359       elif sys.platform.startswith('win32'):
360         conf.fatal("Could not autodetect OpenSSL support. " +
361                    "Use the --openssl-libpath and --openssl-includes options to set the search path. " +
362                    "Use configure --without-ssl to disable this message.")
363       else:
364         conf.fatal("Could not autodetect OpenSSL support. " +
365                    "Make sure OpenSSL development packages are installed. " +
366                    "Use configure --without-ssl to disable this message.")
367   else:
368     Options.options.use_openssl = conf.env["USE_OPENSSL"] = False
369
370   conf.check(lib='util', libpath=['/usr/lib', '/usr/local/lib'],
371              uselib_store='UTIL')
372
373   # normalize DEST_CPU from --dest-cpu, DEST_CPU or built-in value
374   if Options.options.dest_cpu and Options.options.dest_cpu:
375     conf.env['DEST_CPU'] = canonical_cpu_type(Options.options.dest_cpu)
376   elif 'DEST_CPU' in os.environ and os.environ['DEST_CPU']:
377     conf.env['DEST_CPU'] = canonical_cpu_type(os.environ['DEST_CPU'])
378   elif 'DEST_CPU' in conf.env and conf.env['DEST_CPU']:
379     conf.env['DEST_CPU'] = canonical_cpu_type(conf.env['DEST_CPU'])
380
381   have_librt = conf.check(lib='rt', uselib_store='RT')
382
383   have_monotonic = False
384   if have_librt:
385     code =  """
386       #include <time.h>
387       int main(void) {
388         struct timespec now;
389         clock_gettime(CLOCK_MONOTONIC, &now);
390         return 0;
391       }
392     """
393     have_monotonic = conf.check_cc(lib="rt", msg="Checking for CLOCK_MONOTONIC", fragment=code)
394
395   if have_monotonic:
396     conf.env.append_value('CPPFLAGS', '-DHAVE_MONOTONIC_CLOCK=1')
397   else:
398     conf.env.append_value('CPPFLAGS', '-DHAVE_MONOTONIC_CLOCK=0')
399
400   if sys.platform.startswith("sunos"):
401     code =  """
402       #include <ifaddrs.h>
403       int main(void) {
404         struct ifaddrs hello;
405         return 0;
406       }
407     """
408
409     if conf.check_cc(msg="Checking for ifaddrs on solaris", fragment=code):
410       conf.env.append_value('CPPFLAGS',  '-DSUNOS_HAVE_IFADDRS')
411
412     if not conf.check(lib='socket', uselib_store="SOCKET"):
413       conf.fatal("Cannot find socket library")
414     if not conf.check(lib='nsl', uselib_store="NSL"):
415       conf.fatal("Cannot find nsl library")
416     if not conf.check(lib='kstat', uselib_store="KSTAT"):
417       conf.fatal("Cannot find kstat library")
418
419   if conf.env['USE_SHARED_V8']:
420     v8_includes = [];
421     if o.shared_v8_includes: v8_includes.append(o.shared_v8_includes);
422
423     v8_libpath = [];
424     if o.shared_v8_libpath: v8_libpath.append(o.shared_v8_libpath);
425
426     if not o.shared_v8_libname: o.shared_v8_libname = 'v8'
427
428     if not conf.check_cxx(lib=o.shared_v8_libname, header_name='v8.h',
429                           uselib_store='V8',
430                           includes=v8_includes,
431                           libpath=v8_libpath):
432       conf.fatal("Cannot find v8")
433
434     if o.debug:
435       if not conf.check_cxx(lib=o.shared_v8_libname + '_g', header_name='v8.h',
436                             uselib_store='V8_G',
437                             includes=v8_includes,
438                             libpath=v8_libpath):
439         conf.fatal("Cannot find v8_g")
440
441   conf.define("HAVE_CONFIG_H", 1)
442
443   if sys.platform.startswith("sunos"):
444     conf.env.append_value ('CCFLAGS', '-threads')
445     conf.env.append_value ('CXXFLAGS', '-threads')
446     #conf.env.append_value ('LINKFLAGS', ' -threads')
447   elif not sys.platform.startswith("win32"):
448     threadflags='-pthread'
449     conf.env.append_value ('CCFLAGS', threadflags)
450     conf.env.append_value ('CXXFLAGS', threadflags)
451     conf.env.append_value ('LINKFLAGS', threadflags)
452   if sys.platform.startswith("darwin"):
453     # used by platform_darwin_*.cc
454     conf.env.append_value('LINKFLAGS', ['-framework','Carbon'])
455     # cross compile for architecture specified by DEST_CPU
456     if 'DEST_CPU' in conf.env:
457       arch = conf.env['DEST_CPU']
458       # map supported_archs to GCC names:
459       arch_mappings = {'ia32': 'i386', 'x64': 'x86_64'}
460       if arch in arch_mappings:
461         arch = arch_mappings[arch]
462       flags = ['-arch', arch]
463       conf.env.append_value('CCFLAGS', flags)
464       conf.env.append_value('CXXFLAGS', flags)
465       conf.env.append_value('LINKFLAGS', flags)
466   if 'DEST_CPU' in conf.env:
467     arch = conf.env['DEST_CPU']
468     # TODO: -m32 is only available on 64 bit machines, so check host type
469     flags = None
470     if arch == 'ia32':
471       flags = '-m32'
472     if flags:
473       conf.env.append_value('CCFLAGS', flags)
474       conf.env.append_value('CXXFLAGS', flags)
475       conf.env.append_value('LINKFLAGS', flags)
476
477   # LFS
478   conf.env.append_value('CPPFLAGS',  '-D_LARGEFILE_SOURCE')
479   conf.env.append_value('CPPFLAGS',  '-D_FILE_OFFSET_BITS=64')
480
481   # Makes select on windows support more than 64 FDs
482   if sys.platform.startswith("win32"):
483     conf.env.append_value('CPPFLAGS', '-DFD_SETSIZE=1024');
484
485   ## needed for node_file.cc fdatasync
486   ## Strangely on OSX 10.6 the g++ doesn't see fdatasync but gcc does?
487   code =  """
488     #include <unistd.h>
489     int main(void)
490     {
491        int fd = 0;
492        fdatasync (fd);
493        return 0;
494     }
495   """
496   if conf.check_cxx(msg="Checking for fdatasync(2) with c++", fragment=code):
497     conf.env.append_value('CPPFLAGS', '-DHAVE_FDATASYNC=1')
498   else:
499     conf.env.append_value('CPPFLAGS', '-DHAVE_FDATASYNC=0')
500
501   # arch
502   conf.env.append_value('CPPFLAGS', '-DARCH="' + conf.env['DEST_CPU'] + '"')
503
504   # platform
505   conf.env.append_value('CPPFLAGS', '-DPLATFORM="' + conf.env['DEST_OS'] + '"')
506
507   # posix?
508   if not sys.platform.startswith('win'):
509     conf.env.append_value('CPPFLAGS', '-D__POSIX__=1')
510
511   platform_file = "src/platform_%s.cc" % conf.env['DEST_OS']
512   if os.path.exists(join(cwd, platform_file)):
513     Options.options.platform_file = True
514     conf.env["PLATFORM_FILE"] = platform_file
515   else:
516     Options.options.platform_file = False
517     conf.env["PLATFORM_FILE"] = "src/platform_none.cc"
518
519   if conf.env['USE_PROFILING'] == True:
520     conf.env.append_value('CPPFLAGS', '-pg')
521     conf.env.append_value('LINKFLAGS', '-pg')
522
523   if sys.platform.startswith("win32"):
524     conf.env.append_value('LIB', 'psapi')
525     conf.env.append_value('LIB', 'winmm')
526     # This enforces ws2_32 to be linked after crypto, otherwise the linker
527     # will run into undefined references from libcrypto.a
528     if not Options.options.use_openssl:
529       conf.env.append_value('LIB', 'ws2_32')
530
531   conf.env.append_value('CPPFLAGS', '-Wno-unused-parameter');
532   conf.env.append_value('CPPFLAGS', '-D_FORTIFY_SOURCE=2');
533
534   # Split off debug variant before adding variant specific defines
535   debug_env = conf.env.copy()
536   conf.set_env_name('Debug', debug_env)
537
538   # Configure debug variant
539   conf.setenv('Debug')
540   debug_env.set_variant('Debug')
541   debug_env.append_value('CPPFLAGS', '-DDEBUG')
542   debug_compile_flags = ['-g', '-O0', '-Wall', '-Wextra']
543   debug_env.append_value('CCFLAGS', debug_compile_flags)
544   debug_env.append_value('CXXFLAGS', debug_compile_flags)
545   conf.write_config_header("config.h")
546
547   # Configure default variant
548   conf.setenv('Release')
549   default_compile_flags = ['-g', '-O3']
550   conf.env.append_value('CCFLAGS', default_compile_flags)
551   conf.env.append_value('CXXFLAGS', default_compile_flags)
552   conf.write_config_header("config.h")
553
554
555 def v8_cmd(bld, variant):
556   scons = join(cwd, 'tools/scons/scons.py')
557   deps_src = join(bld.path.abspath(),"deps")
558   v8dir_src = join(deps_src,"v8")
559
560   # NOTE: We want to compile V8 to export its symbols. I.E. Do not want
561   # -fvisibility=hidden. When using dlopen() it seems that the loaded DSO
562   # cannot see symbols in the executable which are hidden, even if the
563   # executable is statically linked together...
564
565   # XXX Change this when v8 defaults x86_64 to native builds
566   # Possible values are (arm, ia32, x64, mips).
567   arch = ""
568   if bld.env['DEST_CPU']:
569     arch = "arch="+bld.env['DEST_CPU']
570
571   toolchain = "gcc"
572
573   if variant == "Release":
574     mode = "release"
575   else:
576     mode = "debug"
577
578   if bld.env["SNAPSHOT_V8"]:
579     snapshot = "snapshot=on"
580   else:
581     snapshot = ""
582
583   cmd_R = sys.executable + ' "%s" -j %d -C "%s" -Y "%s" visibility=default mode=%s %s toolchain=%s library=static %s'
584
585   cmd = cmd_R % ( scons
586                 , Options.options.jobs
587                 , safe_path(bld.srcnode.abspath(bld.env_of_name(variant)))
588                 , safe_path(v8dir_src)
589                 , mode
590                 , arch
591                 , toolchain
592                 , snapshot
593                 )
594
595   if bld.env["USE_GDBJIT"]:
596     cmd += ' gdbjit=on '
597
598   if sys.platform.startswith("sunos"):
599     cmd += ' toolchain=gcc strictaliasing=off'
600
601
602
603   return ("echo '%s' && " % cmd) + cmd
604
605
606 def build_v8(bld):
607   v8 = bld.new_task_gen(
608     source        = 'deps/v8/SConstruct '
609                     + bld.path.ant_glob('v8/include/*')
610                     + bld.path.ant_glob('v8/src/*'),
611     target        = bld.env["staticlib_PATTERN"] % "v8",
612     rule          = v8_cmd(bld, "Release"),
613     before        = "cxx",
614     install_path  = None)
615
616   v8.env.env = dict(os.environ)
617   v8.env.env['CC'] = sh_escape(bld.env['CC'][0])
618   v8.env.env['CXX'] = sh_escape(bld.env['CXX'][0])
619
620   v8.uselib = "EXECINFO"
621   bld.env["CPPPATH_V8"] = "deps/v8/include"
622   t = join(bld.srcnode.abspath(bld.env_of_name("Release")), v8.target)
623   bld.env_of_name('Release').append_value("LINKFLAGS_V8", t)
624
625   ### v8 debug
626   if bld.env["USE_DEBUG"]:
627     v8_debug = v8.clone("Debug")
628     v8_debug.rule   = v8_cmd(bld, "Debug")
629     v8_debug.target = bld.env["staticlib_PATTERN"] % "v8_g"
630     v8_debug.uselib = "EXECINFO"
631     bld.env["CPPPATH_V8_G"] = "deps/v8/include"
632     t = join(bld.srcnode.abspath(bld.env_of_name("Debug")), v8_debug.target)
633     bld.env_of_name('Debug').append_value("LINKFLAGS_V8_G", t)
634
635   bld.install_files('${PREFIX}/include/node/', 'deps/v8/include/*.h')
636
637 def sh_escape(s):
638   if sys.platform.startswith('win32'):
639     return '"' + s + '"'
640   else:
641     return s.replace("\\", "\\\\").replace("(","\\(").replace(")","\\)").replace(" ","\\ ")
642
643 def uv_cmd(bld, variant):
644   srcdeps = join(bld.path.abspath(), "deps")
645   srcdir = join(srcdeps, "uv")
646   blddir = bld.srcnode.abspath(bld.env_of_name(variant)) + '/deps/uv'
647   #
648   # FIXME This is awful! We're copying the entire source directory into the
649   # build directory before each compile. This could be much improved by
650   # modifying libuv's build to send object files to a separate directory.
651   #
652   cmd = 'cp -r ' + sh_escape(srcdir)  + '/* ' + sh_escape(blddir)
653   if not sys.platform.startswith('win32'):
654     cmd += ' && if [[ -z "$NODE_MAKE" ]]; then NODE_MAKE=make; fi; '
655     cmd += '$NODE_MAKE -C ' + sh_escape(blddir)
656   else:
657     cmd += ' && make -C ' + sh_escape(blddir)
658   cmd += ' clean all'
659   return cmd
660
661
662 def build_uv(bld):
663   uv = bld.new_task_gen(
664     name = 'uv',
665     source = 'deps/uv/include/uv.h',
666     target = 'deps/uv/uv.a',
667     before = "cxx",
668     rule = uv_cmd(bld, 'Release')
669   )
670
671   uv.env.env = dict(os.environ)
672   uv.env.env['CC'] = sh_escape(bld.env['CC'][0])
673   uv.env.env['CXX'] = sh_escape(bld.env['CXX'][0])
674
675   t = join(bld.srcnode.abspath(bld.env_of_name("Release")), uv.target)
676   bld.env_of_name('Release').append_value("LINKFLAGS_UV", t)
677
678   if bld.env["USE_DEBUG"]:
679     uv_debug = uv.clone("Debug")
680     uv_debug.rule = uv_cmd(bld, 'Debug')
681     uv_debug.env.env = dict(os.environ)
682
683     t = join(bld.srcnode.abspath(bld.env_of_name("Debug")), uv_debug.target)
684     bld.env_of_name('Debug').append_value("LINKFLAGS_UV", t)
685
686   bld.install_files('${PREFIX}/include/node/', 'deps/uv/include/*.h')
687   bld.install_files('${PREFIX}/include/node/uv-private', 'deps/uv/include/uv-private/*.h')
688   bld.install_files('${PREFIX}/include/node/ev', 'deps/uv/src/ev/*.h')
689   bld.install_files('${PREFIX}/include/node/c-ares', """
690     deps/uv/include/ares.h
691     deps/uv/include/ares_version.h
692   """)
693
694
695 def build(bld):
696   ## This snippet is to show full commands as WAF executes
697   import Build
698   old = Build.BuildContext.exec_command
699   def exec_command(self, cmd, **kw):
700     if isinstance(cmd, list): print(" ".join(cmd))
701     return old(self, cmd, **kw)
702   Build.BuildContext.exec_command = exec_command
703
704   Options.options.jobs=jobs
705   product_type = Options.options.product_type
706   product_type_is_lib = product_type != 'program'
707
708   print "DEST_OS: " + bld.env['DEST_OS']
709   print "DEST_CPU: " + bld.env['DEST_CPU']
710   print "Parallel Jobs: " + str(Options.options.jobs)
711   print "Product type: " + product_type
712
713   build_uv(bld)
714
715   if not bld.env['USE_SHARED_V8']: build_v8(bld)
716
717
718   ### http_parser
719   http_parser = bld.new_task_gen("cc")
720   http_parser.source = "deps/http_parser/http_parser.c"
721   http_parser.includes = "deps/http_parser/"
722   http_parser.name = "http_parser"
723   http_parser.target = "http_parser"
724   http_parser.install_path = None
725   if bld.env["USE_DEBUG"]:
726     http_parser.clone("Debug")
727   if product_type_is_lib:
728     http_parser.ccflags = '-fPIC'
729
730   ### src/native.cc
731   def make_macros(loc, content):
732     f = open(loc, 'a')
733     f.write(content)
734     f.close
735
736   macros_loc_debug   = join(
737      bld.srcnode.abspath(bld.env_of_name("Debug")),
738      "macros.py"
739   )
740
741   macros_loc_default = join(
742     bld.srcnode.abspath(bld.env_of_name("Release")),
743     "macros.py"
744   )
745
746   ### We need to truncate the macros.py file
747   f = open(macros_loc_debug, 'w')
748   f.close
749   f = open(macros_loc_default, 'w')
750   f.close
751
752   make_macros(macros_loc_debug, "")  # leave debug(x) as is in debug build
753   # replace debug(x) with nothing in release build
754   make_macros(macros_loc_default, "macro debug(x) = ;\n")
755   make_macros(macros_loc_default, "macro assert(x) = ;\n")
756
757   if not bld.env["USE_DTRACE"]:
758     probes = [
759       'DTRACE_HTTP_CLIENT_REQUEST',
760       'DTRACE_HTTP_CLIENT_RESPONSE',
761       'DTRACE_HTTP_SERVER_REQUEST',
762       'DTRACE_HTTP_SERVER_RESPONSE',
763       'DTRACE_NET_SERVER_CONNECTION',
764       'DTRACE_NET_STREAM_END',
765       'DTRACE_NET_SOCKET_READ',
766       'DTRACE_NET_SOCKET_WRITE'
767     ]
768
769     for probe in probes:
770       make_macros(macros_loc_default, "macro %s(x) = ;\n" % probe)
771       make_macros(macros_loc_debug, "macro %s(x) = ;\n" % probe)
772
773   def javascript_in_c(task):
774     env = task.env
775     source = map(lambda x: x.srcpath(env), task.inputs)
776     targets = map(lambda x: x.srcpath(env), task.outputs)
777     source.append(macros_loc_default)
778     js2c.JS2C(source, targets)
779
780   def javascript_in_c_debug(task):
781     env = task.env
782     source = map(lambda x: x.srcpath(env), task.inputs)
783     targets = map(lambda x: x.srcpath(env), task.outputs)
784     source.append(macros_loc_debug)
785     js2c.JS2C(source, targets)
786
787   native_cc = bld.new_task_gen(
788     source='src/node.js ' + bld.path.ant_glob('lib/*.js'),
789     target="src/node_natives.h",
790     before="cxx",
791     install_path=None
792   )
793
794   # Add the rule /after/ cloning the debug
795   # This is a work around for an error had in python 2.4.3 (I'll paste the
796   # error that was had into the git commit meessage. git-blame to find out
797   # where.)
798   if bld.env["USE_DEBUG"]:
799     native_cc_debug = native_cc.clone("Debug")
800     native_cc_debug.rule = javascript_in_c_debug
801
802   native_cc.rule = javascript_in_c_debug
803
804   if bld.env["USE_DTRACE"]:
805     dtrace = bld.new_task_gen(
806       name   = "dtrace",
807       source = "src/node_provider.d",
808       target = "src/node_provider.h",
809       rule   = "%s -x nolibs -h -o ${TGT} -s ${SRC}" % (bld.env.DTRACE),
810       before = "cxx",
811     )
812
813     if bld.env["USE_DEBUG"]:
814       dtrace_g = dtrace.clone("Debug")
815
816     bld.install_files('${LIBDIR}/dtrace', 'src/node.d')
817
818     if sys.platform.startswith("sunos"):
819       #
820       # The USDT DTrace provider works slightly differently on Solaris than on
821       # the Mac; on Solaris, any objects that have USDT DTrace probes must be
822       # post-processed with the DTrace command.  (This is not true on the
823       # Mac, which has first-class linker support for USDT probes.)  On
824       # Solaris, we must therefore post-process our object files.  Waf doesn't
825       # seem to really have a notion for this, so we inject a task after
826       # compiling and before linking, and then find all of the node object
827       # files and shuck them off to dtrace (which will modify them in place
828       # as appropriate).
829       #
830       def dtrace_postprocess(task):
831         abspath = bld.srcnode.abspath(bld.env_of_name(task.env.variant()))
832         objs = glob.glob(abspath + 'src/*.o')
833         source = task.inputs[0].srcpath(task.env)
834         target = task.outputs[0].srcpath(task.env)
835         cmd = '%s -G -x nolibs -s %s -o %s %s' % (task.env.DTRACE,
836                                                   source,
837                                                   target,
838                                                   ' '.join(objs))
839         Utils.exec_command(cmd)
840
841       dtracepost = bld.new_task_gen(
842         name   = "dtrace-postprocess",
843         source = "src/node_provider.d",
844         target = "node_provider.o",
845         always = True,
846         before = "cxx_link",
847         after  = "cxx",
848         rule = dtrace_postprocess
849       )
850
851       t = join(bld.srcnode.abspath(bld.env_of_name("Release")), dtracepost.target)
852       bld.env_of_name('Release').append_value('LINKFLAGS', t)
853
854       #
855       # Note that for the same (mysterious) issue outlined above with respect
856       # to assigning the rule to native_cc/native_cc_debug, we must apply the
857       # rule to dtracepost/dtracepost_g only after they have been cloned.  We
858       # also must put node_provider.o on the link line, but because we
859       # (apparently?) lack LINKFLAGS in debug, we (shamelessly) stowaway on
860       # LINKFLAGS_V8_G.
861       #
862       if bld.env["USE_DEBUG"]:
863         dtracepost_g = dtracepost.clone("Debug")
864         dtracepost_g.rule = dtrace_postprocess
865         t = join(bld.srcnode.abspath(bld.env_of_name("Debug")), dtracepost.target)
866         bld.env_of_name("Debug").append_value('LINKFLAGS_V8_G', t)
867
868
869   ### node lib
870   node = bld.new_task_gen("cxx", product_type)
871   node.name         = "node"
872   node.target       = "node"
873   node.uselib = 'RT OPENSSL ZLIB CARES EXECINFO DL KVM SOCKET NSL KSTAT UTIL OPROFILE'
874   node.add_objects = 'http_parser'
875   if product_type_is_lib:
876     node.install_path = '${LIBDIR}'
877   else:
878     node.install_path = '${PREFIX}/bin'
879   node.chmod = 0755
880   node.source = """
881     src/node.cc
882     src/node_buffer.cc
883     src/node_javascript.cc
884     src/node_extensions.cc
885     src/node_http_parser.cc
886     src/node_constants.cc
887     src/node_file.cc
888     src/node_script.cc
889     src/node_os.cc
890     src/node_dtrace.cc
891     src/node_string.cc
892     src/node_zlib.cc
893     src/timer_wrap.cc
894     src/handle_wrap.cc
895     src/stream_wrap.cc
896     src/tcp_wrap.cc
897     src/udp_wrap.cc
898     src/pipe_wrap.cc
899     src/cares_wrap.cc
900     src/tty_wrap.cc
901     src/fs_event_wrap.cc
902     src/process_wrap.cc
903     src/v8_typed_array.cc
904   """
905
906   if not sys.platform.startswith("win32"):
907     node.source += " src/node_signal_watcher.cc "
908     node.source += " src/node_stat_watcher.cc "
909     node.source += " src/node_io_watcher.cc "
910
911   node.source += bld.env["PLATFORM_FILE"]
912   if not product_type_is_lib:
913     node.source = 'src/node_main.cc '+node.source
914
915   if bld.env["USE_OPENSSL"]: node.source += " src/node_crypto.cc "
916
917   node.includes = """
918     src/
919     deps/http_parser
920     deps/uv/include
921     deps/uv/src/ev
922     deps/uv/src/ares
923   """
924
925   if not bld.env["USE_SHARED_V8"]: node.includes += ' deps/v8/include '
926
927   if os.environ.has_key('RPATH'):
928     node.rpath = os.environ['RPATH']
929
930   if (sys.platform.startswith("win32")):
931     # Static libgcc
932     bld.env.append_value('LINKFLAGS', '-static-libgcc')
933     bld.env.append_value('LINKFLAGS', '-static-libstdc++')
934
935   def subflags(program):
936     x = { 'CCFLAGS'   : " ".join(program.env["CCFLAGS"]).replace('"', '\\"')
937         , 'CPPFLAGS'  : " ".join(program.env["CPPFLAGS"]).replace('"', '\\"')
938         , 'LIBFLAGS'  : " ".join(program.env["LIBFLAGS"]).replace('"', '\\"')
939         , 'PREFIX'    : safe_path(program.env["PREFIX"])
940         , 'VERSION'   : get_node_version()
941         }
942     return x
943
944   # process file.pc.in -> file.pc
945
946   node_conf = bld.new_task_gen('subst', before="cxx")
947   node_conf.source = 'src/node_config.h.in'
948   node_conf.target = 'src/node_config.h'
949   node_conf.dict = subflags(node)
950   node_conf.install_path = '${PREFIX}/include/node'
951
952   if bld.env["USE_DEBUG"]:
953     node_g = node.clone("Debug")
954     node_g.target = "node"
955     node_g.uselib += ' V8_G UV '
956     node_g.install_path = None
957
958     node_conf_g = node_conf.clone("Debug")
959     node_conf_g.dict = subflags(node_g)
960     node_conf_g.install_path = None
961
962   # After creating the debug clone, append the V8 dep
963   node.uselib += ' V8 UV '
964
965   bld.install_files('${PREFIX}/include/node/', """
966     config.h
967     src/node.h
968     src/node_object_wrap.h
969     src/node_buffer.h
970     src/node_version.h
971   """)
972
973   # Only install the man page if it exists.
974   # Do 'make doc install' to build and install it.
975   if os.path.exists('doc/node.1'):
976     bld.install_files('${PREFIX}/share/man/man1/', 'doc/node.1')
977
978   bld.install_files('${PREFIX}/bin/', 'tools/node-waf', chmod=0755)
979   bld.install_files('${LIBDIR}/node/wafadmin', 'tools/wafadmin/*.py')
980   bld.install_files('${LIBDIR}/node/wafadmin/Tools', 'tools/wafadmin/Tools/*.py')
981
982 def shutdown():
983   Options.options.debug
984   # HACK to get binding.node out of build directory.
985   # better way to do this?
986   if Options.commands['configure']:
987     if not Options.options.use_openssl:
988       print "WARNING WARNING WARNING"
989       print "OpenSSL not found. Will compile Node without crypto support!"
990
991     if not Options.options.platform_file:
992       print "WARNING: Platform not fully supported. Using src/platform_none.cc"
993
994   elif not Options.commands['clean']:
995     if sys.platform.startswith("win32"):
996       if os.path.exists('out/Release/node.exe'):
997         os.system('cp out/Release/node.exe .')
998       if os.path.exists('out/Debug/node.exe'):
999         os.system('cp out/Debug/node.exe node_g.exe')
1000     else:
1001       if os.path.exists('out/Release/node') and not os.path.islink('node'):
1002         os.symlink('out/Release/node', 'node')
1003       if os.path.exists('out/Debug/node') and not os.path.islink('node_g'):
1004         os.symlink('out/Debug/node', 'node_g')
1005   else:
1006     if sys.platform.startswith("win32"):
1007       if os.path.exists('node.exe'): os.unlink('node.exe')
1008       if os.path.exists('node_g.exe'): os.unlink('node_g.exe')
1009     else:
1010       if os.path.exists('node'): os.unlink('node')
1011       if os.path.exists('node_g'): os.unlink('node_g')