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')