3 # Thomas Nagy, 2005 (ita)
5 "Module called for configuring, compiling and installing targets"
7 import os, sys, shutil, traceback, datetime, inspect, errno
9 import Utils, Configure, Build, Logs, Options, Environment, Task
10 from Logs import error, warn, info
11 from Constants import *
16 def prepare_impl(t, cwd, ver, wafdir):
18 Options.launch_dir = cwd
20 # some command-line options can be processed immediately
21 if '--version' in sys.argv:
22 opt_obj = Options.Handler()
27 # now find the wscript file
28 msg1 = 'Waf: Please run waf from a directory containing a file named "%s" or run distclean' % WSCRIPT_FILE
30 # in theory projects can be configured in an autotool-like manner:
31 # mkdir build && cd build && ../waf configure && ../waf
32 build_dir_override = None
37 search_for_candidate = True
38 if WSCRIPT_FILE in lst:
41 elif 'configure' in sys.argv and not WSCRIPT_BUILD_FILE in lst:
42 # autotool-like configuration
43 calldir = os.path.abspath(os.path.dirname(sys.argv[0]))
44 if WSCRIPT_FILE in os.listdir(calldir):
46 search_for_candidate = False
48 error('arg[0] directory does not contain a wscript file')
50 build_dir_override = cwd
52 # climb up to find a script if it is not found
53 while search_for_candidate:
55 break # stop at / or c:
56 dirlst = os.listdir(cwd)
57 if WSCRIPT_FILE in dirlst:
59 if 'configure' in sys.argv and candidate:
61 if Options.lockfile in dirlst:
62 env = Environment.Environment()
64 env.load(os.path.join(cwd, Options.lockfile))
66 error('could not load %r' % Options.lockfile)
72 candidate = env['cwd']
74 cwd = os.path.dirname(cwd) # climb up
77 # check if the user only wanted to display the help
78 if '-h' in sys.argv or '--help' in sys.argv:
79 warn('No wscript file found: the help message may be incomplete')
80 opt_obj = Options.Handler()
87 # We have found wscript, but there is no guarantee that it is valid
91 raise Utils.WafError("the folder %r is unreadable" % candidate)
93 # define the main module containing the functions init, shutdown, ..
94 Utils.set_main_module(os.path.join(candidate, WSCRIPT_FILE))
96 if build_dir_override:
97 d = getattr(Utils.g_module, BLDDIR, None)
99 # test if user has set the blddir in wscript.
100 msg = ' Overriding build directory %s with %s' % (d, build_dir_override)
102 Utils.g_module.blddir = build_dir_override
104 # bind a few methods and classes by default
106 def set_def(obj, name=''):
107 n = name or obj.__name__
108 if not n in Utils.g_module.__dict__:
109 setattr(Utils.g_module, n, obj)
111 for k in [dist, distclean, distcheck, clean, install, uninstall]:
114 set_def(Configure.ConfigurationContext, 'configure_context')
116 for k in ['build', 'clean', 'install', 'uninstall']:
117 set_def(Build.BuildContext, k + '_context')
119 # now parse the options from the user wscript file
120 opt_obj = Options.Handler(Utils.g_module)
121 opt_obj.curdir = candidate
123 f = Utils.g_module.set_options
124 except AttributeError:
127 opt_obj.sub_options([''])
130 if not 'init' in Utils.g_module.__dict__:
131 Utils.g_module.init = Utils.nada
132 if not 'shutdown' in Utils.g_module.__dict__:
133 Utils.g_module.shutdown = Utils.nada
137 def prepare(t, cwd, ver, wafdir):
138 if WAFVERSION != ver:
139 msg = 'Version mismatch: waf %s <> wafadmin %s (wafdir %s)' % (ver, WAFVERSION, wafdir)
140 print('\033[91mError: %s\033[0m' % msg)
145 prepare_impl(t, cwd, ver, wafdir)
146 except Utils.WafError, e:
149 except KeyboardInterrupt:
150 Utils.pprint('RED', 'Interrupted')
153 import cProfile, pstats
154 cProfile.runctx("import Scripting; Scripting.prepare_impl(t, cwd, ver, wafdir)", {},
155 {'t': t, 'cwd':cwd, 'ver':ver, 'wafdir':wafdir},
157 p = pstats.Stats('profi.txt')
158 p.sort_stats('time').print_stats(45)
163 commands = Options.arg_line[:]
168 ini = datetime.datetime.now()
174 fun = getattr(Utils.g_module, x, None)
177 raise Utils.WscriptError('No such command %r' % x)
179 ctx = getattr(Utils.g_module, x + '_context', Utils.Context)()
181 if x in ['init', 'shutdown', 'dist', 'distclean', 'distcheck']:
182 # compatibility TODO remove in waf 1.6
191 if not Options.options.progress_bar:
192 ela = ' (%s)' % Utils.get_elapsed_time(ini)
194 if x != 'init' and x != 'shutdown':
195 info('%r finished successfully%s' % (x, ela))
197 if not commands and x != 'shutdown':
198 commands.append('shutdown')
202 src = getattr(Options.options, SRCDIR, None)
203 if not src: src = getattr(Utils.g_module, SRCDIR, None)
204 if not src: src = getattr(Utils.g_module, 'top', None)
208 src = os.path.abspath(src)
210 bld = getattr(Options.options, BLDDIR, None)
211 if not bld: bld = getattr(Utils.g_module, BLDDIR, None)
212 if not bld: bld = getattr(Utils.g_module, 'out', None)
217 raise Utils.WafError('Setting blddir="." may cause distclean problems')
218 bld = os.path.abspath(bld)
220 try: os.makedirs(bld)
223 # It is not possible to compile specific targets in the configuration
224 # this may cause configuration errors if autoconfig is set
225 targets = Options.options.compile_targets
226 Options.options.compile_targets = None
227 Options.is_install = False
233 if 'incomplete_src' in vars():
234 conf.check_message_1('Setting srcdir to')
235 conf.check_message_2(src)
236 if 'incomplete_bld' in vars():
237 conf.check_message_1('Setting blddir to')
238 conf.check_message_2(bld)
240 # calling to main wscript's configure()
241 conf.sub_config([''])
245 # this will write a configure lock so that subsequent builds will
246 # consider the current path as the root directory (see prepare_impl).
247 # to remove: use 'waf distclean'
248 env = Environment.Environment()
251 env['argv'] = sys.argv
252 env['commands'] = Options.commands
253 env['options'] = Options.options.__dict__
255 # conf.hash & conf.files hold wscript files paths and hash
256 # (used only by Configure.autoconfig)
257 env['hash'] = conf.hash
258 env['files'] = conf.files
259 env['environ'] = dict(conf.environ)
260 env['cwd'] = os.path.split(Utils.g_module.root_path)[0]
262 if Utils.g_module.root_path != src:
263 # in case the source dir is somewhere else
264 env.store(os.path.join(src, Options.lockfile))
266 env.store(Options.lockfile)
268 Options.options.compile_targets = targets
271 '''removes the build files'''
273 proj = Environment.Environment(Options.lockfile)
275 raise Utils.WafError('Nothing to clean (project not configured)')
277 bld.load_dirs(proj[SRCDIR], proj[BLDDIR])
280 bld.is_install = 0 # False
282 # read the scripts - and set the path to the wscript path (useful for srcdir='/foo/bar')
283 bld.add_subdirs([os.path.split(Utils.g_module.root_path)[0]])
290 def check_configured(bld):
291 if not Configure.autoconfig:
294 conf_cls = getattr(Utils.g_module, 'configure_context', Utils.Context)
295 bld_cls = getattr(Utils.g_module, 'build_context', Utils.Context)
298 back = (Options.commands, Options.options.__dict__, Logs.zones, Logs.verbose)
300 Options.commands = proj['commands']
301 Options.options.__dict__ = proj['options']
303 conf.environ = proj['environ']
306 (Options.commands, Options.options.__dict__, Logs.zones, Logs.verbose) = back
309 proj = Environment.Environment(Options.lockfile)
316 bld.load_dirs(proj[SRCDIR], proj[BLDDIR])
318 except Utils.WafError:
323 proj = Environment.Environment(Options.lockfile)
325 raise Utils.WafError('Auto-config: project does not configure (bug)')
329 for file in proj['files']:
330 if file.endswith('configure'):
331 h = hash((h, Utils.readf(file)))
333 mod = Utils.load_module(file)
334 h = hash((h, mod.waf_hash_val))
335 except (OSError, IOError):
336 warn('Reconfiguring the project: a file is unavailable')
339 if (h != proj['hash']):
340 warn('Reconfiguring the project: the configuration has changed')
346 '''installs the build files'''
347 bld = check_configured(bld)
349 Options.commands['install'] = True
350 Options.commands['uninstall'] = False
351 Options.is_install = True
353 bld.is_install = INSTALL
359 '''removes the installed files'''
360 Options.commands['install'] = False
361 Options.commands['uninstall'] = True
362 Options.is_install = True
364 bld.is_install = UNINSTALL
367 def runnable_status(self):
369 setattr(Task.Task, 'runnable_status_back', Task.Task.runnable_status)
370 setattr(Task.Task, 'runnable_status', runnable_status)
375 setattr(Task.Task, 'runnable_status', Task.Task.runnable_status_back)
378 bld = check_configured(bld)
380 Options.commands['install'] = False
381 Options.commands['uninstall'] = False
382 Options.is_install = False
384 bld.is_install = 0 # False
386 return build_impl(bld)
389 # compile the project and/or install the files
391 proj = Environment.Environment(Options.lockfile)
393 raise Utils.WafError("Project not configured (run 'waf configure' first)")
395 bld.load_dirs(proj[SRCDIR], proj[BLDDIR])
398 info("Waf: Entering directory `%s'" % bld.bldnode.abspath())
399 bld.add_subdirs([os.path.split(Utils.g_module.root_path)[0]])
401 # execute something immediately before the build starts
407 if Options.options.progress_bar: print('')
408 info("Waf: Leaving directory `%s'" % bld.bldnode.abspath())
410 # execute something immediately after a successful build
415 excludes = '.bzr .bzrignore .git .gitignore .svn CVS .cvsignore .arch-ids {arch} SCCS BitKeeper .hg _MTN _darcs Makefile Makefile.in config.log .gitattributes .hgignore .hgtags'.split()
416 dist_exts = '~ .rej .orig .pyc .pyo .bak .tar.bz2 tar.gz .zip .swp'.split()
417 def dont_dist(name, src, build_dir):
418 global excludes, dist_exts
420 if (name.startswith(',,')
421 or name.startswith('++')
422 or name.startswith('.waf')
423 or (src == '.' and name == Options.lockfile)
429 for ext in dist_exts:
430 if name.endswith(ext):
435 # like shutil.copytree
436 # exclude files and to raise exceptions immediately
437 def copytree(src, dst, build_dir):
438 names = os.listdir(src)
441 srcname = os.path.join(src, name)
442 dstname = os.path.join(dst, name)
444 if dont_dist(name, src, build_dir):
447 if os.path.isdir(srcname):
448 copytree(srcname, dstname, build_dir)
450 shutil.copy2(srcname, dstname)
452 # TODO in waf 1.6, change this method if "srcdir == blddir" is allowed
453 def distclean(ctx=None):
454 '''removes the build directory'''
456 lst = os.listdir('.')
458 if f == Options.lockfile:
460 proj = Environment.Environment(f)
462 Logs.warn('could not read %r' % f)
466 shutil.rmtree(proj[BLDDIR])
470 if e.errno != errno.ENOENT:
471 Logs.warn('project %r cannot be removed' % proj[BLDDIR])
476 if e.errno != errno.ENOENT:
477 Logs.warn('file %r cannot be removed' % f)
479 # remove the local waf cache
480 if not commands and f.startswith('.waf'):
481 shutil.rmtree(f, ignore_errors=True)
483 # FIXME waf 1.6 a unique ctx parameter, and remove the optional appname and version
484 def dist(appname='', version=''):
485 '''makes a tarball for redistributing the sources'''
486 # return return (distdirname, tarballname)
489 if not appname: appname = Utils.g_module.APPNAME
490 if not version: version = Utils.g_module.VERSION
492 tmp_folder = appname + '-' + version
493 if g_gz in ['gz', 'bz2']:
494 arch_name = tmp_folder + '.tar.' + g_gz
496 arch_name = tmp_folder + '.' + 'zip'
498 # remove the previous dir
500 shutil.rmtree(tmp_folder)
501 except (OSError, IOError):
504 # remove the previous archive
507 except (OSError, IOError):
510 # copy the files into the temporary folder
511 blddir = getattr(Utils.g_module, BLDDIR, None)
513 blddir = getattr(Utils.g_module, 'out', None)
514 copytree('.', tmp_folder, blddir)
516 # undocumented hook for additional cleanup
517 dist_hook = getattr(Utils.g_module, 'dist_hook', None)
524 # go back to the root directory
527 if g_gz in ['gz', 'bz2']:
528 tar = tarfile.open(arch_name, 'w:' + g_gz)
532 Utils.zip_folder(tmp_folder, arch_name, tmp_folder)
534 try: from hashlib import sha1 as sha
535 except ImportError: from sha import sha
537 digest = " (sha=%r)" % sha(Utils.readf(arch_name)).hexdigest()
541 info('New archive created: %s%s' % (arch_name, digest))
543 if os.path.exists(tmp_folder): shutil.rmtree(tmp_folder)
546 # FIXME waf 1.6 a unique ctx parameter, and remove the optional appname and version
547 def distcheck(appname='', version='', subdir=''):
548 '''checks if the sources compile (tarball from 'dist')'''
549 import tempfile, tarfile
551 if not appname: appname = Utils.g_module.APPNAME
552 if not version: version = Utils.g_module.VERSION
554 waf = os.path.abspath(sys.argv[0])
555 tarball = dist(appname, version)
557 path = appname + '-' + version
559 # remove any previous instance
560 if os.path.exists(path):
563 t = tarfile.open(tarball)
564 for x in t: t.extract(x)
567 # build_path is the directory for the waf invocation
569 build_path = os.path.join(path, subdir)
573 instdir = tempfile.mkdtemp('.inst', '%s-%s' % (appname, version))
574 ret = Utils.pproc.Popen([waf, 'configure', 'build', 'install', 'uninstall', '--destdir=' + instdir], cwd=build_path).wait()
576 raise Utils.WafError('distcheck failed with code %i' % ret)
578 if os.path.exists(instdir):
579 raise Utils.WafError('distcheck succeeded, but files were left in %s' % instdir)
583 # FIXME remove in Waf 1.6 (kept for compatibility)
584 def add_subdir(dir, bld):
585 bld.recurse(dir, 'build')