s3: libsmbclient: Add missing talloc stackframe.
[samba.git] / third_party / waf / wafadmin / Scripting.py
1 #!/usr/bin/env python
2 # encoding: utf-8
3 # Thomas Nagy, 2005 (ita)
4
5 "Module called for configuring, compiling and installing targets"
6
7 import os, sys, shutil, traceback, datetime, inspect, errno
8
9 import Utils, Configure, Build, Logs, Options, Environment, Task
10 from Logs import error, warn, info
11 from Constants import *
12
13 g_gz = 'bz2'
14 commands = []
15
16 def prepare_impl(t, cwd, ver, wafdir):
17         Options.tooldir = [t]
18         Options.launch_dir = cwd
19
20         # some command-line options can be processed immediately
21         if '--version' in sys.argv:
22                 opt_obj = Options.Handler()
23                 opt_obj.curdir = cwd
24                 opt_obj.parse_args()
25                 sys.exit(0)
26
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
29
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
33         candidate = None
34
35         lst = os.listdir(cwd)
36
37         search_for_candidate = True
38         if WSCRIPT_FILE in lst:
39                 candidate = cwd
40
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):
45                         candidate = calldir
46                         search_for_candidate = False
47                 else:
48                         error('arg[0] directory does not contain a wscript file')
49                         sys.exit(1)
50                 build_dir_override = cwd
51
52         # climb up to find a script if it is not found
53         while search_for_candidate:
54                 if len(cwd) <= 3:
55                         break # stop at / or c:
56                 dirlst = os.listdir(cwd)
57                 if WSCRIPT_FILE in dirlst:
58                         candidate = cwd
59                 if 'configure' in sys.argv and candidate:
60                         break
61                 if Options.lockfile in dirlst:
62                         env = Environment.Environment()
63                         try:
64                                 env.load(os.path.join(cwd, Options.lockfile))
65                         except:
66                                 error('could not load %r' % Options.lockfile)
67                         try:
68                                 os.stat(env['cwd'])
69                         except:
70                                 candidate = cwd
71                         else:
72                                 candidate = env['cwd']
73                         break
74                 cwd = os.path.dirname(cwd) # climb up
75
76         if not candidate:
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()
81                         opt_obj.curdir = cwd
82                         opt_obj.parse_args()
83                 else:
84                         error(msg1)
85                 sys.exit(0)
86
87         # We have found wscript, but there is no guarantee that it is valid
88         try:
89                 os.chdir(candidate)
90         except OSError:
91                 raise Utils.WafError("the folder %r is unreadable" % candidate)
92
93         # define the main module containing the functions init, shutdown, ..
94         Utils.set_main_module(os.path.join(candidate, WSCRIPT_FILE))
95
96         if build_dir_override:
97                 d = getattr(Utils.g_module, BLDDIR, None)
98                 if d:
99                         # test if user has set the blddir in wscript.
100                         msg = ' Overriding build directory %s with %s' % (d, build_dir_override)
101                         warn(msg)
102                 Utils.g_module.blddir = build_dir_override
103
104         # bind a few methods and classes by default
105
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)
110
111         for k in [dist, distclean, distcheck, clean, install, uninstall]:
112                 set_def(k)
113
114         set_def(Configure.ConfigurationContext, 'configure_context')
115
116         for k in ['build', 'clean', 'install', 'uninstall']:
117                 set_def(Build.BuildContext, k + '_context')
118
119         # now parse the options from the user wscript file
120         opt_obj = Options.Handler(Utils.g_module)
121         opt_obj.curdir = candidate
122         try:
123                 f = Utils.g_module.set_options
124         except AttributeError:
125                 pass
126         else:
127                 opt_obj.sub_options([''])
128         opt_obj.parse_args()
129
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
134
135         main()
136
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)
141                 sys.exit(1)
142
143         #"""
144         try:
145                 prepare_impl(t, cwd, ver, wafdir)
146         except Utils.WafError, e:
147                 error(str(e))
148                 sys.exit(1)
149         except KeyboardInterrupt:
150                 Utils.pprint('RED', 'Interrupted')
151                 sys.exit(68)
152         """
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},
156                  'profi.txt')
157         p = pstats.Stats('profi.txt')
158         p.sort_stats('time').print_stats(45)
159         #"""
160
161 def main():
162         global commands
163         commands = Options.arg_line[:]
164
165         while commands:
166                 x = commands.pop(0)
167
168                 ini = datetime.datetime.now()
169                 if x == 'configure':
170                         fun = configure
171                 elif x == 'build':
172                         fun = build
173                 else:
174                         fun = getattr(Utils.g_module, x, None)
175
176                 if not fun:
177                         raise Utils.WscriptError('No such command %r' % x)
178
179                 ctx = getattr(Utils.g_module, x + '_context', Utils.Context)()
180
181                 if x in ['init', 'shutdown', 'dist', 'distclean', 'distcheck']:
182                         # compatibility TODO remove in waf 1.6
183                         try:
184                                 fun(ctx)
185                         except TypeError:
186                                 fun()
187                 else:
188                         fun(ctx)
189
190                 ela = ''
191                 if not Options.options.progress_bar:
192                         ela = ' (%s)' % Utils.get_elapsed_time(ini)
193
194                 if x != 'init' and x != 'shutdown':
195                         info('%r finished successfully%s' % (x, ela))
196
197                 if not commands and x != 'shutdown':
198                         commands.append('shutdown')
199
200 def configure(conf):
201
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)
205         if not src:
206                 src = '.'
207                 incomplete_src = 1
208         src = os.path.abspath(src)
209
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)
213         if not bld:
214                 bld = 'build'
215                 incomplete_bld = 1
216         if bld == '.':
217                 raise Utils.WafError('Setting blddir="." may cause distclean problems')
218         bld = os.path.abspath(bld)
219
220         try: os.makedirs(bld)
221         except OSError: pass
222
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
228
229         conf.srcdir = src
230         conf.blddir = bld
231         conf.post_init()
232
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)
239
240         # calling to main wscript's configure()
241         conf.sub_config([''])
242
243         conf.store()
244
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()
249         env[BLDDIR] = bld
250         env[SRCDIR] = src
251         env['argv'] = sys.argv
252         env['commands'] = Options.commands
253         env['options'] = Options.options.__dict__
254
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]
261
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))
265
266         env.store(Options.lockfile)
267
268         Options.options.compile_targets = targets
269
270 def clean(bld):
271         '''removes the build files'''
272         try:
273                 proj = Environment.Environment(Options.lockfile)
274         except IOError:
275                 raise Utils.WafError('Nothing to clean (project not configured)')
276
277         bld.load_dirs(proj[SRCDIR], proj[BLDDIR])
278         bld.load_envs()
279
280         bld.is_install = 0 # False
281
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]])
284
285         try:
286                 bld.clean()
287         finally:
288                 bld.save()
289
290 def check_configured(bld):
291         if not Configure.autoconfig:
292                 return bld
293
294         conf_cls = getattr(Utils.g_module, 'configure_context', Utils.Context)
295         bld_cls = getattr(Utils.g_module, 'build_context', Utils.Context)
296
297         def reconf(proj):
298                 back = (Options.commands, Options.options.__dict__, Logs.zones, Logs.verbose)
299
300                 Options.commands = proj['commands']
301                 Options.options.__dict__ = proj['options']
302                 conf = conf_cls()
303                 conf.environ = proj['environ']
304                 configure(conf)
305
306                 (Options.commands, Options.options.__dict__, Logs.zones, Logs.verbose) = back
307
308         try:
309                 proj = Environment.Environment(Options.lockfile)
310         except IOError:
311                 conf = conf_cls()
312                 configure(conf)
313         else:
314                 try:
315                         bld = bld_cls()
316                         bld.load_dirs(proj[SRCDIR], proj[BLDDIR])
317                         bld.load_envs()
318                 except Utils.WafError:
319                         reconf(proj)
320                         return bld_cls()
321
322         try:
323                 proj = Environment.Environment(Options.lockfile)
324         except IOError:
325                 raise Utils.WafError('Auto-config: project does not configure (bug)')
326
327         h = 0
328         try:
329                 for file in proj['files']:
330                         if file.endswith('configure'):
331                                 h = hash((h, Utils.readf(file)))
332                         else:
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')
337                 reconf(proj)
338         else:
339                 if (h != proj['hash']):
340                         warn('Reconfiguring the project: the configuration has changed')
341                         reconf(proj)
342
343         return bld_cls()
344
345 def install(bld):
346         '''installs the build files'''
347         bld = check_configured(bld)
348
349         Options.commands['install'] = True
350         Options.commands['uninstall'] = False
351         Options.is_install = True
352
353         bld.is_install = INSTALL
354
355         build_impl(bld)
356         bld.install()
357
358 def uninstall(bld):
359         '''removes the installed files'''
360         Options.commands['install'] = False
361         Options.commands['uninstall'] = True
362         Options.is_install = True
363
364         bld.is_install = UNINSTALL
365
366         try:
367                 def runnable_status(self):
368                         return SKIP_ME
369                 setattr(Task.Task, 'runnable_status_back', Task.Task.runnable_status)
370                 setattr(Task.Task, 'runnable_status', runnable_status)
371
372                 build_impl(bld)
373                 bld.install()
374         finally:
375                 setattr(Task.Task, 'runnable_status', Task.Task.runnable_status_back)
376
377 def build(bld):
378         bld = check_configured(bld)
379
380         Options.commands['install'] = False
381         Options.commands['uninstall'] = False
382         Options.is_install = False
383
384         bld.is_install = 0 # False
385
386         return build_impl(bld)
387
388 def build_impl(bld):
389         # compile the project and/or install the files
390         try:
391                 proj = Environment.Environment(Options.lockfile)
392         except IOError:
393                 raise Utils.WafError("Project not configured (run 'waf configure' first)")
394
395         bld.load_dirs(proj[SRCDIR], proj[BLDDIR])
396         bld.load_envs()
397
398         info("Waf: Entering directory `%s'" % bld.bldnode.abspath())
399         bld.add_subdirs([os.path.split(Utils.g_module.root_path)[0]])
400
401         # execute something immediately before the build starts
402         bld.pre_build()
403
404         try:
405                 bld.compile()
406         finally:
407                 if Options.options.progress_bar: print('')
408                 info("Waf: Leaving directory `%s'" % bld.bldnode.abspath())
409
410         # execute something immediately after a successful build
411         bld.post_build()
412
413         bld.install()
414
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
419
420         if (name.startswith(',,')
421                 or name.startswith('++')
422                 or name.startswith('.waf')
423                 or (src == '.' and name == Options.lockfile)
424                 or name in excludes
425                 or name == build_dir
426                 ):
427                 return True
428
429         for ext in dist_exts:
430                 if name.endswith(ext):
431                         return True
432
433         return False
434
435 # like shutil.copytree
436 # exclude files and to raise exceptions immediately
437 def copytree(src, dst, build_dir):
438         names = os.listdir(src)
439         os.makedirs(dst)
440         for name in names:
441                 srcname = os.path.join(src, name)
442                 dstname = os.path.join(dst, name)
443
444                 if dont_dist(name, src, build_dir):
445                         continue
446
447                 if os.path.isdir(srcname):
448                         copytree(srcname, dstname, build_dir)
449                 else:
450                         shutil.copy2(srcname, dstname)
451
452 # TODO in waf 1.6, change this method if "srcdir == blddir" is allowed
453 def distclean(ctx=None):
454         '''removes the build directory'''
455         global commands
456         lst = os.listdir('.')
457         for f in lst:
458                 if f == Options.lockfile:
459                         try:
460                                 proj = Environment.Environment(f)
461                         except:
462                                 Logs.warn('could not read %r' % f)
463                                 continue
464
465                         try:
466                                 shutil.rmtree(proj[BLDDIR])
467                         except IOError:
468                                 pass
469                         except OSError, e:
470                                 if e.errno != errno.ENOENT:
471                                         Logs.warn('project %r cannot be removed' % proj[BLDDIR])
472
473                         try:
474                                 os.remove(f)
475                         except OSError, e:
476                                 if e.errno != errno.ENOENT:
477                                         Logs.warn('file %r cannot be removed' % f)
478
479                 # remove the local waf cache
480                 if not commands and f.startswith('.waf'):
481                         shutil.rmtree(f, ignore_errors=True)
482
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)
487         import tarfile
488
489         if not appname: appname = Utils.g_module.APPNAME
490         if not version: version = Utils.g_module.VERSION
491
492         tmp_folder = appname + '-' + version
493         if g_gz in ['gz', 'bz2']:
494                 arch_name = tmp_folder + '.tar.' + g_gz
495         else:
496                 arch_name = tmp_folder + '.' + 'zip'
497
498         # remove the previous dir
499         try:
500                 shutil.rmtree(tmp_folder)
501         except (OSError, IOError):
502                 pass
503
504         # remove the previous archive
505         try:
506                 os.remove(arch_name)
507         except (OSError, IOError):
508                 pass
509
510         # copy the files into the temporary folder
511         blddir = getattr(Utils.g_module, BLDDIR, None)
512         if not blddir:
513                 blddir = getattr(Utils.g_module, 'out', None)
514         copytree('.', tmp_folder, blddir)
515
516         # undocumented hook for additional cleanup
517         dist_hook = getattr(Utils.g_module, 'dist_hook', None)
518         if dist_hook:
519                 back = os.getcwd()
520                 os.chdir(tmp_folder)
521                 try:
522                         dist_hook()
523                 finally:
524                         # go back to the root directory
525                         os.chdir(back)
526
527         if g_gz in ['gz', 'bz2']:
528                 tar = tarfile.open(arch_name, 'w:' + g_gz)
529                 tar.add(tmp_folder)
530                 tar.close()
531         else:
532                 Utils.zip_folder(tmp_folder, arch_name, tmp_folder)
533
534         try: from hashlib import sha1 as sha
535         except ImportError: from sha import sha
536         try:
537                 digest = " (sha=%r)" % sha(Utils.readf(arch_name)).hexdigest()
538         except:
539                 digest = ''
540
541         info('New archive created: %s%s' % (arch_name, digest))
542
543         if os.path.exists(tmp_folder): shutil.rmtree(tmp_folder)
544         return arch_name
545
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
550
551         if not appname: appname = Utils.g_module.APPNAME
552         if not version: version = Utils.g_module.VERSION
553
554         waf = os.path.abspath(sys.argv[0])
555         tarball = dist(appname, version)
556
557         path = appname + '-' + version
558
559         # remove any previous instance
560         if os.path.exists(path):
561                 shutil.rmtree(path)
562
563         t = tarfile.open(tarball)
564         for x in t: t.extract(x)
565         t.close()
566
567         # build_path is the directory for the waf invocation
568         if subdir:
569                 build_path = os.path.join(path, subdir)
570         else:
571                 build_path = path
572
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()
575         if ret:
576                 raise Utils.WafError('distcheck failed with code %i' % ret)
577
578         if os.path.exists(instdir):
579                 raise Utils.WafError('distcheck succeeded, but files were left in %s' % instdir)
580
581         shutil.rmtree(path)
582
583 # FIXME remove in Waf 1.6 (kept for compatibility)
584 def add_subdir(dir, bld):
585         bld.recurse(dir, 'build')