3 # WARNING! Do not edit! https://waf.io/book/index.html#_obtaining_the_waf_file
7 # Thomas Nagy, 2005-2016 (ita)
9 "Module called for configuring, compiling and installing targets"
11 import os, shlex, shutil, traceback, errno, sys, stat
12 from waflib import Utils, Configure, Logs, Options, ConfigSet, Context, Errors, Build, Node
14 build_dir_override = None
16 no_climb_commands = ['configure']
20 def waf_entry_point(current_directory, version, wafdir):
22 This is the main entry point, all Waf execution starts here.
24 :param current_directory: absolute path representing the current directory
25 :type current_directory: string
26 :param version: version number
28 :param wafdir: absolute path representing the directory of the waf library
34 if Context.WAFVERSION != version:
35 Logs.error('Waf script %r and library %r do not match (directory %r)', version, Context.WAFVERSION, wafdir)
38 if '--version' in sys.argv:
39 Context.run_dir = current_directory
40 ctx = Context.create_context('options')
41 ctx.curdir = current_directory
46 # os.path.join handles absolute paths in sys.argv[1] accordingly (it discards the previous ones)
47 # if sys.argv[1] is not an absolute path, then it is relative to the current working directory
48 potential_wscript = os.path.join(current_directory, sys.argv[1])
49 # maybe check if the file is executable
50 # perhaps extract 'wscript' as a constant
51 if os.path.basename(potential_wscript) == 'wscript' and os.path.isfile(potential_wscript):
52 # need to explicitly normalize the path, as it may contain extra '/.'
54 current_directory = os.path.normpath(os.path.dirname(potential_wscript))
57 Context.waf_dir = wafdir
58 Context.launch_dir = current_directory
60 # if 'configure' is in the commands, do not search any further
61 no_climb = os.environ.get('NOCLIMB')
63 for k in no_climb_commands:
69 # if --top is provided assume the build started in the top directory
70 for i, x in enumerate(sys.argv):
71 # WARNING: this modifies sys.argv
72 if x.startswith('--top='):
73 Context.run_dir = Context.top_dir = Utils.sane_path(x[6:])
74 sys.argv[i] = '--top=' + Context.run_dir
75 if x.startswith('--out='):
76 Context.out_dir = Utils.sane_path(x[6:])
77 sys.argv[i] = '--out=' + Context.out_dir
79 # try to find a lock file (if the project was configured)
80 # at the same time, store the first wscript file seen
81 cur = current_directory
82 while cur and not Context.top_dir:
87 Logs.error('Directory %r is unreadable!', cur)
88 if Options.lockfile in lst:
89 env = ConfigSet.ConfigSet()
91 env.load(os.path.join(cur, Options.lockfile))
92 ino = os.stat(cur)[stat.ST_INO]
93 except EnvironmentError:
96 # check if the folder was not moved
97 for x in (env.run_dir, env.top_dir, env.out_dir):
105 # if the filesystem features symlinks, compare the inode numbers
107 ino2 = os.stat(x)[stat.ST_INO]
115 Logs.warn('invalid lock file in %s', cur)
119 Context.run_dir = env.run_dir
120 Context.top_dir = env.top_dir
121 Context.out_dir = env.out_dir
124 if not Context.run_dir:
125 if Context.WSCRIPT_FILE in lst:
126 Context.run_dir = cur
128 next = os.path.dirname(cur)
136 if not Context.run_dir:
137 if '-h' in sys.argv or '--help' in sys.argv:
138 Logs.warn('No wscript file found: the help message may be incomplete')
139 Context.run_dir = current_directory
140 ctx = Context.create_context('options')
141 ctx.curdir = current_directory
144 Logs.error('Waf: Run from a directory containing a file named %r', Context.WSCRIPT_FILE)
148 os.chdir(Context.run_dir)
150 Logs.error('Waf: The folder %r is unreadable', Context.run_dir)
154 set_main_module(os.path.normpath(os.path.join(Context.run_dir, Context.WSCRIPT_FILE)))
155 except Errors.WafError ,e:
156 Logs.pprint('RED', e.verbose_msg)
160 Logs.error('Waf: The wscript in %r is unreadable', Context.run_dir)
161 traceback.print_exc(file=sys.stdout)
164 if '--profile' in sys.argv:
165 import cProfile, pstats
166 cProfile.runctx('from waflib import Scripting; Scripting.run_commands()', {}, {}, 'profi.txt')
167 p = pstats.Stats('profi.txt')
168 p.sort_stats('time').print_stats(75) # or 'cumulative'
172 except Errors.WafError ,e:
174 Logs.pprint('RED', e.verbose_msg)
180 traceback.print_exc(file=sys.stdout)
182 except KeyboardInterrupt:
183 Logs.pprint('RED', 'Interrupted')
186 def set_main_module(file_path):
188 Read the main wscript file into :py:const:`waflib.Context.Context.g_module` and
189 bind default functions such as ``init``, ``dist``, ``distclean`` if not defined.
190 Called by :py:func:`waflib.Scripting.waf_entry_point` during the initialization.
192 :param file_path: absolute path representing the top-level wscript file
193 :type file_path: string
195 Context.g_module = Context.load_module(file_path)
196 Context.g_module.root_path = file_path
198 # note: to register the module globally, use the following:
199 # sys.modules['wscript_main'] = g_module
203 if not name in Context.g_module.__dict__:
204 setattr(Context.g_module, name, obj)
205 for k in (dist, distclean, distcheck):
207 # add dummy init and shutdown functions if they're not defined
208 if not 'init' in Context.g_module.__dict__:
209 Context.g_module.init = Utils.nada
210 if not 'shutdown' in Context.g_module.__dict__:
211 Context.g_module.shutdown = Utils.nada
212 if not 'options' in Context.g_module.__dict__:
213 Context.g_module.options = Utils.nada
217 Parses the command-line options and initialize the logging system.
218 Called by :py:func:`waflib.Scripting.waf_entry_point` during the initialization.
220 Context.create_context('options').execute()
222 for var in Options.envvars:
223 (name, value) = var.split('=', 1)
224 os.environ[name.strip()] = value
226 if not Options.commands:
227 Options.commands = [default_cmd]
228 Options.commands = [x for x in Options.commands if x != 'options'] # issue 1076
230 # process some internal Waf options
231 Logs.verbose = Options.options.verbose
234 if Options.options.zones:
235 Logs.zones = Options.options.zones.split(',')
238 elif Logs.verbose > 0:
239 Logs.zones = ['runner']
244 def run_command(cmd_name):
246 Executes a single Waf command. Called by :py:func:`waflib.Scripting.run_commands`.
248 :param cmd_name: command to execute, like ``build``
249 :type cmd_name: string
251 ctx = Context.create_context(cmd_name)
252 ctx.log_timer = Utils.Timer()
253 ctx.options = Options.options # provided for convenience
264 Execute the Waf commands that were given on the command-line, and the other options
265 Called by :py:func:`waflib.Scripting.waf_entry_point` during the initialization, and executed
266 after :py:func:`waflib.Scripting.parse_options`.
270 while Options.commands:
271 cmd_name = Options.commands.pop(0)
272 ctx = run_command(cmd_name)
273 Logs.info('%r finished successfully (%s)', cmd_name, ctx.log_timer)
274 run_command('shutdown')
276 ###########################################################################################
278 def distclean_dir(dirname):
280 Distclean function called in the particular case when::
284 :param dirname: absolute path of the folder to clean
285 :type dirname: string
287 for (root, dirs, files) in os.walk(dirname):
289 if f.endswith(('.o', '.moc', '.exe')):
290 fname = os.path.join(root, f)
294 Logs.warn('Could not remove %r', fname)
296 for x in (Context.DBFILE, 'config.log'):
303 shutil.rmtree('c4che')
308 '''removes the build directory'''
309 lst = os.listdir('.')
311 if f == Options.lockfile:
313 proj = ConfigSet.ConfigSet(f)
315 Logs.warn('Could not read %r', f)
318 if proj['out_dir'] != proj['top_dir']:
320 shutil.rmtree(proj['out_dir'])
321 except EnvironmentError ,e:
322 if e.errno != errno.ENOENT:
323 Logs.warn('Could not remove %r', proj['out_dir'])
325 distclean_dir(proj['out_dir'])
327 for k in (proj['out_dir'], proj['top_dir'], proj['run_dir']):
328 p = os.path.join(k, Options.lockfile)
332 if e.errno != errno.ENOENT:
333 Logs.warn('Could not remove %r', p)
335 # remove local waf cache folders
336 if not Options.commands:
337 for x in '.waf-1. waf-1. .waf3-1. waf3-1.'.split():
339 shutil.rmtree(f, ignore_errors=True)
341 class Dist(Context.Context):
342 '''creates an archive containing the project source code'''
350 See :py:func:`waflib.Context.Context.execute`
352 self.recurse([os.path.dirname(Context.g_module.root_path)])
357 Creates the source archive.
361 arch_name = self.get_arch_name()
365 except AttributeError:
366 self.base_path = self.path
368 node = self.base_path.make_node(arch_name)
374 files = self.get_files()
376 if self.algo.startswith('tar.'):
377 tar = tarfile.open(node.abspath(), 'w:' + self.algo.replace('tar.', ''))
380 self.add_tar_file(x, tar)
382 elif self.algo == 'zip':
384 zip = zipfile.ZipFile(node.abspath(), 'w', compression=zipfile.ZIP_DEFLATED)
387 archive_name = self.get_base_name() + '/' + x.path_from(self.base_path)
388 zip.write(x.abspath(), archive_name, zipfile.ZIP_DEFLATED)
391 self.fatal('Valid algo types are tar.bz2, tar.gz, tar.xz or zip')
394 from hashlib import sha1
398 digest = ' (sha=%r)' % sha1(node.read(flags='rb')).hexdigest()
400 Logs.info('New archive created: %s%s', self.arch_name, digest)
402 def get_tar_path(self, node):
404 Return the path to use for a node in the tar archive, the purpose of this
405 is to let subclases resolve symbolic links or to change file names
407 :return: absolute path
410 return node.abspath()
412 def add_tar_file(self, x, tar):
414 Adds a file to the tar archive. Symlinks are not verified.
417 :param tar: tar file object
419 p = self.get_tar_path(x)
420 tinfo = tar.gettarinfo(name=p, arcname=self.get_tar_prefix() + '/' + x.path_from(self.base_path))
426 if os.path.isfile(p):
429 tar.addfile(tinfo, fileobj=fu)
435 def get_tar_prefix(self):
437 Returns the base path for files added into the archive tar file
442 return self.tar_prefix
443 except AttributeError:
444 return self.get_base_name()
446 def get_arch_name(self):
448 Returns the archive file name.
449 Set the attribute *arch_name* to change the default value::
452 ctx.arch_name = 'ctx.tar.bz2'
458 except AttributeError:
459 self.arch_name = self.get_base_name() + '.' + self.ext_algo.get(self.algo, self.algo)
460 return self.arch_name
462 def get_base_name(self):
464 Returns the default name of the main directory in the archive, which is set to *appname-version*.
465 Set the attribute *base_name* to change the default value::
468 ctx.base_name = 'files'
474 except AttributeError:
475 appname = getattr(Context.g_module, Context.APPNAME, 'noname')
476 version = getattr(Context.g_module, Context.VERSION, '1.0')
477 self.base_name = appname + '-' + version
478 return self.base_name
482 Returns the patterns to exclude for finding the files in the top-level directory.
483 Set the attribute *excl* to change the default value::
486 ctx.excl = 'build **/*.o **/*.class'
492 except AttributeError:
493 self.excl = Node.exclude_regs + ' **/waf-1.8.* **/.waf-1.8* **/waf3-1.8.* **/.waf3-1.8* **/*~ **/*.rej **/*.orig **/*.pyc **/*.pyo **/*.bak **/*.swp **/.lock-w*'
495 nd = self.root.find_node(Context.out_dir)
497 self.excl += ' ' + nd.path_from(self.base_path)
502 Files to package are searched automatically by :py:func:`waflib.Node.Node.ant_glob`.
503 Set *files* to prevent this behaviour::
506 ctx.files = ctx.path.find_node('wscript')
508 Files are also searched from the directory 'base_path', to change it, set::
513 :rtype: list of :py:class:`waflib.Node.Node`
517 except AttributeError:
518 files = self.base_path.ant_glob('**/*', excl=self.get_excl())
522 '''makes a tarball for redistributing the sources'''
525 class DistCheck(Dist):
527 Creates an archive of the project, then attempts to build the project in a temporary directory::
536 See :py:func:`waflib.Context.Context.execute`
538 self.recurse([os.path.dirname(Context.g_module.root_path)])
542 def make_distcheck_cmd(self, tmpdir):
544 if Options.options.distcheck_args:
545 cfg = shlex.split(Options.options.distcheck_args)
547 cfg = [x for x in sys.argv if x.startswith('-')]
548 cmd = [sys.executable, sys.argv[0], 'configure', 'build', 'install', 'uninstall', '--destdir=' + tmpdir] + cfg
553 Creates the archive, uncompresses it and tries to build the project
555 import tempfile, tarfile
558 t = tarfile.open(self.get_arch_name())
564 instdir = tempfile.mkdtemp('.inst', self.get_base_name())
565 cmd = self.make_distcheck_cmd(instdir)
566 ret = Utils.subprocess.Popen(cmd, cwd=self.get_base_name()).wait()
568 raise Errors.WafError('distcheck failed with code %r' % ret)
570 if os.path.exists(instdir):
571 raise Errors.WafError('distcheck succeeded, but files were left in %s' % instdir)
573 shutil.rmtree(self.get_base_name())
577 '''checks if the project compiles (tarball from 'dist')'''
580 def autoconfigure(execute_method):
582 Decorator that enables context commands to run *configure* as needed.
586 Wraps :py:func:`waflib.Context.Context.execute` on the context class
588 if not Configure.autoconfig:
589 return execute_method(self)
591 env = ConfigSet.ConfigSet()
594 env.load(os.path.join(Context.top_dir, Options.lockfile))
595 except EnvironmentError:
596 Logs.warn('Configuring the project')
599 if env.run_dir != Context.run_dir:
605 h = Utils.h_list((h, Utils.readf(f, 'rb')))
606 except EnvironmentError:
610 do_config = h != env.hash
613 cmd = env.config_cmd or 'configure'
614 if Configure.autoconfig == 'clobber':
615 tmp = Options.options.__dict__
616 Options.options.__dict__ = env.options
620 Options.options.__dict__ = tmp
623 run_command(self.cmd)
625 return execute_method(self)
627 Build.BuildContext.execute = autoconfigure(Build.BuildContext.execute)