commit 44a967e326cc2e670a31b3712e4763b72d65e81b Author: Thomas Nagy Date: Sat Sep 10 11:13:51 2011 +0200 Initial commit diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..a2e1b74e --- /dev/null +++ b/ChangeLog @@ -0,0 +1,211 @@ +NEW IN WAF 1.6.8 +---------------- +* Found a typo that prevented Waf from running on Pypy-trunk +* Fixed a typo in Utils.py affecting Win32 platforms (copystat) #1029 +* Fixed a minor bug in the Eclipse project generator +* Make the xlc/xlc++ compiler detection more accurate by looking at the version number (#1022) +* Minor perl and ruby tool improvements + +NEW IN WAF 1.6.7 +---------------- + +* Provide more diagnostic for invalid build groups #914 +* Various enhancements to msvs.py +* Read MSVC_VERSIONS and MSVC_TARGETS from the command-line +* Minor cross-compiler detection fix on msvc.py +* Fix the redirections with pipes (waf configure > log) +* Do not display runnable_status exceptions when running with -k +* Let -k stop at the first runnable_status error and -kk run even further +* Merge the add_object extension in the main line (source='file.o') +* Make update_outputs more robust with changes in the task definition #1017 +* Qt4 detection on Win32 + +NEW IN WAF 1.6.6 +---------------- + +* Fix the performance regression related to #974 + +NEW IN WAF 1.6.5 +---------------- + +* More documentation +* Re-enable the colors for msys +* Add the .ui files for the qt4 translations +* Fix the conf.check_large_file() test +* Fix the conf.check_library() in C++ mode #955 +* Improve the latex scanner to avoid depending on generated files #943 +* Remove the @file processing from the winrc tasks +* Fix the python detection using python-config +* Add the missing default includes and defines to the moc command +* Improve support for hierarchical go-lang packages #953 +* Fix the gfortran verbose flag detection on Windows +* Fix the support of fortran import libraries #950 +* Added a fix for running with Python 2.4 on Windows #949 +* Limited support for IronPython +* Support for older Visual Studio versions (VC6) #952 +* New waf.bat file #964 +* New method ConfigSet.keys +* New Visual Studio and Eclipse CTD project generators (waflib/extras) +* New lru_cache tool for use with WAFCACHE (waflib/extras) + +NEW IN WAF 1.6.4 +---------------- + +* Fix the Python detection on win32 #883 +* Optimize the Python file installation #892 +* Force +x permissions on installed fortran programs #893 +* Qt library detection fixes #895 +* Ensure that unit tests are executed only after the symlinks are created +* Fix the precedence constraints for classes ending in _task #896 +* Support the install_path attribute with add_pcfile #913 +* Make the goprograms executable when installed #928 +* Allow non-python data files in the waf executable #927 +* Enforce a build order based on the scanner results #777, #922 +* Multiple msvc detection fixes #907 #910 #923 #924 #926 +* Fix the -of flag append with dmd #917 +* Boost detection fixes #920 +* Support newer javac compilers #921 +* Fix the execution on python 2.3 for: "waf update", msvc.py, fc.py +* Improve the support for mac applications (demos/mac_app) +* Better default regexps in "waf step" +* New error check for tasks creating the same nodes or having the same identifiers (waf -v) +* New variables conf.env.NO_LOCK_IN_TOP/OUT/RUN for special projects (top='..') +* New example on avoiding rebuilds when moving a project (playground/relocate) +* Improve go-lang support for cgo-packages (fixes #932) +* Fix the progress bar on cmd and msys + +NEW IN WAF 1.6.3 +---------------- + +* Fixed the interaction of Fortran configuration tests and WAFCACHE #842 +* Various documentation fixes +* Set the PYTHONARCHDIR variable for installing python extensions +* Fixed the Waf file creation with --zip-type=gz (bunzip2 was not replaced by gzip -d) +* Fixed multiple problems in the call to TaskGen.declare_chain(...) #850 +* Fixed the task attribute 'vars' which might cause unnecessary rebuilds #852 +* Return the value of post_check(...) in conf.check(...) #857 +* Rewrite the boost tool (waflib/extras/boost.py) #814, #454, #424 +* More fortran file extensions: .for, .FOR #867 +* Searching above the root nodes no longer raise exceptions #868 +* Msvc detection fixes for non-utf8 encodings #873 +* Fixed the destdir processing on Windows #874 +* Stop changing the flags on waf -v (make the errors more visible) #875 +* Fixed the resource file compilation on Windows #876 +* Fixed the vala file installation #881 +* New system of plugins for C/C++/Fortran compilers (files named c_* in waflib/extras/) +* New examples of interaction between Waf and existing makefiles (playground/) +* New names for @before/@after: @before_method/@after_method + +NEW IN WAF 1.6.2 +---------------- + +* Support for C# debugging files #797 +* Add -relocation-model=pic for shared libraries on ldc +* Fixed 'waf dist' for tar files on python 3 #799 +* Make the latex scanner recursive #798 +* Enable the packing of non-python files in the waf file #802 +* Improve the feature sniffing for hybrid programs/libraries #800 +* New apidocs + tutorial in Sphinx format +* Add the CPPFLAGS from os.environ #803 +* Create the java output directory anywhere #806 +* Enable the .luac file installation +* Process Qt translation files +* Detect when the folders were copied and prompt for "waf configure" +* Parse_flags for the *-config outputs on windows (backslashes) #811 +* Fix the doxygen task build order + improve the dependency scanner #821 +* Various msvc fixes #819, #826, #825 +* Ported the batch executor to waf 1.6 (batched_cc) +* New tools: erlang, scala +* Moved conf.multicheck(..) from playground to the library +* New parameter to avoid reading the same scripts: bld.recurse(dir, once=True) +* Detect invalid method calls in 'waf -v' such as env.append/env.add/env.prepend +* New manifest option for jar targets #832 + +NEW IN WAF 1.6.1 +---------------- + +* Fixed the method check_waf_version #764 +* Fixed the name in ctx.recurse(name) #769 +* Stop caching the install tasks and tasks that have no outputs #770 +* Fix the log in Context.cmd_and_log() when calling with "quiet" #778 +* c_preproc exception when a file has the same name as a directory #777 +* 'intltool_po' does not install the .mo files #782 +* 'intltool_in' was broken #792 +* Bind stderr and stdout to the exception in Context.cmd_and_log #779 +* Tasks not rebuilding properly when the 'run' method changes #786 +* Print the progress bar information as late as possible #787 +* Fix for the FRAMEWORK value processing +* Verbose mode should not require the compat15 tools #790 +* Let static libraries use other static libraries as in 1.5 #768 +* Fix for the boost tool #776 +* boost tool update (in playground) #780 +* Updated the java tool and examples +* New gcj tool in playground +* Update the c# tool and examples (playground) #796 +* Read external c# libraries #774 +* Xelatex support #785 +* Rebuild fortran files when .mod files change #766 +* docs #781 +* Improve the ant_glob behaviour on ctx.root + absolute paths +* Fix for glib_mkenums and dbus-binding-tool #795 +* New feature 'subst' (see demos/subst) + +NEW IN WAF 1.6.0 +---------------- + +General: +* Python 3 syntax by default (runs unmodified for 2.6, 2.7, 3.0 and 3.1) +* Environment -> ConfigSet +* only lists are allowed in ConfigSet +* Better Node apis +* Utils.load_tool -> Context.load_tool +* set_options becomes options +* only the build-related commands require a configured project +* new variant system + build context commands +* removed the pseudo glob in installation methods +* eliminate find_sources_in_dirs +* node.__class__.bld → node.ctx +* bld.new_task_gen(...) disappears, use bld(...) +* network updates for waf tools +* accept node objects in the source and includes attributes +* remove task_gen.allnodes: modify self.source directly +* merge the include system for c, c++, d, gas and nasm +* allow top == out (no build directory) +* merge the Tool/wscript system (detect->configure, set_options->options) +* new command "waf list" to list the x for "waf build --targets=x" +* rename apply_core -> process_source +* rename apply_rule -> process_rule +* rename Task.TaskBase.classes -> Task.classes +* the modules Utils.py and Logs.py are now independent from the rest of waf (imports) +* remove Task.TaskManager and Build.BuildContext.all_task_gen to improve the build group handling +* remove program_USELIB, shlib_USELIB staticlib_USELIB support +* use tasks for target installation +* improve the exception handling (WscriptError was removed, use WafError) +* let the commands access node objects +* infer the build directory from the lock filename +* waf step --file=main.c +* post task generators in a lazy manner + +preview 3: +* remove the /W3 flag from msvc default flags +* opt.tool_options -> opt.load (now all commands inherit the 'tool' method) +* conf.check_tool -> conf.load +* do not copy files when creating tar files in 'waf dist' +* add zip files in 'waf dist' +* fix the behaviour of 'waf distcheck' +* preprocessor optimizations +* python 2 fixes + +release candidate: +* cache fixes +* fortran fixes +* python 2 and 3 fixes +* docs and docstrings +* support for custom waf files and preludes +* fix in waflib.Context for overriding command classes +* port the doxygen tool +* Utils.to_hashtable -> Utils.str2dict +* change the thread pool to enable thread pool sharing +* fixed a regression on win32 + ansiterm.py + python 3 -> thanks to kesselhaus :-) +* various msvc fixes (thanks to Nicolas Mercier) + diff --git a/DEVEL b/DEVEL new file mode 100644 index 00000000..ca291193 --- /dev/null +++ b/DEVEL @@ -0,0 +1,41 @@ +branches +-------- + +trunk the current branch (waf 1.6) +branches/waf-1.5 waf 1.5 (currently waf 1.5.19) +branches/waf-1.6 waf 1.6.0 (before waf 1.6) +tags a tag is created each time a release is made + +docs/apidocs located at the repository root - contains the apidocs + the files have svn properties so they are browsable online from + http://waf.googlecode.com/svn/docs/apidocs/index.html +docs/wafbook the waf book of waf 1.6, browsable online from + http://waf.googlecode.com/svn/docs/wafbook/single.html +docs/ the waf book of waf 1.5, browsable online from + http://waf.googlecode.com/svn/docs/single.html + +other branches contain experimental things, most are unused now + +folders in trunk/ +----------------- + +trunk contains the source code which is being worked on (currently waf 1.6.3) + +waflib the core library +waflib/Tools essential waf tools +waflib/extras tools which are not included in the waf file + by default, except for the tool "compat15" +build_system_kit examples of build systems that can be created + from waf +tests unit tests, most are unused +playground experiments and integration tests for the tools in + the folder waflib/extras +demos integration tests - the folder can be configured + as a standalone project +demos/* integration tests and examples used as documentation +docs documentation +docs/sphinx extract docstrings from the source code to extract + the apidocs, they are put in the folder "docs/apidocs" + at the root of the repository + + diff --git a/README b/README new file mode 100644 index 00000000..569f0f73 --- /dev/null +++ b/README @@ -0,0 +1,36 @@ +WHAT YOU WILL FIND HERE +----------------------- + +Waf (1.6) + +For the manual: http://waf.googlecode.com/svn/docs/wafbook/single.html +For the api docs: http://waf.googlecode.com/svn/docs/apidocs/index.html +For the examples: see the folder demos/ + +HOW TO CREATE THE WAF SCRIPT +---------------------------- + +Python 2.6, 2.7, 3.0 or 3.1 is required to generate the waf script. Execute: +$ ./waf-light configure build +Or, if you have several python versions installed: +$ python3 ./waf-light configure build + +The Waf tools in waflib/extras are not added to the waf script. To add +some of them, use the --tools switch: +$ ./waf-light --tools=compat15,swig + +To add a tool that does not exist in the folder compat15, pass an absolute path +To customize the initialization, pass the parameter 'prelude' +$ ./waf-light --make-waf --tools=compat15,/comp/waf/aba.py --prelude=$'\tfrom waflib.extras import aba\n\taba.foo()' + +HOW TO TRY THE EXAMPLES +----------------------- + +Try this: +$ cp waf demos/c/ +$ cd demos/c/ +$ ./waf configure build + +--------------------------- +Thomas Nagy, 2010 (ita) + diff --git a/TODO b/TODO new file mode 100644 index 00000000..992851bd --- /dev/null +++ b/TODO @@ -0,0 +1,6 @@ +Waf 1.6.x +--------- + +* fix the remaining bugs +* provide more extensions + diff --git a/build_system_kit/README.txt b/build_system_kit/README.txt new file mode 100644 index 00000000..93ee9a03 --- /dev/null +++ b/build_system_kit/README.txt @@ -0,0 +1,33 @@ +The new "concurrent.futures" module from Python 3.2 will make it +even easier to execute tasks concurrently: +http://www.python.org/dev/peps/pep-3148/ + +It may be tempting to try to create a new build system from it, +but that's only a small part of a build system: + +* reinventing a system for handling commands and command-line options +* adding a system of (task) order and dependencies +* creating an extension system for new programming languages +* handling exceptions and errors +* adding support for python versions < 3.2 + +All this represents a lot of work, and there are of course lots of design +mistakes to avoid. It is so easy to create a system with poor usability, +poor extensibility, and poor performance. + +These pitfalls and many others are already solved in the Waf build system, which +also enables the re-use of its components into new build tools. By using these +tested and maintained components, much more time will be left to work +on the interesting problems such as creating an intuitive XML/YAML/JSON schema +or creating a domain-specific programming language (make-like, cmake-like, ...), +or extracting commands and dependencies to create derivated files (Makefiles, Visual studio, ..) + +A few examples are provided to illustrate the range of possibilities: +* overview: how to create a custom file using the waf framework to perform a simple build +* parser: how to add a parser for a domain-specific language +* noscript: infer what to build from given files, use no script file +* makefile_dumper: create a makefile corresponding to the current build, extracting as many dependencies as possible +* nostate: use timestamps only, and no build directory (very make-like) +* extpy: a custom waf file able to read wscript files having the extension ".py" + +Thomas Nagy, 2010-2011 diff --git a/build_system_kit/extpy/extpy.py b/build_system_kit/extpy/extpy.py new file mode 100644 index 00000000..e6446850 --- /dev/null +++ b/build_system_kit/extpy/extpy.py @@ -0,0 +1,54 @@ +#! /usr/bin/env python +# encoding: utf-8 + +import os +from waflib import Errors, Utils +from waflib import Context as mod + +class Context(mod.Context): + cmd = 'all' + def recurse(self, dirs, name=None, mandatory=True, once=True): + try: + cache = self.recurse_cache + except: + cache = self.recurse_cache = {} + + for d in Utils.to_list(dirs): + + if not os.path.isabs(d): + # absolute paths only + d = os.path.join(self.path.abspath(), d) + + WSCRIPT = os.path.join(d, 'wscript.py') + WSCRIPT_FUN = 'wscript_' + (name or self.fun) + '.py' + + node = self.root.find_node(WSCRIPT_FUN) + if node and (not once or node not in cache): + cache[node] = True + self.pre_recurse(node) + try: + function_code = node.read('rU') + exec(compile(function_code, node.abspath(), 'exec'), self.exec_dict) + finally: + self.post_recurse(node) + elif not node: + node = self.root.find_node(WSCRIPT) + if node and (not once or node not in cache): + cache[node] = True + self.pre_recurse(node) + try: + wscript_module = mod.load_module(node.abspath()) + user_function = getattr(wscript_module, (name or self.fun), None) + if not user_function: + if not mandatory: + continue + raise Errors.WafError('No function %s defined in %s' % (name or self.fun, node.abspath())) + user_function(self) + finally: + self.post_recurse(node) + elif not node: + if not mandatory: + continue + raise Errors.WafError('No wscript file in directory %s' % d) +mod.Context = Context +mod.WSCRIPT_FILE = 'wscript.py' diff --git a/build_system_kit/extpy/runme.py b/build_system_kit/extpy/runme.py new file mode 100755 index 00000000..13d51168 --- /dev/null +++ b/build_system_kit/extpy/runme.py @@ -0,0 +1,29 @@ +#! /usr/bin/env python +# encoding: utf-8 + +""" +Create a waf file able to read wscript files ending in ".py" +execute a small test to show that it works + +The waf file includes "extpy.py" which performs the required modifications +""" + +import os, subprocess + +up = os.path.dirname +join = os.path.join + +cwd = os.getcwd() +extpy = join(cwd, 'extpy.py') +args = 'python waf-light --tools=compat15,%s --prelude=$"\tfrom waflib.extras import extpy\n" ' % extpy +root = up(up(cwd)) + +subprocess.Popen(args, cwd=root, shell=True).wait() +os.rename(join(root, 'waf'), join(cwd, 'waf.py')) + +env = dict(os.environ) +if 'WAFDIR' in env: + del env['WAFDIR'] + +subprocess.Popen('python waf.py configure', cwd=cwd, shell=True, env=env).wait() + diff --git a/build_system_kit/extpy/wscript.py b/build_system_kit/extpy/wscript.py new file mode 100644 index 00000000..c0e6ddfd --- /dev/null +++ b/build_system_kit/extpy/wscript.py @@ -0,0 +1,5 @@ +#! /usr/bin/env python +# encoding: utf-8 + +def configure(conf): + print("test succeeded") diff --git a/build_system_kit/makefile_dumper/a.c b/build_system_kit/makefile_dumper/a.c new file mode 100644 index 00000000..e7b1db0d --- /dev/null +++ b/build_system_kit/makefile_dumper/a.c @@ -0,0 +1,5 @@ +#include "a.h" + +void test() { + +} diff --git a/build_system_kit/makefile_dumper/a.h b/build_system_kit/makefile_dumper/a.h new file mode 100644 index 00000000..587ad5ee --- /dev/null +++ b/build_system_kit/makefile_dumper/a.h @@ -0,0 +1 @@ +void test(); diff --git a/build_system_kit/makefile_dumper/main.c b/build_system_kit/makefile_dumper/main.c new file mode 100644 index 00000000..7ea0ac40 --- /dev/null +++ b/build_system_kit/makefile_dumper/main.c @@ -0,0 +1,6 @@ +#include "a.h" + +int main() { + test(); + return 0; +} diff --git a/build_system_kit/makefile_dumper/wscript b/build_system_kit/makefile_dumper/wscript new file mode 100644 index 00000000..5e9b3f24 --- /dev/null +++ b/build_system_kit/makefile_dumper/wscript @@ -0,0 +1,79 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +""" +Calling 'waf build' executes a normal build with Waf +Calling 'waf clean dump' will create a makefile corresponding to the build +The dependencies will be extracted too +""" + +VERSION='0.0.1' +APPNAME='cc_test' + +top = '.' + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + bld.program(source='main.c', target='app', use='mylib', cflags=['-O2']) + bld.stlib(source='a.c', target='mylib') + +# --------------------------------------------------------------------------- + +from waflib import Build, Logs +class Dumper(Build.BuildContext): + fun = 'dump' + cmd = 'dump' + +def dump(bld): + # call the build function as if a real build were performed + build(bld) + + from waflib import Task + bld.commands = [] + bld.targets = [] + + # store the command executed + old_exec = Task.TaskBase.exec_command + def exec_command(self, *k, **kw): + ret = old_exec(self, *k, **kw) + self.command_executed = k[0] + self.path = kw['cwd'] or self.generator.bld.cwd + return ret + Task.TaskBase.exec_command = exec_command + + # perform a fake build, and accumulate the makefile bits + old_process = Task.TaskBase.process + def process(self): + old_process(self) + + lst = [] + for x in self.outputs: + lst.append(x.path_from(self.generator.bld.bldnode)) + bld.targets.extend(lst) + lst.append(':') + for x in self.inputs + self.dep_nodes + self.generator.bld.node_deps.get(self.uid(), []): + lst.append(x.path_from(self.generator.bld.bldnode)) + try: + if isinstance(self.command_executed, list): + self.command_executed = ' '.join(self.command_executed) + except Exception as e: + print(e) + else: + bld.commands.append(' '.join(lst)) + bld.commands.append('\tcd %s && %s' % (self.path, self.command_executed)) + Task.TaskBase.process = process + + # write the makefile after the build is complete + def output_makefile(self): + self.commands.insert(0, "all: %s" % " ".join(self.targets)) + node = self.bldnode.make_node('Makefile') + node.write("\n".join(self.commands)) + Logs.warn('Wrote %s' % node.abspath()) + bld.add_post_fun(output_makefile) + diff --git a/build_system_kit/noscript/README.txt b/build_system_kit/noscript/README.txt new file mode 100644 index 00000000..53eb2e0b --- /dev/null +++ b/build_system_kit/noscript/README.txt @@ -0,0 +1,17 @@ +This example demonstrates the creation of a particular build tool which compiles +specific files directly, for example: + +main.c includes foo.h +foo.h has a corresponding foo.c file +foo.c includes bar.h +bar.h has a corresponding bar.c file + +Calling './dbd build' will then compile and link 'main.c', 'foo.c' and 'bar.c' into the program 'app'. +No script file is required, although the build will create a .lock file and a c4che directory. + +To create the build tool: + ./create_it.sh + +To use on the file bbit which creates a program out of main.c: + ./cbd clean build + diff --git a/build_system_kit/noscript/bar.c b/build_system_kit/noscript/bar.c new file mode 100644 index 00000000..28ef7e89 --- /dev/null +++ b/build_system_kit/noscript/bar.c @@ -0,0 +1 @@ +int bar = 4434; diff --git a/build_system_kit/noscript/bar.h b/build_system_kit/noscript/bar.h new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/build_system_kit/noscript/bar.h @@ -0,0 +1 @@ + diff --git a/build_system_kit/noscript/create_it.sh b/build_system_kit/noscript/create_it.sh new file mode 100755 index 00000000..9360242b --- /dev/null +++ b/build_system_kit/noscript/create_it.sh @@ -0,0 +1,8 @@ +# /bin/bash + +D=$PWD +pushd ../.. +./waf-light configure build --tools=$D/dbdlib.py --prelude=$'\tfrom waflib.extras import dbdlib\n\tdbdlib.start(cwd, VERSION, wafdir)\n\tsys.exit(0)' +popd +cp ../../waf dbd + diff --git a/build_system_kit/noscript/dbdlib.py b/build_system_kit/noscript/dbdlib.py new file mode 100644 index 00000000..c3f9e9d6 --- /dev/null +++ b/build_system_kit/noscript/dbdlib.py @@ -0,0 +1,134 @@ +#! /usr/bin/env python + +import os, sys, imp +from waflib import Context, Options, Configure, Utils, Logs, TaskGen, Task +import waflib.Tools.c + +""" +Compile main.c and dependent object files into a single target (program/shlib/stlib or just object files) + +- no build directory and no script files +- just a c4che directory for the configuration files +- configure, clean or build + +Uses the task signatures and the dependency calculation results to avoid +rescanning/rebuilding the files all the time +""" + +def options(opt): + opt.add_option('--type', action='store', default='program', help='type: program, shlib, stlib, objects', dest='progtype') + opt.add_option('--source', action='store', default='main.c', help='space-separated list of source files', dest='source') + opt.add_option('--app', action='store', default='app', help='name of the binary file to create', dest='app') + opt.load('compiler_c') + +def configure(conf): + conf.options = Options.options + conf.load('compiler_c') + +def build(bld): + tp = Options.options.progtype + features = 'c cprogram' + if tp == 'shlib': + features = 'c cshlib' + elif tp == 'stlib': + features = 'c cstlib' + elif tp == 'objects': + features = 'c' + + source = Options.options.source + app = Options.options.app + bld(features=features, source=source, target=app) + +def recurse_rep(x, y): + f = getattr(Context.g_module, x.cmd or x.fun, Utils.nada) + return f(x) + +def start(cwd, version, wafdir): + # this is the entry point of our small build system + # no script file here + Logs.init_log() + Context.waf_dir = wafdir + Context.out_dir = Context.top_dir = Context.run_dir = cwd + Context.g_module = imp.new_module('wscript') + Context.g_module.root_path = cwd + Context.Context.recurse = recurse_rep + + Context.g_module.configure = configure + Context.g_module.build = build + Context.g_module.options = options + Context.g_module.top = Context.g_module.out = '.' + + Options.OptionsContext().execute() + + do_config = 'configure' in sys.argv + try: + os.stat(cwd + os.sep + 'c4che') + except: + do_config = True + if do_config: + Context.create_context('configure').execute() + + if 'clean' in sys.argv: + Context.create_context('clean').execute() + + if 'build' in sys.argv: + Context.create_context('build').execute() + + +class c2(waflib.Tools.c.c): + # Make a subclass of the default c task, and bind the .c extension to it + + def runnable_status(self): + ret = super(waflib.Tools.c.c, self).runnable_status() + self.more_tasks = [] + + # use a cache to avoid creating the same tasks + # for example, truc.cpp might be compiled twice + try: + shared = self.generator.bld.shared_tasks + except AttributeError: + shared = self.generator.bld.shared_tasks = {} + + if ret != Task.ASK_LATER: + for x in self.generator.bld.node_deps[self.uid()]: + node = x.parent.get_src().find_resource(x.name.replace('.h', '.c')) + if node: + try: + tsk = shared[node] + except: + tsk = shared[node] = self.generator.c_hook(node) + + self.more_tasks.append(tsk) + + # add the node created to the link task outputs + try: + link = self.generator.link_task + except AttributeError: + pass + else: + if not tsk.outputs[0] in link.inputs: + link.inputs.append(tsk.outputs[0]) + link.set_run_after(tsk) + + # any change in the order of the input nodes may cause a recompilation + link.inputs.sort(key=lambda x: x.abspath()) + + # if you want to modify some flags + # you *must* have the task recompute the signature + self.env.append_value('CXXFLAGS', '-O2') + delattr(self, 'cache_sig') + return super(waflib.Tools.c.c, self).runnable_status() + + return ret + +@TaskGen.extension('.c') +def c_hook(self, node): + # re-bind the extension to this new class + return self.create_compiled_task('c2', node) + +# modify the existing class to output the targets in the same directory as the original files +Task.update_outputs(c2) +Task.update_outputs(waflib.Tools.c.cprogram) +Task.update_outputs(waflib.Tools.c.cshlib) +Task.update_outputs(waflib.Tools.c.cstlib) + diff --git a/build_system_kit/noscript/foo.c b/build_system_kit/noscript/foo.c new file mode 100644 index 00000000..9157f236 --- /dev/null +++ b/build_system_kit/noscript/foo.c @@ -0,0 +1,4 @@ +#include "foo.h" +#include "bar.h" + +int k = 334; diff --git a/build_system_kit/noscript/foo.h b/build_system_kit/noscript/foo.h new file mode 100644 index 00000000..1a4baf53 --- /dev/null +++ b/build_system_kit/noscript/foo.h @@ -0,0 +1 @@ + diff --git a/build_system_kit/noscript/main.c b/build_system_kit/noscript/main.c new file mode 100644 index 00000000..40c69f5e --- /dev/null +++ b/build_system_kit/noscript/main.c @@ -0,0 +1,6 @@ +#include "foo.h" +#include +int main() { + printf("hello from app\n"); + return 0; +} diff --git a/build_system_kit/nostate/README.txt b/build_system_kit/nostate/README.txt new file mode 100644 index 00000000..1108f887 --- /dev/null +++ b/build_system_kit/nostate/README.txt @@ -0,0 +1,21 @@ +Note from the author: using signatures is of course much better +this example is for research purposes only + + +In this example, no configuration will be written or read, +and the build will only use timestamps (no cache files +and no signatures). There is no build directory either. + +To build, use "./ebd" + + +Although the wscript file only declares a build function, +the system performs a configuration internally to check for +a C compiler. The configuration and build context classes are +overridden to hide output messages and to avoid creating cache +files. + +The task class is monkey-patched so that all existing build tasks +will execute without using signatures (only file timestamps are considered). +Implicit dependencies such as headers are still computed automatically. + diff --git a/build_system_kit/nostate/a.c b/build_system_kit/nostate/a.c new file mode 100644 index 00000000..e7b1db0d --- /dev/null +++ b/build_system_kit/nostate/a.c @@ -0,0 +1,5 @@ +#include "a.h" + +void test() { + +} diff --git a/build_system_kit/nostate/a.h b/build_system_kit/nostate/a.h new file mode 100644 index 00000000..587ad5ee --- /dev/null +++ b/build_system_kit/nostate/a.h @@ -0,0 +1 @@ +void test(); diff --git a/build_system_kit/nostate/create_it.sh b/build_system_kit/nostate/create_it.sh new file mode 100755 index 00000000..a28f761b --- /dev/null +++ b/build_system_kit/nostate/create_it.sh @@ -0,0 +1,8 @@ +# /bin/bash + +D=$PWD +pushd ../.. +./waf-light configure build --tools=$D/ebdlib.py --prelude=$'\tfrom waflib.extras import ebdlib\n\tebdlib.start(cwd, VERSION, wafdir)\n\tsys.exit(0)' +popd +cp ../../waf ebd + diff --git a/build_system_kit/nostate/ebdlib.py b/build_system_kit/nostate/ebdlib.py new file mode 100644 index 00000000..f8af4a37 --- /dev/null +++ b/build_system_kit/nostate/ebdlib.py @@ -0,0 +1,131 @@ +#! /usr/bin/env python + +import os, sys, imp, time +from waflib import Context, Options, Configure, Utils, Logs, TaskGen, Task, Build, ConfigSet +import waflib.Tools.c + +""" +Create a modified waf file in which tasks use timestamps only +see README.txt +""" + +# we hard-code a configuration for c but it could be left in the script file too +def configure(conf): + conf.load('gcc') + +def recurse_rep(x, y): + f = getattr(Context.g_module, x.cmd or x.fun, Utils.nada) + return f(x) + +def start(cwd, version, wafdir): + # this is the entry point of our small build system + + Logs.init_log() + Context.waf_dir = wafdir + Context.out_dir = Context.top_dir = Context.run_dir = cwd + Context.g_module = Context.load_module(cwd + os.sep + 'wscript') + Context.g_module.configure = configure + Context.g_module.root_path = cwd + Context.Context.recurse = recurse_rep + + Context.g_module.top = Context.g_module.out = '.' # no build directory + + # just parse the options and execute a build + Options.OptionsContext().execute() + + conf = Context.create_context('configure') + conf.options = Options.options + conf.execute() + + bld = Context.create_context('build') + bld.env = conf.env + bld.options = Options.options + bld.environ = os.environ + bld.execute() + +# change the build context so it does not need to write any file +class StatelessBuild(Build.BuildContext): + def load_envs(self): + self.env = ConfigSet.ConfigSet() + def store(self): + pass + def restore(self): + self.init_dirs() + def execute_build(self): + # we override this method to hide the messages "leaving directory" (just because) + self.recurse([self.run_dir]) + self.pre_build() + + self.timer = Utils.Timer() + + if Options.options.progress_bar: + sys.stderr.write(Logs.colors.cursor_off) + try: + self.compile() + finally: + if Options.options.progress_bar: + sys.stderr.write(Logs.colors.cursor_on) + print('') + self.post_build() + +class SilentConf(Configure.ConfigurationContext): + # silent configuration + def __init__(self, **kw): + # disable the configuration messages from Context.start_msg/end_msg + self.in_msg = 1 + super(SilentConf, self).__init__(**kw) + + def execute(self): + + # copy-paste from the original method, but without the cache file creation + self.init_dirs() + + path = os.path.join(self.bldnode.abspath(), 'config.log') + self.logger = Logs.make_logger(path, 'cfg') + + app = getattr(Context.g_module, 'APPNAME', '') + if app: + ver = getattr(Context.g_module, 'VERSION', '') + if ver: + app = "%s (%s)" % (app, ver) + + now = time.ctime() + pyver = sys.hexversion + systype = sys.platform + args = " ".join(sys.argv) + wafver = Context.WAFVERSION + abi = Context.ABI + self.to_log(Configure.conf_template % vars()) + + super(Configure.ConfigurationContext, self).execute() + + +# change the superclass of existing tasks to force timestamps (the build has no state) +def status(self): + for t in self.run_after: + if not t.hasrun: + return Task.ASK_LATER + + implicit_deps = [] + try: + implicit_deps, _ = self.scan() + except: + pass + + # we can add one more node, for example: + implicit_deps.append(self.generator.path.make_node('wscript')) + + for x in self.inputs + self.dep_nodes + implicit_deps: + for y in self.outputs: + try: + if os.stat(x.abspath()).st_mtime > os.stat(y.abspath()).st_mtime: + return Task.RUN_ME + except: + return Task.RUN_ME + + return Task.SKIP_ME +Task.Task.runnable_status = status + +# the post build execution does not need to deal with signatures or anything else +Task.Task.post_run = Utils.nada + diff --git a/build_system_kit/nostate/main.c b/build_system_kit/nostate/main.c new file mode 100644 index 00000000..7ea0ac40 --- /dev/null +++ b/build_system_kit/nostate/main.c @@ -0,0 +1,6 @@ +#include "a.h" + +int main() { + test(); + return 0; +} diff --git a/build_system_kit/nostate/wscript b/build_system_kit/nostate/wscript new file mode 100644 index 00000000..b3e42213 --- /dev/null +++ b/build_system_kit/nostate/wscript @@ -0,0 +1,12 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +""" +See README.txt +""" + +def build(bld): + bld.program(source='main.c', target='app', use='mylib', cflags=['-O2']) + bld.stlib(source='a.c', target='mylib') + diff --git a/build_system_kit/overview/README.txt b/build_system_kit/overview/README.txt new file mode 100644 index 00000000..98cbafd0 --- /dev/null +++ b/build_system_kit/overview/README.txt @@ -0,0 +1,11 @@ +See the corresponding entry on the waf blog: +http://waf-devel.blogspot.com/2010/12/make-your-own-build-system-with-waf.html + +To create the build tool: + ./create_it.sh + +To use on the file bbit which creates a program out of main.c: + ./bbd clean build + +Enjoy your new build system! :-) + diff --git a/build_system_kit/overview/bbdlib.py b/build_system_kit/overview/bbdlib.py new file mode 100644 index 00000000..2cf2c46a --- /dev/null +++ b/build_system_kit/overview/bbdlib.py @@ -0,0 +1,39 @@ +#! /usr/bin/env python + +import os, sys, imp +from waflib import Context, Options, Configure, Utils, Logs + +def start(cwd, version, wafdir): + # simple example, the file main.c is hard-coded + try: + os.stat(cwd + os.sep + 'bbit') + except: + print('call from a folder containing a file named "bbit"') + sys.exit(1) + + Logs.init_log() + Context.waf_dir = wafdir + Context.top_dir = Context.run_dir = cwd + Context.out_dir = os.path.join(cwd, 'build') + Context.g_module = imp.new_module('wscript') + Context.g_module.root_path = os.path.join(cwd, 'bbit') + Context.Context.recurse = \ + lambda x, y: getattr(Context.g_module, x.cmd or x.fun, Utils.nada)(x) + + Context.g_module.configure = lambda ctx: ctx.load('g++') + Context.g_module.build = lambda bld: bld.objects(source='main.c') + + Options.OptionsContext().execute() + + do_config = 'configure' in sys.argv + try: + os.stat(cwd + os.sep + 'build') + except: + do_config = True + if do_config: + Context.create_context('configure').execute() + + if 'clean' in sys.argv: + Context.create_context('clean').execute() + if 'build' in sys.argv: + Context.create_context('build').execute() diff --git a/build_system_kit/overview/bbit b/build_system_kit/overview/bbit new file mode 100644 index 00000000..2c749585 --- /dev/null +++ b/build_system_kit/overview/bbit @@ -0,0 +1 @@ +# empty file (for now) diff --git a/build_system_kit/overview/create_it.sh b/build_system_kit/overview/create_it.sh new file mode 100755 index 00000000..2cfa9e48 --- /dev/null +++ b/build_system_kit/overview/create_it.sh @@ -0,0 +1,8 @@ +# /bin/bash + +D=$PWD +pushd ../.. +./waf-light configure build --tools=$D/bbdlib.py --prelude=$'\tfrom waflib.extras import bbdlib\n\tbbdlib.start(cwd, VERSION, wafdir)\n\tsys.exit(0)' +popd +cp ../../waf bbd + diff --git a/build_system_kit/overview/main.c b/build_system_kit/overview/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/build_system_kit/overview/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/build_system_kit/parser/README.txt b/build_system_kit/parser/README.txt new file mode 100644 index 00000000..df25d211 --- /dev/null +++ b/build_system_kit/parser/README.txt @@ -0,0 +1,11 @@ +This example derives from the 'overview' and demonstrates how to provide +a parser for a make-like language. + +The script files read are named 'cbit' + +To create the build tool: + ./create_it.sh + +To use on the file cbit which creates a program out of main.c: + ./cbd clean build + diff --git a/build_system_kit/parser/cbdlib.py b/build_system_kit/parser/cbdlib.py new file mode 100644 index 00000000..8e0c6b6a --- /dev/null +++ b/build_system_kit/parser/cbdlib.py @@ -0,0 +1,73 @@ +#! /usr/bin/env python + +import os, sys, imp, re +from waflib import Context, Options, Configure, Utils, Logs + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.options = Options.options + conf.load('compiler_c') + + +re_com = re.compile("#.*$", re.M) +def build(bld): + txt = bld.path.find_node('cbit').read() + txt = re_com.sub('', txt) + + tg = None + for x in txt.splitlines(): + if not x: + continue + elif x.startswith('\t') or x.startswith(' '): + tg.rule = x.lstrip() + else: + line = x.split(':') + tgt = line[0].lstrip() + src = line[1].lstrip() + tg = bld() + if src: + tg.source = src + if tgt: + tg.target = tgt + +def recurse_rep(x, y): + f = getattr(Context.g_module, x.cmd or x.fun, Utils.nada) + return f(x) + +def start(cwd, version, wafdir): + # simple example, the file main.c is hard-coded + try: + os.stat(cwd + os.sep + 'cbit') + except: + print('call from a folder containing a file named "cbit"') + sys.exit(1) + + Logs.init_log() + Context.waf_dir = wafdir + Context.top_dir = Context.run_dir = cwd + Context.out_dir = os.path.join(cwd, 'build') + Context.g_module = imp.new_module('wscript') + Context.g_module.root_path = os.path.join(cwd, 'cbit') + Context.Context.recurse = recurse_rep + + # this is a fake module, which looks like a standard wscript file + Context.g_module.options = options + Context.g_module.configure = configure + Context.g_module.build = build + + Options.OptionsContext().execute() + + do_config = 'configure' in sys.argv + try: + os.stat(cwd + os.sep + 'build') + except: + do_config = True + if do_config: + Context.create_context('configure').execute() + + if 'clean' in sys.argv: + Context.create_context('clean').execute() + if 'build' in sys.argv: + Context.create_context('build').execute() diff --git a/build_system_kit/parser/cbit b/build_system_kit/parser/cbit new file mode 100644 index 00000000..f7ab86c5 --- /dev/null +++ b/build_system_kit/parser/cbit @@ -0,0 +1,5 @@ +# a non-python file format (why not?) +# just call "./cbd build" +foo.txt: cbit + cp ../cbit foo.txt + diff --git a/build_system_kit/parser/create_it.sh b/build_system_kit/parser/create_it.sh new file mode 100755 index 00000000..32e74e80 --- /dev/null +++ b/build_system_kit/parser/create_it.sh @@ -0,0 +1,8 @@ +# /bin/bash + +D=$PWD +pushd ../.. +./waf-light configure build --tools=$D/cbdlib.py --prelude=$'\tfrom waflib.extras import cbdlib\n\tcbdlib.start(cwd, VERSION, wafdir)\n\tsys.exit(0)' +popd +cp ../../waf cbd + diff --git a/build_system_kit/parser/main.c b/build_system_kit/parser/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/build_system_kit/parser/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/configure b/configure new file mode 100755 index 00000000..03fab4b1 --- /dev/null +++ b/configure @@ -0,0 +1,119 @@ +#! /bin/sh + +# waf configure wrapper + +# Fancy colors used to beautify the output a bit. +# +if [ "$NOCOLOR" ] ; then + NORMAL="" + BOLD="" + RED="" + YELLOW="" + GREEN="" +else + NORMAL='\033[0m' + BOLD='\033[01;1m' + RED='\033[01;91m' + YELLOW='\033[00;33m' + GREEN='\033[01;92m' +fi + +EXIT_SUCCESS=0 +EXIT_FAILURE=1 +EXIT_ERROR=2 +EXIT_BUG=10 + +CUR_DIR=$PWD + +#possible relative path +WORKINGDIR=`dirname $0` +cd $WORKINGDIR +#abs path +WORKINGDIR=`pwd` +cd $CUR_DIR + + +# Checks for WAF. Honours $WAF if set. Stores path to 'waf' in $WAF. +# Requires that $PYTHON is set. +# +checkWAF() +{ + printf "Checking for WAF\t\t\t: " + #installed miniwaf in sourcedir + if [ -z "$WAF" ] ; then + if [ -f "${WORKINGDIR}/waf" ] ; then + WAF="${WORKINGDIR}/waf" + if [ ! -x "$WAF" ] ; then + chmod +x $WAF + fi + fi + fi + if [ -z "$WAF" ] ; then + if [ -f "${WORKINGDIR}/waf-light" ] ; then + ${WORKINGDIR}/waf-light --make-waf + WAF="${WORKINGDIR}/waf" + fi + fi + #global installed waf with waf->waf.py link + if [ -z "$WAF" ] ; then + WAF=`which waf 2>/dev/null` + fi + # neither waf nor miniwaf could be found + if [ ! -x "$WAF" ] ; then + printf "$RED""not found""$NORMAL""\n" + echo "Go to http://code.google.com/p/waf/" + echo "and download a waf version" + exit $EXIT_FAILURE + else + printf "$GREEN""$WAF""$NORMAL""\n" + fi +} + +# Generates a Makefile. Requires that $WAF is set. +# +generateMakefile() +{ + cat > Makefile << EOF +#!/usr/bin/make -f +# Waf Makefile wrapper +WAF_HOME=$CUR_DIR + +all: +#@$WAF build + +all-debug: + @$WAF -v build + +all-progress: + @$WAF -p build + +install: + $WAF install --yes; + +uninstall: + $WAF uninstall + +clean: + @$WAF clean + +distclean: + @$WAF distclean + @-rm -rf build + @-rm -f Makefile + +check: + @$WAF check + +dist: + @$WAF dist + +.PHONY: clean dist distclean check uninstall install all + +EOF +} + +checkWAF +generateMakefile + +"${WAF}" configure $* +exit $? diff --git a/demos/asm/main.c b/demos/asm/main.c new file mode 100644 index 00000000..a43cd740 --- /dev/null +++ b/demos/asm/main.c @@ -0,0 +1,10 @@ +#include + +int mult10(int); + +int main() +{ + int asm_val = mult10(2); + printf("From ASM: %d\n", asm_val); + return 0; +} diff --git a/demos/asm/test.S b/demos/asm/test.S new file mode 100644 index 00000000..cbbd1659 --- /dev/null +++ b/demos/asm/test.S @@ -0,0 +1,14 @@ + +.text +.align 2 + +val: + .long 10 + +# Multiply input value by 10... +.global mult10 +.type mult10, function +mult10: + movl val,%eax + imul 4(%esp),%eax + ret diff --git a/demos/asm/wscript b/demos/asm/wscript new file mode 100644 index 00000000..6fad3f62 --- /dev/null +++ b/demos/asm/wscript @@ -0,0 +1,12 @@ +#! /usr/bin/env python +# encoding: utf-8 + +def configure(conf): + conf.load('gcc gas') + +def build(bld): + # http://waf.googlecode.com/svn/docs/apidocs/tools/asm.html + bld.program( + source = 'main.c test.S', + target = 'asmtest') + diff --git a/demos/bisonflex/calc.l b/demos/bisonflex/calc.l new file mode 100644 index 00000000..d364ca5f --- /dev/null +++ b/demos/bisonflex/calc.l @@ -0,0 +1,13 @@ +%{ +#define YYSTYPE double +#include "calc.tab.h" +extern YYSTYPE yylval; +%} + +%% +[0-9]+\.?[0-9]* { yylval = atof( yytext ); return NUMBER; }; +[ \t] ; /* ignore whitespace */ +\n { return yytext[0]; }; +. { return yytext[0]; } +<> { printf("eof\n"); return 0; }; +%% diff --git a/demos/bisonflex/calc.y b/demos/bisonflex/calc.y new file mode 100644 index 00000000..d4c68f21 --- /dev/null +++ b/demos/bisonflex/calc.y @@ -0,0 +1,33 @@ +%{ +#define YYSTYPE double +#include +// yylex() is generated by flex +int yylex(void); +// we have to define yyerror() +int yyerror (char const *); + +%} +%token NUMBER + +%left '+' '-' +%left '*' '/' +%right '(' + +%% +stmtlist: statement '\n' stmtlist { + printf("done with statement equal to [%g]\n", $1); + } | // EMPTY RULE i.e. stmtlist -> nil + { printf("DONE\n") ;}; + +statement: expression { printf("VALUE=%g\n",$1); }; + +expression: expression '+' expression { $$ = $1 + $3; } | + expression '-' expression { $$ = $1 - $3; } | + expression '*' expression { $$ = $1 * $3; } | + expression '/' expression { + if($3 !=0) { $$ = $1 / $3; } else + { printf("div by zero\n"); $$=0;} } | + '(' expression ')' { $$ = $2; } | + NUMBER { $$ = $1; } ; + +%% diff --git a/demos/bisonflex/main.c b/demos/bisonflex/main.c new file mode 100644 index 00000000..20a1246a --- /dev/null +++ b/demos/bisonflex/main.c @@ -0,0 +1,24 @@ +#include +/* this file should work in both c and c++ */ +extern int yyparse(); + +int yyerror (char const *a) +{ + printf("yyerror: (%s)\n", a); + return 1; +} + +int main(int argc, char *argv[]) +{ + int yy; + yy = yyparse(); + if (yy != 0) + { + printf("Syntax or parse error %i. Aborting.\n", yy); + return 1; + } + else{ + printf("Success.\n"); + } + return 0; +} diff --git a/demos/bisonflex/wscript b/demos/bisonflex/wscript new file mode 100644 index 00000000..dd42af26 --- /dev/null +++ b/demos/bisonflex/wscript @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# encoding: utf-8 + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c flex bison') + conf.env.LIB_CALC = ['fl'] + +def build(bld): + tg = bld( + features = 'c cprogram', + source = 'calc.l calc.y main.c', + target = 'calc', + use = 'CALC') + + # to compile in c++ mode change to: + # features= 'cxx cxxprogram' + # and add: + # import waflib.Tools.cxx as cxx + # tg.mappings['.c'] = cxx.cxx_hook + + # re-use the files for other targets: + # bld(features='c cshlib', source='calc.tab.c calc.lex.c', use='CALC', target='hmm') + diff --git a/demos/c++/a.cpp b/demos/c++/a.cpp new file mode 100644 index 00000000..642c45f7 --- /dev/null +++ b/demos/c++/a.cpp @@ -0,0 +1,13 @@ + +#ifdef _MSC_VER +# define testshlib_EXPORT __declspec(dllexport) +#else +# define testshlib_EXPORT +#endif + +extern testshlib_EXPORT void foo(); + +static const int truc=5; + +void foo() { } + diff --git a/demos/c++/b.cpp b/demos/c++/b.cpp new file mode 100644 index 00000000..1e09a10f --- /dev/null +++ b/demos/c++/b.cpp @@ -0,0 +1 @@ +int u = 64; diff --git a/demos/c++/main.c b/demos/c++/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/demos/c++/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/demos/c++/main.cpp b/demos/c++/main.cpp new file mode 100644 index 00000000..dacbb25b --- /dev/null +++ b/demos/c++/main.cpp @@ -0,0 +1,8 @@ +#include + +extern void foo(); + +int main() { + foo(); + return 0; +} diff --git a/demos/c++/wscript b/demos/c++/wscript new file mode 100644 index 00000000..1b7751f2 --- /dev/null +++ b/demos/c++/wscript @@ -0,0 +1,33 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +# the following two variables are used by the target "waf dist" +VERSION='0.0.1' +APPNAME='cxx_test' + +# these variables are mandatory ('/' are converted automatically) +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_cxx') + +def configure(conf): + conf.load('compiler_cxx') + conf.check(header_name='stdio.h', features='cxx cxxprogram', mandatory=False) + +def build(bld): + bld.shlib(source='a.cpp', target='mylib', vnum='9.8.7') + bld.program(source='main.cpp', target='app', use='mylib') + bld.stlib(target='foo', source='b.cpp') + + # just a test to check if the .c is compiled as c++ when no c compiler is found + bld.program(features='cxx cxxprogram', source='main.c', target='app2') + + if bld.cmd != 'clean': + from waflib import Logs + bld.logger = Logs.make_logger('test.log', 'build') # just to get a clean output + bld.check(header_name='sadlib.h', features='cxx cxxprogram', mandatory=False) + bld.logger = None + diff --git a/demos/c/program/a.h b/demos/c/program/a.h new file mode 100644 index 00000000..1fc12a6a --- /dev/null +++ b/demos/c/program/a.h @@ -0,0 +1 @@ +int k = 123; diff --git a/demos/c/program/main.c b/demos/c/program/main.c new file mode 100644 index 00000000..e60ed5f6 --- /dev/null +++ b/demos/c/program/main.c @@ -0,0 +1,9 @@ +#include "a.h" +#include "b.h" +#include "config.h" + +#include "abc.h" + +int main() { + return 0; +} diff --git a/demos/c/program/wscript_build b/demos/c/program/wscript_build new file mode 100644 index 00000000..83797145 --- /dev/null +++ b/demos/c/program/wscript_build @@ -0,0 +1,36 @@ +#! /usr/bin/env python + +def write_header(tsk): + tsk.outputs[0].write('int abc = 423;') +bld(rule=write_header, target='b.h', ext_out=['.h']) + +bld.program( + features = 'aaa', + source = 'main.c', + includes = '. ..', + cflags = ['-O3'], + defines = ['foo=bar'], + target = 'myprogram', + use = 'M') + +# make main.c depend on wscript_build, just for the fun of it +bld.add_manual_dependency('main.c', bld.path.find_resource('wscript_build')) + +# ---------------------------------------- + +from waflib import TaskGen +@TaskGen.feature('aaa') +@TaskGen.before('apply_link') +def add_one_task(self): + """this is a task generator method, it is bound to the feature 'aaa' """ + tsk = self.create_task('foo') + tsk.outputs = [self.bld.path.find_or_declare('abc.h')] + +import waflib.Task +class foo(waflib.Task.Task): + """this is a task class""" + before = ['c'] + color = 'BLUE' + def run(self): + self.outputs[0].write('int kik = 343;\n') + diff --git a/demos/c/shlib/foo.def b/demos/c/shlib/foo.def new file mode 100644 index 00000000..dcc7ccc8 --- /dev/null +++ b/demos/c/shlib/foo.def @@ -0,0 +1,2 @@ +EXPORTS + foo diff --git a/demos/c/shlib/main.c b/demos/c/shlib/main.c new file mode 100644 index 00000000..4f56fe06 --- /dev/null +++ b/demos/c/shlib/main.c @@ -0,0 +1,8 @@ + +extern void foo(); + +int main() +{ + foo(); + return 0; +} diff --git a/demos/c/shlib/test_shlib.c b/demos/c/shlib/test_shlib.c new file mode 100644 index 00000000..809257dc --- /dev/null +++ b/demos/c/shlib/test_shlib.c @@ -0,0 +1,5 @@ + +static const int truc=5; + +void foo() { } +void _internal() {} /* this one is not gonna be exported by *.def */ diff --git a/demos/c/shlib/wscript_build b/demos/c/shlib/wscript_build new file mode 100644 index 00000000..c3cbc095 --- /dev/null +++ b/demos/c/shlib/wscript_build @@ -0,0 +1,44 @@ +#! /usr/bin/env python + +bld.shlib( + source = 'test_shlib.c', + target = 'my_shared_lib', + vnum = '1.2.3', + defs = 'foo.def') + + +t = bld.program( + #features = 'my_precious', + source = 'main.c', + target = 'test_shared_link', + use = 'my_shared_lib', + # 1. settings flags directly + #linkflags = ['-L/disk/comp/waf/demos/c/build/shlib', '-lmy_shared_lib'] + ) +# 2. another way of setting flags +#t.env.linkflags = ['-L/disk/comp/waf/demos/c/build/shlib', '-lmy_shared_lib'] + +# 3. advanced flag control through 'feature' methods (standard practice when 1. or 2. is not enough) +#import sys +#from waflib import TaskGen +#@TaskGen.feature('my_precious') +#@TaskGen.after_method('apply_link', 'propagate_uselib_vars') +#def set_flags(self): +# if sys.platform == 'linux2': +# self.link_task.env.LINKFLAGS = ['-Lshlib', '--whole-archive', '-lmy_shared_lib'] +# self.link_task.env.LIB = [] +# self.link_task.env.STLIB = [] +# self.link_task.env.STLIB_MARKER = [] + +# 4. changing the class - setting flags such as LINKFLAGS_cshlib is usually a much better idea +#from waflib.Utils import run_once +#from waflib.Tools.c import cprogram +#class cprogram(cprogram): +# def runnable_status(self): +# self.set_flags() +# self.set_flags() # just to see +# return super(cprogram, self).runnable_status() +# @run_once +# def set_flags(self): +# self.env.append_value('LINKFLAGS', ['-g']) + diff --git a/demos/c/stlib/main.c b/demos/c/stlib/main.c new file mode 100644 index 00000000..52f85343 --- /dev/null +++ b/demos/c/stlib/main.c @@ -0,0 +1,7 @@ + +#include "foo.h" + +int main() +{ + return 0; +} diff --git a/demos/c/stlib/test_staticlib.c b/demos/c/stlib/test_staticlib.c new file mode 100644 index 00000000..d67f6447 --- /dev/null +++ b/demos/c/stlib/test_staticlib.c @@ -0,0 +1,2 @@ +int k = 3; + diff --git a/demos/c/stlib/wscript_build b/demos/c/stlib/wscript_build new file mode 100644 index 00000000..c591ed67 --- /dev/null +++ b/demos/c/stlib/wscript_build @@ -0,0 +1,18 @@ +#! /usr/bin/env python + +bld.stlib( + source = 'test_staticlib.c', + target = 'my_static_lib') + +bld.program( + source = 'main.c', + target = 'test_static_link', + includes = '.', + use = 'my_static_lib') + +def r1(self): + import time + time.sleep(1) + self.outputs[0].write(' ') +bld(rule=r1, target='foo.h') # when in doubt, add before=['c', 'cxx'] + diff --git a/demos/c/wscript b/demos/c/wscript new file mode 100644 index 00000000..13136232 --- /dev/null +++ b/demos/c/wscript @@ -0,0 +1,98 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +# the following two variables are used by the target "waf dist" +VERSION='0.0.1' +APPNAME='cc_test' + +top = '.' + +import waflib.Configure +#waflib.Configure.autoconfig = True + +def options(opt): + opt.load('compiler_c') + opt.load('gnu_dirs') + +def configure(conf): + conf.load('compiler_c') + conf.check_cc(fragment="int main() { return 0; }\n") + conf.check_cc(fragment="int main() { return 0; }\n", execute=True) + conf.check_cc(fragment="""#include\nint main(){fprintf(stderr, "mu"); printf("%d", 22);return 0;}\n""", execute=True, define_name='HAVE_MU') + conf.check_cc(lib='m', cflags='-Wall', defines=['var=foo', 'x=y'], uselib_store='M', mandatory=False) + + conf.check_large_file(mandatory=False) + conf.check_inline() + conf.check_library() + + conf.multicheck( + {'header_name':'stdio.h'}, + {'header_name':'unistd.h'}, + {'header_name':'stdlib.h'}, + msg = 'Checking for standard headers', + mandatory = False + ) + conf.check_cc(header_name='stdio.h', auto_add_header_name=True) + #conf.check_cc(header_name='unistd.h') + conf.check_cc(fragment='int main() {return 0;}\n') + conf.write_config_header('config.h') + + # exclude system libraries, force a particular folder (see strictlib below) + #conf.check(features='c cprogram strictlib', lib = 'gif', libpath = ['/opt/lib']) + +def build(bld): + bld.env.DEFINES=['WAF=1'] + + bld.recurse('program stlib shlib') + #bld.install_files('/tmp/foo', 'wscript') + #bld.env.PREFIX='/tmp/foo' + bld.install_files('${PREFIX}/', 'program/a.h program/main.c', relative_trick=False) + bld.install_as('${PREFIX}/gnigni.txt', 'wscript') + bld.symlink_as('${PREFIX}/libfoo.so', 'wscript') + + bld.env.FOO =['m', 'ncurses'] + bld.env.ST = '-L%s' + bld(rule='echo ${ST:FOO}', always=True, shell=1) + + # illustrate how to add a command 'foo' and to execute things in it + if bld.cmd == 'foo': + def bar(bld): + print('myprogram exit status is', + bld.exec_command(bld.get_tgen_by_name('myprogram').link_task.outputs[0].abspath())) + bld.add_post_fun(bar) + #bld(rule='echo ${SRC} ${tsk.generator.newsize}', newsize='256x256', source='wscript') + +# command examples + +from waflib.Build import BuildContext +class foo_class(BuildContext): + cmd = 'foo' + +from waflib.Context import Context +class package_class(Context): + """just a test, try calling 'waf package' """ + cmd = 'package' + fun = 'package' + +def package(ctx): + print('just a test', ctx.path.ant_glob('wscript')) + +# and a task generator method example + +from waflib import TaskGen +@TaskGen.feature('strictlib') +def check_lib_in_libpath(self): + #For use in a configuration check: raise an exception + #if the library file does not exist in the folders pointed by 'libpath' + pths = self.to_list(getattr(self, 'libpath', [])) + if pths: + for l in self.to_list(Utils.to_list(getattr(self, 'lib', []))): + for x in pths: + names = Utils.listdir(x) + lname = self.env.cshlib_PATTERN % l + if lname in names: + break + else: + self.bld.fatal('wrong path for the library %s' % l) + diff --git a/demos/csharp/Dye.cs b/demos/csharp/Dye.cs new file mode 100644 index 00000000..0ba12436 --- /dev/null +++ b/demos/csharp/Dye.cs @@ -0,0 +1,11 @@ +public class Dye { + private string msg = ""; + + public Dye(string _msg) { + msg = _msg; + } + + public void display() { + System.Console.WriteLine(msg); + } +} diff --git a/demos/csharp/Hi.cs b/demos/csharp/Hi.cs new file mode 100644 index 00000000..9e9de7ac --- /dev/null +++ b/demos/csharp/Hi.cs @@ -0,0 +1,15 @@ +namespace MyApp +{ + using System; + + public class Hi + { + public static int Main(string[] args) + { + My fu = new My("Hello there"); + fu.display(); + return 0; + } + } +} + diff --git a/demos/csharp/My.cs b/demos/csharp/My.cs new file mode 100644 index 00000000..0f850809 --- /dev/null +++ b/demos/csharp/My.cs @@ -0,0 +1,11 @@ +public class My { + private Dye foo; + + public My(string msg) { + foo = new Dye(msg); + } + + public void display() { + foo.display(); + } +} diff --git a/demos/csharp/Simple.cs b/demos/csharp/Simple.cs new file mode 100644 index 00000000..f6a78e0b --- /dev/null +++ b/demos/csharp/Simple.cs @@ -0,0 +1,37 @@ +// taken from the gtk# samples +namespace MyApp +{ + using Gtk; + using System; + + public class Simple + { + + public static int Main(string[] args) + { + Application.Init(); + Window win = new Window("Simple gtk# app"); + win.DefaultWidth = 300; + win.DefaultHeight = 300; + win.DeleteEvent += new DeleteEventHandler(Window_Delete); + Button btn = new Button("Simple button"); + btn.Clicked += new EventHandler(print_line); + win.Add(btn); + win.ShowAll(); + Application.Run(); + return 0; + } + + static void print_line(object obj, EventArgs args) + { + Console.WriteLine("Simple button was clicked!"); + } + + static void Window_Delete(object obj, DeleteEventArgs args) + { + Application.Quit(); + args.RetVal = true; + } + } +} + diff --git a/demos/csharp/wscript b/demos/csharp/wscript new file mode 100644 index 00000000..a638c9fb --- /dev/null +++ b/demos/csharp/wscript @@ -0,0 +1,38 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +FRAG = ''' +namespace Moo { + public class Test { + public static int Main(string[] args) { + return 0; + } + } +} +''' + +def options(opt): + opt.load('cs') + +def configure(conf): + conf.load('cs') + try: + conf.check(features='cs', fragment=FRAG, compile_filename='test.cs', gen='test.exe', + csflags=['-pkg:gtk-sharp-2.0'], msg='Checking for Gtksharp support') + conf.env.HAS_GTKSHARP = True + except: + conf.env.HAS_GTKSHARP = False + +def build(bld): + + # for system libraries, use: + #bld.read_csshlib('ManagedLibrary.dll', paths=[bld.env.mylibrarypath]) + + bld(features='cs', source='My.cs Dye.cs', gen='my.dll', name='mylib', csdebug='full') + bld(features='cs', source='Hi.cs', includes='.', gen='hi.exe', use='mylib', name='hi') + if bld.env.HAS_GTKSHARP: + bld(features='cs', source='Simple.cs', includes='.', gen='mono-hello.exe', csflags=['-pkg:gtk-sharp-2.0']) + + # note: + # bld(features='cs', ..., type='module' # or exe, library, winexe, ... diff --git a/demos/d/example.d b/demos/d/example.d new file mode 100644 index 00000000..afd5710d --- /dev/null +++ b/demos/d/example.d @@ -0,0 +1,35 @@ +module abc. +def. gh; + +import std.stdio; // for writefln() +import std . // system2; +system ; +static import std.date, std.thread /+ /+ +/ , std.io +/ ; + +import testlib.code; + +int main(string[] args) // string is a type alias for const(char)[] +{ + // Declare an associative array with string keys and + // arrays of strings as data + char[][] [char[]] container; + + int result = test_lib(); + + + // Add some people to the container and let them carry some items + container["Anya"] ~= cast(char[]) "scarf"; + container["Dimitri"] ~= cast(char[]) "tickets"; + container["Anya"] ~= cast(char[]) "puppy"; + + // Iterate over all the persons in the container + foreach (const(char)[] person, char[][] items; container) + display_item_count(cast(char[]) person, items); + return 0; +} + +void display_item_count(char[] person, char[][] items) +{ + writefln(person, " is carrying ", items.length, " items"); +} + diff --git a/demos/d/foo.d b/demos/d/foo.d new file mode 100644 index 00000000..cebdfc6e --- /dev/null +++ b/demos/d/foo.d @@ -0,0 +1,7 @@ + +import hmm; +int main() { + gnaa(); + const char[] MakePrintOther = "\"\n" ~ " no ' "; + return 0; +} diff --git a/demos/d/hmm.d b/demos/d/hmm.d new file mode 100644 index 00000000..fe5c5596 --- /dev/null +++ b/demos/d/hmm.d @@ -0,0 +1,3 @@ +int gnaa() { + return 42; +} diff --git a/demos/d/src/extra.d b/demos/d/src/extra.d new file mode 100644 index 00000000..9ccfc220 --- /dev/null +++ b/demos/d/src/extra.d @@ -0,0 +1,7 @@ +module extra; + +char[] abc() +{ + return cast(char[])"abc"; +} + diff --git a/demos/d/src/main.d b/demos/d/src/main.d new file mode 100644 index 00000000..d6561af6 --- /dev/null +++ b/demos/d/src/main.d @@ -0,0 +1,10 @@ +module main; +import extra; +import std.stdio; + +int main() +{ + writefln("%s", abc()); + return 0; +} + diff --git a/demos/d/src/wscript_build b/demos/d/src/wscript_build new file mode 100644 index 00000000..6c750c38 --- /dev/null +++ b/demos/d/src/wscript_build @@ -0,0 +1,5 @@ +obj = bld(features='d dprogram') +obj.source = 'main.d extra.d' +obj.target = 'test2' +obj.importpaths = '.' + diff --git a/demos/d/testlib/code.d b/demos/d/testlib/code.d new file mode 100644 index 00000000..35483c2b --- /dev/null +++ b/demos/d/testlib/code.d @@ -0,0 +1,6 @@ +module testlib.code; + +int test_lib() +{ + return 125; +} diff --git a/demos/d/wscript b/demos/d/wscript new file mode 100644 index 00000000..a673b503 --- /dev/null +++ b/demos/d/wscript @@ -0,0 +1,55 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006 (ita) + +VERSION='0.0.1' +APPNAME='d_test' + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_d') + +def configure(conf): + conf.load('compiler_d') + conf.env.LIB_PTHREAD = ['pthread'] + conf.check_dlibrary() + try: + conf.check(features='d dprogram', fragment='int main() {return 0;}', compile_filename='test.d', dflags=['-version=Posix']) + except: + pass + else: + conf.env.append_value('DFLAGS', ['-version=Posix']) + +def build(bld): + + if bld.env.DLIBRARY != 'tango': + bld.recurse('src') + + # here is how to use the object-oriented notation + bld.stlib( + source = 'testlib/code.d', + includes = '.', + name = 'testlib', + target = 'testlib') + + bld.program( + source = 'example.d', + target = 'd_test', + use = 'testlib PTHREAD', + includes = '.') + + else: + # bad pun hidden + bld.program(source='foo.d', target='bar', use='hmm', includes=[bld.path]) + bld.stlib(source='hmm.d', target='hmm') + + #bld(features='d dshlib', source='shared.d', target='sha') + # shared libs do not work here: + # ldc -relocation-model=pic -L-shared shared.d -offoo + # /usr/lib64/gcc/x86_64-suse-linux/4.5/../../../../x86_64-suse-linux/bin/ld: + # /disk/comp/ldc/bin/../lib/libtango.a(tango-core-rt-compiler-ldc-object_-O2.o): + # relocation R_X86_64_32 against `.rodata.str1.1' can not be used when making a shared object; recompile with -fPIC + + diff --git a/demos/dbus/main.c b/demos/dbus/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/demos/dbus/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/demos/dbus/test.xml b/demos/dbus/test.xml new file mode 100644 index 00000000..21f4c8eb --- /dev/null +++ b/demos/dbus/test.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/demos/dbus/wscript b/demos/dbus/wscript new file mode 100644 index 00000000..7d3683a7 --- /dev/null +++ b/demos/dbus/wscript @@ -0,0 +1,21 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +VERSION = '1.0' + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c dbus') + conf.find_program('glib-genmarshal') # required on a few systems + +def build(bld): + tg = bld.program( + includes = '.', + source = bld.path.ant_glob('*.c'), + target = 'gnome-hello') + + tg.add_dbus_file('test.xml', 'test_prefix', 'glib-server') + diff --git a/demos/doxy/a.cpp b/demos/doxy/a.cpp new file mode 100644 index 00000000..90e1132e --- /dev/null +++ b/demos/doxy/a.cpp @@ -0,0 +1,7 @@ +#include "a.h" + +foo::foo() +{ + +} + diff --git a/demos/doxy/a.h b/demos/doxy/a.h new file mode 100644 index 00000000..f348e805 --- /dev/null +++ b/demos/doxy/a.h @@ -0,0 +1,24 @@ +/*! + * Unused class for demo a.h + */ +class foo +{ + private: + /*! + * attribute x, hmmm :-) + */ + int x; + + public: + /*! + * i love comments such as "this is a constructor" + */ + foo(); + + /*! + * float y, tadam + */ + float y; +}; + + diff --git a/demos/doxy/b.cpp b/demos/doxy/b.cpp new file mode 100644 index 00000000..c193f142 --- /dev/null +++ b/demos/doxy/b.cpp @@ -0,0 +1,12 @@ +#include "b.h" + +bar::bar() +{ + +} + +int main() +{ + return 1; +} + diff --git a/demos/doxy/b.h b/demos/doxy/b.h new file mode 100644 index 00000000..ec88a170 --- /dev/null +++ b/demos/doxy/b.h @@ -0,0 +1,24 @@ + +/*! + * pim pam + */ +class bar +{ + private: + /*! + * zzzz + */ + int z; + + public: + /*! + * guess what this is .. a constructor? + */ + bar(); + + /*! + * out of ideas for comments + */ + float t; +}; + diff --git a/demos/doxy/subdir/c.cpp b/demos/doxy/subdir/c.cpp new file mode 100644 index 00000000..edd7fdf6 --- /dev/null +++ b/demos/doxy/subdir/c.cpp @@ -0,0 +1,7 @@ +#include "c.h" + +meep::meep() +{ + +} + diff --git a/demos/doxy/subdir/c.h b/demos/doxy/subdir/c.h new file mode 100644 index 00000000..06e5e28c --- /dev/null +++ b/demos/doxy/subdir/c.h @@ -0,0 +1,18 @@ +/*! + * Nothing to hide, so all is public + */ +class meep +{ + public: + /*! + * attribute a, hmmm :-) + */ + int a; + + /*! + * no comments + */ + meep(); +}; + + diff --git a/demos/doxy/test.conf b/demos/doxy/test.conf new file mode 100644 index 00000000..63c584df --- /dev/null +++ b/demos/doxy/test.conf @@ -0,0 +1,183 @@ +# this template is provided as an example, it has been generated from 'doxygen -g' and modified afterwards + +# the output directory is set by Waf to the build directory, leave it commented +#OUTPUT_DIRECTORY = + +DOXYFILE_ENCODING = UTF-8 +PROJECT_NAME = test +PROJECT_NUMBER = 1.2.3 +CREATE_SUBDIRS = NO +OUTPUT_LANGUAGE = English +BRIEF_MEMBER_DESC = YES +REPEAT_BRIEF = YES +ABBREVIATE_BRIEF = +ALWAYS_DETAILED_SEC = NO +INLINE_INHERITED_MEMB = NO +FULL_PATH_NAMES = YES +STRIP_FROM_PATH = +STRIP_FROM_INC_PATH = +SHORT_NAMES = NO +JAVADOC_AUTOBRIEF = NO +QT_AUTOBRIEF = NO +MULTILINE_CPP_IS_BRIEF = NO +INHERIT_DOCS = YES +SEPARATE_MEMBER_PAGES = NO +TAB_SIZE = 8 +ALIASES = +OPTIMIZE_OUTPUT_FOR_C = NO +OPTIMIZE_OUTPUT_JAVA = NO +BUILTIN_STL_SUPPORT = NO +CPP_CLI_SUPPORT = NO +DISTRIBUTE_GROUP_DOC = NO +SUBGROUPING = YES +EXTRACT_ALL = NO +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = NO +EXTRACT_LOCAL_CLASSES = YES +EXTRACT_LOCAL_METHODS = NO +EXTRACT_ANON_NSPACES = NO +HIDE_UNDOC_MEMBERS = NO +HIDE_UNDOC_CLASSES = NO +HIDE_FRIEND_COMPOUNDS = NO +HIDE_IN_BODY_DOCS = NO +INTERNAL_DOCS = NO +CASE_SENSE_NAMES = YES +HIDE_SCOPE_NAMES = NO +SHOW_INCLUDE_FILES = YES +INLINE_INFO = YES +SORT_MEMBER_DOCS = YES +SORT_BRIEF_DOCS = NO +SORT_BY_SCOPE_NAME = NO +GENERATE_TODOLIST = YES +GENERATE_TESTLIST = YES +GENERATE_BUGLIST = YES +GENERATE_DEPRECATEDLIST= YES +ENABLED_SECTIONS = +MAX_INITIALIZER_LINES = 30 +SHOW_USED_FILES = YES +SHOW_DIRECTORIES = NO +FILE_VERSION_FILTER = +QUIET = YES +WARNINGS = YES +WARN_IF_UNDOCUMENTED = YES +WARN_IF_DOC_ERROR = YES +WARN_NO_PARAMDOC = NO +WARN_FORMAT = "$file:$line: $text" +WARN_LOGFILE = +INPUT = +INPUT_ENCODING = UTF-8 +FILE_PATTERNS = +RECURSIVE = YES +EXCLUDE = +EXCLUDE_SYMLINKS = NO +EXCLUDE_PATTERNS = */.*/* +EXCLUDE_SYMBOLS = +EXAMPLE_PATH = +EXAMPLE_PATTERNS = +EXAMPLE_RECURSIVE = NO +IMAGE_PATH = +INPUT_FILTER = +FILTER_PATTERNS = +FILTER_SOURCE_FILES = NO +SOURCE_BROWSER = NO +INLINE_SOURCES = NO +STRIP_CODE_COMMENTS = YES +REFERENCED_BY_RELATION = YES +REFERENCES_RELATION = YES +REFERENCES_LINK_SOURCE = YES +USE_HTAGS = NO +VERBATIM_HEADERS = YES +ALPHABETICAL_INDEX = NO +COLS_IN_ALPHA_INDEX = 5 +IGNORE_PREFIX = +GENERATE_HTML = YES +HTML_OUTPUT = html +HTML_FILE_EXTENSION = .html +HTML_HEADER = +HTML_FOOTER = +HTML_STYLESHEET = +HTML_ALIGN_MEMBERS = YES +GENERATE_HTMLHELP = NO +HTML_DYNAMIC_SECTIONS = NO +CHM_FILE = +HHC_LOCATION = +GENERATE_CHI = NO +BINARY_TOC = NO +TOC_EXPAND = NO +DISABLE_INDEX = NO +ENUM_VALUES_PER_LINE = 4 +GENERATE_TREEVIEW = NO +TREEVIEW_WIDTH = 250 +GENERATE_LATEX = YES +LATEX_OUTPUT = latex +LATEX_CMD_NAME = latex +MAKEINDEX_CMD_NAME = makeindex +COMPACT_LATEX = NO +PAPER_TYPE = a4wide +EXTRA_PACKAGES = +LATEX_HEADER = +PDF_HYPERLINKS = NO +USE_PDFLATEX = NO +LATEX_BATCHMODE = NO +LATEX_HIDE_INDICES = NO +GENERATE_RTF = NO +RTF_OUTPUT = rtf +COMPACT_RTF = NO +RTF_HYPERLINKS = NO +RTF_STYLESHEET_FILE = +RTF_EXTENSIONS_FILE = +GENERATE_MAN = NO +MAN_OUTPUT = man +MAN_EXTENSION = .3 +MAN_LINKS = NO +GENERATE_XML = NO +XML_OUTPUT = xml +XML_SCHEMA = +XML_DTD = +XML_PROGRAMLISTING = YES +GENERATE_AUTOGEN_DEF = NO +GENERATE_PERLMOD = NO +PERLMOD_LATEX = NO +PERLMOD_PRETTY = YES +PERLMOD_MAKEVAR_PREFIX = +ENABLE_PREPROCESSING = YES +MACRO_EXPANSION = NO +EXPAND_ONLY_PREDEF = NO +SEARCH_INCLUDES = YES +INCLUDE_PATH = +INCLUDE_FILE_PATTERNS = +PREDEFINED = +EXPAND_AS_DEFINED = +SKIP_FUNCTION_MACROS = YES +TAGFILES = +GENERATE_TAGFILE = +ALLEXTERNALS = NO +EXTERNAL_GROUPS = YES +PERL_PATH = /usr/bin/perl +CLASS_DIAGRAMS = YES +MSCGEN_PATH = +HIDE_UNDOC_RELATIONS = YES +HAVE_DOT = NO +CLASS_GRAPH = YES +COLLABORATION_GRAPH = YES +GROUP_GRAPHS = YES +UML_LOOK = NO +TEMPLATE_RELATIONS = NO +INCLUDE_GRAPH = YES +INCLUDED_BY_GRAPH = YES +CALL_GRAPH = NO +CALLER_GRAPH = NO +GRAPHICAL_HIERARCHY = YES +DIRECTORY_GRAPH = YES +DOT_IMAGE_FORMAT = png +DOT_PATH = +DOTFILE_DIRS = +DOT_GRAPH_MAX_NODES = 50 +MAX_DOT_GRAPH_DEPTH = 0 +DOT_TRANSPARENT = NO +DOT_MULTI_TARGETS = NO +GENERATE_LEGEND = YES +DOT_CLEANUP = YES +SEARCHENGINE = \ + NO + diff --git a/demos/doxy/wscript b/demos/doxy/wscript new file mode 100644 index 00000000..d88ec600 --- /dev/null +++ b/demos/doxy/wscript @@ -0,0 +1,24 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2008-2010 (ita) + +VERSION='0.0.1' +APPNAME='cc_test' + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_cxx') + +def configure(conf): + conf.load('compiler_cxx doxygen') + +def build(bld): + # Note: Doxgen parameters may be passed using pars attribute + # e.g. pars={'EXCLUDE_PATTERNS':'*.babu'} + bld( + features='doxygen', + doxyfile='test.conf', + doxy_tar='docs.tar.bz2') + diff --git a/demos/fluid/PopupDialog.fl b/demos/fluid/PopupDialog.fl new file mode 100644 index 00000000..a8756af3 --- /dev/null +++ b/demos/fluid/PopupDialog.fl @@ -0,0 +1,10 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0300 +header_name {.hpp} +code_name {.cpp} +widget_class PopupDialog { + callback {o->hide(); +Fl::delete_widget(o);} open selected + xywh {51 115 165 55} type Double align 5 + class Fl_Window modal visible +} {} diff --git a/demos/fluid/UserInterface.fl b/demos/fluid/UserInterface.fl new file mode 100644 index 00000000..c09bbd1d --- /dev/null +++ b/demos/fluid/UserInterface.fl @@ -0,0 +1,31 @@ +# data file for the Fltk User Interface Designer (fluid) +version 1.0300 +i18n_type 1 +i18n_include +i18n_function gettext +header_name {.hpp} +code_name {.cpp} +decl {\#include "PopupDialog.hpp"} {public global +} + +class UserInterface {open +} { + Function {UserInterface()} {open + } { + Fl_Window main_win { + callback {Fl::delete_widget(o);} open + private xywh {12 31 125 80} type Double selection_color 48 align 80 when 1 visible + } { + Fl_Button {} { + label {click me} + callback {PopupDialog* p = new PopupDialog(165, 55, "PopupDialog"); +p->show();} selected + xywh {25 25 70 20} + } + } + } + Function {show()} {open + } { + code {main_win->show();} {} + } +} diff --git a/demos/fluid/main.cpp b/demos/fluid/main.cpp new file mode 100644 index 00000000..9183efde --- /dev/null +++ b/demos/fluid/main.cpp @@ -0,0 +1,12 @@ +#include "FL/fl_ask.H" + +#include "UserInterface.hpp" + + +int main(int argc, char **argv) +{ + Fl::scheme("plastic"); + UserInterface ui; + ui.show(); + return Fl::run(); +} diff --git a/demos/fluid/wscript b/demos/fluid/wscript new file mode 100644 index 00000000..c6cecc28 --- /dev/null +++ b/demos/fluid/wscript @@ -0,0 +1,23 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Grygoriy Fuchedzhy 2009 + +top = '.' +out = 'out' + +def options(opt): + opt.load('compiler_cxx') + +def configure(conf): + #conf.recurse('gni') + conf.load('compiler_cxx') + conf.load('fluid') + +def build(bld): + bld.program( + target = 'sample', + source = 'main.cpp PopupDialog.fl UserInterface.fl', + includes = '.', + install_path = '${PREFIX}/bin/', + use = 'FLTK') + diff --git a/demos/fortran/foo.f b/demos/fortran/foo.f new file mode 100644 index 00000000..eb9f2854 --- /dev/null +++ b/demos/fortran/foo.f @@ -0,0 +1,2 @@ + subroutine foo + end diff --git a/demos/fortran/foo_pp.F b/demos/fortran/foo_pp.F new file mode 100644 index 00000000..8d231600 --- /dev/null +++ b/demos/fortran/foo_pp.F @@ -0,0 +1,8 @@ + program main +#ifdef USEFOO + print *,'FOO' +#else + print *,'HELLO' +#endif + end + diff --git a/demos/fortran/hello.f b/demos/fortran/hello.f new file mode 100644 index 00000000..b5eb9ba6 --- /dev/null +++ b/demos/fortran/hello.f @@ -0,0 +1,3 @@ + program main + print *, 'hello' + end diff --git a/demos/fortran/mod/fakecc.py b/demos/fortran/mod/fakecc.py new file mode 100644 index 00000000..485c024d --- /dev/null +++ b/demos/fortran/mod/fakecc.py @@ -0,0 +1,60 @@ +#! /usr/bin/env python +# encoding: utf-8 +# DC 2008 +# Thomas Nagy 2010 (ita) + +import re + +from waflib import Utils, Task, TaskGen, Logs +from TaskGen import feature, before, after, extension +from waflib.Tools import ccroot + +IS_MODULE_R = re.compile('module ([a-z]*)') +USE_MODULE_R = re.compile('use ([a-z]*)') + +@extension('.a') +def hook(self, node): + self.create_compiled_task('fakecc', node) + +def ismodule(node): + deps = [] + for l in node.read().splitlines(): + m = IS_MODULE_R.match(l) + if m: + deps.append(m.group(1) + '.mod') + return deps + +def usemodule(node): + deps = [] + for l in node.read().splitlines(): + m = USE_MODULE_R.match(l) + if m: + deps.append(m.group(1) + '.mod') + return deps + +def compile(tsk): + tsk.outputs[0].write('compiled') + m = ismodule(tsk.inputs[0]) + if m: + print("%s declares module %s" % (tsk.inputs[0], m[0])) + t2 = open(m[0], 'w') + try: + t2.write('module compiled') + finally: + t2.close() + +class fakecc(Task.Task): + color = 'YELLOW' + def run(self): + cmd = [] + if not len(self.outputs) == len(self.inputs) == 1: + pass + + bnodes = self.outputs + m = usemodule(self.inputs[0]) + if m: + print "%s requires module %s" % (self.inputs[0].abspath(), m[0]) + #bnodes.append(self.generator.bld.bldnode.exclusive_build_node(m[0])) + + compile(self) + diff --git a/demos/fortran/mod/two_mods.f90 b/demos/fortran/mod/two_mods.f90 new file mode 100644 index 00000000..952a5809 --- /dev/null +++ b/demos/fortran/mod/two_mods.f90 @@ -0,0 +1,34 @@ +module MOD1 +end module MOD1 + + module mod2 + use mod1 + integer :: mod2_int + + ! FIXME: + ! replace the following line with the commented version, and recompile. + ! mod/uses_two_mods.f90 should be recompiled, but currently isn't. + + integer, parameter :: mod2_param = 5 + ! integer, parameter :: mod2_param = 6 + + interface mod2_proc + module procedure mod2_proc1, mod2_proc2 + end interface + + contains + + subroutine mod2_proc1(a) + implicit none + integer, intent(inout) :: a + a = 10 + end subroutine + + subroutine mod2_proc2(a) + implicit none + real, intent(inout) :: a + a = 10.0 + end subroutine + + end module mod2 + diff --git a/demos/fortran/mod/uses_two_mods.f90 b/demos/fortran/mod/uses_two_mods.f90 new file mode 100644 index 00000000..2e114d36 --- /dev/null +++ b/demos/fortran/mod/uses_two_mods.f90 @@ -0,0 +1,21 @@ + + +! FIXME: +! modifying two_mods.f90 should trigger this file's recompilation, too. + +module uses_TWO_mods + use mod2 + implicit none + + integer, parameter :: uses_two_mods_param = mod2_param * 2 + + contains + + subroutine printer + implicit none + + print *, uses_two_mods_param + + end subroutine printer + +end module uses_TWO_mods diff --git a/demos/fortran/mod/wscript b/demos/fortran/mod/wscript new file mode 100644 index 00000000..198f6d9f --- /dev/null +++ b/demos/fortran/mod/wscript @@ -0,0 +1,17 @@ +#! /usr/bin/env python +# encoding: utf-8 +# DC 2008 +# Thomas Nagy 2010 (ita) + +top = '.' +out = 'build' + +def options(opt): + opt.load('fakecc', tooldir='.') + +def configure(conf): + conf.load('fakecc', tooldir='.') + +def build(bld): + bld(source='hello_main.a hello.a', target='hello') + diff --git a/demos/fortran/src/calculator.f b/demos/fortran/src/calculator.f new file mode 100644 index 00000000..306cc5af --- /dev/null +++ b/demos/fortran/src/calculator.f @@ -0,0 +1,9 @@ + module calculator + implicit none + contains + subroutine add (a, b, output) + integer, intent (in) :: a, b + integer, intent (out) :: output + output = a + b + end subroutine add + end module calculator diff --git a/demos/fortran/src/calculator_main.f b/demos/fortran/src/calculator_main.f new file mode 100644 index 00000000..c40f5878 --- /dev/null +++ b/demos/fortran/src/calculator_main.f @@ -0,0 +1,6 @@ + PROGRAM Test + use calculator, only: add + integer:: result + + call add (2,2,result) + END PROGRAM diff --git a/demos/fortran/src/hello_inc.f b/demos/fortran/src/hello_inc.f new file mode 100644 index 00000000..727f7371 --- /dev/null +++ b/demos/fortran/src/hello_inc.f @@ -0,0 +1,7 @@ + program main + include "hello.h" + real bar + t0 = 1 + bar = t0 + print *,bar + end diff --git a/demos/fortran/src/include/hello.h b/demos/fortran/src/include/hello.h new file mode 100644 index 00000000..541fbd35 --- /dev/null +++ b/demos/fortran/src/include/hello.h @@ -0,0 +1 @@ + real t0 diff --git a/demos/fortran/typemap/basetypes.f90 b/demos/fortran/typemap/basetypes.f90 new file mode 100644 index 00000000..ee65cb25 --- /dev/null +++ b/demos/fortran/typemap/basetypes.f90 @@ -0,0 +1,6 @@ +module basetypes + implicit none + + integer, parameter :: int_kind = selected_int_kind(10) + +end module basetypes diff --git a/demos/fortran/typemap/fsrc.f90 b/demos/fortran/typemap/fsrc.f90 new file mode 100644 index 00000000..9cb5eb36 --- /dev/null +++ b/demos/fortran/typemap/fsrc.f90 @@ -0,0 +1,25 @@ +module example_mod + use basetypes + implicit none + + ! uses the kind defined in basetypes. + integer(kind=int_kind) :: an_int + + ! Useless example to demonstrate that arg_kind is set to a compile-time + ! expression; the expression could be arbitrary, and would yield different + ! values for different compilers. + integer, parameter :: arg_kind = kind(an_int) + + contains + + subroutine sub1(a, b, c, d) + implicit none + integer(kind=arg_kind), intent(inout) :: a,b,c,d + + a = 1 + b = 2 + c = 3 + d = 4 + end subroutine sub1 + +end module example_mod diff --git a/demos/fortran/typemap/fwrap_ktp.in b/demos/fortran/typemap/fwrap_ktp.in new file mode 100644 index 00000000..c35f66d9 --- /dev/null +++ b/demos/fortran/typemap/fwrap_ktp.in @@ -0,0 +1 @@ +CTPS = [{'odecl': 'integer(kind=arg_kind)', 'basetype': 'integer', 'use': 'example_mod', 'name': 'int_arg_kind'}] diff --git a/demos/fortran/typemap/wscript b/demos/fortran/typemap/wscript new file mode 100644 index 00000000..2ba212f7 --- /dev/null +++ b/demos/fortran/typemap/wscript @@ -0,0 +1,190 @@ +#! /usr/bin/env python + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_c') + opt.load('compiler_fc') + +def configure(conf): + conf.load('compiler_c') + conf.load('compiler_fc') + conf.check_fortran() + conf.check_fortran_verbose_flag() + conf.check_fortran_clib() + +def build(bld): + + bld( + features = 'fc typemap fcshlib', + source = 'fsrc.f90 basetypes.f90', + target = 'foo', + ) + +import os +from waflib import Logs, Build, Utils + +from waflib import TaskGen, Task +from waflib.ConfigSet import ConfigSet + +#@TaskGen.feature('typemap') <- python >= 2.4 +#@TaskGen.after('process_source') +#@TaskGen.before('apply_link') +def process_typemaps(self): + """ + modmap: *.f90 + foo.in -> foo.h + foo.f90 + compile foo.f90 like the others + """ + node = self.path.find_resource(getattr(self, 'typemap', 'fwrap_ktp.in')) + if not node: + raise self.bld.errors.WafError('no typemap file declared for %r' % self) + + f90out = node.change_ext('.f90') + lst = [node] + for x in self.tasks: + if x.inputs and x.inputs[0].name.endswith('.f90'): + lst.append(x.inputs[0]) + tmtsk = self.typemap_task = self.create_task('modmap', lst, [f90out, node.change_ext('.h')]) + + for x in self.tasks: + if x.inputs and x.inputs[0].name.endswith('.f90'): + tmtsk.set_run_after(x) + + tsk = self.create_compiled_task('fc', f90out) + tsk.nomod = True # the fortran files won't compile unless all the .mod files are set, ick + +# for python 2.3 +TaskGen.feature('typemap')(process_typemaps) +TaskGen.after('process_source')(process_typemaps) +TaskGen.before('apply_link')(process_typemaps) + +class modmap(Task.Task): + """ + create .h and .f90 files, so this must run be executed before any c task + """ + ext_out = ['.h'] # before any c task is not mandatory since #732 but i want to be sure (ita) + def run(self): + """ + we need another build context, because we cannot really disable the logger here + """ + obld = self.generator.bld + bld = Build.BuildContext(top_dir=obld.srcnode.abspath(), out_dir=obld.bldnode.abspath()) + bld.init_dirs() + bld.in_msg = 1 # disable all that comes from bld.msg(..), bld.start_msg(..) and bld.end_msg(...) + bld.env = self.env.derive() + node = self.inputs[0] + bld.logger = Logs.make_logger(node.parent.get_bld().abspath() + os.sep + node.name + '.log', 'build') + + gen_type_map_files(bld, self.inputs, self.outputs) + +class ctp(object): + def __init__(self, name, basetype, odecl, use): + self.name = name + self.basetype = basetype + self.odecl = odecl + self.use = use + self.fc_type = None + +def gen_type_map_files(bld, inputs, outputs): + + # The ctp properties (name, basetype, odecl, etc.) would be listed in a + # configuration file and the ctp list below would be loaded from that file. + # But the type resolution must be determined at *compile time* (i.e. build + # time), and can't be determined by static analysis. This is because each + # fortran compiler can have a different value for the type resolution. + # Moreover, the type resolution can depend on an arbitrary number of .mod + # files and integer expressions. + + ktp_in = [ip for ip in inputs if ip.name.endswith('.in')][0] + env = ConfigSet() + env.load(ktp_in.abspath()) + ctps = [] + for ctp_ in env.CTPS: + ctps.append(ctp(**ctp_)) + + # the 'use' attribute of the ctp instances above uses the .mod file created + # after the compilation of fsrc.f90. The type mapping depends on the .mod + # file generated, and thus the mapping needs to be discovered during the + # build stage, not the configuration stage. + + find_types(bld, ctps) + + # write fortran -> C mapping to file. + fort_file = [ff for ff in outputs if ff.name.endswith('.f90')][0] + c_header = [ch for ch in outputs if ch.name.endswith('.h')][0] + write_type_map(bld, ctps, fort_file, c_header) + +def find_types(bld, ctps): + for ctp in ctps: + fc_type = None + fc_type = find_fc_type(bld, ctp.basetype, + ctp.odecl, ctp.use) + if not fc_type: + raise bld.errors.WafError( + "unable to find C type for type %s" % ctp.odecl) + ctp.fc_type = fc_type + +type_dict = {'integer' : ['c_signed_char', 'c_short', 'c_int', 'c_long', 'c_long_long']} + +def find_fc_type(bld, base_type, decl, use): + fsrc_tmpl = '''\ +subroutine outer(a) + use, intrinsic :: iso_c_binding + implicit none + %(TEST_DECL)s, intent(inout) :: a + interface + subroutine inner(a) + use, intrinsic :: iso_c_binding + use %(USE)s + implicit none + %(TYPE_DECL)s, intent(inout) :: a + end subroutine inner + end interface + call inner(a) +end subroutine outer +''' + for ctype in type_dict[base_type]: + test_decl = '%s(kind=%s)' % (base_type, ctype) + fsrc = fsrc_tmpl % {'TYPE_DECL' : decl, + 'TEST_DECL' : test_decl, + 'USE' : use} + try: + bld.check_cc( + fragment=fsrc, + compile_filename='test.f90', + features='fc', + includes = bld.bldnode.abspath(), + ) + except bld.errors.ConfigurationError: + pass + else: + res = ctype + break + else: + res = '' + return res + +def write_type_map(bld, ctps, fort_file, c_header): + fort_file.write('''\ +module type_maps +use, intrinsic :: iso_c_binding +implicit none +''', flags='w') + for ctp in ctps: + fort_file.write('integer, parameter :: %s = %s\n' % (ctp.name, ctp.fc_type), + flags='a') + fort_file.write('end module type_maps\n', flags='a') + + cap_name = '%s__' % c_header.name.upper().replace('.', '_') + c_header.write('''\ +#ifndef %s +#define %s +''' % (cap_name, cap_name), flags='w') + for ctp in ctps: + # This is just an example, so this would be customized. The 'long long' + # would correspond to the actual C type... + c_header.write('typedef long long %s\n' % ctp.name, flags='a') + c_header.write('#endif\n', flags='a') + +# vim:ft=python:noet diff --git a/demos/fortran/wscript b/demos/fortran/wscript new file mode 100644 index 00000000..07ce305d --- /dev/null +++ b/demos/fortran/wscript @@ -0,0 +1,83 @@ +#! /usr/bin/env python +# encoding: utf-8 +# DC 2008 +# Thomas Nagy 2010 (ita) + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_c') + opt.load('compiler_fc') + opt.recurse('typemap') + +def configure(conf): + conf.load('compiler_c') + conf.load('compiler_fc') + if conf.env.FC_NAME == 'IFORT': + conf.env['FCFLAGS'] = ['-warn'] + elif conf.env.FC_NAME == 'GFORTRAN': + conf.env['FCFLAGS'] = ['-Wall', '-W'] + #conf.env['INCLUDES'] = ['hfloupi'] + + conf.check_fortran() + conf.check_fortran_verbose_flag() + conf.check_fortran_clib() + conf.check_fortran_dummy_main() + conf.check_fortran_mangling() + conf.recurse('typemap') + +def build(bld): + + bld( + features = 'fc', + source = 'hello.f') + + bld( + features = 'fc fcprogram', + source = 'hello.f', + target = 'hello', + use = 'DEBUG') + + bld( + features = 'fc fcshlib', + source = 'foo.f', + target = 'shlib1', + vnum = '2.3.9') + + bld( + features = 'fc fcstlib', + source = 'foo.f', + target = 'foo') + + bld( + features = 'fc fcprogram', + source = 'foo_pp.F', + target = 'foo', + defines = ['USEFOO', 'blah=1'], + use = 'shlib1') + + bld.add_group() + + bld( + features = 'fc fcprogram', + includes = 'src/include', + source = 'src/hello_inc.f', + target = 'hello_inc') + + bld( + features = 'fc', + source = 'src/calculator_main.f src/calculator.f', + target = 'calc_objs') + + bld( + features = 'fc fcprogram', + use = 'calc_objs', + target = 'calculator') + + bld( + features = 'fc fcstlib', + source = 'mod/two_mods.f90 mod/uses_two_mods.f90', + target = 'mod/two_mods') + + bld.recurse('typemap') diff --git a/demos/glib2/enums.h b/demos/glib2/enums.h new file mode 100644 index 00000000..896e4de1 --- /dev/null +++ b/demos/glib2/enums.h @@ -0,0 +1,9 @@ +/* Some test enums */ + +typedef enum { + WAF = 1, + AUTOTOOLS = 2, + SCONS = 3, + CMAKE = 4, + JAM = 5 +} BuildSystemType; diff --git a/demos/glib2/main.c b/demos/glib2/main.c new file mode 100644 index 00000000..d5fc8437 --- /dev/null +++ b/demos/glib2/main.c @@ -0,0 +1,5 @@ +#include + +int main (int argc, char *argv[]) { + return 0; +} \ No newline at end of file diff --git a/demos/glib2/marshal.list b/demos/glib2/marshal.list new file mode 100644 index 00000000..82086e59 --- /dev/null +++ b/demos/glib2/marshal.list @@ -0,0 +1,4 @@ +BOOLEAN:OBJECT +BOOLEAN:OBJECT,OBJECT +BOOLEAN:OBJECT,UINT +BOOLEAN:VOID diff --git a/demos/glib2/org.gsettings.test-2.gschema.xml b/demos/glib2/org.gsettings.test-2.gschema.xml new file mode 100644 index 00000000..f54bd219 --- /dev/null +++ b/demos/glib2/org.gsettings.test-2.gschema.xml @@ -0,0 +1,9 @@ + + + + + + 'autotools' + + + \ No newline at end of file diff --git a/demos/glib2/org.gsettings.test.gschema.xml b/demos/glib2/org.gsettings.test.gschema.xml new file mode 100644 index 00000000..27e87c55 --- /dev/null +++ b/demos/glib2/org.gsettings.test.gschema.xml @@ -0,0 +1,17 @@ + + + + "Hello, earthlings" + A greeting + + Greeting of the invading martians + + + + + + + 'waf' + + + \ No newline at end of file diff --git a/demos/glib2/wscript b/demos/glib2/wscript new file mode 100644 index 00000000..9c6d47d3 --- /dev/null +++ b/demos/glib2/wscript @@ -0,0 +1,40 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005, 2010 (ita) + +VERSION='0.0.1' +APPNAME='glib2_test' + +top = '.' +out = 'build' + +def options(opt): + opt.load ('compiler_c glib2') + +def configure(conf): + conf.load ('compiler_c glib2') + + conf.check_cfg (package='glib-2.0', uselib_store='GLIB', atleast_version='2.25.0', + args='--cflags --libs') + conf.check_cfg (package='gobject-2.0', uselib_store='GOBJECT', atleast_version='2.25.0', + args='--cflags --libs') + + +def build(bld): + app = bld ( + features = 'c cprogram glib2', + uselib = 'GLIB GOBJECT', + source = 'main.c', + target = 'gsettings-test' + + ## An alternate way of doing this (no need to call add_ functions): + # settings_enum_namespace = 'org.gsettings.test' + # settings_enum_files = ['enums.h'] + # settings_schema_files = ['org.gsettings.test.gschema.xml'] + ) + + app.add_settings_enums ('org.gsettings.test', 'enums.h') + app.add_settings_schemas (['org.gsettings.test.gschema.xml', + 'org.gsettings.test-2.gschema.xml']) + + app.add_marshal_file('marshal.list', 'test_marshal') diff --git a/demos/go/LICENSE b/demos/go/LICENSE new file mode 100644 index 00000000..6a66aea5 --- /dev/null +++ b/demos/go/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2009 The Go Authors. All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/demos/go/gmp.go b/demos/go/gmp.go new file mode 100644 index 00000000..c138fa13 --- /dev/null +++ b/demos/go/gmp.go @@ -0,0 +1,278 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package gmp + +// #include +// #include +// #cgo LDFLAGS: -lgmp +import "C" + +import ( + "os" + "unsafe" +) + +/* + * one of a kind + */ + +// An Int represents a signed multi-precision integer. +// The zero value for an Int represents the value 0. +type Int struct { + i C.mpz_t + init bool +} + +// NewInt returns a new Int initialized to x. +func NewInt(x int64) *Int { return new(Int).SetInt64(x) } + +// Int promises that the zero value is a 0, but in gmp +// the zero value is a crash. To bridge the gap, the +// init bool says whether this is a valid gmp value. +// doinit initializes z.i if it needs it. This is not inherent +// to FFI, just a mismatch between Go's convention of +// making zero values useful and gmp's decision not to. +func (z *Int) doinit() { + if z.init { + return + } + z.init = true + C.mpz_init(&z.i[0]) +} + +// Bytes returns z's representation as a big-endian byte array. +func (z *Int) Bytes() []byte { + b := make([]byte, (z.Len()+7)/8) + n := C.size_t(len(b)) + C.mpz_export(unsafe.Pointer(&b[0]), &n, 1, 1, 1, 0, &z.i[0]) + return b[0:n] +} + +// Len returns the length of z in bits. 0 is considered to have length 1. +func (z *Int) Len() int { + z.doinit() + return int(C.mpz_sizeinbase(&z.i[0], 2)) +} + +// Set sets z = x and returns z. +func (z *Int) Set(x *Int) *Int { + z.doinit() + C.mpz_set(&z.i[0], &x.i[0]) + return z +} + +// SetBytes interprets b as the bytes of a big-endian integer +// and sets z to that value. +func (z *Int) SetBytes(b []byte) *Int { + z.doinit() + if len(b) == 0 { + z.SetInt64(0) + } else { + C.mpz_import(&z.i[0], C.size_t(len(b)), 1, 1, 1, 0, unsafe.Pointer(&b[0])) + } + return z +} + +// SetInt64 sets z = x and returns z. +func (z *Int) SetInt64(x int64) *Int { + z.doinit() + // TODO(rsc): more work on 32-bit platforms + C.mpz_set_si(&z.i[0], C.long(x)) + return z +} + +// SetString interprets s as a number in the given base +// and sets z to that value. The base must be in the range [2,36]. +// SetString returns an error if s cannot be parsed or the base is invalid. +func (z *Int) SetString(s string, base int) os.Error { + z.doinit() + if base < 2 || base > 36 { + return os.EINVAL + } + p := C.CString(s) + defer C.free(unsafe.Pointer(p)) + if C.mpz_set_str(&z.i[0], p, C.int(base)) < 0 { + return os.EINVAL + } + return nil +} + +// String returns the decimal representation of z. +func (z *Int) String() string { + if z == nil { + return "nil" + } + z.doinit() + p := C.mpz_get_str(nil, 10, &z.i[0]) + s := C.GoString(p) + C.free(unsafe.Pointer(p)) + return s +} + +func (z *Int) destroy() { + if z.init { + C.mpz_clear(&z.i[0]) + } + z.init = false +} + + +/* + * arithmetic + */ + +// Add sets z = x + y and returns z. +func (z *Int) Add(x, y *Int) *Int { + x.doinit() + y.doinit() + z.doinit() + C.mpz_add(&z.i[0], &x.i[0], &y.i[0]) + return z +} + +// Sub sets z = x - y and returns z. +func (z *Int) Sub(x, y *Int) *Int { + x.doinit() + y.doinit() + z.doinit() + C.mpz_sub(&z.i[0], &x.i[0], &y.i[0]) + return z +} + +// Mul sets z = x * y and returns z. +func (z *Int) Mul(x, y *Int) *Int { + x.doinit() + y.doinit() + z.doinit() + C.mpz_mul(&z.i[0], &x.i[0], &y.i[0]) + return z +} + +// Div sets z = x / y, rounding toward zero, and returns z. +func (z *Int) Div(x, y *Int) *Int { + x.doinit() + y.doinit() + z.doinit() + C.mpz_tdiv_q(&z.i[0], &x.i[0], &y.i[0]) + return z +} + +// Mod sets z = x % y and returns z. +// Like the result of the Go % operator, z has the same sign as x. +func (z *Int) Mod(x, y *Int) *Int { + x.doinit() + y.doinit() + z.doinit() + C.mpz_tdiv_r(&z.i[0], &x.i[0], &y.i[0]) + return z +} + +// Lsh sets z = x << s and returns z. +func (z *Int) Lsh(x *Int, s uint) *Int { + x.doinit() + z.doinit() + C.mpz_mul_2exp(&z.i[0], &x.i[0], C.mp_bitcnt_t(s)) + return z +} + +// Rsh sets z = x >> s and returns z. +func (z *Int) Rsh(x *Int, s uint) *Int { + x.doinit() + z.doinit() + C.mpz_div_2exp(&z.i[0], &x.i[0], C.mp_bitcnt_t(s)) + return z +} + +// Exp sets z = x^y % m and returns z. +// If m == nil, Exp sets z = x^y. +func (z *Int) Exp(x, y, m *Int) *Int { + m.doinit() + x.doinit() + y.doinit() + z.doinit() + if m == nil { + C.mpz_pow_ui(&z.i[0], &x.i[0], C.mpz_get_ui(&y.i[0])) + } else { + C.mpz_powm(&z.i[0], &x.i[0], &y.i[0], &m.i[0]) + } + return z +} + +func (z *Int) Int64() int64 { + if !z.init { + return 0 + } + return int64(C.mpz_get_si(&z.i[0])) +} + + +// Neg sets z = -x and returns z. +func (z *Int) Neg(x *Int) *Int { + x.doinit() + z.doinit() + C.mpz_neg(&z.i[0], &x.i[0]) + return z +} + +// Abs sets z to the absolute value of x and returns z. +func (z *Int) Abs(x *Int) *Int { + x.doinit() + z.doinit() + C.mpz_abs(&z.i[0], &x.i[0]) + return z +} + + +/* + * functions without a clear receiver + */ + +// CmpInt compares x and y. The result is +// +// -1 if x < y +// 0 if x == y +// +1 if x > y +// +func CmpInt(x, y *Int) int { + x.doinit() + y.doinit() + switch cmp := C.mpz_cmp(&x.i[0], &y.i[0]); { + case cmp < 0: + return -1 + case cmp == 0: + return 0 + } + return +1 +} + +// DivModInt sets q = x / y and r = x % y. +func DivModInt(q, r, x, y *Int) { + q.doinit() + r.doinit() + x.doinit() + y.doinit() + C.mpz_tdiv_qr(&q.i[0], &r.i[0], &x.i[0], &y.i[0]) +} + +// GcdInt sets d to the greatest common divisor of a and b, +// which must be positive numbers. +// If x and y are not nil, GcdInt sets x and y such that d = a*x + b*y. +// If either a or b is not positive, GcdInt sets d = x = y = 0. +func GcdInt(d, x, y, a, b *Int) { + d.doinit() + x.doinit() + y.doinit() + a.doinit() + b.doinit() + C.mpz_gcdext(&d.i[0], &x.i[0], &y.i[0], &a.i[0], &b.i[0]) +} + +// ProbablyPrime performs n Miller-Rabin tests to check whether z is prime. +// If it returns true, z is prime with probability 1 - 1/4^n. +// If it returns false, z is not prime. +func (z *Int) ProbablyPrime(n int) bool { + z.doinit() + return int(C.mpz_probab_prime_p(&z.i[0], C.int(n))) > 0 +} diff --git a/demos/go/gmp/impl.go b/demos/go/gmp/impl.go new file mode 100644 index 00000000..07559f2f --- /dev/null +++ b/demos/go/gmp/impl.go @@ -0,0 +1,7 @@ +package gmp + +/* + #include +*/ +import "C" +// EOF diff --git a/demos/go/main.go b/demos/go/main.go new file mode 100644 index 00000000..76a8fd7a --- /dev/null +++ b/demos/go/main.go @@ -0,0 +1,10 @@ +// By: Tom Wambold +package main + +import "other" + +func main() { + a := other.Vector3 {1, 2, 3}; + a.Size(); + return; +} diff --git a/demos/go/my-c-lib-2.go b/demos/go/my-c-lib-2.go new file mode 100644 index 00000000..62735655 --- /dev/null +++ b/demos/go/my-c-lib-2.go @@ -0,0 +1,16 @@ +package foo + +/* + #cgo LDFLAGS: -lmy-c-lib + + #include "my-c-lib.h" + #include +*/ +import "C" +import "unsafe" + +func MyBye(msg string) { + c_msg := C.CString(msg) + defer C.free(unsafe.Pointer(c_msg)) + C.my_c_bye(c_msg) +} diff --git a/demos/go/my-c-lib.go b/demos/go/my-c-lib.go new file mode 100644 index 00000000..31ad7e8a --- /dev/null +++ b/demos/go/my-c-lib.go @@ -0,0 +1,16 @@ +package foo + +/* + #cgo LDFLAGS: -lmy-c-lib + + #include "my-c-lib.h" + #include +*/ +import "C" +import "unsafe" + +func MyHello(msg string) { + c_msg := C.CString(msg) + defer C.free(unsafe.Pointer(c_msg)) + C.my_c_hello(c_msg) +} diff --git a/demos/go/my-c-lib/includes/my-c-lib.h b/demos/go/my-c-lib/includes/my-c-lib.h new file mode 100644 index 00000000..8623b516 --- /dev/null +++ b/demos/go/my-c-lib/includes/my-c-lib.h @@ -0,0 +1,7 @@ +#ifndef MY_C_LIB_H +#define MY_C_LIB_H 1 + +void my_c_hello(const char *msg); +void my_c_bye(const char *msg); + +#endif diff --git a/demos/go/my-c-lib/src/foo.c b/demos/go/my-c-lib/src/foo.c new file mode 100644 index 00000000..de0c4e00 --- /dev/null +++ b/demos/go/my-c-lib/src/foo.c @@ -0,0 +1,15 @@ +/* simple C library to exercize CGO */ + +#include +#include "my-c-lib.h" + +void my_c_hello(const char *msg) +{ + fprintf(stdout, msg); +} + +void my_c_bye(const char *msg) +{ + fprintf(stdout, msg); +} + diff --git a/demos/go/my-cgo-test.go b/demos/go/my-cgo-test.go new file mode 100644 index 00000000..556b4247 --- /dev/null +++ b/demos/go/my-cgo-test.go @@ -0,0 +1,10 @@ +package main + +import "foo" + +func main() { + foo.MyHello("hello from my-c-lib\n") + foo.MyBye("bye from my-c-lib\n") +} + +// EOF diff --git a/demos/go/other/a.go b/demos/go/other/a.go new file mode 100644 index 00000000..1af17d5e --- /dev/null +++ b/demos/go/other/a.go @@ -0,0 +1,11 @@ +// By: Tom Wambold +package other + +import "math" + +// A three-value vector (i, j, k) +type Vector3 [3]float64 + +func (a *Vector3) Size() float64 { + return math.Sqrt(float64(a[0] * a[0] + a[1] * a[1] + a[2] * a[2])); +} diff --git a/demos/go/other/b.go b/demos/go/other/b.go new file mode 100644 index 00000000..6c2eb820 --- /dev/null +++ b/demos/go/other/b.go @@ -0,0 +1,6 @@ +package other + +type Foo interface { + Get(i int, j int) float64; + Set(i int, j int, v float64); +} diff --git a/demos/go/pi.go b/demos/go/pi.go new file mode 100644 index 00000000..45f61abb --- /dev/null +++ b/demos/go/pi.go @@ -0,0 +1,104 @@ +/* +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + * Neither the name of "The Computer Language Benchmarks Game" nor the + name of "The Computer Language Shootout Benchmarks" nor the names of + its contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" +AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE +ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE +LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR +CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF +SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS +INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN +CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) +ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +*/ + +/* The Computer Language Benchmarks Game + * http://shootout.alioth.debian.org/ + * + * contributed by The Go Authors. + * based on pidigits.c (by Paolo Bonzini & Sean Bartlett, + * modified by Michael Mellor) + */ + +package main + +import ( + big "gmp" + "fmt" + "runtime" +) + +var ( + tmp1 = big.NewInt(0) + tmp2 = big.NewInt(0) + numer = big.NewInt(1) + accum = big.NewInt(0) + denom = big.NewInt(1) + ten = big.NewInt(10) +) + +func extractDigit() int64 { + if big.CmpInt(numer, accum) > 0 { + return -1 + } + tmp1.Lsh(numer, 1).Add(tmp1, numer).Add(tmp1, accum) + big.DivModInt(tmp1, tmp2, tmp1, denom) + tmp2.Add(tmp2, numer) + if big.CmpInt(tmp2, denom) >= 0 { + return -1 + } + return tmp1.Int64() +} + +func nextTerm(k int64) { + y2 := k*2 + 1 + accum.Add(accum, tmp1.Lsh(numer, 1)) + accum.Mul(accum, tmp1.SetInt64(y2)) + numer.Mul(numer, tmp1.SetInt64(k)) + denom.Mul(denom, tmp1.SetInt64(y2)) +} + +func eliminateDigit(d int64) { + accum.Sub(accum, tmp1.Mul(denom, tmp1.SetInt64(d))) + accum.Mul(accum, ten) + numer.Mul(numer, ten) +} + +func main() { + i := 0 + k := int64(0) + for { + d := int64(-1) + for d < 0 { + k++ + nextTerm(k) + d = extractDigit() + } + eliminateDigit(d) + fmt.Printf("%c", d+'0') + + if i++; i%50 == 0 { + fmt.Printf("\n") + if i >= 1000 { + break + } + } + } + + fmt.Printf("\n%d calls; bit sizes: %d %d %d\n", runtime.Cgocalls(), numer.Len(), accum.Len(), denom.Len()) +} diff --git a/demos/go/stdio/chain.go b/demos/go/stdio/chain.go new file mode 100644 index 00000000..5561cb1c --- /dev/null +++ b/demos/go/stdio/chain.go @@ -0,0 +1,43 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Pass numbers along a chain of threads. + +package main + +import ( + "runtime" + "cgo/stdio" + "strconv" +) + +const N = 10 +const R = 5 + +func link(left chan<- int, right <-chan int) { + // Keep the links in dedicated operating system + // threads, so that this program tests coordination + // between pthreads and not just goroutines. + runtime.LockOSThread() + for { + v := <-right + stdio.Stdout.WriteString(strconv.Itoa(v) + "\n") + left <- 1 + v + } +} + +func main() { + leftmost := make(chan int) + var left chan int + right := leftmost + for i := 0; i < N; i++ { + left, right = right, make(chan int) + go link(left, right) + } + for i := 0; i < R; i++ { + right <- 0 + x := <-leftmost + stdio.Stdout.WriteString(strconv.Itoa(x) + "\n") + } +} diff --git a/demos/go/stdio/fib.go b/demos/go/stdio/fib.go new file mode 100644 index 00000000..d1692c86 --- /dev/null +++ b/demos/go/stdio/fib.go @@ -0,0 +1,47 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Compute Fibonacci numbers with two goroutines +// that pass integers back and forth. No actual +// concurrency, just threads and synchronization +// and foreign code on multiple pthreads. + +package main + +import ( + "runtime" + "cgo/stdio" + "strconv" +) + +func fibber(c, out chan int64, i int64) { + // Keep the fibbers in dedicated operating system + // threads, so that this program tests coordination + // between pthreads and not just goroutines. + runtime.LockOSThread() + + if i == 0 { + c <- i + } + for { + j := <-c + stdio.Stdout.WriteString(strconv.Itoa64(j) + "\n") + out <- j + <-out + i += j + c <- i + } +} + +func main() { + c := make(chan int64) + out := make(chan int64) + go fibber(c, out, 0) + go fibber(c, out, 1) + <-out + for i := 0; i < 90; i++ { + out <- 1 + <-out + } +} diff --git a/demos/go/stdio/file.go b/demos/go/stdio/file.go new file mode 100644 index 00000000..021cbf90 --- /dev/null +++ b/demos/go/stdio/file.go @@ -0,0 +1,44 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +/* +A trivial example of wrapping a C library in Go. +For a more complex example and explanation, +see ../gmp/gmp.go. +*/ + +package stdio + +/* +#include +#include +#include +#include + +char* greeting = "hello, world"; +*/ +import "C" +import "unsafe" + +type File C.FILE + +var Stdout = (*File)(C.stdout) +var Stderr = (*File)(C.stderr) + +// Test reference to library symbol. +// Stdout and stderr are too special to be a reliable test. +var myerr = C.sys_errlist + +func (f *File) WriteString(s string) { + p := C.CString(s) + C.fputs(p, (*C.FILE)(f)) + C.free(unsafe.Pointer(p)) + f.Flush() +} + +func (f *File) Flush() { + C.fflush((*C.FILE)(f)) +} + +var Greeting = C.GoString(C.greeting) diff --git a/demos/go/stdio/hello.go b/demos/go/stdio/hello.go new file mode 100644 index 00000000..75244e8b --- /dev/null +++ b/demos/go/stdio/hello.go @@ -0,0 +1,11 @@ +// Copyright 2009 The Go Authors. All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package main + +import "cgo/stdio" + +func main() { + stdio.Stdout.WriteString(stdio.Greeting + "\n") +} diff --git a/demos/go/wscript b/demos/go/wscript new file mode 100644 index 00000000..842531f9 --- /dev/null +++ b/demos/go/wscript @@ -0,0 +1,147 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Tom Wambold tom5760 gmail +# Thomas Nagy, 2010 (ita) + +""" +if libgmp is present, try building with 'waf --exe' +""" + +top = '.' +out = 'build' + +def options(opt): + opt.add_option('--exe', action='store_true', default=False, help='Execute the program after it is compiled') + +def configure(ctx): + ctx.load('go') + + # the compiler keeps changing, think twice before trying it for a serious project + ctx.env.TRY_CGO = False + + return + try: + ctx.load('gcc') + ctx.check_cc(fragment='#include \nint main() {return 0;}\n', uselib_store='GMP', lib='gmp') + except ctx.errors.ConfigurationError: + ctx.env.TRY_CGO = False + else: + ctx.env.TRY_CGO = True + +def build(ctx): + + ctx( + features = 'go gopackage', + target = 'other', + source = [ + 'other/a.go', + 'other/b.go', # gopack sux + ], + ) + + ctx( + features = 'go goprogram', + target = 'test', + use = 'other', + source = 'main.go', + includes = '.', + ) + + # NOTE: if you use ant_glob, use it like this: bld.path.ant_glob('*.go', excl='*_test.go') + + if ctx.env.TRY_CGO: + ctx.read_shlib('gmp') + ctx( + features = 'c cshlib', + source = 'my-c-lib/src/foo.c', + target = 'my-c-lib', + includes = 'my-c-lib/includes', + export_includes=['my-c-lib/includes'], + ) + ctx( + features = 'cgopackage', + name = 'go-gmp', + target = 'gmp', + source = 'gmp.go gmp/impl.go', + use = 'gmp', + ) + + # testing multiple cgopackage targets... + ctx( + features = 'cgopackage', + name = 'go-stdio', + target = 'cgo/stdio', + source = 'stdio/file.go', + ) + + ctx( + features = 'cgopackage', + name = 'go-my-c-lib', + target = 'foo', + source = 'my-c-lib.go my-c-lib-2.go', + use = ['my-c-lib',], + ) + + #ctx.add_group() + ctx(features='go goprogram uselib', + source='pi.go', + target='pi', + use='go-gmp', + #gocflags=['-I.', '-I..'], + ) + + ctx( + features='go goprogram uselib', + source='stdio/hello.go', + target='go-stdio-hello', + use='go-stdio', + ) + + ctx( + features='go goprogram uselib', + source='stdio/fib.go', + target='go-stdio-fib', + use='go-stdio', + ) + + ctx( + features='go goprogram uselib', + source='stdio/chain.go', + target='go-stdio-chain', + use='go-stdio', + ) + + ctx( + features='go goprogram uselib', + source='my-cgo-test.go', + target='my-cgo-test', + use='go-my-c-lib', + ) + + from waflib import Options, Utils + if ctx.env.TRY_CGO and Options.options.exe: + def exe_pi(bld): + p = Utils.subprocess.Popen('LD_LIBRARY_PATH=build ./build/pi', shell=True) + p.wait() + ctx.add_post_fun(exe_pi) + + def exe_hello(bld): + p = Utils.subprocess.Popen('LD_LIBRARY_PATH=build ./build/go-stdio-hello', shell=True) + p.wait() + ctx.add_post_fun(exe_hello) + + def exe_fib(bld): + p = Utils.subprocess.Popen('LD_LIBRARY_PATH=build ./build/go-stdio-fib', shell=True) + p.wait() + ctx.add_post_fun(exe_fib) + + def exe_chain(bld): + p = Utils.subprocess.Popen('LD_LIBRARY_PATH=build ./build/go-stdio-chain', shell=True) + p.wait() + ctx.add_post_fun(exe_chain) + + def exe_mycgolib(bld): + p = Utils.subprocess.Popen('LD_LIBRARY_PATH=build ./build/my-cgo-test', shell=True) + p.wait() + ctx.add_post_fun(exe_mycgolib) + diff --git a/demos/intltool/data/kupfer-mimetypes.xml.in b/demos/intltool/data/kupfer-mimetypes.xml.in new file mode 100644 index 00000000..116ae327 --- /dev/null +++ b/demos/intltool/data/kupfer-mimetypes.xml.in @@ -0,0 +1,11 @@ + + + + <_comment>Saved Kupfer Command + + + + + + + diff --git a/demos/intltool/data/kupfer.desktop.in b/demos/intltool/data/kupfer.desktop.in new file mode 100644 index 00000000..f382fa56 --- /dev/null +++ b/demos/intltool/data/kupfer.desktop.in @@ -0,0 +1,11 @@ +[Desktop Entry] +Version=1.0 +_Name=Kupfer +_GenericName=Application Launcher +_Comment=Convenient command and access tool for applications and documents +Icon=kupfer +Exec=kupfer %F +Type=Application +Categories=Utility; +StartupNotify=true +X-UserData=$CONFIG/kupfer;$DATA/kupfer;$CACHE/kupfer diff --git a/demos/intltool/data/wscript b/demos/intltool/data/wscript new file mode 100644 index 00000000..811eb6ab --- /dev/null +++ b/demos/intltool/data/wscript @@ -0,0 +1,32 @@ +#! /usr/bin/env python + +from waflib import Utils + +def build(bld): + + if bld.cmd == 'install': + try: + bld.exec_command(["update-mime-database", Utils.subst_vars("${DATADIR}/mime", bld.env)]) + bld.exec_command(["update-desktop-database", Utils.subst_vars("${DATADIR}/applications", bld.env)]) + except: + pass + + bld( + features = "intltool_in", + podir = "../po", + flags = ["-d", "-q", "-u", "-c"], + source = 'kupfer.desktop.in', + target = 'kupfer.desktop', + install_path = "${DATADIR}/applications", + ) + + bld( + features = "intltool_in", + podir = "../po", + flags = ["-x", "-q", "-u", "-c"], + source = 'kupfer-mimetypes.xml.in', + target = 'kupfer-mimetypes.xml', + install_path = "${DATADIR}/mime/packages/", + ) + + diff --git a/demos/intltool/po/LINGUAS b/demos/intltool/po/LINGUAS new file mode 100644 index 00000000..a1731e21 --- /dev/null +++ b/demos/intltool/po/LINGUAS @@ -0,0 +1,2 @@ +# please keep this list sorted alphabetically +sv diff --git a/demos/intltool/po/POTFILES.in b/demos/intltool/po/POTFILES.in new file mode 100644 index 00000000..706c1e01 --- /dev/null +++ b/demos/intltool/po/POTFILES.in @@ -0,0 +1,6 @@ +# List of source files containing translatable strings. +# Please keep this file sorted alphabetically. +[encoding: UTF-8] +data/kupfer.desktop.in +data/kupfer-mimetypes.xml.in + diff --git a/demos/intltool/po/sv.po b/demos/intltool/po/sv.po new file mode 100644 index 00000000..1b681b6e --- /dev/null +++ b/demos/intltool/po/sv.po @@ -0,0 +1,30 @@ +# public domain, no copyright +msgid "" +msgstr "" +"Project-Id-Version: test\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-11-06 17:19+0100\n" +"PO-Revision-Date: 2009-06-23 20:19+0200\n" +"Last-Translator: Ulrik Sverdrup \n" +"Language-Team: Swedish\n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +#: ../data/kupfer.desktop.in.h:1 +msgid "Application Launcher" +msgstr "Programstartare" + +#: ../data/kupfer.desktop.in.h:2 +msgid "Convenient command and access tool for applications and documents" +msgstr "Flexibelt kommandoprogram för program och dokument" + +#: ../data/kupfer.desktop.in.h:3 +msgid "Kupfer" +msgstr "Kupfer" + +#: ../data/kupfer-mimetypes.xml.in.h:1 +msgid "Saved Kupfer Command" +msgstr "Sparat Kupfer-kommando" diff --git a/demos/intltool/wscript b/demos/intltool/wscript new file mode 100644 index 00000000..dfd6263b --- /dev/null +++ b/demos/intltool/wscript @@ -0,0 +1,17 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +""" +help welcome to improve this demo folder +""" + +APPNAME = 'app' + +def configure(conf): + conf.load('gnu_dirs intltool') + +def build(bld): + bld.recurse('data') + bld(features='intltool_po', appname=APPNAME, podir='po', install_path="${LOCALEDIR}") + diff --git a/demos/java/animals/junit/org/example/AnimalTest.java b/demos/java/animals/junit/org/example/AnimalTest.java new file mode 100644 index 00000000..64c7a85a --- /dev/null +++ b/demos/java/animals/junit/org/example/AnimalTest.java @@ -0,0 +1,13 @@ +package org.example; + +import junit.framework.TestCase; + +public class AnimalTest extends TestCase { + public AnimalTest() { + } + + public void testAnimal() { + System.out.println("Test run successfully!"); + } +} + diff --git a/demos/java/animals/manifest b/demos/java/animals/manifest new file mode 100644 index 00000000..c6488124 --- /dev/null +++ b/demos/java/animals/manifest @@ -0,0 +1,3 @@ +Manifest-Version: 1.0 +Created-By: Waf 1.6.2 (rev >= 10780) + diff --git a/demos/java/animals/src/org/example/Animal.java b/demos/java/animals/src/org/example/Animal.java new file mode 100644 index 00000000..c6e7b92c --- /dev/null +++ b/demos/java/animals/src/org/example/Animal.java @@ -0,0 +1,9 @@ +package org.example; + +class Animal { + + public String sound() { + return null; + } + +} diff --git a/demos/java/animals/wscript b/demos/java/animals/wscript new file mode 100644 index 00000000..f2557979 --- /dev/null +++ b/demos/java/animals/wscript @@ -0,0 +1,28 @@ +#! /usr/bin/env python + +def build(bld): + + t = bld( + features = 'javac jar', + name = 'animals', + + # javac + srcdir = 'src', + compat = '1.5', + + # jar + basedir = '.', + destfile = '../animals.jar', + manifest = 'manifest', + use = 'NNN', + ) + t.env.JAVACFLAGS = ['-Xlint:unchecked', '-verbose'] + + if bld.env.DO_JUNIT: + t.features += ' junit' + t.srcdir = 'src junit' + t.junitsrc = 'junit' + t.junitclasspath = '.' + t.use += ' JUNIT' + t.env.JUNIT_EXEC_FLAGS = ['-ea'] + diff --git a/demos/java/cats/src/org/example/Cat.java b/demos/java/cats/src/org/example/Cat.java new file mode 100644 index 00000000..bf937dc9 --- /dev/null +++ b/demos/java/cats/src/org/example/Cat.java @@ -0,0 +1,14 @@ + +package org.example; + +import org.example.Animal; + +class Cat extends Animal { + + public String sound() { + return "Meow!"; + } + + +} + diff --git a/demos/java/cats/wscript b/demos/java/cats/wscript new file mode 100644 index 00000000..3288d26a --- /dev/null +++ b/demos/java/cats/wscript @@ -0,0 +1,18 @@ +#! /usr/bin/env python + +def build(bld): + + bld(features = 'javac', + srcdir = 'src', + compat = '1.5', + use = 'animals', + name = 'cats-src', + ) + + bld(features = 'jar', + basedir = '.', + destfile = '../cats.jar', + name = 'cats', + use = 'cats-src' + ) + diff --git a/demos/java/junit.py b/demos/java/junit.py new file mode 100644 index 00000000..4a271e51 --- /dev/null +++ b/demos/java/junit.py @@ -0,0 +1,85 @@ +#! /usr/bin/env python +# encoding: utf-8 + +""" +JUnit test system + + - executes all junit tests in the specified subtree (junitsrc) + - only if --junit is given on the commandline + - method: + - add task to compile junitsrc after compiling srcdir + - additional junit_classpath specifiable + - defaults to classpath + destdir + - add task to run junit tests after they're compiled. +""" + +import os +from waflib import Task, TaskGen, Utils, Options +from waflib.TaskGen import feature, before, after +from waflib.Configure import conf + +JUNIT_RUNNER = 'org.junit.runner.JUnitCore' + +def options(opt): + opt.add_option('--junit', action='store_true', default=False, + help='Run all junit tests', dest='junit') + opt.add_option('--junitpath', action='store', default='', + help='Give a path to the junit jar') + +def configure(ctx): + cp = ctx.options.junitpath + val = ctx.env.JUNIT_RUNNER = ctx.env.JUNIT_RUNNER or JUNIT_RUNNER + if ctx.check_java_class(val, with_classpath=cp): + ctx.fatal('Could not run junit from %r' % val) + ctx.env.CLASSPATH_JUNIT = cp + +#@feature('junit') +#@after('apply_java', 'use_javac_files') +def make_test(self): + """make the unit test task""" + if not getattr(self, 'junitsrc', None): + return + junit_task = self.create_task('junit_test') + try: + junit_task.set_run_after(self.javac_task) + except AttributeError: + pass +feature('junit')(make_test) +after('apply_java', 'use_javac_files')(make_test) + +class junit_test(Task.Task): + color = 'YELLOW' + vars = ['JUNIT_EXEC_FLAGS', 'JUNIT_RUNNER'] + + def runnable_status(self): + """ + Only run if --junit was set as an option + """ + for t in self.run_after: + if not t.hasrun: + return Task.ASK_LATER + + n = self.generator.path.find_dir(self.generator.junitsrc) + if not n: + self.generator.bld.fatal('no such junit directory %r' % self.generator.junitsrc) + self.base = n + + # make sure the tests are executed whenever the .class files change + self.inputs = n.ant_glob('**/*.java') + + ret = super(junit_test, self).runnable_status() + if ret == Task.SKIP_ME: + if getattr(Options.options, 'junit', False): + ret = Task.RUN_ME + return ret + + def run(self): + cmd = [] + cmd.extend(self.env.JAVA) + cmd.append('-classpath') + cmd.append(self.generator.javac_task.env.CLASSPATH + os.pathsep + self.generator.javac_task.env.OUTDIR) + cmd.extend(self.env.JUNIT_EXEC_FLAGS) + cmd.append(self.env.JUNIT_RUNNER) + cmd.extend([x.path_from(self.base).replace('.java', '').replace(os.sep, '.') for x in self.inputs]) + return self.exec_command(cmd) + diff --git a/demos/java/src/com/meow/Hello.java b/demos/java/src/com/meow/Hello.java new file mode 100644 index 00000000..f685ac58 --- /dev/null +++ b/demos/java/src/com/meow/Hello.java @@ -0,0 +1,34 @@ +package com.meow; // obligatory + +public class Hello +{ + int m_var = 0; + public Hello() + { + this.m_var = 2; + } + + class MyHelperClass + { + MyHelperClass() { } + int someHelperMethod(int z, int q) { return 2; } + } + + public Object makeObj(String name) + { + final String objName = "My name is " + name; + + return new Object() { + public String toString() + { + return objName; + } + }; + } + + public static void main(String args[]) + { + System.out.println("Hello, world"); + } +} + diff --git a/demos/java/src/com/meow/package-info.java b/demos/java/src/com/meow/package-info.java new file mode 100644 index 00000000..116cfb49 --- /dev/null +++ b/demos/java/src/com/meow/package-info.java @@ -0,0 +1,2 @@ +package com.meow; + diff --git a/demos/java/src/com/meow/truc/bar/Hello.java b/demos/java/src/com/meow/truc/bar/Hello.java new file mode 100644 index 00000000..61af11cf --- /dev/null +++ b/demos/java/src/com/meow/truc/bar/Hello.java @@ -0,0 +1,34 @@ +package com.meow.truc.bar; // obligatory + +public class Hello +{ + int m_var = 0; + public Hello() + { + this.m_var = 2; + } + + class MyHelperClass + { + MyHelperClass() { } + int someHelperMethod(int z, int q) { return 2; } + } + + public Object makeObj(String name) + { + final String objName = "My name is " + name; + + return new Object() { + public String toString() + { + return objName; + } + }; + } + + public static void main(String args[]) + { + System.out.println("Hello, world"); + } +} + diff --git a/demos/java/src/com/meow/truc/foo/Hello.java b/demos/java/src/com/meow/truc/foo/Hello.java new file mode 100644 index 00000000..ab45d8c7 --- /dev/null +++ b/demos/java/src/com/meow/truc/foo/Hello.java @@ -0,0 +1,34 @@ +package com.meow.truc.foo; // obligatory + +public class Hello +{ + int m_var = 0; + public Hello() + { + this.m_var = 2; + } + + class MyHelperClass + { + MyHelperClass() { } + int someHelperMethod(int z, int q) { return 2; } + } + + public Object makeObj(String name) + { + final String objName = "My name is " + name; + + return new Object() { + public String toString() + { + return objName; + } + }; + } + + public static void main(String args[]) + { + System.out.println("Hello, world"); + } +} + diff --git a/demos/java/sup/org/test/Hella.java b/demos/java/sup/org/test/Hella.java new file mode 100644 index 00000000..08dcd1c6 --- /dev/null +++ b/demos/java/sup/org/test/Hella.java @@ -0,0 +1,10 @@ +package org.test; // obligatory + +public class Hella +{ + public static void main(String args[]) + { + System.out.println("Hella, world"); + } +} + diff --git a/demos/java/wscript b/demos/java/wscript new file mode 100644 index 00000000..21b218f2 --- /dev/null +++ b/demos/java/wscript @@ -0,0 +1,49 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +""" +java example + +The gcj compiler has a very different command-line - see playground/gcj +""" + +VERSION = '0.0.4' +APPNAME = 'java_test' + +top = '.' +out = 'build' + +def options(opt): + opt.load('junit', tooldir='.') + +def configure(conf): + conf.load('java') + + try: + ret = conf.load('junit', tooldir='.') + conf.env.DO_JUNIT = True + except: + pass + + conf.check_java_class('java.io.FileOutputStream') + conf.check_java_class('FakeClass') + + conf.env.CLASSPATH_NNN = ['aaaa.jar', 'bbbb.jar'] + +def build(bld): + + bld(features = 'javac jar', + srcdir = 'src/', # folder containing the sources to compile + outdir = 'src', # folder where to output the classes (in the build directory) + compat = '1.3', # java compatibility version number + sourcepath = ['src', 'sup'], + classpath = ['.', '..'], + #jaropts = '-C default/src/ .', # can be used to give files + basedir = 'src', # folder containing the classes and other files to package (must match outdir) + destfile = 'foo.jar', # do not put the destfile in the folder of the java classes! + use = 'NNN' + ) + + bld.recurse('animals cats') + diff --git a/demos/jni/src/java/StringUtils.java b/demos/jni/src/java/StringUtils.java new file mode 100644 index 00000000..39ec05c4 --- /dev/null +++ b/demos/jni/src/java/StringUtils.java @@ -0,0 +1,27 @@ +public final class StringUtils +{ + + public static final String LIBRARY_NAME = "stringUtils"; + + static + { + System.loadLibrary(LIBRARY_NAME); + } + + private StringUtils() + { + } + + public static native boolean isAlpha(String string); + + public static native boolean isEmpty(String string); + + public static void main(String[] args) + { + System.out.println(StringUtils.isAlpha("sureIs")); + System.out.println(StringUtils.isAlpha("nope!")); + System.out.println(StringUtils.isEmpty(" ")); + System.out.println(StringUtils.isEmpty("nope")); + } + +} diff --git a/demos/jni/src/jni/include/StringUtils.h b/demos/jni/src/jni/include/StringUtils.h new file mode 100644 index 00000000..2064f96d --- /dev/null +++ b/demos/jni/src/jni/include/StringUtils.h @@ -0,0 +1,29 @@ +/* DO NOT EDIT THIS FILE - it is machine generated */ +#include +/* Header for class StringUtils */ + +#ifndef _Included_StringUtils +#define _Included_StringUtils +#ifdef __cplusplus +extern "C" { +#endif +/* + * Class: StringUtils + * Method: isAlpha + * Signature: (Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_StringUtils_isAlpha + (JNIEnv *, jclass, jstring); + +/* + * Class: StringUtils + * Method: isEmpty + * Signature: (Ljava/lang/String;)Z + */ +JNIEXPORT jboolean JNICALL Java_StringUtils_isEmpty + (JNIEnv *, jclass, jstring); + +#ifdef __cplusplus +} +#endif +#endif diff --git a/demos/jni/src/jni/source/StringUtils.c b/demos/jni/src/jni/source/StringUtils.c new file mode 100644 index 00000000..7262ef3d --- /dev/null +++ b/demos/jni/src/jni/source/StringUtils.c @@ -0,0 +1,51 @@ +#include "StringUtils.h" +#include + + +JNIEXPORT jboolean JNICALL Java_StringUtils_isAlpha(JNIEnv *env, jclass clazz, + jstring jStr) +{ + jboolean ret = JNI_TRUE; + char *sp = NULL, *s = NULL; + + if (!jStr) + return JNI_FALSE; + + s = (char*)(*env)->GetStringUTFChars(env, jStr, 0); + sp = s + strlen(s); + if (sp <= s) + ret = JNI_FALSE; + do + { + if (!isalpha(*(--sp))) + ret = JNI_FALSE; + } + while (sp > s); + + (*env)->ReleaseStringUTFChars(env, jStr, s); + return ret; +} + +JNIEXPORT jboolean JNICALL Java_StringUtils_isEmpty(JNIEnv *env, jclass clazz, + jstring jStr) +{ + jboolean ret = JNI_TRUE; + char *sp = NULL, *s = NULL; + + if (!jStr) + return JNI_TRUE; + + s = (char*)(*env)->GetStringUTFChars(env, jStr, 0); + sp = s + strlen(s); + if (sp <= s) + ret = JNI_TRUE; + do + { + if (!isspace(*(--sp))) + ret = JNI_FALSE; + } + while (sp > s); + + (*env)->ReleaseStringUTFChars(env, jStr, s); + return ret; +} diff --git a/demos/jni/wscript b/demos/jni/wscript new file mode 100644 index 00000000..9dda3733 --- /dev/null +++ b/demos/jni/wscript @@ -0,0 +1,26 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Zellman 2009 + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_c') + opt.load('java') + +def configure(conf): + conf.load('compiler_c') + conf.load('java') + + conf.check_jni_headers() + +def build(bld): + bld(features='javac jar', srcdir='src/java', outdir='src/java', basedir='src/java', destfile='stringUtils.jar') + + bld.shlib( + source = 'src/jni/source/StringUtils.c', + includes = 'src/jni/include', + target = 'stringUtils', + uselib = 'JAVA') + diff --git a/demos/lua/foo.lua b/demos/lua/foo.lua new file mode 100644 index 00000000..1ff72d9c --- /dev/null +++ b/demos/lua/foo.lua @@ -0,0 +1 @@ +print (2 + 2) diff --git a/demos/lua/wscript b/demos/lua/wscript new file mode 100644 index 00000000..895e9190 --- /dev/null +++ b/demos/lua/wscript @@ -0,0 +1,10 @@ +#! /usr/bin/env python +# coding: utf-8 + +def configure(conf): + conf.load('lua') + # to enable luac file installation globally: + conf.env.LUADIR = '/opt/share/myapp/scripts/' + +def build(bld): + bld(source='foo.lua') diff --git a/demos/mac_app/Info.plist b/demos/mac_app/Info.plist new file mode 100644 index 00000000..d280ea87 --- /dev/null +++ b/demos/mac_app/Info.plist @@ -0,0 +1,33 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleExecutable + MacApp + CFBundleIconFile + MacApp.icns + CFBundleIdentifier + com.googlecode.waf.mac-app + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + MacApp + CFBundlePackageType + APPL + CFBundleSignature + ???? + CFBundleShortVersionString + 0.0.1 + LSMinimumSystemVersion + 10.6 + CFBundleVersion + 1 + NSMainNibFile + MainMenu + NSPrincipalClass + NSApplication + + + diff --git a/demos/mac_app/resources/MacApp.icns b/demos/mac_app/resources/MacApp.icns new file mode 100644 index 00000000..3c4a1631 Binary files /dev/null and b/demos/mac_app/resources/MacApp.icns differ diff --git a/demos/mac_app/resources/MainMenu.nib/designable.nib b/demos/mac_app/resources/MainMenu.nib/designable.nib new file mode 100644 index 00000000..b3b5c395 --- /dev/null +++ b/demos/mac_app/resources/MainMenu.nib/designable.nib @@ -0,0 +1,1857 @@ + + + + 1060 + 10J567 + 823 + 1038.35 + 462.00 + + com.apple.InterfaceBuilder.CocoaPlugin + 823 + + + YES + + + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + + + PluginDependencyRecalculationVersion + + + + YES + + NSApplication + + + FirstResponder + + + NSApplication + + + NSFontManager + + + Main Menu + + YES + + + MacApp + + 2147483647 + + NSImage + NSMenuCheckmark + + + NSImage + NSMenuMixedState + + submenuAction: + + MacApp + + YES + + + About MacApp + + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + YES + Preferences… + , + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Services + + 2147483647 + + + submenuAction: + + Services + + YES + + _NSServicesMenu + + + + + YES + YES + + + 2147483647 + + + + + + Hide MacApp + h + 1048576 + 2147483647 + + + + + + Hide Others + h + 1572864 + 2147483647 + + + + + + Show All + + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Quit MacApp + q + 1048576 + 2147483647 + + + + + _NSAppleMenu + + + + + Edit + + 2147483647 + + + submenuAction: + + Edit + + YES + + + Undo + z + 1048576 + 2147483647 + + + + + + Redo + Z + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Cut + x + 1048576 + 2147483647 + + + + + + Copy + c + 1048576 + 2147483647 + + + + + + Paste + v + 1048576 + 2147483647 + + + + + + Paste and Match Style + V + 1572864 + 2147483647 + + + + + + Delete + + 2147483647 + + + + + + Select All + a + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Find + + 2147483647 + + + submenuAction: + + Find + + YES + + + Find… + f + 1048576 + 2147483647 + + + 1 + + + + Find Next + g + 1048576 + 2147483647 + + + 2 + + + + Find Previous + G + 1048576 + 2147483647 + + + 3 + + + + Use Selection for Find + e + 1048576 + 2147483647 + + + 7 + + + + Jump to Selection + j + 1048576 + 2147483647 + + + + + + + + + Spelling and Grammar + + 2147483647 + + + submenuAction: + + Spelling + + YES + + + Show Spelling and Grammar + : + 1048576 + 2147483647 + + + + + + Check Document Now + ; + 1048576 + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Check Spelling While Typing + + 2147483647 + + + + + + Check Grammar With Spelling + + 2147483647 + + + + + + Correct Spelling Automatically + + 2147483647 + + + + + + + + + Substitutions + + 2147483647 + + + submenuAction: + + Substitutions + + YES + + + Show Substitutions + + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Smart Copy/Paste + + 2147483647 + + + + + + Smart Quotes + + 2147483647 + + + + + + Smart Dashes + + 2147483647 + + + + + + Smart Links + + 2147483647 + + + + + + Data Detectors + + 2147483647 + + + + + + Text Replacement + + 2147483647 + + + + + + + + + Transformations + + 2147483647 + + + submenuAction: + + Transformations + + YES + + + Make Upper Case + + 2147483647 + + + + + + Make Lower Case + + 2147483647 + + + + + + Capitalize + + 2147483647 + + + + + + + + + Speech + + 2147483647 + + + submenuAction: + + Speech + + YES + + + Start Speaking + + 2147483647 + + + + + + Stop Speaking + + 2147483647 + + + + + + + + + + + + Window + + 2147483647 + + + submenuAction: + + Window + + YES + + + Minimize + m + 1048576 + 2147483647 + + + + + + Zoom + + 2147483647 + + + + + + YES + YES + + + 2147483647 + + + + + + Bring All to Front + + 2147483647 + + + + + _NSWindowsMenu + + + + + Help + + 2147483647 + + + submenuAction: + + Help + + YES + + + YES + MacApp Help + ? + 1048576 + 2147483647 + + + + + _NSHelpMenu + + + + _NSMainMenu + + + 5 + 2 + {{196, 413}, {350, 97}} + 611844096 + MacApp + NSWindow + + {1.79769e+308, 1.79769e+308} + + + 256 + + YES + + + 268 + {{17, 20}, {316, 57}} + + YES + + 68288064 + 272630784 + Hello, world! + + LucidaGrande-Bold + 48 + 16 + + + + 6 + System + controlColor + + 3 + MC42NjY2NjY2NjY3AA + + + + 6 + System + controlTextColor + + 3 + MAA + + + + + + {350, 97} + + + {{0, 0}, {1680, 1028}} + {1.79769e+308, 1.79769e+308} + + + + + YES + + + performMiniaturize: + + + + 37 + + + + arrangeInFront: + + + + 39 + + + + orderFrontStandardAboutPanel: + + + + 142 + + + + performZoom: + + + + 240 + + + + showHelp: + + + + 360 + + + + hide: + + + + 369 + + + + hideOtherApplications: + + + + 370 + + + + unhideAllApplications: + + + + 372 + + + + terminate: + + + + 448 + + + + capitalizeWord: + + + + 767 + + + + cut: + + + + 768 + + + + paste: + + + + 769 + + + + toggleSmartInsertDelete: + + + + 770 + + + + toggleAutomaticQuoteSubstitution: + + + + 771 + + + + redo: + + + + 772 + + + + toggleAutomaticDashSubstitution: + + + + 773 + + + + toggleContinuousSpellChecking: + + + + 774 + + + + toggleAutomaticDataDetection: + + + + 775 + + + + undo: + + + + 776 + + + + toggleGrammarChecking: + + + + 777 + + + + startSpeaking: + + + + 778 + + + + showGuessPanel: + + + + 779 + + + + checkSpelling: + + + + 780 + + + + pasteAsPlainText: + + + + 781 + + + + copy: + + + + 782 + + + + delete: + + + + 783 + + + + lowercaseWord: + + + + 784 + + + + selectAll: + + + + 785 + + + + stopSpeaking: + + + + 786 + + + + orderFrontSubstitutionsPanel: + + + + 787 + + + + toggleAutomaticTextReplacement: + + + + 788 + + + + toggleAutomaticLinkDetection: + + + + 789 + + + + toggleAutomaticSpellingCorrection: + + + + 790 + + + + uppercaseWord: + + + + 791 + + + + performFindPanelAction: + + + + 798 + + + + performFindPanelAction: + + + + 799 + + + + performFindPanelAction: + + + + 800 + + + + centerSelectionInVisibleArea: + + + + 801 + + + + performFindPanelAction: + + + + 802 + + + + + YES + + 0 + + YES + + + + + + -2 + + + File's Owner + + + -1 + + + First Responder + + + -3 + + + Application + + + 29 + + + YES + + + + + + + Main Menu + + + 19 + + + YES + + + + + + 56 + + + YES + + + + + + 103 + + + YES + + + + + + 106 + + + YES + + + + + + 111 + + + + + 57 + + + YES + + + + + + + + + + + + + + + + 58 + + + + + 134 + + + + + 150 + + + + + 136 + + + + + 144 + + + + + 129 + + + + + 143 + + + + + 236 + + + + + 131 + + + YES + + + + + + 149 + + + + + 145 + + + + + 130 + + + + + 24 + + + YES + + + + + + + + + 92 + + + + + 5 + + + + + 239 + + + + + 23 + + + + + 367 + + + YES + + + + MacApp (Window) + + + 368 + + + YES + + + + + + 373 + + + + + 711 + + + YES + + + + + + 712 + + + YES + + + + + + + + + + + + + + + + + + + + 713 + + + + + 714 + + + + + 715 + + + + + 716 + + + + + 717 + + + + + 718 + + + + + 719 + + + + + 720 + + + + + 721 + + + + + 722 + + + + + 723 + + + YES + + + + + + 724 + + + YES + + + + + + 725 + + + YES + + + + + + 726 + + + YES + + + + + + 727 + + + YES + + + + + + 738 + + + YES + + + + + + + 739 + + + + + 740 + + + + + 741 + + + YES + + + + + + + + 742 + + + + + 743 + + + + + 744 + + + + + 745 + + + YES + + + + + + + + + + + + + 746 + + + + + 747 + + + + + 748 + + + + + 749 + + + + + 750 + + + + + 751 + + + + + 752 + + + + + 753 + + + + + 754 + + + YES + + + + + + + + + + + 755 + + + + + 756 + + + + + 757 + + + + + 758 + + + + + 759 + + + + + 760 + + + + + 761 + + + YES + + + + + + + + + + 762 + + + + + 763 + + + + + 764 + + + + + 765 + + + + + 766 + + + + + 826 + + + YES + + + + + + 827 + + + + + + + YES + + YES + -3.IBPluginDependency + 103.IBPluginDependency + 103.ImportedFromIB2 + 106.IBEditorWindowLastContentRect + 106.IBPluginDependency + 106.ImportedFromIB2 + 106.editorWindowContentRectSynchronizationRect + 111.IBPluginDependency + 111.ImportedFromIB2 + 129.IBPluginDependency + 129.ImportedFromIB2 + 130.IBEditorWindowLastContentRect + 130.IBPluginDependency + 130.ImportedFromIB2 + 130.editorWindowContentRectSynchronizationRect + 131.IBPluginDependency + 131.ImportedFromIB2 + 134.IBPluginDependency + 134.ImportedFromIB2 + 136.IBPluginDependency + 136.ImportedFromIB2 + 143.IBPluginDependency + 143.ImportedFromIB2 + 144.IBPluginDependency + 144.ImportedFromIB2 + 145.IBPluginDependency + 145.ImportedFromIB2 + 149.IBPluginDependency + 149.ImportedFromIB2 + 150.IBPluginDependency + 150.ImportedFromIB2 + 19.IBPluginDependency + 19.ImportedFromIB2 + 23.IBPluginDependency + 23.ImportedFromIB2 + 236.IBPluginDependency + 236.ImportedFromIB2 + 239.IBPluginDependency + 239.ImportedFromIB2 + 24.IBEditorWindowLastContentRect + 24.IBPluginDependency + 24.ImportedFromIB2 + 24.editorWindowContentRectSynchronizationRect + 29.IBEditorWindowLastContentRect + 29.IBPluginDependency + 29.ImportedFromIB2 + 29.WindowOrigin + 29.editorWindowContentRectSynchronizationRect + 367.IBEditorWindowLastContentRect + 367.IBPluginDependency + 367.IBWindowTemplateEditedContentRect + 367.NSWindowTemplate.visibleAtLaunch + 367.editorWindowContentRectSynchronizationRect + 367.windowTemplate.maxSize + 368.IBPluginDependency + 5.IBPluginDependency + 5.ImportedFromIB2 + 56.IBPluginDependency + 56.ImportedFromIB2 + 57.IBEditorWindowLastContentRect + 57.IBPluginDependency + 57.ImportedFromIB2 + 57.editorWindowContentRectSynchronizationRect + 58.IBPluginDependency + 58.ImportedFromIB2 + 711.IBPluginDependency + 712.IBEditorWindowLastContentRect + 712.IBPluginDependency + 713.IBPluginDependency + 714.IBPluginDependency + 715.IBPluginDependency + 716.IBPluginDependency + 717.IBPluginDependency + 718.IBPluginDependency + 719.IBPluginDependency + 720.IBPluginDependency + 721.IBPluginDependency + 722.IBPluginDependency + 723.IBPluginDependency + 724.IBPluginDependency + 725.IBPluginDependency + 726.IBPluginDependency + 727.IBPluginDependency + 738.IBPluginDependency + 739.IBPluginDependency + 740.IBPluginDependency + 741.IBEditorWindowLastContentRect + 741.IBPluginDependency + 742.IBPluginDependency + 743.IBPluginDependency + 744.IBPluginDependency + 745.IBEditorWindowLastContentRect + 745.IBPluginDependency + 746.IBPluginDependency + 747.IBPluginDependency + 748.IBPluginDependency + 749.IBPluginDependency + 750.IBPluginDependency + 751.IBPluginDependency + 752.IBPluginDependency + 753.IBPluginDependency + 754.IBPluginDependency + 755.IBPluginDependency + 756.IBPluginDependency + 757.IBPluginDependency + 758.IBPluginDependency + 759.IBPluginDependency + 760.IBPluginDependency + 761.IBPluginDependency + 762.IBPluginDependency + 763.IBPluginDependency + 764.IBPluginDependency + 765.IBPluginDependency + 766.IBPluginDependency + 826.IBPluginDependency + 826.IBViewBoundsToFrameTransform + 827.IBPluginDependency + 92.IBPluginDependency + 92.ImportedFromIB2 + + + YES + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + {{512, 1113}, {161, 23}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{596, 852}, {216, 23}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{531, 606}, {64, 6}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{436, 809}, {64, 6}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{441, 1063}, {194, 73}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{525, 802}, {197, 73}} + {{309, 1136}, {264, 20}} + com.apple.InterfaceBuilder.CocoaPlugin + + {74, 862} + {{11, 977}, {478, 20}} + {{546, 928}, {350, 97}} + com.apple.InterfaceBuilder.CocoaPlugin + {{546, 928}, {350, 97}} + + {{11, 666}, {480, 270}} + {3.40282e+38, 3.40282e+38} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + + {{321, 953}, {190, 183}} + com.apple.InterfaceBuilder.CocoaPlugin + + {{23, 794}, {245, 183}} + com.apple.InterfaceBuilder.CocoaPlugin + + com.apple.InterfaceBuilder.CocoaPlugin + {{397, 853}, {254, 283}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{762, 442}, {170, 63}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + {{762, 372}, {182, 153}} + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + P4AAAL+AAABDFgAAwrgAAA + + com.apple.InterfaceBuilder.CocoaPlugin + com.apple.InterfaceBuilder.CocoaPlugin + + + + + YES + + + YES + + + + + YES + + + YES + + + + 827 + + + 0 + IBCocoaFramework + + com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3 + + + YES + + 3 + + YES + + YES + NSMenuCheckmark + NSMenuMixedState + + + YES + {9, 8} + {7, 2} + + + + diff --git a/demos/mac_app/resources/MainMenu.nib/keyedobjects.nib b/demos/mac_app/resources/MainMenu.nib/keyedobjects.nib new file mode 100644 index 00000000..79da7cb9 Binary files /dev/null and b/demos/mac_app/resources/MainMenu.nib/keyedobjects.nib differ diff --git a/demos/mac_app/sources/main.m b/demos/mac_app/sources/main.m new file mode 100644 index 00000000..4c696b73 --- /dev/null +++ b/demos/mac_app/sources/main.m @@ -0,0 +1,8 @@ +// waf sample Mac application - main.m +// Chris Pickel, 2011 + +#import + +int main(int argc, const char* argv[]) { + return NSApplicationMain(argc, argv); +} diff --git a/demos/mac_app/wscript b/demos/mac_app/wscript new file mode 100644 index 00000000..c9e12433 --- /dev/null +++ b/demos/mac_app/wscript @@ -0,0 +1,36 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Chris Pickel, 2011 + +VERSION='0.0.1' +APPNAME='mac_app_test' + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + conf.env.append_value('FRAMEWORK_COCOA', 'Cocoa') + +def build(bld): + bld.program( + features = 'c cprogram', + target = 'MacApp', + source = 'sources/main.m', + mac_app = True, + mac_plist = 'Info.plist', + mac_resources = 'resources/MainMenu.nib resources/MacApp.icns', + use = 'COCOA', + install_path = '${PREFIX}', + ) + +from waflib import TaskGen +@TaskGen.extension('.m') +def m_hook(self, node): + """Alias .m files to be compiled the same as .c files, gcc will do the right thing.""" + return self.create_compiled_task('c', node) + +# -*- indent-tabs-mode: t -*- diff --git a/demos/ocaml/main.ml b/demos/ocaml/main.ml new file mode 100644 index 00000000..6b1099df --- /dev/null +++ b/demos/ocaml/main.ml @@ -0,0 +1,20 @@ + +open Printf;; + +open Somemodule;; + +(* coin coin ( ) ( ( (* ) ) ) ( ) muahha open nono;; *) *) +(* ( *) + +let rec fib n = + if n < 1 then 1 + else if n == 1 then 1 + else fib(n-1) + fib(n-2);; + +for i=0 to 10 do + Somemodule.pprint "next"; + Printf.printf " %d %d\n" i (fib i) +done;; + +print_newline();; + diff --git a/demos/ocaml/somemodule.ml b/demos/ocaml/somemodule.ml new file mode 100644 index 00000000..b31f2daa --- /dev/null +++ b/demos/ocaml/somemodule.ml @@ -0,0 +1,7 @@ +open Printf;; + +let pprint str = + Printf.printf "* %s" str +;; + + diff --git a/demos/ocaml/wscript b/demos/ocaml/wscript new file mode 100644 index 00000000..77ae7e5c --- /dev/null +++ b/demos/ocaml/wscript @@ -0,0 +1,24 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 (ita) + +# the following two variables are used by the target "waf dist" +VERSION = '0.0.1' +APPNAME = 'ocaml_test' + +# these variables are mandatory, +top = '.' +out = 'build' + +def configure(conf): + conf.load('ocaml') + +def build(bld): + + bld( + features = 'ocaml', + kind = 'all', + includes = '.', + target = 'camlprog', + source = bld.path.ant_glob('*.ml')) + diff --git a/demos/pch/main.cpp b/demos/pch/main.cpp new file mode 100644 index 00000000..adadf2ee --- /dev/null +++ b/demos/pch/main.cpp @@ -0,0 +1,6 @@ +#include "all.h" + +int main() +{ + return 0; +} diff --git a/demos/pch/subdir/all.h b/demos/pch/subdir/all.h new file mode 100644 index 00000000..4eaee554 --- /dev/null +++ b/demos/pch/subdir/all.h @@ -0,0 +1 @@ +#include "w.h" diff --git a/demos/pch/w.h b/demos/pch/w.h new file mode 100644 index 00000000..fb060265 --- /dev/null +++ b/demos/pch/w.h @@ -0,0 +1 @@ +#warning testpch diff --git a/demos/pch/wscript b/demos/pch/wscript new file mode 100644 index 00000000..a126f7db --- /dev/null +++ b/demos/pch/wscript @@ -0,0 +1,53 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006 (ita) + +""" +for some obscure reason, the precompiled header will not be taken if +all.h is in the same directory as main.cpp +we recommend to add the header to compile in a separate directory without any sources + +Note: the #warning will come once when the .h is compiled, it will not come when the .cpp is compiled +Note: do not forget to set the include paths (include=...) +""" + +from waflib.TaskGen import feature, after +from waflib.Task import Task +from waflib.Tools import c_preproc + +#@feature('cxx') <- python >= 2.4 +#@after('apply_link') +def process_pch(self): + if getattr(self, 'pch', ''): + nodes = self.to_nodes(self.pch) + for x in nodes: + self.create_task('gchx', x, x.change_ext('.gch')) +feature('cxx')(process_pch) +after('apply_link')(process_pch) + +class gchx(Task): + run_str = '${CXX} ${CXXFLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${CPPPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} ${CXX_SRC_F}${SRC} ${CXX_TGT_F}${TGT}' + scan = c_preproc.scan + ext_out = ['.h'] + color = 'BLUE' + +################################################# +# example below + +VERSION='0.0.1' +APPNAME='pch_test' + +top = '.' +out = 'build' + +def configure(conf): + conf.load('g++') + +def build(bld): + bld.program( + source = 'main.cpp', + includes = '. subdir', + target = 'test', + pch = 'subdir/all.h') + + diff --git a/demos/perl/Mytest.pm b/demos/perl/Mytest.pm new file mode 100644 index 00000000..02c31191 --- /dev/null +++ b/demos/perl/Mytest.pm @@ -0,0 +1,41 @@ +package Mytest; + +use 5.008008; +use strict; +use warnings; + +require Exporter; + +our @ISA = qw(Exporter); + +# Items to export into callers namespace by default. Note: do not export +# names by default without a very good reason. Use EXPORT_OK instead. +# Do not simply export all your public functions/methods/constants. + +# This allows declaration use Mytest ':all'; +# If you do not need this, moving things directly into @EXPORT or @EXPORT_OK +# will save memory. +our %EXPORT_TAGS = ( 'all' => [ qw( + +) ] ); + +our @EXPORT_OK = ( @{ $EXPORT_TAGS{'all'} } ); + +our @EXPORT = qw( + +); + +our $VERSION = '0.01'; + +require XSLoader; +XSLoader::load('Mytest', $VERSION); + +# Preloaded methods go here. + +1; +__END__ +# Below is stub documentation for your module. You'd better edit it! + + + +=cut diff --git a/demos/perl/Mytest.xs b/demos/perl/Mytest.xs new file mode 100644 index 00000000..4130bfb8 --- /dev/null +++ b/demos/perl/Mytest.xs @@ -0,0 +1,14 @@ +/* Shamelessy stolen from perlxstut */ + +#include +#include +#include + + + +MODULE = Mytest PACKAGE = Mytest + +void +hello() + CODE: + printf("Hello, world\n"); diff --git a/demos/perl/wscript b/demos/perl/wscript new file mode 100644 index 00000000..a20c80f7 --- /dev/null +++ b/demos/perl/wscript @@ -0,0 +1,40 @@ +#! /usr/bin/env python +# encoding: utf-8 +# anonymous coward, 2007 +# Thomas Nagy, 2010 + +VERSION='0.0.1' +APPNAME='perl_test' + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_c') + opt.load('perl') + +def configure(conf): + conf.load('compiler_c') + conf.load('perl') + + # check for perl + conf.check_perl_version((5,6,0)) + + conf.check_perl_ext_devel() + + # check for some perl module... + conf.check_perl_module('Cairo') + # ...and a specific version + conf.check_perl_module('Devel::PPPort 4.89') + +def build(bld): + + # a perl extension module + bld( + features = 'c cshlib perlext', + source = 'Mytest.xs', + target = 'Mytest', + install_path = '${ARCHDIR_PERL}/auto') + + bld.install_files('${ARCHDIR_PERL}', 'Mytest.pm') + diff --git a/demos/precious/precious.c b/demos/precious/precious.c new file mode 100644 index 00000000..fb4f7ef2 --- /dev/null +++ b/demos/precious/precious.c @@ -0,0 +1,2 @@ +#include +int main(){ printf("%d", 108608662); return 0;} \ No newline at end of file diff --git a/demos/precious/wscript b/demos/precious/wscript new file mode 100644 index 00000000..ae2fa343 --- /dev/null +++ b/demos/precious/wscript @@ -0,0 +1,32 @@ +#! /usr/bin/env python +# encoding: utf-8 + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + + # the file precious.c is updated in the source directory + # the actual application is produced in the build directory + + node = bld.path.find_resource('precious.c') + + def fun(task): + import random + val = random.randint(0, 222111000) + task.outputs[0].write('#include \nint main(){ printf("%%d", %d); return 0;}' % val) + + bld( + #rule = '''echo -e "#include \\nint main(){ printf(\\"%%d\\", $$RANDOM); return 0;}" > ${TGT}''', + rule = fun, + target = node, + always = True, + update_outputs = True) + + bld.program( + source = 'precious.c', + target = 'app') + diff --git a/demos/python/foo.py b/demos/python/foo.py new file mode 100644 index 00000000..91b5078e --- /dev/null +++ b/demos/python/foo.py @@ -0,0 +1,5 @@ + +zbr = "bar" + +assert zbr + diff --git a/demos/python/spammodule.c b/demos/python/spammodule.c new file mode 100644 index 00000000..ebe277dc --- /dev/null +++ b/demos/python/spammodule.c @@ -0,0 +1,50 @@ +#include + +static PyObject * +spam_system(PyObject *self, PyObject *args) +{ + const char *command; + int sts; + + if (!PyArg_ParseTuple(args, "s", &command)) + return NULL; + sts = system(command); + return Py_BuildValue("i", sts); +} + +static PyMethodDef SpamMethods[] = { + {"system", spam_system, METH_VARARGS, + "Execute a shell command."}, + {NULL, NULL, 0, NULL} /* Sentinel */ +}; + +#if PY_VERSION_HEX >= 0x03000000 + +/* Python 3.x code */ + +static struct PyModuleDef spammodule = { + PyModuleDef_HEAD_INIT, + "spam", /* name of module */ + "spam_doc", /* module documentation, may be NULL */ + -1, /* size of per-interpreter state of the module, + or -1 if the module keeps state in global variables. */ + SpamMethods +}; + +PyMODINIT_FUNC +PyInit_spam(void) +{ + (void) PyModule_Create(&spammodule); +} + +#else + +/* Python 2.x code */ + +PyMODINIT_FUNC +initspam(void) +{ + (void) Py_InitModule("spam", SpamMethods); +} + +#endif diff --git a/demos/python/test.c b/demos/python/test.c new file mode 100644 index 00000000..bfe85a2b --- /dev/null +++ b/demos/python/test.c @@ -0,0 +1,57 @@ +#include + +static int numargs=0; + +static PyObject* emb_numargs(PyObject *self, PyObject *args) +{ + if(!PyArg_ParseTuple(args, ":numargs")) + return NULL; + return Py_BuildValue("i", numargs); +} + +static PyMethodDef EmbMethods[] = { + {"numargs", emb_numargs, METH_VARARGS, + "Return the number of arguments received by the process."}, + {NULL, NULL, 0, NULL} +}; + + +#if PY_VERSION_HEX >= 0x03000000 + +/* Python 3.x code */ + +static struct PyModuleDef embmodule = { + PyModuleDef_HEAD_INIT, + "emb", /* name of module */ + "emb_doc", /* module documentation, may be NULL */ + -1, /* size of per-interpreter state of the module, + or -1 if the module keeps state in global variables. */ + EmbMethods +}; + +PyMODINIT_FUNC +PyInit_emb(void) +{ + (void) PyModule_Create(&embmodule); +} + +#endif + + +int main(int argc, char *argv[]) +{ +#if PY_VERSION_HEX >= 0x03000000 + PyImport_AppendInittab("emb", PyInit_emb); +#endif + + Py_Initialize(); + numargs = argc; + +#if PY_VERSION_HEX < 0x03000000 + Py_InitModule("emb", EmbMethods); +#endif + + PyRun_SimpleString("import emb; print('Number of arguments', emb.numargs())"); + Py_Finalize(); + return 0; +} diff --git a/demos/python/wscript b/demos/python/wscript new file mode 100644 index 00000000..870e6209 --- /dev/null +++ b/demos/python/wscript @@ -0,0 +1,44 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Gustavo Carneiro, 2007 + +import sys + +VERSION='0.0.1' +APPNAME='python_test' + +top = '.' +out = 'build' + +def options(opt): + opt.load('python') # options for disabling pyc or pyo compilation + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + conf.load('python') + conf.check_python_version((2,4,2)) + conf.check_python_headers() + + try: + conf.check_python_module('pygccxml') + except conf.errors.ConfigurationError: + print('could not find pygccxml (ignored)') + +def build(bld): + + # first compile a few pyc and pyo files + bld(features='py', source=bld.path.ant_glob('*.py'), install_from='.') + + # then a c extension module + bld( + features = 'c cshlib pyext', + source = 'spammodule.c', + target = 'spam') + + # then a c program + bld( + features = 'c cprogram pyembed', + source = 'test.c', + target = 'test') + diff --git a/demos/qt4/but.ui b/demos/qt4/but.ui new file mode 100644 index 00000000..40c8c4f2 --- /dev/null +++ b/demos/qt4/but.ui @@ -0,0 +1,32 @@ + + + Form + + + + 0 + 0 + 208 + 113 + + + + Form + + + + + 40 + 30 + 83 + 26 + + + + Hello, world! + + + + + + diff --git a/demos/qt4/data/some.txt b/demos/qt4/data/some.txt new file mode 100644 index 00000000..3c58ea34 --- /dev/null +++ b/demos/qt4/data/some.txt @@ -0,0 +1 @@ +tada tadam tadadam diff --git a/demos/qt4/foo.cpp b/demos/qt4/foo.cpp new file mode 100644 index 00000000..45075687 --- /dev/null +++ b/demos/qt4/foo.cpp @@ -0,0 +1,11 @@ +// Thomas Nagy, 2011 + +#include "foo.h" + +Foo::Foo() : QWidget(NULL) { + +} + +#if WAF +#include "foo.moc" +#endif diff --git a/demos/qt4/foo.h b/demos/qt4/foo.h new file mode 100644 index 00000000..1c54ef61 --- /dev/null +++ b/demos/qt4/foo.h @@ -0,0 +1,16 @@ +// Thomas Nagy, 2011 + +#ifndef _FOO +#define _FOO + +#include + +class Foo : public QWidget { + Q_OBJECT + signals: + void test(); + public: + Foo(); +}; + +#endif diff --git a/demos/qt4/linguist/fr.ts b/demos/qt4/linguist/fr.ts new file mode 100644 index 00000000..415c174a --- /dev/null +++ b/demos/qt4/linguist/fr.ts @@ -0,0 +1,4 @@ + + + + diff --git a/demos/qt4/main.cpp b/demos/qt4/main.cpp new file mode 100644 index 00000000..4708f63c --- /dev/null +++ b/demos/qt4/main.cpp @@ -0,0 +1,14 @@ +// Thomas Nagy, 2011 + +#include + +#include "ui_but.h" + +int main(int argc, char *argv[]) +{ + QApplication app(argc, argv); + QWidget window; + Ui::Form ui; + ui.setupUi(&window); + return app.exec(); +} diff --git a/demos/qt4/textures.qrc b/demos/qt4/textures.qrc new file mode 100644 index 00000000..dfde5e12 --- /dev/null +++ b/demos/qt4/textures.qrc @@ -0,0 +1,5 @@ + + + data/some.txt + + diff --git a/demos/qt4/wscript b/demos/qt4/wscript new file mode 100644 index 00000000..cd777002 --- /dev/null +++ b/demos/qt4/wscript @@ -0,0 +1,49 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005, 2011 (ita) + +""" +Including the moc files *is* the best practice (KDE), not doing it is easy, +but makes the compilations about 30-40% slower on average. + +If you still want the slow version (we warned you!), see the example located +in the folder playground/slow_qt/ +""" + +VERSION='0.0.1' +APPNAME='qt4_test' + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_cxx qt4') + +def configure(conf): + conf.load('compiler_cxx qt4') + conf.env.append_value('CXXFLAGS', ['-g']) # test + +def build(bld): + bld( + features = 'qt4 cxx cxxprogram', + uselib = 'QTCORE QTGUI QTOPENGL QTSVG', + source = 'main.cpp textures.qrc but.ui foo.cpp', + target = 'window', + includes = '.', + defines = 'WAF=1', # test + lang = bld.path.ant_glob('linguist/*.ts'), + langname = 'somefile', # include the .qm files from somefile.qrc + ) + +# use the following if you want to add the include paths automatically +""" +from waflib.TaskGen import feature, before, after +@feature('cxx') +@after('process_source') +@before('apply_incpaths') +def add_includes_paths(self): + incs = set(self.to_list(getattr(self, 'includes', ''))) + for x in self.compiled_tasks: + incs.add(x.inputs[0].parent.path_from(self.path)) + self.includes = list(incs) +""" diff --git a/demos/ruby/Mytest.rb b/demos/ruby/Mytest.rb new file mode 100644 index 00000000..1ee8d62f --- /dev/null +++ b/demos/ruby/Mytest.rb @@ -0,0 +1,9 @@ + +require 'mytest_ext' + +module Mytest + def hello + puts MytestInt::hello() + end + module_function :hello +end diff --git a/demos/ruby/hello_world.rb b/demos/ruby/hello_world.rb new file mode 100644 index 00000000..c7ed64ad --- /dev/null +++ b/demos/ruby/hello_world.rb @@ -0,0 +1,2 @@ +puts 'hello world' +#require 'hello2' diff --git a/demos/ruby/rb_mytest.c b/demos/ruby/rb_mytest.c new file mode 100644 index 00000000..af34431f --- /dev/null +++ b/demos/ruby/rb_mytest.c @@ -0,0 +1,16 @@ + +#include + +static VALUE +m_hello (VALUE self) +{ + return rb_str_new2("Hello World"); +} + +void +Init_mytest_ext (void) +{ + VALUE mTest = rb_define_module ("MytestInt"); + + rb_define_module_function (mTest, "hello", m_hello, 0); +} diff --git a/demos/ruby/wscript b/demos/ruby/wscript new file mode 100644 index 00000000..0b1d2c42 --- /dev/null +++ b/demos/ruby/wscript @@ -0,0 +1,47 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Frauendorfer, 2010 + +VERSION='0.0.1' +APPNAME='ruby_test' + +# these variables are mandatory ('/' are converted automatically) +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_c') + opt.load('ruby') + +def configure(conf): + conf.load('compiler_c') + conf.load('ruby') + + # check for ruby + conf.check_ruby_version((1,8,0)) + conf.check_ruby_ext_devel() + conf.check_ruby_module('libxml', mandatory=False) + +def build(bld): + + # Build a ruby extension module + bld( + features = 'c cshlib rubyext', + source = 'rb_mytest.c', + target = 'mytest_ext', + install_path = '${ARCHDIR_RUBY}') + + bld.install_files('${LIBDIR_RUBY}', 'Mytest.rb') + + if bld.cmd == 'runit': + def foo(bld): + bld.exec_command(bld.env.RUBY + ' -I' + bld.get_variant_dir() + ' -rMytest -e "Mytest.hello()"') + bld.add_post_fun(foo) + + # or, another way + bld(source='hello_world.rb') + +from waflib.Build import BuildContext +class one(BuildContext): + cmd = 'runit' + diff --git a/demos/subst/foo.in b/demos/subst/foo.in new file mode 100644 index 00000000..ca332d31 --- /dev/null +++ b/demos/subst/foo.in @@ -0,0 +1,2 @@ +prefix: @PREFIX@ +bindir: @BINDIR@ diff --git a/demos/subst/test.pc.in b/demos/subst/test.pc.in new file mode 100644 index 00000000..66818564 --- /dev/null +++ b/demos/subst/test.pc.in @@ -0,0 +1,5 @@ +Name: test +Description: bork bork bork! +Version: @VERSION@ +Libs: -L${libdir} -lgnaa @LIBS@ @XPM_LIBS@ @LIBICONV@ +Cflags: -I${includedir} @XPM_CFLAGS@ diff --git a/demos/subst/wscript b/demos/subst/wscript new file mode 100644 index 00000000..739a22a1 --- /dev/null +++ b/demos/subst/wscript @@ -0,0 +1,32 @@ +#! /usr/bin/env python +# encoding: utf-8 + +top = '.' +out = 'bin' + +from waflib import Utils + +def configure(conf): + pass + +def build(bld): + + # the test.pc.in is a special case which is always handled + bld(source='test.pc.in', VERSION='1.1', LIBS='moo', XPM_LIBS='-lxpm', LIBICONV='-liconv', XPM_CFLAGS='-O3') + + tg = bld( + features = 'subst', # the feature 'subst' overrides the source/target processing + source = 'foo.in', # list of string or nodes + target = 'foo.txt', # list of strings or nodes + install_path = '/tmp/uff/', # installation path, optional + chmod = Utils.O755, # installation mode, optional + PREFIX = bld.env.PREFIX, # variables to use in the substitution + BINDIR = bld.env.BINDIR) + + # if you are using an external dict, here is to merge the key/values: + dct = {'BINDIR': '/opt'} + tg.__dict__.update(dct) + + # this one is just a reminder that simple files can be created (and a test too) + #bld(rule='echo "การไฟ่" > ${TGT}', target='foo.txt') + diff --git a/demos/swig/embed/bind.i b/demos/swig/embed/bind.i new file mode 100644 index 00000000..a6cc87c9 --- /dev/null +++ b/demos/swig/embed/bind.i @@ -0,0 +1,4 @@ +%{ +#include "src1.h" +%} +%include "src1.h" diff --git a/demos/swig/embed/bind.swig b/demos/swig/embed/bind.swig new file mode 100644 index 00000000..a6d1a3db --- /dev/null +++ b/demos/swig/embed/bind.swig @@ -0,0 +1,4 @@ +%module swigdemo +%include "std_string.i" +%include "bind.i" + diff --git a/demos/swig/embed/src1.cpp b/demos/swig/embed/src1.cpp new file mode 100644 index 00000000..25f84820 --- /dev/null +++ b/demos/swig/embed/src1.cpp @@ -0,0 +1,24 @@ +#include "Python.h" +#include "src1.h" + +extern "C" +{ + void init_swigdemo(void); +} + +TestClass* TestClass::_instance = 0; + +int main() +{ + Py_Initialize(); + init_swigdemo(); + + /*FILE* file_py; + file_py = fopen(i_oFile.toLocal8Bit(), "r"); + PyRun_SimpleFile(file_py, i_oFile.toLocal8Bit()); + fclose(file_py); + */ + PyRun_SimpleString("import swigdemo, sys\nsys.stderr.write(str(swigdemo.TestClass.instance().test()))"); + Py_Finalize(); +} + diff --git a/demos/swig/embed/src1.h b/demos/swig/embed/src1.h new file mode 100644 index 00000000..e5e89c44 --- /dev/null +++ b/demos/swig/embed/src1.h @@ -0,0 +1,30 @@ +#ifndef SWIGTOOLDEMO_HPP +#define SWIGTOOLDEMO_HPP + +// singleton shared between test app and python +// (Note: this is a demo, remember singletons should not be used) +class TestClass +{ + public: + static TestClass* instance() + { + if (_instance == 0) + _instance = new TestClass(); + return _instance; + } + + void destroy () { delete _instance; _instance = 0; } + + protected: + TestClass() {}; + ~TestClass(){}; + + public: + const char* test() { return "Hello World from C++\n"; } + + private: + static TestClass* _instance; +}; + +#endif //SWIGTOOLDEMO_HPP + diff --git a/demos/swig/extend/a.cpp b/demos/swig/extend/a.cpp new file mode 100644 index 00000000..ad13450f --- /dev/null +++ b/demos/swig/extend/a.cpp @@ -0,0 +1,10 @@ +#include "a.h" + +A::A( ) { } + +A::~A( ) { } + +int A::add(int a, int b) { + return a + b; +} + diff --git a/demos/swig/extend/a.h b/demos/swig/extend/a.h new file mode 100644 index 00000000..efbdacfa --- /dev/null +++ b/demos/swig/extend/a.h @@ -0,0 +1,13 @@ +#ifndef A_H +#define A_H + +class A { + public: + A(); + ~A(); + + int add(int a, int b); +}; + +#endif /* A_H */ + diff --git a/demos/swig/extend/java/Foo.java b/demos/swig/extend/java/Foo.java new file mode 100644 index 00000000..be0ec5cc --- /dev/null +++ b/demos/swig/extend/java/Foo.java @@ -0,0 +1,14 @@ +package foo.bar.pouet; + + +class Foo { + public Foo() { + // TODO how to test the library from java? + } + + public static void main(String[] args) { + System.loadLibrary("_test_swig_waf"); + A test = new A(); + System.out.println(test.add(17, 28)); + } +} diff --git a/demos/swig/extend/java/test_swig_waf.i b/demos/swig/extend/java/test_swig_waf.i new file mode 100644 index 00000000..8a4291e7 --- /dev/null +++ b/demos/swig/extend/java/test_swig_waf.i @@ -0,0 +1,8 @@ +%{ +#include "a.h" +%} + + +%include "a.h" + +%module test_swig_waf diff --git a/demos/swig/extend/python/test_swig_waf.i b/demos/swig/extend/python/test_swig_waf.i new file mode 100644 index 00000000..8a4291e7 --- /dev/null +++ b/demos/swig/extend/python/test_swig_waf.i @@ -0,0 +1,8 @@ +%{ +#include "a.h" +%} + + +%include "a.h" + +%module test_swig_waf diff --git a/demos/swig/wscript b/demos/swig/wscript new file mode 100644 index 00000000..d42d9ff8 --- /dev/null +++ b/demos/swig/wscript @@ -0,0 +1,187 @@ +#! /usr/bin/env python +# encoding: UTF-8 +# Peter Forai +# Thomas Nagy, 2008 + +""" +Demonstrates how to create a c++ app that runs python scripts + +Useful for apps providing script extensions +""" + +VERSION='0.0.1' +APPNAME='swig_test' +top = '.' +out = 'build' + +def options(opt): + opt.load('g++ python') + +def configure(conf): + conf.load('g++ python') + conf.check_python_version((2,4,2)) + conf.check_python_headers() + + conf.load('swig') + if conf.check_swig_version() < (1, 2, 27): + conf.fatal('this swig version is too old') + + try: + conf.load('java') + # on mandriva, at least, libjvm.so is difficult to find + #conf.env.LIBPATH_JAVA = "/usr/lib/jvm/java-1.6.0-sun-1.6.0.13/jre/lib/amd64/server/" + conf.check_jni_headers() + conf.env.HAVE_JAVA = True + except conf.errors.ConfigurationError: + conf.env.HAVE_JAVA = False + +def build(bld): + + # embedding + # + # use swig_flags = '-c++ -python -dump_classes' for debugging + + obj = bld( + features = 'cxx cxxprogram pyembed', + source = 'embed/src1.cpp embed/bind.swig', + target = 'embed/embed_demo', + swig_flags = '-c++ -python -Wall', + includes = '. embed') + + + # extending + # + # be careful that the .py produced by swig is mandatory for using the library + # + # it is possible to disable 'mylib', and to add extend/a.cpp + # to the source of extend/python/_test_swig_waf and remove use + + bld( + features = 'cxx cxxshlib', + source = 'extend/a.cpp', + target = 'extend/mylib', + includes = 'extend', + export_includes = 'extend', + vnum = '1.2.3', + name = 'mylib') + + bld( + features = 'cxx cxxshlib pyext', + source = 'extend/python/test_swig_waf.i', + target = 'extend/python/_test_swig_waf', + swig_flags = '-c++ -python -Wall', + includes = 'extend', + vnum = '1.2.3', + use = 'mylib') + + bld.install_files('${PREFIX}/lib', 'extend/python/test_swig_waf.py') + + bld.add_post_fun(exec_test_python) + + # some java stuff + if not bld.env.HAVE_JAVA: + return + + from waflib.extras import swig + + srcdir = bld.path.get_bld().make_node('extend/java/hmm') # destination for generated java file (without the packages!) + + #""" # BEGIN BLOCK 1 + d = bld.path.make_node('extend/java') + javanodes = [d.find_or_declare(x) for x in 'A.java test_swig_waf.java test_swig_wafJNI.java'.split()] + dec = bld.tools['swig'].swigf + #@dec <- python 2.3 does not support the @decorator notation + def swig_java(tsk): + tsk.outputs.extend(javanodes) + bld.tools['swig'].swigf(swig_java) + """ # END BLOCK 1 + #"""# do not remove + + + + + + bld( + features = 'cxx cxxshlib', + source = 'extend/java/test_swig_waf.i', + target = 'extend/java/_test_swig_waf', + swig_flags = '-c++ -java -package foo.bar.pouet', + includes = 'extend', + vnum = '1.2.3', + uselib = 'JAVA', + use = 'mylib') + + #""" # BEGIN BLOCK 2 + for x in javanodes: + bld(rule='cp ${SRC} ${TGT}', source=x, + target=srcdir.make_node('foo/bar/pouet/' + x.name), before=['javac'], after=['swig']) + """ # END BLOCK 2 + + def move_java_files(task): + import os, shutil + from waflib import Utils + + node = srcdir.make_node('foo/bar/pouet/') + node.mkdir() + orig = task.inputs[0].parent.get_bld().abspath() + files = Utils.listdir(orig) + for x in files: + if x.endswith('.java'): + # create a node in the directory we want to + j = node.make_node(x) # create a node + shutil.copy2(orig + os.sep + x, j.abspath()) # create the physical file for the node + j.sig = Utils.h_file(j.abspath()) # update the node signature + # depend on the .i file to make sure the .java files are copied after swig is executed + bld(name='move_and_read', rule=move_java_files, source='extend/java/test_swig_waf.i', after=['swig'], before=['javac']) + #""" + + + bld(rule='cp ${SRC} ${TGT}', source=bld.path.find_resource('extend/java/Foo.java'), + target=srcdir.make_node('foo/bar/pouet/Foo.java'), before=['javac'], after=['swig']) + + tmp = bld.path.get_bld().make_node('maha') + + bld(features = 'javac jar', + srcdir = srcdir, + sourcepath = [], + outdir = tmp, # we do need another folder here + basedir = tmp, + destfile = 'maha.jar' + ) + + bld.add_post_fun(exec_test_java) + + ######################################### + # listing the java nodes is required to ensure the swig task + # is executed whenever the java files are removed from + # the build directory + # + # to list the java files automatically, comment the starting character '#' in the lines "BEGIN BLOCK 1" and "BEGIN BLOCK 2" + + +def exec_test_java(bld): + try: + bld.cmd_and_log('LD_LIBRARY_PATH=$LD_LIBRARY_PATH:build/extend/java:build/extend java -classpath "build/maha.jar:." foo.bar.pouet.Foo') + except: + pass + +def exec_test_python(bld): + import os, stat + try: + import subprocess + proc = subprocess.Popen(''' +PYTHONPATH=$PYTHONPATH:build/extend/python +LD_LIBRARY_PATH=$LD_LIBRARY_PATH:build/extend/python:build/extend +python -c "import test_swig_waf; a=test_swig_waf.A(); print 'Testing: a.add(2, 3) ->', a.add(2, 3)" +'''.replace('\n', ' '), shell=True) + proc.wait() + except: + pass + + # why does this fail now on mandriva??? + try: + os.stat('build/embed/embed_demo') + bld.cmd_and_log('PYTHONPATH=$PYTHONPATH:build/embed/ build/embed/embed_demo') + except: + pass + diff --git a/demos/tex/src/bib.bib b/demos/tex/src/bib.bib new file mode 100644 index 00000000..cee5ec4c --- /dev/null +++ b/demos/tex/src/bib.bib @@ -0,0 +1,7 @@ +@Book{WinderRoberts2007, + author = "Russel Winder and Graham Roberts", + title = "Developing Java Software", + publisher = "Wiley", + year = 2007, + edition = "third" +} diff --git a/demos/tex/src/bibtex.ltx b/demos/tex/src/bibtex.ltx new file mode 100644 index 00000000..124af1ba --- /dev/null +++ b/demos/tex/src/bibtex.ltx @@ -0,0 +1,3 @@ +\section{Testing \BibTeX} + +\citet{WinderRoberts2007} is a book for learning to programming using the Java programming language. diff --git a/demos/tex/src/conclusions.ltx b/demos/tex/src/conclusions.ltx new file mode 100644 index 00000000..119cdaa0 --- /dev/null +++ b/demos/tex/src/conclusions.ltx @@ -0,0 +1,6 @@ +\section{Conclusions} + +Hopefully, the text will create a sensible PostScript or \acronym{PDF} document. That choice is +made in the wscript file in this directory. It perhas needs making into a command line option. +Also there should be the possiblity of making both PostScript and \acronym{PDF} on the same run +-- at least if generating \acronym{PDF} from \acronym{DVI}. diff --git a/demos/tex/src/crossreferencing.ltx b/demos/tex/src/crossreferencing.ltx new file mode 100644 index 00000000..9b0e814e --- /dev/null +++ b/demos/tex/src/crossreferencing.ltx @@ -0,0 +1,4 @@ +\section{Testing Cross-referencing} +\label{sec:xref} + +This \LaTeX\ cross-reference to page~\pageref{pg:first-page} should result in a reference to page~2. diff --git a/demos/tex/src/document.ltx b/demos/tex/src/document.ltx new file mode 100644 index 00000000..0ca59669 --- /dev/null +++ b/demos/tex/src/document.ltx @@ -0,0 +1,42 @@ +\documentclass[a4paper,12pt]{article} + +\usepackage{mathptmx} +\usepackage{natbib} +\usepackage{xspace} +\usepackage{makeidx} + +\newcommand \Waf{\textbf{\emph{Waf}}\xspace} +\newcommand \BibTeX{Bib\TeX\xspace} +\newcommand \acronym[1]{{\small #1}} + +\newcommand \bringin[1]{\clearpage\input{#1.ltx}} + +\title{A Test Document For Waf Construction} +\author{Russel Winder} + +\makeindex + +\begin{document} + +\bibliographystyle{apalike} + +\maketitle + +\vfil + +\tableofcontents + +\bringin{introduction} +\bringin{indexing} +\bringin{bibtex} +\bringin{crossreferencing} +\bringin{conclusions} +%\bringin{conclusions2} % make it fail on purpose to test the batch mode + +\clearpage + +\bibliography{bib} + +\printindex + +\end{document} diff --git a/demos/tex/src/indexing.ltx b/demos/tex/src/indexing.ltx new file mode 100644 index 00000000..a88a323e --- /dev/null +++ b/demos/tex/src/indexing.ltx @@ -0,0 +1,3 @@ +\section{Testing Indexing} + +We have already had an index entry but here is another so there are two\index{indexing}. diff --git a/demos/tex/src/introduction.ltx b/demos/tex/src/introduction.ltx new file mode 100644 index 00000000..d2bdd2bb --- /dev/null +++ b/demos/tex/src/introduction.ltx @@ -0,0 +1,13 @@ +\section{Introduction} + +This document is a null content document that uses as many features of the +\LaTeX\index{LaTeX@\LaTeX} document processing system as possible. It is not a test of \LaTeX\ +typesetting so the content is not sophisticated, it is for testing the build capabilities of +\Waf. So we use use index features to drive the use of makeindex and \BibTeX features so that +the \BibTeX system has to run. + +This document is a grauitously multi-page document so that +cross-referencing\label{pg:first-page} can be tested, this will happen in Section~\ref{sec:xref} +on page~\pageref{sec:xref}. There ought to be a reference to Section~4 on page~5. + +\bringin{introsub} diff --git a/demos/tex/src/introsub.ltx b/demos/tex/src/introsub.ltx new file mode 100644 index 00000000..96fb2b99 --- /dev/null +++ b/demos/tex/src/introsub.ltx @@ -0,0 +1,8 @@ +\subsection{Introduction subsection} + +This is a simple subsection to show why the scan of source documents should be +recursive. In the current version of \texttt{waf} any changes made to this +file will not cause a regeneration of the full document, and it ideally +should. + +This is a modification diff --git a/demos/tex/src/wscript b/demos/tex/src/wscript new file mode 100644 index 00000000..b2b3a727 --- /dev/null +++ b/demos/tex/src/wscript @@ -0,0 +1,22 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +def build(bld): + + # we can modify the way tex files are scanned + #import re, tex + #tex.tex_regexp = re.compile('^\\\\bringin{(?P.*)}', re.M) + + # now create your tex object for compiling the files + obj = bld( + features = 'tex', + type='latex', # default is pdflatex + source = 'document.ltx' # mandatory, the source + ) + + # optional parameters + obj.outs = 'ps' # we want a postscript output too - 'ps pdf' works too + obj.prompt = 1 # put 0 for the batchmode (conceals the debug output) + obj.deps = 'crossreferencing.ltx' # use this to give dependencies directly + diff --git a/demos/tex/wscript b/demos/tex/wscript new file mode 100644 index 00000000..6dca1e56 --- /dev/null +++ b/demos/tex/wscript @@ -0,0 +1,18 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005, 2010 (ita) + +VERSION='1.0.0' +APPNAME='tex_test' + +top = '.' +out = 'build' + +def configure(conf): + conf.load('tex') + if not conf.env.PDFLATEX: + conf.fatal('could not find the program pdflatex') + +def build(bld): + bld.recurse('src') + diff --git a/demos/unit_test/src/Accumulator.cpp b/demos/unit_test/src/Accumulator.cpp new file mode 100644 index 00000000..ee84ce3b --- /dev/null +++ b/demos/unit_test/src/Accumulator.cpp @@ -0,0 +1,16 @@ +#include +#include "Accumulator.h" + +Accumulator::Accumulator():m_total(0) +{ +} + +void Accumulator::accumulate(const char * data) +{ + m_total += strtol(data, 0, 0); +} + +int Accumulator::total() const +{ + return m_total; +} diff --git a/demos/unit_test/src/Accumulator.h b/demos/unit_test/src/Accumulator.h new file mode 100644 index 00000000..86c64c5c --- /dev/null +++ b/demos/unit_test/src/Accumulator.h @@ -0,0 +1,23 @@ +#ifndef Accumulator_h_seen +#define Accumulator_h_seen + +/** + * A not-very-useful class that accumulates int values from input data. + */ +class Accumulator +{ + public: + Accumulator(); + + /** + * Given some input data, read to end of line and convert to an int + */ + void accumulate(const char * data); + + int total() const; + + private: + int m_total; + +}; +#endif diff --git a/demos/unit_test/src/HelloWorld.cpp b/demos/unit_test/src/HelloWorld.cpp new file mode 100644 index 00000000..e3bbfde4 --- /dev/null +++ b/demos/unit_test/src/HelloWorld.cpp @@ -0,0 +1,18 @@ + +#include "HelloWorld.h" +HelloWorld::HelloWorld():m_message("Hello World") +{ +} + +HelloWorld::HelloWorld(const std::string & msg):m_message(msg) +{} + +std::string HelloWorld::message() const +{ + return m_message; +} + +void HelloWorld::setMessage(const std::string & msg) +{ + m_message = msg; +} diff --git a/demos/unit_test/src/HelloWorld.h b/demos/unit_test/src/HelloWorld.h new file mode 100644 index 00000000..57737b47 --- /dev/null +++ b/demos/unit_test/src/HelloWorld.h @@ -0,0 +1,15 @@ +#ifndef HelloWorld_h_seen +#define HelloWorld_h_seen +#include +class HelloWorld +{ + public: + HelloWorld(); + HelloWorld(const std::string & msg); + + std::string message() const; + void setMessage(const std::string & msg); + private: + std::string m_message; +}; +#endif diff --git a/demos/unit_test/src/wscript_build b/demos/unit_test/src/wscript_build new file mode 100644 index 00000000..e003b921 --- /dev/null +++ b/demos/unit_test/src/wscript_build @@ -0,0 +1,10 @@ +#! /usr/bin/env python +# encoding: utf-8 + +bld( + features = 'cxx cxxstlib', + source = 'Accumulator.cpp HelloWorld.cpp', + target = 'useless', + export_includes = '.', +) + diff --git a/demos/unit_test/tests/main.cpp b/demos/unit_test/tests/main.cpp new file mode 100644 index 00000000..f3036897 --- /dev/null +++ b/demos/unit_test/tests/main.cpp @@ -0,0 +1,22 @@ +#include +#include +#include + +int main(int argc, char* argv[]) +{ + // Get the top level suite from the registry + CPPUNIT_NS::Test *suite = CPPUNIT_NS::TestFactoryRegistry::getRegistry().makeTest(); + + // Adds the test to the list of test to run + CPPUNIT_NS::TextTestRunner runner; + runner.addTest( suite ); + + // Change the default outputter to a compiler error format outputter + runner.setOutputter( new CPPUNIT_NS::CompilerOutputter( &runner.result(), std::cout ) ); + // Run the test. + bool wasSucessful = runner.run(); + + // Return error code 1 if the one of test failed. + return wasSucessful ? 0 : 1; +} + diff --git a/demos/unit_test/tests/test0/HelloWorldTest.cpp b/demos/unit_test/tests/test0/HelloWorldTest.cpp new file mode 100644 index 00000000..f5908f2e --- /dev/null +++ b/demos/unit_test/tests/test0/HelloWorldTest.cpp @@ -0,0 +1,40 @@ +#include +#include "HelloWorld.h" +#include + +using namespace std; + +class HelloWorldTest : public CPPUNIT_NS::TestFixture +{ + private: + CPPUNIT_TEST_SUITE( HelloWorldTest ); + CPPUNIT_TEST( test0 ); + CPPUNIT_TEST( test1 ); + CPPUNIT_TEST_SUITE_END(); + + public: + void test0(); + void test1(); + +}; + +// Registers the fixture into the 'registry' +CPPUNIT_TEST_SUITE_REGISTRATION( HelloWorldTest ); + +void HelloWorldTest::test0() +{ + HelloWorld hello; + string expected("Hello World"); + CPPUNIT_ASSERT_EQUAL(expected, hello.message()); +} + +void HelloWorldTest::test1() +{ + string expected("Hola Mundo"); + HelloWorld hello(expected); + CPPUNIT_ASSERT_EQUAL(expected, hello.message()); + expected = "Hello, world!"; + hello.setMessage(expected); + CPPUNIT_ASSERT_EQUAL(expected, hello.message()); + +} diff --git a/demos/unit_test/tests/test0/wscript_build b/demos/unit_test/tests/test0/wscript_build new file mode 100644 index 00000000..33b8d3d9 --- /dev/null +++ b/demos/unit_test/tests/test0/wscript_build @@ -0,0 +1,10 @@ +#! /usr/bin/env python +# encoding: utf-8 + +bld( + features = 'cxx cxxprogram test', + source = 'HelloWorldTest.cpp', + target = 'unit_test_program', + use = 'unittestmain useless CPPUNIT', +) + diff --git a/demos/unit_test/tests/test1/AccumulatorTest.cpp b/demos/unit_test/tests/test1/AccumulatorTest.cpp new file mode 100644 index 00000000..07694b26 --- /dev/null +++ b/demos/unit_test/tests/test1/AccumulatorTest.cpp @@ -0,0 +1,85 @@ +#include +#include "Accumulator.h" +#include +#include +#include + +using namespace std; + +class AccumulatorTest : public CPPUNIT_NS::TestFixture +{ + private: + CPPUNIT_TEST_SUITE( AccumulatorTest ); + CPPUNIT_TEST( test0 ); + CPPUNIT_TEST( test1 ); + CPPUNIT_TEST_SUITE_END(); + + public: + void test0(); + void test1(); + + void setUp(); + void tearDown(); + + private: + Accumulator * m_accumulator; + +}; + +// Registers the fixture into the 'registry' +CPPUNIT_TEST_SUITE_REGISTRATION( AccumulatorTest ); + +void AccumulatorTest::setUp() +{ + m_accumulator = new Accumulator; +} + +void AccumulatorTest::tearDown() +{ + delete m_accumulator; +} + +static void readlines(const char * filename, vector & lines) +{ + string datafile("input"); + datafile += "/"; + datafile += filename; + ifstream infile; + infile.open(datafile.c_str()); + if (infile.is_open()) + { + char buffer[BUFSIZ]; + while (!infile.eof()) + { + infile.getline(buffer, BUFSIZ); + lines.push_back(buffer); + } + } +} + +void AccumulatorTest::test0() +{ + vector lines; + readlines("test0.txt", lines); + size_t expected(2); + CPPUNIT_ASSERT_EQUAL(expected, lines.size()); + m_accumulator->accumulate(lines[0].c_str()); + CPPUNIT_ASSERT_EQUAL(10, m_accumulator->total()); + +} + +void AccumulatorTest::test1() +{ + vector lines; + readlines("test1.txt", lines); + size_t expected(6); + CPPUNIT_ASSERT_EQUAL(expected, lines.size()); + for (vector::const_iterator it(lines.begin()); + it != lines.end(); ++it) + { + const string & line(*it); + m_accumulator->accumulate(line.c_str()); + } + CPPUNIT_ASSERT_EQUAL(1+2+3+4+5, m_accumulator->total()); + +} diff --git a/demos/unit_test/tests/test1/input/test0.txt b/demos/unit_test/tests/test1/input/test0.txt new file mode 100644 index 00000000..f599e28b --- /dev/null +++ b/demos/unit_test/tests/test1/input/test0.txt @@ -0,0 +1 @@ +10 diff --git a/demos/unit_test/tests/test1/input/test1.txt b/demos/unit_test/tests/test1/input/test1.txt new file mode 100644 index 00000000..8a1218a1 --- /dev/null +++ b/demos/unit_test/tests/test1/input/test1.txt @@ -0,0 +1,5 @@ +1 +2 +3 +4 +5 diff --git a/demos/unit_test/tests/test1/wscript_build b/demos/unit_test/tests/test1/wscript_build new file mode 100644 index 00000000..e3ffd328 --- /dev/null +++ b/demos/unit_test/tests/test1/wscript_build @@ -0,0 +1,17 @@ +#! /usr/bin/env python +# encoding: utf-8 + + +def fun(task): + #print task.generator.bld.name_to_obj('somelib').link_task.outputs[0].abspath(task.env) + task.ut_exec.append('--help') + +bld( + features = 'cxx cxxprogram test', + source = 'AccumulatorTest.cpp', + target = 'unit_test_program', + use = 'unittestmain useless CPPUNIT', + ut_cwd = bld.path.abspath(), + ut_fun = fun +) + diff --git a/demos/unit_test/tests/wscript b/demos/unit_test/tests/wscript new file mode 100644 index 00000000..947759c8 --- /dev/null +++ b/demos/unit_test/tests/wscript @@ -0,0 +1,12 @@ +#! /usr/bin/env python +# encoding: utf-8 + +def build(bld): + bld.recurse('test0 test1') + + obj = bld( + features = 'cxx cxxstlib', + source = 'main.cpp', + use = 'CPPUNIT', + target = 'unittestmain') + diff --git a/demos/unit_test/wscript b/demos/unit_test/wscript new file mode 100644 index 00000000..477bd4e0 --- /dev/null +++ b/demos/unit_test/wscript @@ -0,0 +1,63 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Richard Quirk, 2008 + +""" +execute tests during the build +requires cppunit +""" + +from waflib.Tools import waf_unit_test + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_cxx') + opt.load('waf_unit_test') + opt.add_option('--onlytests', action='store_true', default=True, help='Exec unit tests only', dest='only_tests') + +def configure(conf): + conf.load('compiler_cxx') + conf.load('waf_unit_test') + + # the demo files require cppunit - but the waf tool does not + conf.check_cfg(package='cppunit', args='--cflags --libs', mandatory=True) + if 'dl' not in conf.env.LIB_CPPUNIT: + l = conf.check(lib='dl', uselib_store='CPPUNIT') + +from waflib import Logs +def summary(bld): + lst = getattr(bld, 'utest_results', []) + if lst: + total = len(lst) + tfail = len([x for x in lst if x[1]]) + + val = 100 * (total - tfail) / (1.0 * total) + Logs.pprint('CYAN', 'test report %3.0f%% success' % val) + + Logs.pprint('CYAN', ' tests that fail %d/%d' % (tfail, total)) + for (f, code, out, err) in lst: + if code: + Logs.pprint('CYAN', ' %s' % f) + Logs.pprint('RED', 'status: %r' % code) + if out: Logs.pprint('RED', 'out: %r' % out) + if err: Logs.pprint('RED', 'err: %r' % err) + +def build(bld): + bld.recurse('src tests') + + # unittestw.summary is a pretty ugly function for displaying a report (feel free to improve!) + # results -> bld.utest_results [(filename, returncode, stdout, stderr), (..., ), ...] + #bld.add_post_fun(waf_unit_test.summary) + bld.add_post_fun(summary) + + # to execute all tests: + # $ waf --alltests + # to set this behaviour permanenly: + bld.options.all_tests = True + + # debugging zone: + # $ waf --zones=ut + # setting the cwd for a unit test execution: see tests/test1/wscript_build (ut_cwd) + diff --git a/demos/vala/multi-file/bar/bar.vala b/demos/vala/multi-file/bar/bar.vala new file mode 100644 index 00000000..149319ac --- /dev/null +++ b/demos/vala/multi-file/bar/bar.vala @@ -0,0 +1,12 @@ +class Bar : Object +{ + public signal bool an_event(); + + public Bar(MainLoop loop) + { + var time = new TimeoutSource(8000); + time.set_callback(() => {return an_event();}); + time.attach(loop.get_context()); + } +} + diff --git a/demos/vala/multi-file/foo/foo.vala b/demos/vala/multi-file/foo/foo.vala new file mode 100644 index 00000000..f6395c86 --- /dev/null +++ b/demos/vala/multi-file/foo/foo.vala @@ -0,0 +1,14 @@ +using GLib; + +class Foo : Object +{ + public signal bool an_event(); + + public Foo(MainLoop loop) + { + var time = new TimeoutSource(5000); + time.set_callback(() => {return an_event();}); + time.attach(loop.get_context()); + } +} + diff --git a/demos/vala/multi-file/main.vala b/demos/vala/multi-file/main.vala new file mode 100644 index 00000000..af0a2fe9 --- /dev/null +++ b/demos/vala/multi-file/main.vala @@ -0,0 +1,14 @@ +int main(string[] args) +{ + var loop = new MainLoop(); + + var foo = new Foo(loop); + foo.an_event.connect(() => {stdout.printf("Foo"); stdout.flush(); return false;}); + + var bar = new Bar(loop); + bar.an_event.connect(() => {stdout.printf("Bar\n"); loop.quit(); return false;}); + + loop.run(); + + return 0; +} diff --git a/demos/vala/multi-file/wscript_build b/demos/vala/multi-file/wscript_build new file mode 100644 index 00000000..4596ee67 --- /dev/null +++ b/demos/vala/multi-file/wscript_build @@ -0,0 +1,10 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Joshua Simmons 2010 + +bld.program( + target = 'foobar', + uselib = 'GLIB', + source = ['foo/foo.vala', 'bar/bar.vala', 'main.vala'] + ) + diff --git a/demos/vala/shlib/hello.vala b/demos/vala/shlib/hello.vala new file mode 100644 index 00000000..4d43d196 --- /dev/null +++ b/demos/vala/shlib/hello.vala @@ -0,0 +1,8 @@ +public class Hello : Object { + public Hello() { + } + public void say_it() { + stdout.printf("Hello world"); + } +} + diff --git a/demos/vala/shlib/wscript_build b/demos/vala/shlib/wscript_build new file mode 100644 index 00000000..3d6f7395 --- /dev/null +++ b/demos/vala/shlib/wscript_build @@ -0,0 +1,14 @@ +#! /usr/bin/env python + +bld.shlib( + #packages = 'gtk+-2.0', + features = 'c cshlib', + target = 'hello-world', + #uselib = 'GTK GLIB', + source = 'hello.vala', + gir = 'hello-1.0', + #gir_path = '/tmp', + #vapi_path = '/tmp', + pkg_name = 'hello' + ) + diff --git a/demos/vala/src/vala-gtk-example.vala b/demos/vala/src/vala-gtk-example.vala new file mode 100644 index 00000000..61dd66ca --- /dev/null +++ b/demos/vala/src/vala-gtk-example.vala @@ -0,0 +1,40 @@ +/* GTK+ Vala Sample Code */ +using GLib; +using Gtk; + +public void trace (string message) { + #if DEBUG + stdout.printf (message); + #endif +} + +public class Sample : Window { + construct { + title = "Sample Window"; + create_widgets (); + } + + public void create_widgets () { + destroy += Gtk.main_quit; + + var button = new Button.with_label ("Hello World"); + button.clicked += btn => { + title = btn.label; + }; + + add (button); + } + + static int main (string[] args) { + Gtk.init (ref args); + + trace ("testing vala conditional compilation\n"); + + var sample = new Sample (); + sample.show_all (); + + Gtk.main (); + return 0; + } +} + diff --git a/demos/vala/src/wscript_build b/demos/vala/src/wscript_build new file mode 100644 index 00000000..c88254a8 --- /dev/null +++ b/demos/vala/src/wscript_build @@ -0,0 +1,13 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Jaap Haitsma, 2008 +# Thomas Nagy, 2010 + +bld.program( + packages = 'gtk+-2.0', + target = 'vala-gtk-example', + uselib = 'GTK GLIB', + source = 'vala-gtk-example.vala', + vala_defines = ['DEBUG'] + ) + diff --git a/demos/vala/stlib/program.vala b/demos/vala/stlib/program.vala new file mode 100644 index 00000000..17284cc9 --- /dev/null +++ b/demos/vala/stlib/program.vala @@ -0,0 +1,4 @@ +public static void main () +{ + WafDemo.MyObj variable = new WafDemo.MyObj (); +} diff --git a/demos/vala/stlib/src/static_lib.vala b/demos/vala/stlib/src/static_lib.vala new file mode 100644 index 00000000..194ac95b --- /dev/null +++ b/demos/vala/stlib/src/static_lib.vala @@ -0,0 +1,6 @@ +namespace WafDemo +{ + public class MyObj : Object + { + } +} diff --git a/demos/vala/stlib/wscript_build b/demos/vala/stlib/wscript_build new file mode 100644 index 00000000..3b59b253 --- /dev/null +++ b/demos/vala/stlib/wscript_build @@ -0,0 +1,10 @@ +bld.stlib ( + target = 'statlic_lib', + source = ['src/static_lib.vala'], +) + +bld.program ( + target = 'program', + use = 'statlic_lib', + source = 'program.vala' +) diff --git a/demos/vala/wscript b/demos/vala/wscript new file mode 100644 index 00000000..43248460 --- /dev/null +++ b/demos/vala/wscript @@ -0,0 +1,24 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Jaap Haitsma, 2008 + +# the following two variables are used by the target "waf dist" +VERSION = '0.0.1' +APPNAME = 'vala-gtk-example' + +# these variables are mandatory ('/' are converted automatically) +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_c') + opt.load('vala') + +def configure(conf): + conf.load('compiler_c vala') + conf.check_cfg(package='glib-2.0', uselib_store='GLIB', atleast_version='2.10.0', mandatory=1, args='--cflags --libs') + conf.check_cfg(package='gtk+-2.0', uselib_store='GTK', atleast_version='2.10.0', mandatory=1, args='--cflags --libs') + +def build(bld): + bld.recurse('src shlib multi-file stlib') + diff --git a/demos/variants/main.c b/demos/variants/main.c new file mode 100644 index 00000000..72388780 --- /dev/null +++ b/demos/variants/main.c @@ -0,0 +1,8 @@ + +#include "config.h" + +#if A +int main() { + return 0; +} +#endif diff --git a/demos/variants/wscript b/demos/variants/wscript new file mode 100644 index 00000000..fb29c2e4 --- /dev/null +++ b/demos/variants/wscript @@ -0,0 +1,82 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +VERSION='0.0.1' +APPNAME='cc_test' + +top = '.' +out = 'build' + +""" +Variant system for waf 1.6 + +call for example +$ waf configure build_debug build_release clean_debug clean_release +""" + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + + conf.setenv('debug') + conf.load('compiler_c') + conf.define("A", 1) + conf.define("B", 1.1) + conf.define("C", "1.1e19", quote=False) + # the configuration file must be written in each variant + conf.write_config_header('debug/config.h', remove=False) + + conf.setenv('release', env=conf.env.derive()) # start with a copy instead of a new env + conf.env.CFLAGS = ['-O2'] + conf.options.prefix = '/opt' # warning: this changes the options globally + conf.load('compiler_c') + conf.define('E', 1) + conf.write_config_header('release/config.h') + +def build(bld): + + # cleaning from the top-level directory might remove + # the file 'config.h' from the variants, so we + # are forcing the use of *debug or *release commands + # + # the config set 'debug' is loaded automatically when the 'debug' variant is used + if not bld.variant: + bld.fatal('call "waf build_debug" or "waf build_release", and try "waf --help"') + + # the includes='.' add the build directory path to the command arguments + # (look at the -I flags by using waf -v) + bld.program(source='main.c', target='app', includes='.') + + # To use multiple compilers at once, either: + # + # * use a particular environment from the configuration: + # bld.env = bld.all_envs['debug'] + # bld.program(source='main.c', target='app2', includes='.') + # * add an 'env' parameter to a particular task generator: + # bld.program(..., env=bld.all_envs['release'].derive()) + +# ------ new declaration for variants ------- +# +# calling 'waf clean_debug debug' will build into another output directory +# note how "bld.variant" is used to detect the current variant +# + +from waflib.Build import BuildContext, CleanContext, \ + InstallContext, UninstallContext + +for x in 'debug release'.split(): + for y in (BuildContext, CleanContext, InstallContext, UninstallContext): + name = y.__name__.replace('Context','').lower() + class tmp(y): + cmd = name + '_' + x + variant = x + +def buildall(ctx): + import waflib.Options + for x in ['build_debug', 'build_release']: + waflib.Options.commands.insert(0, x) + + + diff --git a/demos/wscript b/demos/wscript new file mode 100644 index 00000000..2a8dd98a --- /dev/null +++ b/demos/wscript @@ -0,0 +1,136 @@ +#! /usr/bin/env python +# encoding: utf-8 +# J. Carretero, 2010 (zougloub) +# Thomas Nagy, 2010 (ita) + +""" +https://launchpad.net/subunit/ +""" + + + +import sys, os + +if "uname" in dir(os): machine = os.uname()[1] +elif sys.platform == "win32": machine = os.environ["COMPUTERNAME"] +else: raise Exception("Unknown platform, cannot get machine name") + +from waflib import Logs + +# python 2.3 tends to hang for whatever reason :-/ +PYTHONS = "2.4 2.5 2.6 2.7 3.0 3.1 3.2".split() + +def options(opt): + for d in opt.path.ant_glob('*', excl=['build', 'variants'], src=False, dir=True): + if d.name[0] == '.' or d.name == 'variants' or d.name == 'build': + continue + + try: + opt.recurse(d.name) + except: + pass + # one sub-project uses autoconfig, but i do not want it here + from waflib import Configure + Configure.autoconfig = False + +def configure(conf): + + #Logs.info('Running action configure') # build farm + try: + sub = conf.find_file('subprocess.py', ['/usr/lib64/python', '/usr/lib/python', '/usr/local/lib64/python', '/usr/local/lib/python']) + except: + sub = '' + + conf.exec_command('./waf-light configure build --zip-type=gz --tools=doxygen,fluid,ocaml,swig,compiler_fc,fc_config,fc,fc_scan,g95,ifort,gfortran,batched_cc,%s --prelude='' && /bin/cp waf demos/' % sub, cwd=conf.path.parent.abspath()) + node = conf.path.find_resource('waf') + if not node: + conf.fatal('could not find Waf') + #if conf.exec_command([node.abspath(), '--help'], shell=False, env={}, cwd=node.parent.abspath()): + # conf.fatal('the waf file cannot be executed') + conf.env.WAF = node.abspath() + + conf.in_msg += 1 + for d in conf.path.ant_glob('*', excl=['build', 'variants', 'precious'], src=False, dir=True): + if d.name[0] == '.': + continue + + try: + conf.recurse(d.name) + except Exception: + e = sys.exc_info()[1] + print(e) + try: + #print("-- BEGIN %s config.log --\n%s-- END %s config.log --" % (d.name, conf.path.find_node('%s/build/config.log' % d.name).read(), d.name)) + print(e.stdout, e.stderr) + except: + pass + else: + conf.env.append_value('CFG', [d.name]) + + # now remove the cache folders and re-create them + conf.cmd_and_log('rm -rf .waf*') + for x in PYTHONS: + try: + conf.find_program('python'+x, var=x) + # unpacking the waf directory concurrently can lead to a race condition, we'll need to take care of this (thanks, build farm!) + conf.cmd_and_log(conf.env[x] + " ./waf --version", env={}) + except: + pass + else: + conf.env.append_value('PYTHONS', x) + + Logs.info("executing the build for folders %r and with pythons %r" % (conf.env.CFG, conf.env.PYTHONS)) + Logs.info("contents of config.log:") + Logs.info(conf.path.find_node('build/config.log').read()) + +def build(bld): + print('Note: call "waf installcheck" (the default build does not do anything)') + +from waflib.Build import BuildContext +class abc(BuildContext): + cmd = "installcheck" + fun = "installcheck" + +def installcheck(bld): + bld.jobs = 2 + #if bld.cmd == 'build': + # Logs.info('Running action build') # build farm + + #print('testsuite: waflib') + def waf_cmd(self): + cmd = [self.env[self.generator.python], self.env.WAF, 'distclean', 'configure', 'build', 'clean', 'build', '-o', 'build' + self.generator.python] + cwd = self.generator.cwd + env = dict(os.environ) + env['WAFDIR'] = '' + env['WAFLOCK'] = '.lock-wscript' + self.generator.python # use a different build directory for each build + try: + bld.cmd_and_log(cmd, cwd=cwd, env=env, quiet=0, ) + except: + e = sys.exc_info()[1] + s = "testsuite: %s\ntestsuite-xfail: %s [ %s \n %s ]\n" % (self.generator.name, self.generator.name, e.stderr, e.stdout) + Logs.info(s) + else: + s = "testsuite: %s\ntestsuite-success: %s\n" % (self.generator.name, self.generator.name) + Logs.info(s) + + for x in bld.env.PYTHONS: + for dirname in bld.env.CFG: + bld(rule = waf_cmd, + cwd = dirname, + always = 1, + python = x, + name = '%s_%s' % (dirname, x)) + + #if bld.cmd == 'build': + # Logs.info('BUILD STATUS: 0\nACTION PASSED: build') # build farm + # Logs.info('Running action test') # build farm + #Logs.info('testsuite: abc') + #def end(bld): + # Logs.info('testsuite-success: abc') + # Logs.info('TEST STATUS: 0\nACTION FAILED: test') + #bld.add_post_fun(end) + #elif bld.cmd == 'install': + # Logs.info('Running action install') # build farm + # Logs.info('INSTALL STATUS: 0\nACTION PASSED: install') # build farm + + diff --git a/docs/book/advbuild.txt b/docs/book/advbuild.txt new file mode 100644 index 00000000..c654486c --- /dev/null +++ b/docs/book/advbuild.txt @@ -0,0 +1,399 @@ +== Advanced build definitions + +=== Custom commands + +==== Context inheritance + +An instance of the class _waflib.Context.Context_ is used by default for the custom commands. To provide a custom context object it is necessary to create a context subclass: + +// advbuild_subclass +[source,python] +--------------- +def configure(ctx): + print(type(ctx)) + +def foo(ctx): <1> + print(type(ctx)) + +def bar(ctx): + print(type(ctx)) + +from waflib.Context import Context + +class one(Context): + cmd = 'foo' <2> + +class two(Context): + cmd = 'tak' <3> + fun = 'bar' +--------------- + +<1> A custom command using the default context +<2> Bind a context class to the command _foo_ +<3> Declare a new command named _tak_, but set it to call the script function _bar_ + +The execution output will be: + +[source,shishell] +--------------- +$ waf configure foo bar tak +Setting top to : /tmp/advbuild_subclass +Setting out to : /tmp/advbuild_subclass/build + +'configure' finished successfully (0.008s) + +'foo' finished successfully (0.001s) + +'bar' finished successfully (0.001s) + +'tak' finished successfully (0.001s) +--------------- + +A typical application of custom context is subclassing the build context to use the configuration data loaded in *ctx.env*: + +[source,python] +--------------- +def configure(ctx): + ctx.env.FOO = 'some data' + +def build(ctx): + print('build command') + +def foo(ctx): + print(ctx.env.FOO) + +from waflib.Build import BuildContext +class one(BuildContext): + cmd = 'foo' + fun = 'foo' +--------------- + +The output will be the following: + +[source,shishell] +--------------- +$ waf configure foo +Setting top to : /tmp/advbuild_confdata +Setting out to : /tmp/advbuild_confdata/build +'configure' finished successfully (0.006s) +Waf: Entering directory `/disk/comp/waf/docs/book/examples/advbuild_confdata/build' +some data +Waf: Leaving directory `/disk/comp/waf/docs/book/examples/advbuild_confdata/build' +'foo' finished successfully (0.004s) +--------------- + +NOTE: The build commands are using this system: _waf install_ → _waflib.Build.InstallContext_, _waf step_ → _waflib.Build.StepContext_, etc + +==== Command composition + +To re-use existing commands that have incompatible context classes, insert them in the _command stack_: + +// advbuild_composition +[source,python] +--------------- +def configure(ctx): + pass + +def build(ctx): + pass + +def cleanbuild(ctx): + from waflib import Options + Options.commands = ['clean', 'build'] + Options.commands +--------------- + +This technique is useful for writing testcases. By executing 'waf test', the following script will configure a project, create source files in the source directory, build a program, modify the sources, and rebuild the program. In this case, the program must be rebuilt because a header (implicit dependency) has changed. + +[source,python] +--------------- +def options(ctx): + ctx.load('compiler_c') + +def configure(ctx): + ctx.load('compiler_c') + +def setup(ctx): + n = ctx.path.make_node('main.c') + n.write('#include "foo.h"\nint main() {return 0;}\n') + + global v + m = ctx.path.make_node('foo.h') + m.write('int k = %d;\n' % v) + v += 1 + +def build(ctx): + ctx.program(source='main.c', target='app') + +def test(ctx): + global v <1> + v = 12 + + import Options <2> + lst = ['configure', 'setup', 'build', 'setup', 'build'] + Options.commands = lst + Options.commands +--------------- + +<1> To share data between different commands, use a global variable +<2> The test command is used to add more commands + +The following output will be observed: + +[source,shishell] +--------------- +$ waf test +'test' finished successfully (0.000s) +Setting top to : /tmp/advbuild_testcase +Setting out to : /tmp/advbuild_testcase/build +Checking for 'gcc' (c compiler) : ok +'configure' finished successfully (0.092s) +'setup' finished successfully (0.001s) +Waf: Entering directory `/tmp/advbuild_testcase/build' +[1/2] c: main.c -> build/main.c.0.o +[2/2] cprogram: build/main.c.0.o -> build/app +Waf: Leaving directory `/tmp/advbuild_testcase/build' +'build' finished successfully (0.137s) +'setup' finished successfully (0.002s) +Waf: Entering directory `/tmp/advbuild_testcase/build' +[1/2] c: main.c -> build/main.c.0.o +[2/2] cprogram: build/main.c.0.o -> build/app +Waf: Leaving directory `/tmp/advbuild_testcase/build' +'build' finished successfully (0.125s) +--------------- + +==== Binding a command from a Waf tool + +When the top-level wscript is read, it is converted into a python module and kept in memory. Commands may be added dynamically by injecting the desired function into that module. We will now show how to load a waf tool to count the amount of task generators in the project. + +// advbuild_cmdtool +[source,python] +--------------- +top = '.' +out = 'build' + +def options(opt): + opt.load('some_tool', tooldir='.') + +def configure(conf): + pass +--------------- + +Waf tools are loaded once for the configuration and for the build. To ensure that the tool is always enabled, it is mandatory to load its options, even if the tool does not actually provide options. Our tool 'some_tool.py', located next to the 'wscript' file, will contain the following code: + +[source,python] +--------------- +from waflib import Context + +def cnt(ctx): + """do something""" + print('just a test') + +Context.g_module.__dict__['cnt'] = cnt +--------------- + +The execution output will be the following. + +[source,shishell] +--------------- +$ waf configure cnt +Setting top to : /tmp/examples/advbuild_cmdtool +Setting out to : /tmp/advbuild_cmdtool/build +'configure' finished successfully (0.006s) +just a test +'cnt' finished successfully (0.001s) +--------------- + +=== Custom build outputs + +==== Multiple configurations + +The _WAFLOCK_ environment variable is used to control the configuration lock and to point at the default build directory. Observe the results on the following project: + +// advbuild_waflock +[source,python] +--------------- +def configure(conf): + pass + +def build(bld): + bld(rule='touch ${TGT}', target='foo.txt') +--------------- + +We will change the _WAFLOCK_ variable in the execution: + +[source,shishell] +--------------- +$ export WAFLOCK=.lock-wafdebug <1> + +$ waf +Waf: Entering directory `/tmp/advbuild_waflock/debug' +[1/1] foo.txt: -> debug//foo.txt <2> +Waf: Leaving directory `/tmp/advbuild_waflock/debug' +'build' finished successfully (0.012s) + +$ export WAFLOCK=.lock-wafrelease + +$ waf distclean configure +'distclean' finished successfully (0.001s) +'configure' finished successfully (0.176s) + +$ waf +Waf: Entering directory `/tmp/advbuild_waflock/release' <3> +[1/1] foo.txt: -> release/foo.txt +Waf: Leaving directory `/tmp/advbuild_waflock/release' +'build' finished successfully (0.034s) + +$ tree -a +. +|-- .lock-debug <4> +|-- .lock-release +|-- debug +| |-- .wafpickle-7 +| |-- c4che +| | |-- build.config.py +| | `-- _cache.py +| |-- config.log +| `-- foo.txt +|-- release +| |-- .wafpickle-7 +| |-- c4che +| | |-- build.config.py +| | `-- _cache.py +| |-- config.log +| `-- foo.txt +`-- wscript +--------------- + +<1> The lock file points at the configuration of the project in use and at the build directory to use +<2> The files are output in the build directory +debug+ +<3> The configuration _release_ is used with a different lock file and a different build directory. +<4> The contents of the project directory contain the two lock files and the two build folders. + +The lock file may also be changed from the code by changing the appropriate variable in the waf scripts: + +[source,python] +--------------- +from waflib import Options +Options.lockfile = '.lock-wafname' +--------------- + +NOTE: The output directory pointed at by the waf lock file only has effect when not given in the waf script + +==== Changing the output directory + +===== Variant builds + +In the previous section, two different configurations were used for similar builds. We will now show how to inherit the same configuration by two different builds, and how to output the targets in different folders. Let's start with the project file: + +// advbuild_variant +[source,python] +--------------- +def configure(ctx): + pass + +def build(ctx): + ctx(rule='touch ${TGT}', target=ctx.cmd + '.txt') <1> + +from waflib.Build import BuildContext +class debug(BuildContext): <2> + cmd = 'debug' + variant = 'debug' <3> +--------------- + +<1> The command being called is _self.cmd_ +<2> Create the _debug_ command inheriting the build context +<3> Declare a folder for targets of the _debug_ command + +This projet declares two different builds _build_ and _debug_. Let's examine the execution output: + +[source,shishell] +--------------- +waf configure build debug +Setting top to : /tmp/advbuild_variant +Setting out to : /tmp/advbuild_variant/build +'configure' finished successfully (0.007s) +Waf: Entering directory `/tmp/advbuild_variant/build' +[1/1] build.txt: -> build/build.txt +Waf: Leaving directory `/tmp/advbuild_variant/build' +'build' finished successfully (0.020s) +Waf: Entering directory `/tmp/build_variant/build/debug' +[1/1] debug.txt: -> build/debug/debug.txt <1> +Waf: Leaving directory `/tmp/advbuild_variant/build/debug' +'debug' finished successfully (0.021s) + +$ tree +. +|-- build +| |-- build.txt <2> +| |-- c4che +| | |-- build.config.py +| | `-- _cache.py +| |-- config.log +| `-- debug +| `-- debug.txt <3> +`-- wscript +--------------- + +<1> Commands are executed from _build/variant_ +<2> The default _build_ command does not have any variant +<3> The target _debug_ is under the variant directory in the build directory + +===== Configuration sets for variants + +The variants may require different configuration sets created during the configuration. Here is an example: + +// advbuild_variant +[source,python] +--------------- +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.setenv('debug') <1> + conf.load('compiler_c') + conf.env.CFLAGS = ['-g'] <2> + + conf.setenv('release') + conf.load('compiler_c') + conf.env.CFLAGS = ['-O2'] + +def build(bld): + if not bld.variant: <3> + bld.fatal('call "waf build_debug" or "waf build_release", and try "waf --help"') + bld.program(source='main.c', target='app', includes='.') <4> + +from waflib.Build import BuildContext, CleanContext, \ + InstallContext, UninstallContext + +for x in 'debug release'.split(): + for y in (BuildContext, CleanContext, InstallContext, UninstallContext): + name = y.__name__.replace('Context','').lower() + class tmp(y): <5> + cmd = name + '_' + x + variant = x +--------------- + +<1> Create a new configuration set to be returned by 'conf.env', and stored in 'c4che/debug_cache.py' +<2> Modify some data in the configuration set +<3> Make sure a variant is set, this will disable the normal commands 'build', 'clean' and 'install' +<4> 'bld.env' will load the configuration set of the appropriate variant ('debug_cache.py' when in 'debug') +<5> Create new commands such as 'clean_debug' or 'install_debug' (the class name does not matter) + +The execution output will be similar to the following: + +[source,shishell] +--------------- +$ waf clean_debug build_debug clean_release build_release +'clean_debug' finished successfully (0.005s) +Waf: Entering directory `/tmp/examples/advbuild_variant_env/build/debug' +[1/2] c: main.c -> build/debug/main.c.0.o +[2/2] cprogram: build/debug/main.c.0.o -> build/debug/app +Waf: Leaving directory `/tmp/examples/advbuild_variant_env/build/debug' +'build_debug' finished successfully (0.051s) +'clean_release' finished successfully (0.003s) +Waf: Entering directory `/tmp/examples/advbuild_variant_env/build/release' +[1/2] c: main.c -> build/release/main.c.0.o +[2/2] cprogram: build/release/main.c.0.o -> build/release/app +Waf: Leaving directory `/tmp/examples/advbuild_variant_env/build/release' +'build_release' finished successfully (0.052s) +--------------- + diff --git a/docs/book/architecture.txt b/docs/book/architecture.txt new file mode 100644 index 00000000..041c2624 --- /dev/null +++ b/docs/book/architecture.txt @@ -0,0 +1,337 @@ + +== Waf architecture overview + +This chapter provides describes the Waf library and the interaction between the components. + +=== Modules and classes + +==== Core modules + +Waf consists of the following modules which constitute the core library. They are located in the directory `waflib/`. The modules located under `waflib/Tools` and `waflib/extras` are extensions which are not part of the Waf core. + +.List of core modules +[options="header", cols="1,6"] +|================= +|Module | Role +|Build | Defines the build context classes (build, clean, install, uninstall), which holds the data for one build (paths, configuration data) +|Configure | Contains the configuration context class, which is used for launching configuration tests and writing the configuration settings for the build +|ConfigSet | Contains a dictionary class which supports a lightweight copy scheme and provides persistence services +|Context | Contains the base class for all waf commands (context parameters of the Waf commands) +|Errors | Exceptions used in the Waf code +|Logs | Loggging system wrapping the calls to the python logging module +|Node | Contains the file system representation class +|Options | Provides a custom command-line option processing system based on optparse +|Runner | Contains the task execution system (thread-based producer-consumer) +|Scripting | Constitutes the entry point of the Waf application, executes the user commands such as build, configuration and installation +|TaskGen | Provides the task generator system, and its extension system based on method addition +|Task | Contains the task class definitions, and factory functions for creating new task classes +|Utils | Contains support functions and classes used by other Waf modules +|================= + +Not all core modules are required for using Waf as a library. The dependencies between the modules are represented on the following diagram. For example, the module 'Node' requires both modules 'Utils' and 'Errors'. Conversely, if the module 'Build' is used alone, then the modules 'Scripting' and 'Configure' can be removed safely. + +image::core{PIC}["Module dependencies"{backend@docbook:,height=400:},align="center"] + +==== Context classes + +User commands, such as 'configure' or 'build', are represented by classes derived from 'waflib.Context.Context'. When a command does not have a class associated, the base class 'waflib.Context.Context' is used instead. + +The method 'execute' is the start point for a context execution, it often calls the method 'recurse' to start reading the user scripts and execute the functions referenced by the 'fun' class attribute. + +The command is associated to a context class by the class attribute 'cmd' set on the class. Context subclasses are added in 'waflib.Context.classes' by the metaclass 'store_context' and loaded through the function 'waflib.Context.create_context'. The classes defined last will replace existing commands. + +As an example, the following context class will define or override the 'configure' command. When calling 'waf configure', the function 'foo' will be called from wscript files: + +[source,python] +--------------- +from waflib.Context import Context +class somename(Context): + cmd = 'configure' + fun = 'foo' +--------------- + +image::classes{PIC}["Context classes"{backend@docbook:,width=500:},align="center"] + +==== Build classes + +The class 'waflib.Build.BuildContext' and its subclasses such as 'waflib.Build.InstallContext' or 'waflib.Build.StepContext' have task generators created when reading the user scripts. The task generators will usually have task instances, depending on the operations performed after all task generators have been processed. + +The 'ConfigSet' instances are copied from the build context to the tasks ('waflib.ConfigSet.ConfigSet.derive') to propagate values such as configuration flags. A copy-on-write is performed through most methods of that class (append_value, prepend_value, append_unique). + +The 'Parallel' object encapsulates the iteration over all tasks of the build context, and delegates the execution to thread objects (producer-consumer). + +The overall structure is represented on the following diagram: + +image::classes_build{PIC}["Build classes"{backend@docbook:,width=500:},align="center"] + +=== Context objects + +==== Context commands and recursion + +The context commands are designed to be as independent as possible, and may be executed concurrently. The main application is the execution of small builds as part of configuration tests. For example, the method 'waflib.Tools.c_config.run_c_code' creates a private build context internally to perform the tests. +Here is an example of a build that creates and executes simple configuration contexts concurrently: + +// architecture_link +[source,python] +--------------- +import os +from waflib.Configure import conf, ConfigurationContext +from waflib import Task, Build, Logs + +def options(ctx): + ctx.load('compiler_c') + +def configure(ctx): + ctx.load('compiler_c') + +def build(ctx): + ctx(rule=run_test, always=True, header_name='stdio.h') <1> + ctx(rule=run_test, always=True, header_name='unistd.h') + +def run_test(self): + top = self.generator.bld.srcnode.abspath() + out = self.generator.bld.bldnode.abspath() + + ctx = ConfigurationContext(top_dir=top, out_dir=out) <2> + ctx.init_dirs() <3> + + ctx.in_msg = 1 <4> + ctx.msg('test') <5> + + header = self.generator.header_name + logfile = self.generator.path.get_bld().abspath() + os.sep \ + + header + '.log' + ctx.logger = Logs.make_logger(logfile, header) <6> + + ctx.env = self.env.derive() <7> + ctx.check(header_name=header) <8> +--------------- + +<1> Create task generators which will run the method 'run_test' method defined below +<2> Create a new configuration context as part of a 'Task.run' call +<3> Initialize ctx.srcnode and ctx.bldnode (build and configuration contexts only) +<4> Set the internal counter for the context methods 'msg', 'start_msg' and 'end_msg' +<5> The console output is disabled (non-zero counter value to disable nested messages) +<6> Each context may have a logger to redirect the error messages +<7> Initialize the default environment to a copy of the task one +<8> Perform a configuration check + +After executing 'waf build', the project folder will contain the new log files: + +[source,shishell] +--------------- +$ tree +. +|-- build +| |-- c4che +| | |-- build.config.py +| | `-- _cache.py +| |-- config.log +| |-- stdio.h.log +| `-- unistd.h.log +`-- wscript +--------------- + +A few measures are set to ensure that the contexts can be executed concurrently: + +. Context objects may use different loggers derived from the 'waflib.Logs' module. +. Each context object is associated to a private subclass of 'waflib.Node.Node' to ensure that the node objects are unique. To pickle Node objects, it is important to prevent concurrent access by using the lock object 'waflib.Node.pickle_lock'. + +==== Build context and persistence + +The build context holds all the information necessary for a build. To accelerate the start-up, a part of the information is stored and loaded between the runs. The persistent attributes are the following: + +.Persistent attributes +[options="header", cols="1,3,3"] +|================= +|Attribute | Description | Type +|root | Node representing the root of the file system | Node +|node_deps | Implicit dependencies | dict mapping Node to signatures +|raw_deps | Implicit file dependencies which could not be resolved | dict mapping Node ids to any serializable type +|task_sigs | Signature of the tasks executed | dict mapping a Task computed uid to a hash +|================= + + +=== Support for c-like languages + +==== Compiled tasks and link tasks + +The tool _waflib.Tools.ccroot_ provides a system for creating object files and linking them into a single final file. The method _waflib.Tools.ccroot.apply_link_ is called after the method _waflib.TaskGen.process_source_ to create the link task. In pseudocode: + +[source,shishell] +--------------- +call the method process_source: + for each source file foo.ext: + process the file by extension + if the method create_compiled_task is used: + create a new task + set the output file name to be foo.ext.o + add the task to the list self.compiled_tasks + +call the method apply_link + for each name N in self.features: + find a class named N: + if the class N derives from 'waflib.Tools.ccroot.link_task': + create a task of that class, assign it to self.link_task + set the link_task inputs from self.compiled_tasks + set the link_task output name to be env.N_PATTERN % self.target + stop +--------------- + +This system is used for _assembly_, _C_, _C++_, _D_ and _fortran_ by default. Note that the method _apply_link_ is supposed to be called after the method _process_source_. + +We will now demonstrate how to support the following mini language: + +[source,shishell] +--------------- +cp: .ext -> .o +cat: *.o -> .exe +--------------- + +Here is the project file: + +// architecture_link +[source,python] +--------------- +def configure(ctx): + pass + +def build(ctx): + ctx(features='mylink', source='foo.ext faa.ext', target='bingo') + +from waflib.Task import Task +from waflib.TaskGen import feature, extension, after_method +from waflib.Tools import ccroot <1> + +@after_method('process_source') +@feature('mylink') +def call_apply_link(self): <2> + self.apply_link() + +class mylink(ccroot.link_task): <3> + run_str = 'cat ${SRC} > ${TGT}' + +class ext2o(Task): + run_str = 'cp ${SRC} ${TGT}' + +@extension('.ext') +def process_ext(self, node): + self.create_compiled_task('ext2o', node) <4> +--------------- + +<1> This import will bind the methods such as _create_compiled_task_ and _apply_link_task_ +<2> An alternate definition would be calling _waflib.TaskGen.feats[`mylink'] = [`apply_link']_ +<3> The link task must be a subclass of another link task class +<4> Calling the method _create_compiled_task_ + +The execution outputs will be the following: +// why the extra space after "setting top to"? +[source,shishell] +--------------- +$ waf distclean configure build -v +'distclean' finished successfully (0.005s) +Setting top to : /tmp/architecture_link +Setting out to : /tmp/architecture_link/build +'configure' finished successfully (0.008s) +Waf: Entering directory `/tmp/architecture_link/build' +[1/3] ext2o: foo.ext -> build/foo.ext.0.o +12:50:25 runner ['cp', '../foo.ext', 'foo.ext.0.o'] +[2/3] ext2o: faa.ext -> build/faa.ext.0.o +12:50:25 runner ['cp', '../faa.ext', 'faa.ext.0.o'] +[3/3] mylink: build/foo.ext.0.o build/faa.ext.0.o -> build/bingo +12:50:25 runner 'cat foo.ext.0.o faa.ext.0.o > bingo' +Waf: Leaving directory `/tmp/architecture_link/build' +'build' finished successfully (0.041s) +--------------- + +NOTE: Task generator instances have at most one link task instance + + + + +=== Writing re-usable Waf tools + +==== Adding a waf tool + +===== Importing the code + +The intent of the Waf tools is to promote high cohesion by moving all conceptually related methods and classes into separate files, hidden from the Waf core, and as independent from each other as possible. + +Custom Waf tools can be left in the projects, added to a custom waf file through the 'waflib/extras' folder, or used through 'sys.path' changes. + +The tools can import other tools directly through the 'import' keyword. The scripts however should always import the tools to the 'ctx.load' to limit the coupling. Compare for example: + +[source,python] +--------------- +def configure(ctx): + from waflib.extras.foo import method1 + method1(ctx) +--------------- + +and: + +[source,python] +--------------- +def configure(ctx): + ctx.load('foo') + ctx.method1() +--------------- + +The second version should be preferred, as it makes fewer assumptions on whether 'method1' comes from the module 'foo' or not, and on where the module 'foo' is located. + +===== Naming convention for C/C++/Fortran + +The tools 'compiler_c', 'compiler_cxx' and 'compiler_fc' use other waf tools to detect the presense of particular compilers. They provide a particular naming convention to give a chance to new tools to register themselves automatically and save the import in user scripts. The tools having names beginning by 'c_', 'cxx_' and 'fc_' will be tested. + +The registration code will be similar to the following: + +[source,python] +--------------- +from waflib.Tools.compiler_X import X_compiler +X_compiler['platform'].append('module_name') +--------------- + +where *X* represents the type of compiler ('c', 'cxx' or 'fc'), *platform* is the platform on which the detection should take place (linux, win32, etc), and *module_name* is the name of the tool to use. + +==== Command methods + +===== Subclassing is only for commands + +As a general rule, subclasses of 'waflib.Context.Context' are created only when a new user command is necessary. This is the case for example when a command for a specific variant (output folder) is required, or to provide a new behaviour. When this happens, the class methods 'recurse', 'execute' or the class attributes 'cmd', 'fun' are usually overridden. + +NOTE: If there is no new command needed, do not use subclassing. + +===== Domain-specific methods are convenient for the end users + +Although the Waf framework promotes the most flexible way of declaring tasks through task generators, it is often more convenient to declare domain-specific wrappers in large projects. For example, the samba project provides a function used as: + +[source,python] +--------------- +bld.SAMBA_SUBSYSTEM('NDR_NBT_BUF', + source = 'nbtname.c', + deps = 'talloc', + autoproto = 'nbtname.h' + ) +--------------- + +===== How to bind new methods + +New methods are commonly bound to the build context or to the configuration context by using the '@conf' decorator: + +[source,python] +--------------- +from waflib.Configure import conf + +@conf +def enterprise_program(self, *k, **kw): + kw['features'] = 'c cprogram debug_tasks' + return self(*k, **kw) + +def build(bld): + # no feature line + bld.enterprise_program(source='main.c', target='app') +--------------- + +The methods should always be bound in this manner or manually, as subclassing may create conflicts between tools written for different purposes. + + diff --git a/docs/book/build.txt b/docs/book/build.txt new file mode 100644 index 00000000..4b8cde76 --- /dev/null +++ b/docs/book/build.txt @@ -0,0 +1,389 @@ +== Builds + +We will now provide a detailed description of the build phase, which is used for processing the build targets. + +=== Essential build concepts + +==== Build order and dependencies + +To illustrate the various concepts that are part of the build process, we are now going to use a new example. +The files +foo.txt+ and +bar.txt+ will be created by copying the file +wscript+, and the file +foobar.txt+ will be created from the concatenation of the generated files. Here is a summary: footnote:[The examples are provided for demonstration purposes. It is actually considered a best practice to avoid copying files] + +[source,shishell] +--------------- +cp: wscript -> foo.txt +cp: wscript -> bar.txt +cat: foo.txt, bar.txt -> foobar.txt +-------------- + +Each of the three lines represents a command to execute. While the _cp_ commands may be executed in any order or even in parallel, the _cat_ command may only be executed after all the others are done. The constraints on *the build order* are represented on the following http://en.wikipedia.org/wiki/Directed_acyclic_graph[Directed acyclic graph]: + +image::dag_tasks{PIC}["Task representation of the same build"{backend@docbook:,width=260:},align="center"] + +When the +wscript+ input file changes, the +foo.txt+ output file has to be created once again. The file +foo.txt+ is said to depend on the +wscript+ file. The *file dependencies* can be represented by a Direct acyclic graph too: + +image::dag_nodes{PIC}["File dependencies on a simple build"{backend@docbook:,width=120:},align="center"] + +Building a project consists in executing the commands according to a schedule which will respect these constraints. Faster build will be obtained when commands are executed in parallel (by using the build order), and when commands can be skipped (by using the dependencies). + +In Waf, the commands are represented by *task objects*. The dependencies are used by the task classes, and may be file-based or abstract to enforce particular constraints. + +==== Direct task declaration + +We will now represent the build from the previous section by declaring the tasks directly in the build section: + +// build_manual_tasks +[source,python] +--------------- +def configure(ctx): + pass + +from waflib.Task import Task +class cp(Task): <1> + def run(self): <2> + return self.exec_command('cp %s %s' % ( + self.inputs[0].abspath(), <3> + self.outputs[0].abspath() + ) + ) + +class cat(Task): + def run(self): + return self.exec_command('cat %s %s > %s' % ( + self.inputs[0].abspath(), + self.inputs[1].abspath(), + self.outputs[0].abspath() + ) + ) + +def build(ctx): + + cp_1 = cp(env=ctx.env) <4> + cp_1.set_inputs(ctx.path.find_resource('wscript')) <5> + cp_1.set_outputs(ctx.path.find_or_declare('foo.txt')) + ctx.add_to_group(cp_1) <6> + + cp_2 = cp(env=ctx.env) + cp_2.set_inputs(ctx.path.find_resource('wscript')) + cp_2.set_outputs(ctx.path.find_or_declare('bar.txt')) + ctx.add_to_group(cp_2) + + cat_1 = cat(env=ctx.env) + cat_1.set_inputs(cp_1.outputs + cp_2.outputs) + cat_1.set_outputs(ctx.path.find_or_declare('foobar.txt')) + ctx.add_to_group(cat_1) +--------------- + +<1> Task class declaration +<2> Waf tasks have a method named *run* to generate the targets +<3> Instances of _waflib.Task.Task_ have input and output objects representing the files to use (Node objects) +<4> Create a new task instance manually +<5> Set input and output files represented as _waflib.Node.Node_ objects +<6> Add the task to the build context for execution (but do not execute them immediately) + +The execution output will be the following: + +[source,shishell] +--------------- +$ waf clean build <1> +'clean' finished successfully (0.003s) +Waf: Entering directory `/tmp/build_manual_tasks/build' +[1/3] cp: wscript -> build/foo.txt +[2/3] cp: wscript -> build/bar.txt +[3/3] cat: build/foo.txt build/bar.txt -> build/foobar.txt +Waf: Leaving directory `/tmp/build_manual_tasks/build' +'build' finished successfully (0.047s) + +$ waf build <2> +Waf: Entering directory `/tmp/build_manual_tasks/build' +Waf: Leaving directory `/tmp/build_manual_tasks/build' +'build' finished successfully (0.007s) + +$ echo " " >> wscript <3> + +$ waf build +Waf: Entering directory `/tmp/build_manual_tasks/build' +[1/3] cp: wscript -> build/foo.txt <4> +[2/3] cp: wscript -> build/bar.txt +[3/3] cat: build/foo.txt build/bar.txt -> build/foobar.txt +Waf: Leaving directory `/tmp/build_manual_tasks/build' +'build' finished successfully (0.043s) +--------------- + +<1> The tasks are not executed in the _clean_ command +<2> The build keeps track of the files that were generated to avoid generating them again +<3> Modify one of the source files +<4> Rebuild according to the dependency graph + +Please remember: + +. The execution order was *computed automatically*, by using the file inputs and outputs set on the task instances +. The dependencies were *computed automatically* (the files were rebuilt when necessary), by using the node objects (hashes of the file contents were stored between the builds and then compared) +. The tasks that have no order constraints are executed in parallel by default + +==== Task encapsulation by task generators + +Declaring the tasks directly is tedious and results in lengthy scripts. Feature-wise, the following is equivalent to the previous example: + +// build_task_gen +[source,python] +--------------- +def configure(ctx): + pass + +def build(ctx): + ctx(rule='cp ${SRC} ${TGT}', source='wscript', target='foo.txt') + ctx(rule='cp ${SRC} ${TGT}', source='wscript', target='bar.txt') + ctx(rule='cat ${SRC} > ${TGT}', source='foo.txt bar.txt', target='foobar.txt') +--------------- + +The *ctx(...)* call is a shortcut to the class _waflib.TaskGen.task_gen_, instances of this class are called *task generator objects*. The task generators are lazy containers and will only create the tasks and the task classes when they are actually needed: + +// build_lazy_tg +[source,python] +--------------- +def configure(ctx): + pass + +def build(ctx): + tg = ctx(rule='touch ${TGT}', target='foo') + print(type(tg)) + print(tg.tasks) + tg.post() + print(tg.tasks) + print(type(tg.tasks[0])) +--------------- + +Here is the output: + +[source,shishell] +--------------- +waf configure build +Setting top to : /tmp/build_lazy_tg +Setting out to : /tmp/build_lazy_tg/build +'configure' finished successfully (0.204s) +Waf: Entering directory `/tmp/build_lazy_tg/build' + <1> +[] <2> +[{task: foo -> foo}] <3> + <4> +[1/1] foo: -> build/foo +Waf: Leaving directory `/tmp/build_lazy_tg/build' +'build' finished successfully (0.023s) +--------------- + +<1> Task generator type +<2> The tasks created are stored in the list _tasks_ (0..n tasks may be added) +<3> Tasks are created after calling the method post() - it is usually called automatically internally +<4> A new task class was created dynamically for the target +foo+ + +==== Overview of the build phase + +A high level overview of the build process is represented on the following diagram: + +image::build_overview{PIC}["Overview of the build phase"{backend@docbook:,width=250:},align="center"] + +NOTE: The tasks are all created before any of them is executed. New tasks may be created after the build has started, but the dependencies have to be set by using low-level apis. + +=== More build options + +Although any operation can be executed as part of a task, a few scenarios are typical and it makes sense to provide convenience functions for them. + +==== Executing specific routines before or after the build + +User functions may be bound to be executed at two key moments during the build command (callbacks): + +. immediately before the build starts (bld.add_pre_fun) +. immediately after the build is completed successfully (bld.add_post_fun) + +Here is how to execute a test after the build is finished: + +// build_pre_post +[source,python] +--------------- +top = '.' +out = 'build' + +def options(ctx): + ctx.add_option('--exe', action='store_true', default=False, + help='execute the program after it is built') + +def configure(ctx): + pass + +def pre(ctx): <1> + print('before the build is started') + +def post(ctx): + print('after the build is complete') + if ctx.cmd == 'install': <2> + if ctx.options.exe: <3> + ctx.exec_command('/sbin/ldconfig') <4> + +def build(ctx): + ctx.add_pre_fun(pre) <5> + ctx.add_post_fun(post) +--------------- + +<1> The callbacks take the build context as unique parameter 'ctx' +<2> Access the command type +<3> Access to the command-line options +<4> A common scenario is to call ldconfig after the files are installed. +<5> Scheduling the functions for later execution. Python functions are objects too. + +Upon execution, the following output will be produced: + +[source,shishell] +--------------- +$ waf distclean configure build install --exe +'distclean' finished successfully (0.005s) +'configure' finished successfully (0.011s) +Waf: Entering directory `/tmp/build_pre_post/build' +before the build is started <1> +Waf: Leaving directory `/tmp/build_pre_post/build' +after the build is complete <2> +'build' finished successfully (0.004s) +Waf: Entering directory `/tmp/build_pre_post/build' +before the build is started +Waf: Leaving directory `/tmp/build_pre_post/build' +after the build is complete +/sbin/ldconfig: Can't create temporary cache file /etc/ld.so.cache~: Permission denied <3> +'install' finished successfully (15.730s) +--------------- + +<1> output of the function bound by 'bld.add_pre_fun' +<2> output of the function bound by 'bld.add_post_fun' +<3> execution at installation time + + +==== Installing files + +Three build context methods are provided for installing files created during or after the build: + +. install_files: install several files in a folder +. install_as: install a target with a different name +. symlink_as: create a symbolic link on the platforms that support it + +[source,python] +--------------- +def build(bld): + bld.install_files('${PREFIX}/include', ['a1.h', 'a2.h']) <1> + bld.install_as('${PREFIX}/dir/bar.png', 'foo.png') <2> + bld.symlink_as('${PREFIX}/lib/libfoo.so.1', 'libfoo.so.1.2.3') <3> + + env_foo = bld.env.derive() + env_foo.PREFIX = '/opt' + bld.install_as('${PREFIX}/dir/test.png', 'foo.png', env=env_foo) <4> + + start_dir = bld.path.find_dir('src/bar') + bld.install_files('${PREFIX}/share', ['foo/a1.h'], + cwd=start_dir, relative_trick=True) <5> + + bld.install_files('${PREFIX}/share', start_dir.ant_glob('**/*.png'), <6> + cwd=start_dir, relative_trick=True) +--------------- + +<1> Install various files in the target destination +<2> Install one file, changing its name +<3> Create a symbolic link +<4> Overridding the configuration set ('env' is optional in the three methods install_files, install_as and symlink_as) +<5> Install src/bar/foo/a1.h as seen from the current script into '$\{PREFIX}/share/foo/a1.h' +<6> Install the png files recursively, preserving the folder structure read from src/bar/ + +NOTE: the methods _install_files_, _install_as_ and _symlink_as_ will do something only during _waf install_ or _waf uninstall_, they have no effect in other build commands + +==== Listing the task generators and forcing specific task generators + +The _list_ command is used to display the task generators that are declared: + +// build_list +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + pass + +def build(ctx): + ctx(source='wscript', target='foo.txt', rule='cp ${SRC} ${TGT}') + ctx(target='bar.txt', rule='touch ${TGT}', name='bar') +--------------- + +By default, the name of the task generator is computed from the _target_ attribute: + +[source,shishell] +--------------- +$ waf configure list +'configure' finished successfully (0.005s) +foo.txt +bar +'list' finished successfully (0.008s) +--------------- + +The main usage of the name values is to force a partial build with the _--targets_ option. Compare the following: + +[source,shishell] +--------------- +$ waf clean build +'clean' finished successfully (0.003s) +Waf: Entering directory `/tmp/build_list/build' +[1/2] foo.txt: wscript -> build/foo.txt +[2/2] bar: -> build/bar.txt +Waf: Leaving directory `/tmp/build_list/build' +'build' finished successfully (0.028s) + +$ waf clean build --targets=foo.txt +'clean' finished successfully (0.003s) +Waf: Entering directory `/tmp/build_list/build' +[1/1] foo.txt: wscript -> build/foo.txt +Waf: Leaving directory `/tmp/build_list/build' +'build' finished successfully (0.022s) +--------------- + +==== Execution step by step for debugging (the _step_ command) + +The _step_ is used to execute specific tasks and to return the exit status and any error message. It is particularly useful for debugging: + +[source,shishell] +--------------- +waf step --files=test_shlib.c,test_staticlib.c +Waf: Entering directory `/tmp/demos/c/build' +c: shlib/test_shlib.c -> build/shlib/test_shlib.c.1.o + -> 0 +cshlib: build/shlib/test_shlib.c.1.o -> build/shlib/libmy_shared_lib.so + -> 0 +c: stlib/test_staticlib.c -> build/stlib/test_staticlib.c.1.o + -> 0 +cstlib: build/stlib/test_staticlib.c.1.o -> build/stlib/libmy_static_lib.a + -> 0 +Waf: Leaving directory `/tmp/demos/c/build' +'step' finished successfully (0.201s) +--------------- + +In this case the +.so+ files were also rebuilt. Since the files attribute is interpreted as a comma-separated list of regular expressions, the following will produce a different output: + +[source,shishell] +--------------- +$ waf step --files=test_shlib.c$ +Waf: Entering directory `/tmp/demos/c/build' +c: shlib/test_shlib.c -> build/shlib/test_shlib.c.1.o + -> 0 +Waf: Leaving directory `/tmp/demos/c/build' +'step' finished successfully (0.083s) +--------------- + +Finally, the tasks to execute may be prefixed by 'in:' or 'out:' to specify if it is a source or a target file: + +[source,shishell] +--------------- +$ waf step --files=out:build/shlib/test_shlib.c.1.o +Waf: Entering directory `/tmp/demos/c/build' +cc: shlib/test_shlib.c -> build/shlib/test_shlib.c.1.o + -> 0 +Waf: Leaving directory `/tmp/demos/c/build' +'step' finished successfully (0.091s) +--------------- + +NOTE: when using _waf step_, all tasks are executed sequentially, even if some of them return a non-zero exit status + diff --git a/docs/book/build_overview.dia b/docs/book/build_overview.dia new file mode 100644 index 00000000..3c68f8eb --- /dev/null +++ b/docs/book/build_overview.dia @@ -0,0 +1,690 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Execute the build functions +from user scripts# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Have the task generators +generate the tasks# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Set the build order +on the tasks# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Execute the tasks +that must be executed# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Save the BuildContext +data to the cache# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Start# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #End# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Read the BuildContext +data from the cache# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/book/callouts/1.png b/docs/book/callouts/1.png new file mode 100644 index 00000000..7d473430 Binary files /dev/null and b/docs/book/callouts/1.png differ diff --git a/docs/book/callouts/10.png b/docs/book/callouts/10.png new file mode 100644 index 00000000..997bbc82 Binary files /dev/null and b/docs/book/callouts/10.png differ diff --git a/docs/book/callouts/11.png b/docs/book/callouts/11.png new file mode 100644 index 00000000..ce47dac3 Binary files /dev/null and b/docs/book/callouts/11.png differ diff --git a/docs/book/callouts/12.png b/docs/book/callouts/12.png new file mode 100644 index 00000000..31daf4e2 Binary files /dev/null and b/docs/book/callouts/12.png differ diff --git a/docs/book/callouts/13.png b/docs/book/callouts/13.png new file mode 100644 index 00000000..14021a89 Binary files /dev/null and b/docs/book/callouts/13.png differ diff --git a/docs/book/callouts/14.png b/docs/book/callouts/14.png new file mode 100644 index 00000000..64014b75 Binary files /dev/null and b/docs/book/callouts/14.png differ diff --git a/docs/book/callouts/15.png b/docs/book/callouts/15.png new file mode 100644 index 00000000..0d65765f Binary files /dev/null and b/docs/book/callouts/15.png differ diff --git a/docs/book/callouts/2.png b/docs/book/callouts/2.png new file mode 100644 index 00000000..5d09341b Binary files /dev/null and b/docs/book/callouts/2.png differ diff --git a/docs/book/callouts/3.png b/docs/book/callouts/3.png new file mode 100644 index 00000000..ef7b7004 Binary files /dev/null and b/docs/book/callouts/3.png differ diff --git a/docs/book/callouts/4.png b/docs/book/callouts/4.png new file mode 100644 index 00000000..adb8364e Binary files /dev/null and b/docs/book/callouts/4.png differ diff --git a/docs/book/callouts/5.png b/docs/book/callouts/5.png new file mode 100644 index 00000000..4d7eb460 Binary files /dev/null and b/docs/book/callouts/5.png differ diff --git a/docs/book/callouts/6.png b/docs/book/callouts/6.png new file mode 100644 index 00000000..0ba694af Binary files /dev/null and b/docs/book/callouts/6.png differ diff --git a/docs/book/callouts/7.png b/docs/book/callouts/7.png new file mode 100644 index 00000000..472e96f8 Binary files /dev/null and b/docs/book/callouts/7.png differ diff --git a/docs/book/callouts/8.png b/docs/book/callouts/8.png new file mode 100644 index 00000000..5e60973c Binary files /dev/null and b/docs/book/callouts/8.png differ diff --git a/docs/book/callouts/9.png b/docs/book/callouts/9.png new file mode 100644 index 00000000..a0676d26 Binary files /dev/null and b/docs/book/callouts/9.png differ diff --git a/docs/book/chains.txt b/docs/book/chains.txt new file mode 100644 index 00000000..561c09d4 --- /dev/null +++ b/docs/book/chains.txt @@ -0,0 +1,371 @@ + +=== Name and extension-based file processing + +Transformations may be performed automatically based on the file name or on the extension. + +==== Refactoring repeated rule-based task generators into implicit rules + +The explicit rules described in the previous chapter become limited for processing several files of the same kind. The following code may lead to unmaintainable scripts and to slow builds (for loop): + +[source, python] +---------------- +def build(bld): + for x in 'a.lua b.lua c.lua'.split(): + y = x.replace('.lua', '.luac') + bld(source=x, target=y, rule='${LUAC} -s -o ${TGT} ${SRC}') + bld.install_files('${LUADIR}', x) +---------------- + +Rather, the rule should be removed from the user script, like this: + +[source,python] +--------------- +def build(bld): + bld(source='a.lua b.lua c.lua') +--------------- + +The equivalent logic may then be provided by using the following code. It may be located in either the same 'wscript', or in a waf tool: + +[source,python] +--------------- +from waflib import TaskGen +TaskGen.declare_chain( + name = 'luac', <1> + rule = '${LUAC} -s -o ${TGT} ${SRC}', <2> + shell = False, + ext_in = '.lua', <3> + ext_out = '.luac', <4> + reentrant = False, <5> + install_path = '${LUADIR}', <6> +) +--------------- + +<1> The name for the corresponding task class to use +<2> The rule is the same as for any rule-based task generator +<3> Input file, processed by extension +<4> Output files extensions separated by spaces. In this case there is only one output file +<5> The reentrant attribute is used to add the output files as source again, for processing by another implicit rule +<6> String representing the installation path for the output files, similar to the destination path from 'bld.install_files'. To disable installation, set it to False. + +==== Chaining more than one command + +Now consider the long chain 'uh.in' → 'uh.a' → 'uh.b' → 'uh.c'. The following implicit rules demonstrate how to generate the files while maintaining a minimal user script: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + bld(source='uh.in') + +from waflib import TaskGen +TaskGen.declare_chain(name='a', rule='cp ${SRC} ${TGT}', ext_in='.in', ext_out='.a',) +TaskGen.declare_chain(name='b', rule='cp ${SRC} ${TGT}', ext_in='.a', ext_out='.b',) +TaskGen.declare_chain(name='c', rule='cp ${SRC} ${TGT}', ext_in='.b', ext_out='.c', reentrant = False) +--------------- + +During the build phase, the correct compilation order is computed based on the extensions given: + +[source,shishell] +--------------- +$ waf distclean configure build +'distclean' finished successfully (0.000s) +'configure' finished successfully (0.090s) +Waf: Entering directory `/comp/waf/demos/simple_scenarios/chaining/build' +[1/3] a: uh.in -> build/uh.a +[2/3] b: build/uh.a -> build/uh.b +[3/3] c: build/uh.b -> build/uh.c +Waf: Leaving directory `/comp/waf/demos/simple_scenarios/chaining/build' +'build' finished successfully (0.034s) +--------------- + +==== Scanner methods + +Because transformation chains rely on implicit transformations, it may be desirable to hide some files from the list of sources. Or, some dependencies may be produced conditionally and may not be known in advance. A 'scanner method' is a kind of callback used to find additional dependencies just before the target is generated. For illustration purposes, let us start with an empty project containing three files: the 'wscript', 'ch.in' and 'ch.dep' + +[source,shishell] +--------------- +$ cd /tmp/smallproject + +$ tree +. +|-- ch.dep +|-- ch.in +`-- wscript +--------------- + +The build will create a copy of 'ch.in' called 'ch.out'. Also, 'ch.out' must be rebuild whenever 'ch.dep' changes. This corresponds more or less to the following Makefile: + +[source,make] +----------------- +ch.out: ch.in ch.dep + cp ch.in ch.out +----------------- + +The user script should only contain the following code: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + bld(source = 'ch.in') +--------------- + +The code below is independent from the user scripts and may be located in a Waf tool. + +[source,python] +--------------- +def scan_meth(task): <1> + node = task.inputs[0] + dep = node.parent.find_resource(node.name.replace('.in', '.dep')) <2> + if not dep: + raise ValueError("Could not find the .dep file for %r" % node) + return ([dep], []) <3> + +from waflib import TaskGen +TaskGen.declare_chain( + name = 'copy', + rule = 'cp ${SRC} ${TGT}', + ext_in = '.in', + ext_out = '.out', + reentrant = False, + scan = scan_meth, <4> +) +-------------- +<1> The scanner method accepts a task object as input (not a task generator) +<2> Use node methods to locate the dependency (and raise an error if it cannot be found) +<3> Scanner methods return a tuple containing two lists. The first list contains the list of node objects to depend on. The second list contains private data such as debugging information. The results are cached between build calls so the contents must be serializable. +<4> Add the scanner method to chain declaration + +The execution trace will be the following: + +[source,shishell] +-------------- +$ echo 1 > ch.in +$ echo 1 > ch.dep <1> + +$ waf distclean configure build +'distclean' finished successfully (0.001s) +'configure' finished successfully (0.001s) +Waf: Entering directory `/tmp/smallproject/build' +[1/1] copy: ch.in -> build/ch.out <2> +Waf: Leaving directory `/tmp/smallproject/build' +'build' finished successfully (0.010s) + +$ waf +Waf: Entering directory `/tmp/smallproject/build' +Waf: Leaving directory `/tmp/smallproject/build' +'build' finished successfully (0.005s) <3> + +$ echo 2 > ch.dep <4> + +$ waf +Waf: Entering directory `/tmp/smallproject/build' +[1/1] copy: ch.in -> build/ch.out <5> +Waf: Leaving directory `/tmp/smallproject/build' +'build' finished successfully (0.012s) +-------------- + +<1> Initialize the file contents of 'ch.in' and 'ch.dep' +<2> Execute a first clean build. The file 'ch.out' is produced +<3> The target 'ch.out' is up-to-date because nothing has changed +<4> Change the contents of 'ch.dep' +<5> The dependency has changed, so the target is rebuilt + +Here are a few important points about scanner methods: + +. they are executed only when the target is not up-to-date. +. they may not modify the 'task' object or the contents of the configuration set 'task.env' +. they are executed in a single main thread to avoid concurrency issues +. the results of the scanner (tuple of two lists) are re-used between build executions (and it is possible to access programatically those results) +. the make-like rules also accept a 'scan' argument (scanner methods are bound to the task rather than the task generators) +. they are used by Waf internally for c/c++ support, to add dependencies dynamically on the header files ('.c' → '.h') + + +==== Extension callbacks + +In the chain declaration from the previous sections, the attribute 'reentrant' was described to control if the generated files are to be processed or not. There are cases however where one of the two generated files must be declared (because it will be used as a dependency) but where it cannot be considered as a source file in itself (like a header in c/c\++). Now consider the following two chains ('uh.in' → 'uh.a1' + 'uh.a2') and ('uh.a1' → 'uh.b') in the following example: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + obj = bld(source='uh.in') + +from waflib import TaskGen +TaskGen.declare_chain( + name = 'a', + action = 'cp ${SRC} ${TGT}', + ext_in = '.in', + ext_out = ['.a1', '.a2'], + reentrant = True, +) + +TaskGen.declare_chain( + name = 'b', + action = 'cp ${SRC} ${TGT}', + ext_in = '.a1', + ext_out = '.b', + reentrant = False, +) +-------------- + +The following error message will be produced: + +[source,shishell] +-------------- +$ waf distclean configure build +'distclean' finished successfully (0.001s) +'configure' finished successfully (0.001s) +Waf: Entering directory `/tmp/smallproject' +Waf: Leaving directory `/tmp/smallproject' +Cannot guess how to process bld:///tmp/smallproject/uh.a2 (got mappings ['.a1', '.in'] in + class TaskGen.task_gen) -> try conf.load(..)? +-------------- + +The error message indicates that there is no way to process 'uh.a2'. Only files of extension '.a1' or '.in' can be processed. Internally, extension names are bound to callback methods. The error is raised because no such method could be found, and here is how to register an extension callback globally: + +[source,python] +--------------- +@TaskGen.extension('.a2') +def foo(*k, **kw): + pass +--------------- + +To register an extension callback locally, a reference to the task generator object must be kept: + +[source,python] +--------------- +def build(bld): + obj = bld(source='uh.in') + def callback(*k, **kw): + pass + obj.mappings['.a2'] = callback +--------------- + +The exact method signature and typical usage for the extension callbacks is the following: + +[source,python] +--------------- +from waflib import TaskGen +@TaskGen.extension(".a", ".b") <1> +def my_callback(task_gen_object<2>, node<3>): + task_gen_object.create_task( + task_name, <4> + node, <5> + output_nodes) <6> +--------------- + +<1> Comma-separated list of extensions (strings) +<2> Task generator instance holding the data +<3> Instance of Node, representing a file (either source or build) +<4> The first argument to create a task is the name of the task class +<5> The second argument is the input node (or a list of nodes for several inputs) +<6> The last parameter is the output node (or a list of nodes for several outputs) + +The creation of new task classes will be described in the next section. + +==== Task class declaration + +Waf tasks are instances of the class Task.TaskBase. Yet, the base class contains the real minimum, and the immediate subclass 'Task.Task' is usually chosen in user scripts. We will now start over with a simple project containing only one project 'wscript' file and and example file named 'ah.in'. A task class will be added. + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + bld(source='uh.in') + +from waflib import Task, TaskGen + +@TaskGen.extension('.in') +def process(self, node): + tsk = self.create_task('abcd') <1> + print(tsk.__class__) + +class abcd(Task.Task): <2> + def run(self): <3> + print('executing...') + return 0 <4> +--------------- + +<1> Create a new instance of 'abcd'. The method 'create_task' is a shortcut to make certain the task will keep a reference on its task generator. +<2> Inherit the class Task located in the module Task.py +<3> The method run is called when the task is executed +<4> The task return status must be an integer, which is zero to indicate success. The tasks that have failed will be executed on subsequent builds + +The output of the build execution will be the following: + +[source,shishell] +--------------- +$ waf distclean configure build +'distclean' finished successfully (0.002s) +'configure' finished successfully (0.001s) +Waf: Entering directory `/tmp/simpleproject/build' + +[1/1] abcd: +executing... +Waf: Leaving directory `/tmp/simpleproject/build' +'build' finished successfully (0.005s) +--------------- + +Although it is possible to write down task classes in plain python, two functions (factories) are provided to simplify the work, for example: + +[source,python] +--------------- +Task.simple_task_type( <1> + 'xsubpp', <2> + rule = '${PERL} ${XSUBPP} ${SRC} > ${TGT}', <3> + color = 'BLUE', <4> + before = 'cc') <5> + +def build_it(task): + return 0 + +Task.task_type_from_func(<6> + 'sometask', <7> + func = build_it, <8> + vars = ['SRT'], + color = 'RED', + ext_in = '.in', + ext_out = '.out') <9> +--------------- + +<1> Create a new task class executing a rule string +<2> Task class name +<3> Rule to execute during the build +<4> Color for the output during the execution +<5> Execute the task instance before any instance of task classes named 'cc'. The opposite of 'before' is 'after' +<6> Create a new task class from a custom python function. The 'vars' attribute represents additional configuration set values to use as dependencies +<7> Task class name +<8> Function to use +<9> In this context, the extension names are meant to be used for computing the execution order with other tasks, without naming the other task classes explicitly + +Note that most attributes are common between the two function factories. More usage examples may be found in most Waf tools. + +==== Source attribute processing + +The first step in processing the source file attribute is to convert all file names into Nodes. Special methods may be mapped to intercept names by the exact file name entry (no extension). The Node objects are then added to the task generator attribute 'source'. + +The list of nodes is then consumed by regular extension mappings. Extension methods may re-inject the output nodes for further processing by appending them to the the attribute 'source' (hence the name re-entrant provided in declare_chain). + +image::source{PIC}["Source attribute processing"{backend@docbook:,width=250:},align="center"] + diff --git a/docs/book/classes.dia b/docs/book/classes.dia new file mode 100644 index 00000000..5d0f39da --- /dev/null +++ b/docs/book/classes.dia @@ -0,0 +1,3567 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #BuildContext# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #srcnode# + + + #Node# + + + ## + + + ## + + + + + + + + + + + + + + #bldnode# + + + #Node# + + + ## + + + ## + + + + + + + + + + + + + + #env# + + + #ConfigSet# + + + ## + + + ## + + + + + + + + + + + + + + #groups# + + + #list<task_gen># + + + ## + + + ## + + + + + + + + + + + + + + + + #store# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + #filename# + + + ## + + + ## + + + ## + + + + + + + + + + #load# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + #filename# + + + ## + + + ## + + + ## + + + + + + + + + + #install_files# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + + #install_as# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + + #symlink_as# + + + ## + + + ## + + + + + + ## + + + + + + + + + + + + + + + + + + #init_dirs# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + + #get_tgen_by_name# + + + ## + + + #task_gen# + + + + + + ## + + + + + + + + + + + + + + + + + #name# + + + #string# + + + ## + + + ## + + + + + + + + + + #add_group# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + + #load_envs# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Node# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #name# + + + #string# + + + ## + + + ## + + + + + + + + + + + + + + #parent# + + + #Node# + + + ## + + + ## + + + + + + + + + + + + + + #sig# + + + #long# + + + ## + + + ## + + + + + + + + + + + + + + #children# + + + #dict<string, Node># + + + ## + + + ## + + + + + + + + + + + + + + #cache_abspath# + + + #string# + + + ## + + + ## + + + + + + + + + + + + + + #cache_isdir# + + + #boolean# + + + ## + + + ## + + + + + + + + + + + + + + + + #abspath# + + + ## + + + #string# + + + + + + ## + + + + + + + + + + + + + + + + + + #find_dir# + + + ## + + + #Node# + + + + + + ## + + + + + + + + + + + + + + + + + #path# + + + ## + + + ## + + + ## + + + + + + + + + + #find_resource# + + + ## + + + #Node# + + + + + + ## + + + + + + + + + + + + + + + + + #path# + + + ## + + + ## + + + ## + + + + + + + + + + #find_or_declare# + + + ## + + + #Node# + + + + + + ## + + + + + + + + + + + + + + + + + #path# + + + ## + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #ConfigSet# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #parent# + + + #Environment# + + + ## + + + ## + + + + + + + + + + + + + + #table# + + + #dict# + + + ## + + + ## + + + + + + + + + + + + + + + + #derive# + + + ## + + + #Environment# + + + + + + ## + + + + + + + + + + + + + + + + + + #store# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + #filename# + + + ## + + + ## + + + ## + + + + + + + + + + #load# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + + #append_value# + + + ## + + + ## + + + + + + ## + + + + + + + + + + + + + + + + + #var# + + + #string# + + + ## + + + ## + + + + + + + + #val# + + + #string# + + + ## + + + ## + + + + + + + + + + #append_unique# + + + ## + + + ## + + + + + + ## + + + + + + + + + + + + + + + + + #var# + + + #string# + + + ## + + + ## + + + + + + + + #val# + + + #string# + + + ## + + + ## + + + + + + + + + + #prepend_value# + + + ## + + + ## + + + + + + ## + + + + + + + + + + + + + + + + + #var# + + + #string# + + + ## + + + ## + + + + + + + + #val# + + + #string# + + + ## + + + ## + + + + + + + + + + #__getitem__# + + + ## + + + #object# + + + + + + ## + + + + + + + + + + + + + + + + + #variable# + + + ## + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #ConfigurationContext# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #srcnode# + + + #Node# + + + ## + + + ## + + + + + + + + + + + + + + #bldnode# + + + #Node# + + + ## + + + ## + + + + + + + + + + + + + + #env# + + + #ConfigSet# + + + ## + + + ## + + + + + + + + + + + + + + + + #check_tool# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + #name# + + + #string# + + + ## + + + ## + + + + + + + + + + #eval_rules# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + #list# + + + ## + + + ## + + + ## + + + + + + + + + + #error_handler# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + #function# + + + ## + + + ## + + + ## + + + + + + + + #error# + + + ## + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Context# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #root# + + + #Node# + + + ## + + + ## + + + + + + + + + + + + + + #path# + + + #Node# + + + ## + + + ## + + + + + + + + + + + + + + #logger# + + + #logging.Logger# + + + ## + + + ## + + + + + + + + + + + + + + #node_class# + + + #class<Node># + + + ## + + + ## + + + + + + + + + + + + + + #cmd# + + + #string# + + + ## + + + ## + + + + + + + + + + + + + + #fun# + + + #string# + + + ## + + + ## + + + + + + + + + + + + + + + + #execute# + + + ## + + + ## + + + + + + ## + + + + + + + + + + + + + + + + + + #recurse# + + + ## + + + ## + + + + + + ## + + + + + + + + + + + + + + + + + #dirs# + + + #List<string># + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #store_context# + + + #metaclass# + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + #InstallContext# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #UninstallContext# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + #CleanContext# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #StepContext# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #ListContext# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + ## + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + ## + + + + + + + + + + + + ## + + + + + + + + + + + + ## + + + ## + + + + + + + + + #all_envs# + + + #0..n# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + ## + + + ## + + + + + + + + + #all_envs# + + + #0..n# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + ## + + + ## + + + + + + + + + #children# + + + #0..n# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + ## + + + ## + + + + + + + + + #parent# + + + #0..1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + ## + + + ## + + + + + + + + + #root# + + + #1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/book/classes_build.dia b/docs/book/classes_build.dia new file mode 100644 index 00000000..5d043e97 --- /dev/null +++ b/docs/book/classes_build.dia @@ -0,0 +1,4058 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #BuildContext# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #groups# + + + #task_gen# + + + ## + + + ## + + + + + + + + + + + + + + #env# + + + #ConfigSet# + + + ## + + + ## + + + + + + + + + + + + + + + + #store# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + #filename# + + + ## + + + ## + + + ## + + + + + + + + + + #load# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + #filename# + + + ## + + + ## + + + ## + + + + + + + + + + #install_files# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + + #install_as# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + + #symlink_as# + + + ## + + + ## + + + + + + ## + + + + + + + + + + + + + + + + + + #init_dirs# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + + #get_tgen_by_name# + + + ## + + + #task_gen# + + + + + + ## + + + + + + + + + + + + + + + + + #name# + + + #string# + + + ## + + + ## + + + + + + + + + + #add_group# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #ConfigSet# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #parent# + + + #Environment# + + + ## + + + ## + + + + + + + + + + + + + + #table# + + + #dict# + + + ## + + + ## + + + + + + + + + + + + + + + + #derive# + + + ## + + + #Environment# + + + + + + ## + + + + + + + + + + + + + + + + + + #store# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + #filename# + + + ## + + + ## + + + ## + + + + + + + + + + #load# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + + #append_value# + + + ## + + + ## + + + + + + ## + + + + + + + + + + + + + + + + + #var# + + + #string# + + + ## + + + ## + + + + + + + + #val# + + + #string# + + + ## + + + ## + + + + + + + + + + #append_unique# + + + ## + + + ## + + + + + + ## + + + + + + + + + + + + + + + + + #var# + + + #string# + + + ## + + + ## + + + + + + + + #val# + + + #string# + + + ## + + + ## + + + + + + + + + + #prepend_value# + + + ## + + + ## + + + + + + ## + + + + + + + + + + + + + + + + + #var# + + + #string# + + + ## + + + ## + + + + + + + + #val# + + + #string# + + + ## + + + ## + + + + + + + + + + #__getitem__# + + + ## + + + #object# + + + + + + ## + + + + + + + + + + + + + + + + + #variable# + + + ## + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #task_gen# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #tasks# + + + #TaskBase# + + + ## + + + ## + + + + + + + + + + + + + + + + #get_name# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + + #post# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + + #apply# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + + #create_task# + + + ## + + + #TaskBase# + + + + + + ## + + + + + + + + + + + + + + + + + #name# + + + #string# + + + ## + + + ## + + + + + + + + + + #clone# + + + ## + + + #task_gen# + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Parallel# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #bld# + + + #BuildContext# + + + ## + + + ## + + + + + + + + + + + + + + #numjobs# + + + #int# + + + ## + + + ## + + + + + + + + + + + + + + #maxjobs# + + + #int# + + + ## + + + ## + + + + + + + + + + + + + + #processed# + + + #int# + + + ## + + + ## + + + + + + + + + + + + + + #total# + + + #int# + + + ## + + + ## + + + + + + + + + + + + + + #count# + + + #int# + + + ## + + + ## + + + + + + + + + + + + + + #outstanding# + + + #List<TaskBase># + + + ## + + + ## + + + + + + + + + + + + + + #frozen# + + + #List<TaskBase># + + + ## + + + ## + + + + + + + + + + + + + + #out# + + + #Queue<TaskBase># + + + ## + + + ## + + + + + + + + + + + + + + #error# + + + #List<TaskBase># + + + ## + + + ## + + + + + + + + + + + + + + #stop# + + + #boolean# + + + ## + + + ## + + + + + + + + + + + + + + #biter# + + + #PyGenerator<TaskBase># + + + ## + + + ## + + + + + + + + + + + + + + + + #postpone# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + #TaskBase# + + + ## + + + ## + + + ## + + + + + + + + + + #get_next_task# + + + ## + + + #TaskBase# + + + + + + ## + + + + + + + + + + + + + + + + + + #refill_task_list# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + + #start# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + + #error_handler# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + #task# + + + #TaskBase# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #TaskConsumer# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pool# + + + #List<TaskConsumer># + + + ## + + + ## + + + + + + + + + + + + + + #ready# + + + #Queue<Task># + + + ## + + + ## + + + + + + + + + + + + + + + + #run# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #TaskBase# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #classes (static)# + + + #class# + + + ## + + + ## + + + + + + + + + + + + + + + + #run# + + + ## + + + #int# + + + + + + ## + + + + + + + + + + + + + + + + + + #runnable_status# + + + ## + + + #int# + + + + + + ## + + + + + + + + + + + + + + + + + + #post_run# + + + ## + + + #void# + + + + + + ## + + + + + + + + + + + + + + + + + + #unique_id# + + + ## + + + #int# + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Task# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #inputs# + + + #Node# + + + ## + + + ## + + + + + + + + + + + + + + #outputs# + + + #Node# + + + ## + + + ## + + + + + + + + + + + + + + #run_after# + + + #list<TaskBase># + + + ## + + + ## + + + + + + + + + + + + + + + + #unique_id# + + + ## + + + #int# + + + + + + ## + + + + + + + + + + + + + + + + + + #signature# + + + ## + + + #int# + + + + + + ## + + + + + + + + + + + + + + + + + + #sign_explicit_deps# + + + ## + + + #int# + + + + + + ## + + + + + + + + + + + + + + + + + + #sign_implicit_deps# + + + ## + + + #int# + + + + + + ## + + + + + + + + + + + + + + + + + + #sign_vars# + + + ## + + + #int# + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #store_task_type# + + + #metaclass# + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #use# + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #use# + + + ## + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + #Node# + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #name# + + + #string# + + + ## + + + ## + + + + + + + + + + + + + + #parent# + + + #Node# + + + ## + + + ## + + + + + + + + + + + + + + #sig# + + + #long# + + + ## + + + ## + + + + + + + + + + + + + + #children# + + + #dict<string, Node># + + + ## + + + ## + + + + + + + + + + + + + + #cache_abspath# + + + #string# + + + ## + + + ## + + + + + + + + + + + + + + #cache_isdir# + + + #boolean# + + + ## + + + ## + + + + + + + + + + + + + + + + #abspath# + + + ## + + + #string# + + + + + + ## + + + + + + + + + + + + + + + + + + #find_dir# + + + ## + + + #Node# + + + + + + ## + + + + + + + + + + + + + + + + + #path# + + + ## + + + ## + + + ## + + + + + + + + + + #find_resource# + + + ## + + + #Node# + + + + + + ## + + + + + + + + + + + + + + + + + #path# + + + ## + + + ## + + + ## + + + + + + + + + + #find_or_declare# + + + ## + + + #Node# + + + + + + ## + + + + + + + + + + + + + + + + + #path# + + + ## + + + ## + + + ## + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + ## + + + ## + + + + + + + + + #children# + + + #0..n# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + ## + + + ## + + + + + + + + + #parent# + + + #0..1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + #outputs# + + + #0..n# + + + + + + + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + #inputs# + + + #0..n# + + + + + + + + + ## + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + ## + + + ## + + + + + + + + + #tasks# + + + #0..n# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + ## + + + ## + + + + + + + + + #groups# + + + #0..n# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + ## + + + ## + + + + + + + + + #env# + + + #1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + ## + + + ## + + + + + + + + + #env# + + + #1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ## + + + + + + + + + + + + ## + + + ## + + + + + + + + + #env# + + + #1# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/book/cls.eps b/docs/book/cls.eps new file mode 100644 index 00000000..939895bd --- /dev/null +++ b/docs/book/cls.eps @@ -0,0 +1,836 @@ +%!PS-Adobe-1.0 EPSF-3.0 +%%BoundingBox: 24 631 400 819 +%%Creator: Qt 4.6.2 +%%CreationDate: vie jun 25 14:14:20 2010 +%%Orientation: Portrait +%%Pages: (atend) +%%DocumentFonts: (atend) +%%EndComments +%%BeginProlog +% Prolog copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). +% You may copy this prolog in any way that is directly related to this document. +% For other use of this prolog, see your licensing agreement for Qt. +/BD{bind def}bind def/d2{dup dup}BD/ED{exch def}BD/D0{0 ED}BD/F{setfont}BD +/RL{rlineto}BD/CM{currentmatrix}BD/SM{setmatrix}BD/TR{translate}BD/SD +{setdash}BD/SC{aload pop setrgbcolor}BD/CR{currentfile read pop}BD/i{index} +BD/scs{setcolorspace}BD/DB{dict dup begin}BD/DE{end def}BD/ie{ifelse}BD/gs +{gsave}BD/gr{grestore}BD/w{setlinewidth}BD/d{setdash}BD/J{setlinecap}BD/j +{setlinejoin}BD/scn{3 array astore/BCol exch def}BD/SCN{3 array astore/PCol +exch def}BD/cm{6 array astore concat}BD/m{moveto}BD/l{lineto}BD/c{curveto}BD +/h{closepath}BD/W{clip}BD/W*{eoclip}BD/n{newpath}BD/q{gsave 10 dict begin}BD +/Q{end grestore}BD/re{4 2 roll m dup 0 exch RL exch 0 RL 0 exch neg RL h}BD +/S{gs PCol SC stroke gr n}BD/BT{gsave 10 dict begin/_m matrix CM def BCol +SC}BD/ET{end grestore}BD/Tf{/_fs ED findfont[_fs 0 0 _fs 0 0]makefont F}BD +/Tm{6 array astore concat}BD/Td{translate}BD/Tj{0 0 m show}BD/BDC{pop pop}BD +/EMC{}BD/BSt 0 def/WFi false def/BCol[1 1 1]def/PCol[0 0 0]def/BDArr[0.94 +0.88 0.63 0.50 0.37 0.12 0.06]def/level3{/languagelevel where{pop +languagelevel 3 ge}{false}ie}BD/QCIgray D0/QCIcolor D0/QCIindex D0/QCI{ +/colorimage where{pop false 3 colorimage}{exec/QCIcolor ED/QCIgray QCIcolor +length 3 idiv string def 0 1 QCIcolor length 3 idiv 1 sub{/QCIindex ED/_x +QCIindex 3 mul def QCIgray QCIindex QCIcolor _x get 0.30 mul QCIcolor _x 1 +add get 0.59 mul QCIcolor _x 2 add get 0.11 mul add add cvi put}for QCIgray +image}ie}BD/di{gs TR 1 i 1 eq{pop pop false 3 1 roll BCol SC imagemask}{dup +false ne{level3}{false}ie{/_ma ED 8 eq{/_dc[0 1]def/DeviceGray}{/_dc[0 1 0 1 +0 1]def/DeviceRGB}ie scs/_im ED/_mt ED/_h ED/_w ED <>/MaskDict <> +/InterleaveType 3 >> image}{pop 8 4 1 roll 8 eq{image}{QCI}ie}ie}ie gr}BD/BF +{gs BSt 1 eq{BCol SC WFi{fill}{eofill}ie}if BSt 2 ge BSt 8 le and{BDArr BSt +2 sub get/_sc ED BCol{1. exch sub _sc mul 1. exch sub}forall 3 array astore +SC WFi{fill}{eofill}ie}if BSt 9 ge BSt 14 le and{WFi{W}{W*}ie pathbbox 3 i 3 +i TR 4 2 roll 3 2 roll exch sub/_h ED sub/_w ED BCol SC 0.3 w n BSt 9 eq BSt +11 eq or{0 4 _h{dup 0 exch m _w exch l}for}if BSt 10 eq BSt 11 eq or{0 4 _w{ +dup 0 m _h l}for}if BSt 12 eq BSt 14 eq or{_w _h gt{0 6 _w _h add{dup 0 m _h +sub _h l}for}{0 6 _w _h add{dup 0 exch m _w sub _w exch l}for}ie}if BSt 13 +eq BSt 14 eq or{_w _h gt{0 6 _w _h add{dup _h m _h sub 0 l}for}{0 6 _w _h +add{dup _w exch m _w sub 0 exch l}for}ie}if stroke}if BSt 15 eq{}if BSt 24 +eq{}if gr}BD/f{/WFi true def BF n}BD/f*{/WFi false def BF n}BD/B{/WFi true +def BF S n}BD/B*{/WFi false def BF S n}BD/QI{/C save def pageinit q n}BD/QP{ +Q C restore showpage}BD/SPD{/setpagedevice where{<< 3 1 roll >> +setpagedevice}{pop pop}ie}BD/T1AddMapping{10 dict begin/glyphs ED/fnt ED +/current fnt/NumGlyphs get def/CMap fnt/CMap get def 0 1 glyphs length 1 sub +{glyphs exch get/gn ED current dup 256 mod/min ED 256 idiv/maj ED CMap dup +maj get dup null eq{pop 256 array 0 1 255{1 i exch/.notdef put}for}if dup +min gn put maj exch put/current current 1 add def}for fnt/CMap CMap put fnt +/NumGlyphs current put end}def/T1AddGlyphs{10 dict begin/glyphs ED/fnt ED +/current fnt/NumGlyphs get def/CMap fnt/CMap get def/CharStrings fnt +/CharStrings get def 0 1 glyphs length 2 idiv 1 sub{2 mul dup glyphs exch +get/gn ED 1 add glyphs exch get/cs ED current dup 256 mod/min ED 256 idiv +/maj ED CMap dup maj get dup null eq{pop 256 array 0 1 255{1 i exch/.notdef +put}for}if dup min gn put maj exch put CharStrings gn cs put/current current +1 add def}for fnt/CharStrings CharStrings put fnt/CMap CMap put fnt +/NumGlyphs current put end}def/StringAdd{1 i length 1 i length add string 3 +1 roll 2 i 0 3 i putinterval 2 i 2 i length 2 i putinterval pop pop}def +/T1Setup{10 dict begin dup/FontName ED (-Base) StringAdd cvx cvn/Font ED +/MaxPage Font/NumGlyphs get 1 sub 256 idiv def/FDepVector MaxPage 1 add +array def/Encoding MaxPage 1 add array def 0 1 MaxPage{dup Encoding exch dup +put dup/Page ED FontName (-) StringAdd exch 20 string cvs StringAdd cvn Font +0 dict copy d2/CMap get Page get/Encoding exch put definefont FDepVector +exch Page exch put}for FontName cvn <> definefont pop end}def + +/pageinit { +24 24 translate +% 193 *280 mm (portrait) +0 794 translate 0.666666666 -0.666666666 scale } def +%%EndProlog +/F1-Base +<< +/FontName /LiberationSans-Bold +/FontInfo <> +/FontType 1 +/PaintType 0 +/FontMatrix [.001 0 0 .001 0 0] +/FontBBox { 0 0 0 0 } +/Private << +/password 5839 +/MinFeature {16 16} +/BlueValues [] +/lenIV -1 +>> +/CharStrings << >> +/NumGlyphs 0 +/CMap 256 array +>> def +F1-Base [ +/.notdef + +/l + +/i + +/n + +/k + +/underscore +<81F8C00DFB0E04B107F8D3066507FCD306090E> +/t +<97F7E10DF7558215638B6C9675A10875A180AC8BB808F7CC074806E707D506B6F71005E106FB1007EF062F072706FBA7078B719078947F08957F9A85A08B08938B938C928C08928C928D948D0836077A85798778880878897789748B08090E> +/a + +/s + +/c + +/b + +/x +<92F8C00DF81D16FB0FF75305FB10FB5305FB2606F755F7A505FB4CF79305F72806F705FB4105F704F74105F72906FB4CFB9205F757FBA605FB2906090E> +/p + +/r + +/o + +/g + +/m + +/d + +/h + +] T1AddGlyphs +%%Page: 1 1 +%%BeginPageSetup +QI +(F1) T1Setup +%%EndPageSetup +q +Q +Q q +q +0 0 0 scn +/BSt 0 def +Q +q +0 0 0 scn +/BSt 0 def +0 0 0 SCN +0 w 2 J 2 j [] 0 d +Q +q +0 0 0 scn +/BSt 0 def +0 0 0 SCN +0 w 2 J 2 j [] 0 d +Q +q +1.01851851 0 0 1 0 0 cm +0 0 0 scn +/BSt 0 def +0 0 0 SCN +0 w 2 J 2 j [] 0 d +Q +q +1 1 1 scn +/BSt 1 def +0 0 m +564 0 l +564 282 l +0 282 l +0 0 l +h +f* +Q +q +1 0 0 1 -40 -40 cm +0 0 0 scn +/BSt 0 def +1 0 0 SCN +0 w 2 J 2 j [] 0 d +300 310 m +300 70 l +S +300 110 m +300 70 l +S +470 190 m +470 70 l +S +380 190 m +470 190 l +S +430 60 m +330 60 l +S +300 210 m +300 70 l +S +120 60 m +260 60 l +S +130 160 m +300 160 l +S +540 190 m +470 190 l +S +300 260 m +300 70 l +S +90 110 m +300 110 l +S +110 210 m +300 210 l +S +90 310 m +300 310 l +S +300 160 m +300 70 l +S +120 260 m +300 260 l +S +470 190 m +470 70 l +S +470 280 m +470 70 l +S +Q +q +1 1 1 scn +/BSt 1 def +260 30 m +253 41 l +267 41 l +260 30 l +h +f* +1 1 1 scn +/BSt 1 def +260 30 m +253 41 l +267 41 l +260 30 l +h +f* +1 1 1 scn +/BSt 1 def +430 30 m +423 41 l +437 41 l +430 30 l +h +f* +1 1 1 scn +/BSt 1 def +430 30 m +423 41 l +437 41 l +430 30 l +h +f* +1 1 1 scn +/BSt 1 def +290 20 m +301 27 l +301 13 l +290 20 l +h +f* +1 1 1 scn +/BSt 1 def +220 20 m +209 13 l +209 27 l +220 20 l +h +f* +1 1 1 scn +/BSt 1 def +260 30 m +253 41 l +267 41 l +260 30 l +h +f* +1 1 1 scn +/BSt 1 def +260 30 m +253 41 l +267 41 l +260 30 l +h +f* +1 1 1 scn +/BSt 1 def +260 30 m +253 41 l +267 41 l +260 30 l +h +f* +1 1 1 scn +/BSt 1 def +430 30 m +423 41 l +437 41 l +430 30 l +h +f* +Q +q +1 0 0 1 -40 -40 cm +0 0 0 scn +/BSt 0 def +1 0 0 SCN +0 w 2 J 2 j [] 0 d +249 53 m +249 67 l +S +300 70 m +307 81 l +S +1 1 0 scn +/BSt 1 def +260 40 m +333 40 l +333 72 l +260 72 l +260 40 l +h +B* +0 0 0 SCN +0 w 2 J 2 j [] 0 d +q +0 0 0 scn +/BSt 1 def +BT +/F1 13.5000000 Tf 1 0 0 -1 0 0 Tm +265 -53 Td <0001> Tj +4 0 Td <0002> Tj +4 0 Td <0003> Tj +9 0 Td <0004> Tj +8 0 Td <0005> Tj +8 0 Td <0006> Tj +5 0 Td <0007> Tj +8 0 Td <0008> Tj +8 0 Td <0004> Tj +ET +Q +1 0 0 SCN +0 w 2 J 2 j [] 0 d +260 56 m +332 56 l +B* +260 64 m +332 64 l +B* +0 0 0 scn +/BSt 0 def +300 70 m +293 81 l +S +330 60 m +341 67 l +S +300 70 m +293 81 l +S +470 70 m +477 81 l +S +470 70 m +463 81 l +S +300 70 m +293 81 l +S +300 70 m +307 81 l +S +463 81 m +477 81 l +S +463 81 m +477 81 l +S +293 81 m +307 81 l +S +470 70 m +477 81 l +S +293 81 m +307 81 l +S +300 70 m +307 81 l +S +260 60 m +249 53 l +S +300 70 m +293 81 l +S +470 70 m +463 81 l +S +1 1 0 scn +/BSt 1 def +330 170 m +378 170 l +378 202 l +330 202 l +330 170 l +h +B* +0 0 0 SCN +0 w 2 J 2 j [] 0 d +q +0 0 0 scn +/BSt 1 def +BT +/F1 13.5000000 Tf 1 0 0 -1 0 0 Tm +335 -183 Td <0009> Tj +8 0 Td <0008> Tj +8 0 Td <0006> Tj +5 0 Td <0001> Tj +4 0 Td <0002> Tj +4 0 Td <000A> Tj +ET +Q +1 0 0 SCN +0 w 2 J 2 j [] 0 d +330 186 m +377 186 l +B* +330 194 m +377 194 l +B* +0 0 0 scn +/BSt 0 def +300 70 m +307 81 l +S +470 70 m +463 81 l +S +293 81 m +307 81 l +S +300 70 m +307 81 l +S +341 67 m +341 53 l +S +260 60 m +249 67 l +S +293 81 m +307 81 l +S +300 70 m +293 81 l +S +293 81 m +307 81 l +S +330 60 m +341 53 l +S +470 70 m +477 81 l +S +463 81 m +477 81 l +S +1 1 0 scn +/BSt 1 def +430 40 m +512 40 l +512 72 l +430 72 l +430 40 l +h +B* +0 0 0 SCN +0 w 2 J 2 j [] 0 d +q +0 0 0 scn +/BSt 1 def +BT +/F1 13.5000000 Tf 1 0 0 -1 0 0 Tm +435 -53 Td <0008> Tj +8 0 Td <0006> Tj +5 0 Td <0007> Tj +8 0 Td <0006> Tj +5 0 Td <0002> Tj +4 0 Td <0009> Tj +8 0 Td <0005> Tj +8 0 Td <0001> Tj +4 0 Td <0002> Tj +4 0 Td <0003> Tj +9 0 Td <0004> Tj +ET +Q +1 0 0 SCN +0 w 2 J 2 j [] 0 d +430 56 m +511 56 l +B* +430 64 m +511 64 l +B* +1 1 0 scn +/BSt 1 def +40 140 m +133 140 l +133 172 l +40 172 l +40 140 l +h +B* +0 0 0 SCN +0 w 2 J 2 j [] 0 d +q +0 0 0 scn +/BSt 1 def +BT +/F1 13.5000000 Tf 1 0 0 -1 0 0 Tm +45 -153 Td <0009> Tj +8 0 Td <000B> Tj +8 0 Td <000B> Tj +8 0 Td <000C> Tj +9 0 Td <000D> Tj +6 0 Td <000E> Tj +9 0 Td <000F> Tj +9 0 Td <000D> Tj +6 0 Td <0007> Tj +8 0 Td <0010> Tj +ET +Q +1 0 0 SCN +0 w 2 J 2 j [] 0 d +40 156 m +132 156 l +B* +40 164 m +132 164 l +B* +1 1 0 scn +/BSt 1 def +540 170 m +604 170 l +604 202 l +540 202 l +540 170 l +h +B* +0 0 0 SCN +0 w 2 J 2 j [] 0 d +q +0 0 0 scn +/BSt 1 def +BT +/F1 13.5000000 Tf 1 0 0 -1 0 0 Tm +545 -183 Td <0009> Tj +8 0 Td <000B> Tj +8 0 Td <000B> Tj +8 0 Td <0008> Tj +8 0 Td <0006> Tj +5 0 Td <0001> Tj +4 0 Td <0002> Tj +4 0 Td <000A> Tj +ET +Q +1 0 0 SCN +0 w 2 J 2 j [] 0 d +540 186 m +603 186 l +B* +540 194 m +603 194 l +B* +1 1 0 scn +/BSt 1 def +40 290 m +93 290 l +93 322 l +40 322 l +40 290 l +h +B* +0 0 0 SCN +0 w 2 J 2 j [] 0 d +q +0 0 0 scn +/BSt 1 def +BT +/F1 13.5000000 Tf 1 0 0 -1 0 0 Tm +45 -303 Td <0011> Tj +9 0 Td <0008> Tj +8 0 Td <0012> Tj +9 0 Td <0001> Tj +4 0 Td <0002> Tj +4 0 Td <000A> Tj +ET +Q +1 0 0 SCN +0 w 2 J 2 j [] 0 d +40 306 m +92 306 l +B* +40 314 m +92 314 l +B* +1 1 0 scn +/BSt 1 def +440 280 m +489 280 l +489 312 l +440 312 l +440 280 l +h +B* +0 0 0 SCN +0 w 2 J 2 j [] 0 d +q +0 0 0 scn +/BSt 1 def +BT +/F1 13.5000000 Tf 1 0 0 -1 0 0 Tm +445 -293 Td <0011> Tj +9 0 Td <0008> Tj +8 0 Td <0006> Tj +5 0 Td <0001> Tj +4 0 Td <0002> Tj +4 0 Td <000A> Tj +ET +Q +1 0 0 SCN +0 w 2 J 2 j [] 0 d +440 296 m +488 296 l +B* +440 304 m +488 304 l +B* +1 1 0 scn +/BSt 1 def +40 40 m +117 40 l +117 72 l +40 72 l +40 40 l +h +B* +0 0 0 SCN +0 w 2 J 2 j [] 0 d +q +0 0 0 scn +/BSt 1 def +BT +/F1 13.5000000 Tf 1 0 0 -1 0 0 Tm +45 -53 Td <0009> Tj +8 0 Td <000C> Tj +9 0 Td <000D> Tj +6 0 Td <000E> Tj +9 0 Td <000F> Tj +9 0 Td <000D> Tj +6 0 Td <0007> Tj +8 0 Td <0010> Tj +ET +Q +1 0 0 SCN +0 w 2 J 2 j [] 0 d +40 56 m +116 56 l +B* +40 64 m +116 64 l +B* +1 1 0 scn +/BSt 1 def +40 90 m +92 90 l +92 122 l +40 122 l +40 90 l +h +B* +0 0 0 SCN +0 w 2 J 2 j [] 0 d +q +0 0 0 scn +/BSt 1 def +BT +/F1 13.5000000 Tf 1 0 0 -1 0 0 Tm +45 -103 Td <0009> Tj +8 0 Td <0008> Tj +8 0 Td <0012> Tj +9 0 Td <0001> Tj +4 0 Td <0002> Tj +4 0 Td <000A> Tj +ET +Q +1 0 0 SCN +0 w 2 J 2 j [] 0 d +40 106 m +91 106 l +B* +40 114 m +91 114 l +B* +1 1 0 scn +/BSt 1 def +40 240 m +118 240 l +118 272 l +40 272 l +40 240 l +h +B* +0 0 0 SCN +0 w 2 J 2 j [] 0 d +q +0 0 0 scn +/BSt 1 def +BT +/F1 13.5000000 Tf 1 0 0 -1 0 0 Tm +45 -253 Td <0011> Tj +9 0 Td <000C> Tj +9 0 Td <000D> Tj +6 0 Td <000E> Tj +9 0 Td <000F> Tj +9 0 Td <000D> Tj +6 0 Td <0007> Tj +8 0 Td <0010> Tj +ET +Q +1 0 0 SCN +0 w 2 J 2 j [] 0 d +40 256 m +117 256 l +B* +40 264 m +117 264 l +B* +1 1 0 scn +/BSt 1 def +40 190 m +108 190 l +108 222 l +40 222 l +40 190 l +h +B* +0 0 0 SCN +0 w 2 J 2 j [] 0 d +q +0 0 0 scn +/BSt 1 def +BT +/F1 13.5000000 Tf 1 0 0 -1 0 0 Tm +45 -203 Td <0009> Tj +8 0 Td <000B> Tj +8 0 Td <000B> Tj +8 0 Td <0008> Tj +8 0 Td <0012> Tj +9 0 Td <0001> Tj +4 0 Td <0002> Tj +4 0 Td <000A> Tj +ET +Q +1 0 0 SCN +0 w 2 J 2 j [] 0 d +40 206 m +107 206 l +B* +40 214 m +107 214 l +B* +Q +q +1.01851851 0 0 1 0 0 cm +0 0 0 scn +/BSt 0 def +0 0 0 SCN +0 w 2 J 2 j [] 0 d +Q +q +0 0 0 scn +/BSt 0 def +0 0 0 SCN +0 w 2 J 2 j [] 0 d +Q +q +1 0 0 1 -40 -40 cm +1 1 0 scn +/BSt 1 def +0 0 0 SCN +0 w 2 J 2 j [] 0 d + +Q QP +%%Trailer +%%Pages: 1 +%%DocumentFonts: +%%EOF diff --git a/docs/book/cls.xmi b/docs/book/cls.xmi new file mode 100644 index 00000000..4529a5c3 --- /dev/null +++ b/docs/book/cls.xmi @@ -0,0 +1,255 @@ + + + + + umbrello uml modeller http://uml.sf.net + 1.5.8 + UnicodeUTF8 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/book/conclusion.txt b/docs/book/conclusion.txt new file mode 100644 index 00000000..2c215e78 --- /dev/null +++ b/docs/book/conclusion.txt @@ -0,0 +1,15 @@ +== Further reading + +Due to the amount of features provided by Waf, this book cannot be both complete and up-to-date. For greater understanding and practice the following links are recommended to the reader: + +.Recommended links +[options="header"] +|================ +|Link|Description +|http://waf.googlecode.com/svn/docs/apidocs/index.html|The apidocs +|http://code.google.com/p/waf|The Waf project page +|http://code.google.com/p/waf/w/list|The Waf wiki, including the frequently asked questions (FAQ) +|http://groups.google.com/group/waf-users|The Waf mailing-list +|http://waf-devel.blogspot.com/2011/01/python-32-and-build-system-kit.html|Information on the build system kit +|================ + diff --git a/docs/book/configuration.txt b/docs/book/configuration.txt new file mode 100644 index 00000000..b566d6dc --- /dev/null +++ b/docs/book/configuration.txt @@ -0,0 +1,476 @@ +== Project configuration + +The _configuration_ command is used to check if the requiremements for working on a project are met and to store the information. The parameters are then stored for use by other commands, such as the build command. + +=== Using persistent data + +==== Sharing data with the build + +The configuration context is used to store data which may be re-used during the build. Let's begin with the following example: + +// configuration_build +[source,python] +--------------- +top = '.' +out = 'build' + +def options(ctx): + ctx.add_option('--foo', action='store', default=False, help='Silly test') + +def configure(ctx): + ctx.env.FOO = ctx.options.foo <1> + ctx.find_program('touch', var='TOUCH') <2> + +def build(bld): + print(bld.env.TOUCH) + print(bld.env.FOO) <3> + bld(rule='${TOUCH} ${TGT}', target='foo.txt') <4> +--------------- + +<1> Store the option _foo_ into the variable _env_ (dict-like structure) +<2> Configuration routine used to find the program _touch_ and to store it into _ctx.env.TOUCH_ footnote:['find_program' may use the same variable from the OS environment during the search, for example 'CC=gcc waf configure'] +<3> Print the value of _ctx.env.FOO_ that was set during the configuration +<4> The variable _$\{TOUCH}_ corresponds to the variable _ctx.env.TOUCH_. + +Here is the execution output: + +[source,shishell] +--------------- +$ waf distclean configure build --foo=abcd -v +'distclean' finished successfully (0.005s) +Checking for program touch : /usr/bin/touch <1> +'configure' finished successfully (0.007s) +Waf: Entering directory `/tmp/configuration_build/build' +/usr/bin/touch <2> +abcd +[1/1] foo.txt: -> build/foo.txt +10:56:41 runner '/usr/bin/touch foo.txt' <3> +Waf: Leaving directory `/tmp/configuration_build/build' +'build' finished successfully (0.021s) +--------------- +<1> Output of the configuration test _find_program_ +<2> The value of _TOUCH_ +<3> Command-line used to create the target 'foo.txt' + +The variable _ctx.env_ is called a *Configuration set*, and is an instance of the class 'ConfigSet'. The class is a wrapper around Python dicts to handle serialization. For this reason it should be used for simple variables only (no functions or classes). The values are stored in a python-like format in the build directory: + +[source,shishell] +--------------- +$ tree +build/ +|-- foo.txt +|-- c4che +| |-- build.config.py +| `-- _cache.py +`-- config.log + +$ cat build/c4che/_cache.py +FOO = 'abcd' +PREFIX = '/usr/local' +TOUCH = '/usr/bin/touch' +--------------- + +NOTE: Reading and writing values to _ctx.env_ is possible in both configuration and build commands. Yet, the values are stored to a file only during the configuration phase. + +==== Configuration set usage + +We will now provide more examples of the configuration set usage. The object *ctx.env* provides convenience methods to access its contents: + +// configuration_sets +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + ctx.env['CFLAGS'] = ['-g'] <1> + ctx.env.CFLAGS = ['-g'] <2> + ctx.env.append_value('CXXFLAGS', ['-O2', '-g']) <3> + ctx.env.append_unique('CFLAGS', ['-g', '-O2']) + ctx.env.prepend_value('CFLAGS', ['-O3']) <4> + + print(type(ctx.env)) + print(ctx.env) + print(ctx.env.FOO) +---------------- + +<1> Key-based access; storing a list +<2> Attribute-based access (the two forms are equivalent) +<3> Append each element to the list _ctx.env.CXXFLAGS_, assuming it is a list +<4> Insert the values at the beginning. Note that there is no such method as _prepend_unique_ + +The execution will produce the following output: + +[source,shishell] +--------------- +$ waf configure + <1> +'CFLAGS' ['-O3', '-g', '-O2'] <2> +'CXXFLAGS' ['-O2', '-g'] +'PREFIX' '/usr/local' +[] <3> + +$ cat build/c4che/_cache.py <4> +CFLAGS = ['-O3', '-g', '-O2'] +CXXFLAGS = ['-O2', '-g'] +PREFIX = '/usr/local' +--------------- + +<1> The object _conf.env_ is an instance of the class ConfigSet defined in _waflib/ConfigSet.py_ +<2> The contents of _conf.env_ after the modifications +<3> When a key is undefined, it is assumed that it is a list (used by *append_value* above) +<4> The object _conf.env_ is stored by default in this file + +Copy and serialization apis are also provided: + +// configuration_copysets +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + ctx.env.FOO = 'TEST' + + env_copy = ctx.env.derive() <1> + + node = ctx.path.make_node('test.txt') <2> + env_copy.store(node.abspath()) <3> + + from waflib.ConfigSet import ConfigSet + env2 = ConfigSet() <4> + env2.load(node.abspath()) <5> + + print(node.read()) <6> +--------------- + +<1> Make a copy of _ctx.env_ - this is a shallow copy +<2> Use *ctx.path* to create a node object representing the file +test.txt+ +<3> Store the contents of *env_copy* into +test.txt+ +<4> Create a new empty ConfigSet object +<5> Load the values from +test.txt+ +<6> Print the contents of +test.txt+ + +Upon execution, the output will be the following: + +[source,shishell] +--------------- +$ waf distclean configure +'distclean' finished successfully (0.005s) +FOO = 'TEST' +PREFIX = '/usr/local' +'configure' finished successfully (0.006s) +--------------- + +// ===== multiple configuration sets? + +=== Configuration utilities + +==== Configuration methods + +The method _ctx.find_program_ seen previously is an example of a configuration method. Here are more examples: + +// configuration_methods +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + ctx.find_program('touch', var='TOUCH') + ctx.check_waf_version(mini='1.6.8') + ctx.find_file('fstab', ['/opt', '/etc']) +--------------- + +Although these methods are provided by the context class _waflib.Configure.ConfigurationContext_, they will not appear on it in http://waf.googlecode.com/svn/docs/apidocs/index.html[API documentation]. For modularity reasons, they are defined as simple functions and then bound dynamically: + +[source,python] +--------------- +top = '.' +out = 'build' + +from waflib.Configure import conf <1> + +@conf <2> +def hi(ctx): + print('→ hello, world!') + +# hi = conf(hi) <3> + +def configure(ctx): + ctx.hi() <4> +--------------- + +<1> Import the decorator *conf* +<2> Use the decorator to bind the method _hi_ to the configuration context and build context classes. In practice, the configuration methods are only used during the configuration phase. +<3> Decorators are simple python function. Python 2.3 does not support the *@* syntax so the function has to be called after the function declaration +<4> Use the method previously bound to the configuration context class + +The execution will produce the following output: + +[source,shishell] +--------------- +$ waf configure +→ hello, world! +'configure' finished successfully (0.005s) +--------------- + +==== Loading and using Waf tools + +For efficiency reasons, only a few configuration methods are present in the Waf core. Most configuration methods are loaded by extensions called *Waf tools*. +The main tools are located in the folder +waflib/Tools+, and the tools in testing phase are located under the folder +waflib/extras+. +Yet, Waf tools may be used from any location on the filesystem. + +We will now demonstrate a very simple Waf tool named +dang.py+ which will be used to set 'ctx.env.DANG' from a command-line option: + +// configuration_tool +[source,python] +--------------- +#! /usr/bin/env python +# encoding: utf-8 + +print('→ loading the dang tool') + +from waflib.Configure import conf + +def options(opt): <1> + opt.add_option('--dang', action='store', default='', dest='dang') + +@conf +def read_dang(ctx): <2> + ctx.start_msg('Checking for the variable DANG') + if ctx.options.dang: + ctx.env.DANG = ctx.options.dang <3> + ctx.end_msg(ctx.env.DANG) + else: + ctx.end_msg('DANG is not set') + +def configure(ctx): <4> + ctx.read_dang() +--------------- + +<1> Provide command-line options +<2> Bind the function 'read_dang' as a new configuration method to call ctx.read_dang() below +<3> Set an persistent value from the current command-line options +<4> Provide a command named _configure_ accepting a build context instance as parameter + +For loading a tool, the method 'load' must be used during the configuration: + +[source,python] +--------------- +top = '.' +out = 'build' + +def options(ctx): + ctx.load('dang', tooldir='.') <1> + +def configure(ctx): + ctx.load('dang', tooldir='.') <2> + +def build(ctx): + print(ctx.env.DANG) <3> +--------------- + +<1> Load the options defined in _dang.py_ +<2> Load the tool dang.py. By default, load calls the method 'configure' defined in the tools. +<3> The tool modifies the value of _ctx.env.DANG_ during the configuration + +Upon execution, the output will be the following: + +[source,shishell] +--------------- +$ waf configure --dang=hello +→ loading the dang tool +Checking for DANG : hello <1> +'configure' finished successfully (0.006s) + +$ waf +→ loading the dang tool <2> +Waf: Entering directory `/tmp/configuration_dang/build' +hello +Waf: Leaving directory `/tmp/configuration_dang/build' +'build' finished successfully (0.004s) +--------------- + +<1> First the tool is imported as a python module, and then the method _configure_ is called by _load_ +<2> The tools loaded during the configuration will be loaded during the build phase + +==== Multiple configurations + +The 'conf.env' object is an important point of the configuration which is accessed and modified by Waf tools and by user-provided configuration functions. The Waf tools do not enforce a particular structure for the build scripts, so the tools will only modify the contents of the default object. The user scripts may provide several 'env' objects in the configuration and pre-set or post-set specific values: + +[source,python] +--------------- +def configure(ctx): + env = ctx.env <1> + ctx.setenv('debug') <2> + ctx.env.CC = 'gcc' <3> + ctx.load('gcc') + + ctx.setenv('release', env) <4> + ctx.load('msvc') + ctx.env.CFLAGS = ['/O2'] + + print ctx.all_envs['debug'] <5> +--------------- + +<1> Save a reference to 'conf.env' +<2> Copy and replace 'conf.env' +<3> Modify 'conf.env' +<4> Copy and replace 'conf.env' again, from the initial data +<5> Recall a configuration set by its name + +=== Exception handling + +==== Launching and catching configuration exceptions + +Configuration helpers are methods provided by the conf object to help find parameters, for example the method 'conf.find_program' + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + ctx.find_program('some_app') +--------------- + +When a test cannot complete properly, an exception of the type 'waflib.Errors.ConfigurationError' is raised. This often occurs when something is missing in the operating system environment or because a particular condition is not satisfied. For example: + +[source,shishell] +--------------- +$ waf +Checking for program some_app : not found + error: The program some_app could not be found +--------------- + +These exceptions may be raised manually by using 'conf.fatal': + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + ctx.fatal("I'm sorry Dave, I'm afraid I can't do that") +--------------- + +Which will display the same kind of error: + +[source,shishell] +--------------- +$ waf configure + error: I'm sorry Dave, I'm afraid I can't do that +$ echo $? +1 +--------------- + +Here is how to catch configuration exceptions: + +// configuration_exception + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + try: + ctx.find_program('some_app') + except ctx.errors.ConfigurationError: <1> + self.to_log('some_app was not found (ignoring)') <2> +--------------- + +<1> For convenience, the module _waflib.Errors_ is bound to _ctx.errors_ +<2> Adding information to the log file + +The execution output will be the following: + +[source,shishell] +--------------- +$ waf configure +Checking for program some_app : not found +'configure' finished successfully (0.029s) <1> + +$ cat build/config.log <2> +# project configured on Tue Jul 13 19:15:04 2010 by +# waf 1.6.8 (abi 98, python 20605f0 on linux2) +# using /home/waf/bin/waf configure +# +Checking for program some_app +not found +find program=['some_app'] paths=['/usr/local/bin', '/usr/bin'] var=None -> '' +from /tmp/configuration_exception: The program ['some_app'] could not be found +some_app was not found (ignoring) <3> +--------------- + +<1> The configuration completes without errors +<2> The log file contains useful information about the configuration execution +<3> Our log entry + +Catching the errors by hand can be inconvenient. For this reason, all *@conf* methods accept a parameter named 'mandatory' to suppress configuration errors. The code snippet is therefore equivalent to: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + ctx.find_program('some_app', mandatory=False) +--------------- + +As a general rule, clients should never rely on exit codes or returned values and must catch configuration exceptions. The tools should always raise configuration errors to display the errors and to give a chance to the clients to process the exceptions. + +==== Transactions + +Waf tools called during the configuration may use and modify the contents of 'conf.env' at will. Those changes may be complex to track and to undo. Fortunately, the configuration exceptions make it possible to simplify the logic and to go back to a previous state easily. The following example illustrates how to use a transaction to to use several tools at once: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + for compiler in ('gcc', 'msvc'): + try: + ctx.env.stash() + ctx.load(compiler) + except ctx.errors.ConfigurationError: + ctx.env.revert() + else: + break + else: + ctx.fatal('Could not find a compiler') +--------------- + +Though several calls to 'stash' can be made, the copies made are shallow, which means that any complex object (such as a list) modification will be permanent. For this reason, the following is a configuration anti-pattern: + +[source,python] +--------------- +def configure(ctx): + ctx.env.CFLAGS += ['-O2'] +--------------- + +The methods should always be used instead: + +[source,python] +--------------- +def configure(ctx): + ctx.env.append_value('CFLAGS', '-O2') +--------------- + +//// +To conclude this chapter on the configuration, we will now insist a little bit on the roles of the configuration context and of the configuration set objects. The configuration context is meant as a container for non-persistent data such as methods, functions, code and utilities. This means in particular that the following is an acceptable way of sharing data with scripts and tools: + +[source,python] +--------------- +def configure(ctx): + ctx.logfile = ctx.bldnode.make_node('config.log').abspath() + ctx.load('some_tool') # ... def configure(ctx): print ctx.logfile ... +--------------- + +In practice, values are frequently needed in the build section too. Adding the data to 'conf.env' is therefore a logical way of separating the concerns between the code (configuration methods) and the persistent data. + +A typical application of this is +//// + diff --git a/docs/book/conftest.dia b/docs/book/conftest.dia new file mode 100644 index 00000000..d0461c96 --- /dev/null +++ b/docs/book/conftest.dia @@ -0,0 +1,867 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Get one method# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Execute the test# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Error handler# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Failure# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #More tests +to execute?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Success# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Fatal error# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Recoverable +error# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Start# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #End# + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/book/core.dot b/docs/book/core.dot new file mode 100644 index 00000000..b3e7bfa5 --- /dev/null +++ b/docs/book/core.dot @@ -0,0 +1,46 @@ +digraph G { + label="Dependencies between the core modules"; + + Build[fillcolor="#fffea6",style=filled] + ConfigSet[fillcolor="#fffea6",style=filled] + Configure[fillcolor="#fffea6",style=filled] + Context[fillcolor="#fffea6",style=filled] + Logs[fillcolor="#fffea6",style=filled] + Nod3[fillcolor="#fffea6",style=filled, label="Node"] + Options[fillcolor="#fffea6",style=filled] + Runner[fillcolor="#fffea6",style=filled] + Scripting[fillcolor="#fffea6",style=filled] + TaskGen[fillcolor="#fffea6",style=filled] + Task[fillcolor="#fffea6",style=filled] + Utils[fillcolor="#fffea6",style=filled] + Errors[fillcolor="#fffea6",style=filled] + + Build -> Runner; + Build -> TaskGen; + Build -> ConfigSet; + Build -> Options; + + ConfigSet -> Utils; + ConfigSet -> Logs; + + Configure -> Build; + + Context -> Logs; + Context -> Nod3; + + Nod3 -> Utils; + + Options -> Context; + + Runner -> Task; + + Scripting -> Configure; + + TaskGen -> Task; + + //Task -> Utils; + Task -> Logs; + + Utils -> Errors; +} + diff --git a/docs/book/cprog.txt b/docs/book/cprog.txt new file mode 100644 index 00000000..384b77b2 --- /dev/null +++ b/docs/book/cprog.txt @@ -0,0 +1,757 @@ +== C and C++ projects + +Although Waf is language neutral, it is used very often for C and C++ projects. This chapter describes the Waf tools and functions used for these languages. + +=== Common script for C, C++ and D applications + +==== Predefined task generators + +The C/C++ builds consist in transforming (compiling) source files into object files, and to assemble (link) the object files at the end. In theory a single programming language should be sufficient for writing any application, but the situation is usually more complicated: + +. Source files may be created by other compilers in other languages (IDL, ASN1, etc) +. Additional files may enter in the link step (libraries, object files) and applications may be divided in dynamic or static libraries +. Different platforms may require different processing rules (manifest files on MS-Windows, etc) + +To conceal the implementation details and the portability concerns, each target (program, library) can be wrapped as single task generator object as in the following example: + +// cprog_wrappers +[source,python] +--------------- +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') <1> + +def build(bld): + bld.program(source='main.c', target='app', use='myshlib mystlib') <2> + bld.stlib(source='a.c', target='mystlib') <3> + bld.shlib(source='b.c', target='myshlib', use='myobjects') <4> + bld.objects(source='c.c', target='myobjects') +--------------- + +<1> Use compiler_c to load the c routines and to find a compiler (for c++ use 'compiler_cxx' and 'compiler_d' for d) +<2> Declare a program built from _main.c_ and using two other libraries +<3> Declare a static library +<4> Declare a shared library, using the objects from 'myobjects' + +The targets will have different extensions and names depending on the platform. For example on Linux, the contents of the build directory will be: + +[source,shishell] +--------------- +$ tree build +build/ +|-- c4che +| |-- build.config.py +| `-- _cache.py +|-- a.c.1.o +|-- app <1> +|-- b.c.2.o +|-- c.c.3.o +|-- config.log +|-- libmyshlib.so <2> +|-- libmystlib.a +`-- main.c.0.o <3> +--------------- + +<1> Programs have no extension on Linux but will have '.exe' on Windows +<2> The '.so' extension for shared libraries on Linux will be '.dll' on Windows +<3> The '.o' object files use the original file name and an index to avoid errors in multiple compilations + +The build context methods _program_, _shlib_, _stlib_ and _objects_ return a single task generator with the appropriate features detected from the source list. For example, for a program having _.c_ files in the source attribute, the features added will be _"c cprogram"_, for a _d_ static library, _"d dstlib"_. + +==== Additional attributes + +The methods described previously can process many more attributes than just 'use'. Here is an advanced example: + +[source,python] +--------------- +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + bld.program( + source = 'main.c', <1> + target = 'appname', <2> + features = ['more', 'features'], <3> + + includes = ['.'], <4> + defines = ['LINUX=1', 'BIDULE'], + + lib = ['m'], <5> + libpath = ['/usr/lib'], + stlib = ['dl'], <6> + stlibpath = ['/usr/local/lib'], + linkflags = ['-g'], <7> + rpath = ['/opt/kde/lib'] <8> + vnum = '1.2.3', + + install_path = '${SOME_PATH}/bin', <9> + cflags = ['-O2', '-Wall'], <10> + cxxflags = ['-O3'], + dflags = ['-g'], + ) +--------------- + +<1> Source file list +<2> Target, converted automatically to +target.exe+ or +libtarget.so+, depending on the platform and type +<3> Additional features to add (for a program consisting in c files, the default will be _'c cprogram'_) +<4> Includes and defines +<5> Shared libraries and shared libraries link paths +<6> Static libraries and link paths +<7> Use linkflags for specific link flags (not for passing libraries) +<8> rpath and vnum, ignored on platforms that do not support them +<9> Programs and shared libraries are installed by default. To disable the installation, set None. +<10> Miscalleneous flags, applied to the source files that support them (if present) + +=== Include processing + +==== Execution path and flags + +Include paths are used by the C/C++ compilers for finding headers. When one header changes, the files are recompiled automatically. For example on a project having the following structure: + +[source,shishell] +--------------- +$ tree +. +|-- foo.h +|-- src +| |-- main.c +| `-- wscript +`-- wscript +--------------- + +The file 'src/wscript' will contain the following code: + +[source,python] +--------------- +def build(bld): + bld.program( + source = 'main.c', + target = 'myapp', + includes = '.. .') +--------------- + +The command-line (output by `waf -v`) will have the following form: + +[source,shishell] +--------------- +cc -I. -I.. -Isrc -I../src ../src/main.c -c -o src/main_1.o +--------------- + +Because commands are executed from the build directory, the folders have been converted to include flags in the following way: + +[source,shishell] +--------------- +.. -> -I.. -I. +. -> -I../src -Isrc +--------------- + +There are the important points to remember: + +. The includes are always given relative to the directory containing the wscript file +. The includes add both the source directory and the corresponding build directory for the task generator variant +. Commands are executed from the build directory, so the include paths must be converted +. System include paths should be defined during the configuration and added to INCLUDES variables (uselib) + +==== The Waf preprocessor + +Waf uses a preprocessor written in Python for adding the dependencies on the headers. A simple parser looking at #include statements would miss constructs such as: + +[source,c] +--------------- +#define mymacro "foo.h" +#include mymacro +--------------- + +Using the compiler for finding the dependencies would not work for applications requiring file preprocessing such as Qt. For Qt, special include files having the '.moc' extension must be detected by the build system and produced ahead of time. The c compiler could not parse such files. + +[source,c] +--------------- +#include "foo.moc" +--------------- + +Since system headers are not tracked by default, the waf preprocessor may miss dependencies written in the following form: + +[source,c] +--------------- +#if SOMEMACRO + /* an include in the project */ + #include "foo.h" +#endif +--------------- + +To write portable code and to ease debugging, it is strongly recommended to put all the conditions used within a project into a 'config.h' file. + +[source,python] +--------------- +def configure(conf): + conf.check( + fragment = 'int main() { return 0; }\n', + define_name = 'FOO', + mandatory = True) + conf.write_config_header('config.h') +--------------- + +For performance reasons, the implicit dependency on the system headers is ignored by default. The following code may be used to enable this behaviour: + +[source,python] +--------------- +from waflib import c_preproc +c_preproc.go_absolute = True +--------------- + +Additional tools such as http://code.google.com/p/waf/source/browse/trunk/waflib/extras/gccdeps.py[gccdeps] or http://code.google.com/p/waf/source/browse/trunk/waflib/extras/dumbpreproc.py[dumbpreproc] provide alternate dependency scanners that can be faster in certain cases (boost). + +NOTE: The Waf engine will detect if tasks generate headers necessary for the compilation and compute the build order accordingly. It may sometimes improve the performance of the scanner if the tasks creating headers provide the hint 'ext_out=[".h"]'. + +==== Dependency debugging + +The Waf preprocessor contains a specific debugging zone: + +[source,shishell] +--------------- +$ waf --zones=preproc +--------------- + +To display the dependencies obtained or missed, use the following: + +[source,shishell] +--------------- +$ waf --zones=deps + +23:53:21 deps deps for src:///comp/waf/demos/qt4/src/window.cpp: <1> + [src:///comp/waf/demos/qt4/src/window.h, bld:///comp/waf/demos/qt4/src/window.moc]; <2> + unresolved ['QtGui', 'QGLWidget', 'QWidget'] <3> +--------------- + +<1> File being preprocessed +<2> Headers found +<3> System headers discarded + +The dependency computation is performed only when the files are not up-to-date, so these commands will display something only when there is a file to compile. + +NOTE: The scanner is only called when C files or dependencies change. In the rare case of adding headers after a successful compilation, then it may be necessary to run 'waf clean build' to force a full scanning. + +=== Library interaction (use) + +==== Local libraries + +The attribute 'use' enables the link against libraries (static or shared), or the inclusion of object files when the task generator referenced is not a library. + +// cprog_use +[source,python] +--------------- +def build(bld): + bld.stlib( + source = 'test_staticlib.c', + target = 'mylib', + name = 'stlib1') <1> + + bld.program( + source = 'main.c', + target = 'app', + includes = '.', + use = ['stlib1']) <2> +--------------- + +<1> The name attribute must point at exactly one task generator +<2> The attribute 'use' contains the task generator names to use + +In this example, the file 'app' will be re-created whenever 'mylib' changes (order and dependency). By using task generator names, the programs and libraries declarations may appear in any order and across scripts. For convenience, the name does not have to be defined, and will be pre-set from the target name: + +[source,python] +--------------- +def build(bld): + bld.stlib( + source = 'test_staticlib.c', + target = 'mylib') + + bld.program( + source = 'main.c', + target = 'app', + includes = '.', + use = ['mylib']) +--------------- + +The 'use' processing also exhibits a recursive behaviour. Let's illustrate it by the following example: + +// cprog_propagation +[source,python] +--------------- +def build(bld): + bld.shlib( + source = 'a.c', <1> + target = 'lib1') + + bld.stlib( + source = 'b.c', + use = 'cshlib', <2> + target = 'lib2') + + bld.shlib( + source = 'c.c', + target = 'lib3', + use = 'lib1 lib2') <3> + + bld.program( <4> + source = 'main.c', + target = 'app', + use = 'lib3') +--------------- + +<1> A simple shared library +<2> The 'cshlib' flags will be propagated to both the library and the program. footnote:[To prevent the propagation, see http://code.google.com/p/waf/source/browse/trunk/docs/book/examples/cprog_propagation/wscript] +<3> 'lib3' uses both a shared library and a static library +<4> A program using 'lib3' + +Because of the shared library dependency 'lib1' → 'lib2', the program 'app' should link against both 'lib1' and 'lib3', but not against 'lib2': + +[source,shishell] +--------------- +$ waf -v +'clean' finished successfully (0.004s) +Waf: Entering directory `/tmp/cprog_propagation/build' +[1/8] c: a.c -> build/a.c.0.o +12:36:17 runner ['/usr/bin/gcc', '-fPIC', '../a.c', '-c', '-o', 'a.c.0.o'] +[2/8] c: b.c -> build/b.c.1.o +12:36:17 runner ['/usr/bin/gcc', '../b.c', '-c', '-o', 'b.c.1.o'] +[3/8] c: c.c -> build/c.c.2.o +12:36:17 runner ['/usr/bin/gcc', '-fPIC', '../c.c', '-c', '-o', 'c.c.2.o'] +[4/8] c: main.c -> build/main.c.3.o +12:36:17 runner ['/usr/bin/gcc', '../main.c', '-c', '-o', 'main.c.3.o'] +[5/8] cstlib: build/b.c.1.o -> build/liblib2.a +12:36:17 runner ['/usr/bin/ar', 'rcs', 'liblib2.a', 'b.c.1.o'] +[6/8] cshlib: build/a.c.0.o -> build/liblib1.so +12:36:17 runner ['/usr/bin/gcc', 'a.c.0.o', '-o', 'liblib1.so', '-shared'] +[7/8] cshlib: build/c.c.2.o -> build/liblib3.so +12:36:17 runner ['/usr/bin/gcc', 'c.c.2.o', '-o', 'liblib3.so', '-Wl,-Bstatic', '-L.', '-llib2', '-Wl,-Bdynamic', '-L.', '-llib1', '-shared'] +[8/8] cprogram: build/main.c.3.o -> build/app +12:36:17 runner ['/usr/bin/gcc', 'main.c.3.o', '-o', 'app', '-Wl,-Bdynamic', '-L.', '-llib1', '-llib3'] +Waf: Leaving directory `/tmp/cprog_propagation/build' +'build' finished successfully (0.144s) +--------------- + +To sum up the two most important aspects of the 'use' attribute: + +. The task generators may be created in any order and in different files, but must provide a unique name for the 'use' attribute +. The 'use' processing will iterate recursively over all the task generators involved, but the flags added depend on the target kind (shared/static libraries) + +==== Special local libraries + +===== Includes folders + +The use keywork may point at special libraries that do not actually declare a target. For example, header-only libraries are commonly used to add specific include paths to several targets: + +// cprog_incdirs +[source,python] +--------------- +def build(bld): + bld( + includes = '. src', + export_includes = 'src', <1> + name = 'com_includes') + + bld.stlib( + source = 'a.c', + target = 'shlib1', + use = 'com_includes') <2> + + bld.program( + source = 'main.c', + target = 'app', + use = 'shlib1', <3> + ) +--------------- + +<1> The 'includes' attribute is private, but 'export_includes' will be used by other task generators +<2> The paths added are relative to the other task generator +<3> The 'export_includes' will be propagated to other task generators + +===== Object files + +Here is how to enable specific compilation flags for particular files: + +// cprog_objects +[source,python] +--------------- +def build(bld): + bld.objects( <1> + source = 'test.c', + cflags = '-O3', + target = 'my_objs') + + bld.shlib( + source = 'a.c', + cflags = '-O2', <2> + target = 'lib1', + use = 'my_objs') <3> + + bld.program( + source = 'main.c', + target = 'test_c_program', + use = 'lib1') <4> +--------------- + +<1> Files will be compiled in c mode, but no program or library will be produced +<2> Different compilation flags may be used +<3> The objects will be added automatically in the link stage +<4> There is no object propagation to other programs or libraries to avoid duplicate symbol errors + +WARNING: Like static libraries, object files are often abused to copy-paste binary code. Try to minimize the executables size by using shared libraries whenever possible. + +===== Fake libraries + +Local libraries will trigger a recompilation whenever they change. The methods 'read_shlib' and 'read_stlib' can be used to add this behaviour to external libraries or to binary files present in the project. + +// cprog_fakelibs +[source,python] +--------------- +def build(bld): + bld.read_shlib('m', paths=['.', '/usr/lib64']) + bld.program(source='main.c', target='app', use='m') +--------------- + +The methods will try to find files such as 'libm.so' or 'libm.dll' in the specified paths to compute the required paths and dependencies. In this example, the target 'app' will be re-created whenever '/usr/lib64/libm.so' changes. These libraries are propagated between task generators just like shared or static libraries declared locally. + +==== Foreign libraries and flags + +When an element in the attribute 'use' does not match a local library, it is assumed that it represents a system library, and the the required flags are present in the configuration set 'env'. This system enables the addition of several compilation and link flags at once, as in the following example: + +// cprog_system +[source,python] +--------------- +import sys + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + conf.env.INCLUDES_TEST = ['/usr/include'] <1> + + if sys.platform != 'win32': <2> + conf.env.DEFINES_TEST = ['TEST'] + conf.env.CFLAGS_TEST = ['-O0'] <3> + conf.env.LIB_TEST = ['m'] + conf.env.LIBPATH_TEST = ['/usr/lib'] + conf.env.LINKFLAGS_TEST = ['-g'] + conf.env.INCLUDES_TEST = ['/opt/gnome/include'] + +def build(bld): + mylib = bld.stlib( + source = 'test_staticlib.c', + target = 'teststaticlib', + use = 'TEST') <4> + + if mylib.env.CC_NAME == 'gcc': + mylib.cxxflags = ['-O2'] <5> +--------------- + +<1> For portability reasons, it is recommended to use INCLUDES instead of giving flags of the form -I/include. Note that the INCLUDES use used by both c and c++ +<2> Variables may be left undefined in platform-specific settings, yet the build scripts will remain identical. +<3> Declare a few variables during the configuration, the variables follow the convention VAR_NAME +<4> Add all the VAR_NAME corresponding to the _use variable_ NAME, which is 'TEST' in this example +<5> 'Model to avoid': setting the flags and checking for the configuration should be performed in the configuration section + +The variables used for C/C++ are the following: + +.Use variables and task generator attributes for C/C++ +[options="header",cols="1,1,3"] +|================= +|Uselib variable | Attribute | Usage +|LIB |lib | list of sharedlibrary names to use, without prefix or extension +|LIBPATH |libpath | list of search path for shared libraries +|STLIB |stlib | list of static library names to use, without prefix or extension +|STLIBPATH|stlibpath| list of search path for static libraries +|LINKFLAGS|linkflags| list of link flags (use other variables whenever possible) +|RPATH |rpath | list of paths to hard-code into the binary during linking time +|CFLAGS |cflags | list of compilation flags for c files +|CXXFLAGS |cxxflags | list of compilation flags for c++ files +|DFLAGS |dflags | list of compilation flags for d files +|INCLUDES |includes | include paths +|CXXDEPS | | a variable/list to trigger c++ file recompilations when it changes +|CCDEPS | | same as above, for c +|LINKDEPS | | same as above, for the link tasks +|DEFINES |defines | list of defines in the form [`key=value', ...] +|FRAMEWORK|framework| list of frameworks to use +|FRAMEWORKPATH|frameworkpath| list of framework paths to use +|ARCH |arch | list of architectures in the form ['ppc', 'x86'] +|================= + +The variables may be left empty for later use, and will not cause errors. During the development, the configuration cache files (for example, _cache.py) may be modified from a text editor to try different configurations without forcing a whole project reconfiguration. The files affected will be rebuilt however. + +=== Configuration helpers + +==== Configuration tests + +The method 'check' is used to detect parameters using a small build project. The main parameters are the following + +. msg: title of the test to execute +. okmsg: message to display when the test succeeds +. errmsg: message to display when the test fails +. env: environment to use for the build (conf.env is used by default) +. compile_mode: 'cc' or 'cxx' +. define_name: add a define for the configuration header when the test succeeds (in most cases it is calculated automatically) + +The errors raised are instances of 'waflib.Errors.ConfigurationError'. There are no return codes. + +Besides the main parameters, the attributes from c/c++ task generators may be used. Here is a concrete example: + +// cprog_conf +[source,python] +--------------- +def configure(conf): + + conf.check(header_name='time.h', features='c cprogram') <1> + conf.check_cc(function_name='printf', header_name="stdio.h", mandatory=False) <2> + conf.check_cc(fragment='int main() {2+2==4;}\n', define_name="boobah") <3> + conf.check_cc(lib='m', cflags='-Wall', defines=['var=foo', 'x=y'], + uselib_store='M') <4> + conf.check_cxx(lib='linux', use='M', cxxflags='-O2') <5> + + conf.check_cc(fragment=''' + #include + int main() { printf("4"); return 0; } ''', + define_name = "booeah", + execute = True, + define_ret = True, + msg = "Checking for something") <6> + + conf.check(features='c', fragment='int main(){return 0;}') <7> + + conf.write_config_header('config.h') <8> +--------------- + +<1> Try to compile a program using the configuration header time.h, if present on the system, if the test is successful, the define HAVE_TIME_H will be added +<2> Try to compile a program with the function printf, adding the header stdio.h (the header_name may be a list of additional headers). All configuration tests are required by default (@conf methods) and will raise configuration exceptions. To conceal them, set the attribute 'mandatory' to False. +<3> Try to compile a piece of code, and if the test is successful, define the name boobah +<4> Modifications made to the task generator environment are not stored. When the test is successful and when the attribute uselib_store is provided, the names lib, cflags and defines will be converted into _use variables_ LIB_M, CFLAGS_M and DEFINES_M and the flag values are added to the configuration environment. +<5> Try to compile a simple c program against a library called 'linux', and reuse the previous parameters for libm by _use_ +<6> Execute a simple program, collect the output, and put it in a define when successful +<7> The tests create a build with a single task generator. By passing the 'features' attribute directly it is possible to disable the compilation or to create more complicated configuration tests. +<8> After all the tests are executed, write a configuration header in the build directory (optional). The configuration header is used to limit the size of the command-line. + +Here is an example of a +config.h+ produced with the previous test code: + +[source,c] +--------------- +/* Configuration header created by Waf - do not edit */ +#ifndef _CONFIG_H_WAF +#define _CONFIG_H_WAF + +#define HAVE_PRINTF 1 +#define HAVE_TIME_H 1 +#define boobah 1 +#define booeah "4" + +#endif /* _CONFIG_H_WAF */ +--------------- + +The file +_cache.py+ will contain the following variables: + +[source,python] +--------------- +DEFINES_M = ['var=foo', 'x=y'] +CXXFLAGS_M = ['-Wall'] +CFLAGS_M = ['-Wall'] +LIB_M = ['m'] +boobah = 1 +booeah = '4' +defines = {'booeah': '"4"', 'boobah': 1, 'HAVE_TIME_H': 1, 'HAVE_PRINTF': 1} +dep_files = ['config.h'] +waf_config_files = ['/compilation/waf/demos/adv/build/config.h'] +--------------- + +==== Advanced tests + +The methods 'conf.check' create a build context and a task generator internally. This means that the attributes 'includes', 'defines', 'cxxflags' may be used (not all shown here). Advanced tests may be created by passing feature arguments: + +// cprog_cfg_advanced +[source,python] +--------------- +from waflib.TaskGen import feature, before_method + +@feature('special_test') +@before_method('process_source') +def my_special_test(self): + self.bld(rule='touch ${TGT}', target='foo') <1> + self.bld(rule='cp ${SRC} ${TGT}', source='foo', target='bar') + self.source = [] <2> + +def configure(conf): + conf.check_cc(features='special_test', msg='my test!') <3> +--------------- + +<1> Create a task generator from another task generator +<2> Disable the compilation of +test.c+ by setting no source files +<3> Use the feature special_test + +==== Creating configuration headers + +Adding lots of command-line define values increases the size of the command-line and conceals the useful information (differences). Some projects use headers which are generated during the configuration, they are not modified during the build and they are not installed or redistributed. This system is useful for huge projects, and has been made popular by autoconf-based projects. + +Writing configuration headers can be performed using the following methods: + +[source,python] +--------------- +def configure(conf): + conf.define('NOLIBF', 1) + conf.undefine('NOLIBF') + conf.define('LIBF', 1) + conf.define('LIBF_VERSION', '1.0.2') + conf.write_config_header('config.h') +--------------- + +The code snipped will produce the following 'config.h' in the build directory: + +[source,shishell] +--------------- +build/ +|-- c4che +| |-- build.config.py +| `-- _cache.py +|-- config.log +`-- config.h +--------------- + +The contents of the config.h for this example are: + +[source,c] +--------------- +/* Configuration header created by Waf - do not edit */ +#ifndef _CONFIG_H_WAF +#define _CONFIG_H_WAF + +/* #undef NOLIBF */ +#define LIBF 1 +#define LIBF_VERSION "1.0.2" + +#endif /* _CONFIG_H_WAF */ +--------------- + +NOTE: By default, the defines are moved from the command-line into the configuration header. This means that the attribute _conf.env.DEFINE_ is cleared by this operation. To prevent this behaviour, use 'conf.write_config_header(remove=False)' + +==== Pkg-config + +Instead of duplicating the configuration detection in all dependent projects, configuration files may be written when libraries are installed. To ease the interaction with build systems based on Make (cannot query databases or apis), small applications have been created for reading the cache files and to interpret the parameters (with names traditionally ending in '-config'): http://pkg-config.freedesktop.org/wiki/[pkg-config], wx-config, sdl-config, etc. + +The method 'check_cfg' is provided to ease the interaction with these applications. Here are a few examples: + +// cprog_pkgconfig +[source,python] +--------------- +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + + conf.check_cfg(atleast_pkgconfig_version='0.0.0') <1> + pango_version = conf.check_cfg(modversion='pango') <2> + + conf.check_cfg(package='pango') <3> + conf.check_cfg(package='pango', uselib_store='MYPANGO', + args=['--cflags', '--libs']) <4> + + conf.check_cfg(package='pango', <5> + args=['pango >= 0.1.0', 'pango < 9.9.9', '--cflags', '--libs'], + msg="Checking for 'pango 0.1.0'") <6> + + conf.check_cfg(path='sdl-config', args='--cflags --libs', + package='', uselib_store='SDL') <7> + conf.check_cfg(path='mpicc', args='--showme:compile --showme:link', + package='', uselib_store='OPEN_MPI', mandatory=False) <8> +--------------- + +<1> Check for the pkg-config version +<2> Retrieve the module version for a package as a string. If there were no errors, 'PANGO_VERSION' is defined. It can be overridden with the attribute _uselib_store='MYPANGO'_. +<3> Check if the pango package is present, and define _HAVE_PANGO_ (calculated automatically from the package name) +<4> Beside defining _HAVE_MYPANGO_, extract and store the relevant flags to the _use variable_ MYPANGO (_LIB_MYPANGO_, _LIBPATH_MYPANGO_, etc) +<5> Like the previous test, but with pkg-config clauses to enforce a particular version number +<6> Display a custom message on the output. The attributes 'okmsg' and 'errmsg' represent the messages to display in case of success and error respectively +<7> Obtain the flags for sdl-config. The example is applicable for other configuration programs such as wx-config, pcre-config, etc +<8> Suppress the configuration error which is raised whenever the program to execute is not found or returns a non-zero exit status + +Due to the amount of flags, the lack of standards between config applications, and to the compiler-dependent flags (-I for gcc, /I for msvc), the pkg-config output is parsed before setting the corresponding _use variables_ in a go. The function 'parse_flags(line, uselib, env)' in the Waf module c_config.py performs the flag extraction. + +The outputs are written in the build directory into the file 'config.log': + +[source,shishell] +------------------ +# project configured on Tue Aug 31 17:30:21 2010 by +# waf 1.6.8 (abi 98, python 20605f0 on linux2) +# using /home/waf/bin/waf configure +# +--- +Setting top to +/disk/comp/waf/docs/book/examples/cprog_pkgconfig +--- +Setting out to +/disk/comp/waf/docs/book/examples/cprog_pkgconfig/build +--- +Checking for program pkg-config +/usr/bin/pkg-config +find program=['pkg-config'] paths=['/usr/local/bin', '/usr/bin'] var='PKGCONFIG' -> '/usr/bin/pkg-config' +--- +Checking for pkg-config version >= 0.0.0 +['/usr/bin/pkg-config', '--atleast-pkgconfig-version=0.0.0'] +yes +['/usr/bin/pkg-config', '--modversion', 'pango'] +out: 1.28.0 + +--- +Checking for pango +['/usr/bin/pkg-config', 'pango'] +yes +--- +Checking for pango +['/usr/bin/pkg-config', 'pango'] +yes +--- +Checking for pango 0.1.0 +['/usr/bin/pkg-config', 'pango >= 0.1.0', 'pango < 9.9.9', '--cflags', '--libs', 'pango'] +out: -pthread -I/usr/include/pango-1.0 -I/usr/include/glib-2.0 -I/usr/lib64/glib-2.0/include + -pthread -lpango-1.0 -lgobject-2.0 -lgmodule-2.0 -lgthread-2.0 -lrt -lglib-2.0 + +yes +--- +Checking for sdl-config +['sdl-config', '--cflags', '--libs'] +out: -I/usr/include/SDL -D_GNU_SOURCE=1 -D_REENTRANT +-L/usr/lib64 -lSDL -lpthread + +yes +--- +Checking for mpicc +['mpicc', '--showme:compile', '--showme:link'] +out: -pthread libtool: link: -pthread -L/usr/lib64 -llammpio -llamf77mpi -lmpi -llam -lutil -ldl +------------------ + +After such a configuration, the configuration set contents will be similar to the following: + +[source,python] +--------------- +'CFLAGS_OPEN_MPI' ['-pthread'] +'CFLAGS_PANGO' ['-pthread'] +'CXXFLAGS_OPEN_MPI' ['-pthread'] +'CXXFLAGS_PANGO' ['-pthread'] +'DEFINES' ['HAVE_PANGO=1', 'HAVE_MYPANGO=1', 'HAVE_SDL=1', 'HAVE_OPEN_MPI=1'] +'DEFINES_SDL' ['_GNU_SOURCE=1', '_REENTRANT'] +'INCLUDES_PANGO' ['/usr/include/pango-1.0', '/usr/include/glib-2.0', '/usr/lib64/glib-2.0/include'] +'INCLUDES_SDL' ['/usr/include/SDL'] +'LIBPATH_OPEN_MPI' ['/usr/lib64'] +'LIBPATH_SDL' ['/usr/lib64'] +'LIB_OPEN_MPI' ['lammpio', 'lamf77mpi', 'mpi', 'lam', 'util', 'dl'] +'LIB_PANGO' ['pango-1.0', 'gobject-2.0', 'gmodule-2.0', 'gthread-2.0', 'rt', 'glib-2.0'] +'LIB_SDL' ['SDL', 'pthread'] +'LINKFLAGS_OPEN_MPI' ['-pthread'] +'LINKFLAGS_PANGO' ['-pthread'] +'PKGCONFIG' '/usr/bin/pkg-config' +'PREFIX' '/usr/local' +'define_key' ['HAVE_PANGO', 'HAVE_MYPANGO', 'HAVE_SDL', 'HAVE_OPEN_MPI'] +--------------- + diff --git a/docs/book/dag_nodes.dot b/docs/book/dag_nodes.dot new file mode 100644 index 00000000..3f37f81f --- /dev/null +++ b/docs/book/dag_nodes.dot @@ -0,0 +1,12 @@ +digraph G { + + foo [label="foo.txt",fillcolor="#aef9a5",style=filled] + bar [label="bar.txt",fillcolor="#aef9a5",style=filled] + wscript [label="wscript",fillcolor="#aef9a5",style=filled] + foobar [label="foobar.txt",fillcolor="#aef9a5",style=filled] + wscript -> foo; + wscript -> bar; + foo -> foobar; + bar -> foobar; +} + diff --git a/docs/book/dag_tasks.dot b/docs/book/dag_tasks.dot new file mode 100644 index 00000000..cf0ec0ba --- /dev/null +++ b/docs/book/dag_tasks.dot @@ -0,0 +1,9 @@ +digraph G { + + A [label="cp: wscript -> foo.txt",fillcolor="#fffea6",style=filled] + B [label="cp: wscript -> bar.txt",fillcolor="#fffea6",style=filled] + C [label="cat: foo.txt, bar.txt -> foobar.txt",fillcolor="#fffea6",style=filled] + A -> C; + B -> C; +} + diff --git a/docs/book/default.style b/docs/book/default.style new file mode 100644 index 00000000..712f0c2f --- /dev/null +++ b/docs/book/default.style @@ -0,0 +1,76 @@ +bgcolor "white"; // the background color for documents +context gray; // the color for context lines (when specified with line ranges) + +normal black ; +keyword darkblue; // for language keywords +type darkgreen ; // for basic types +usertype teal ; // for user defined types +string darkgreen ; // for strings and chars +regexp orange f ; // for strings and chars +specialchar pink f ; // for special chars, e.g., \n, \t, \\ +comment gray; // for comments +number black ; // for literal numbers +preproc teal b; // for preproc directives (e.g. #include, import) +symbol black ; // for simbols (e.g. <, >, +) +function black; // for function calls and declarations +cbracket black; // for block brackets (e.g. {, }) +todo bg:cyan b; // for TODO and FIXME +code bg:brightgreen b; // for code snippets + +//Predefined variables and functions (for instance glsl) +predef_var darkblue ; +predef_func darkblue b ; + +// for OOP +classname teal ; // for class names, e.g., in Java and C++ + +// line numbers +linenum black f; + +// Internet related +url blue u, f; + +// other elements for ChangeLog and Log files +date blue b ; +time, file darkblue b ; +ip, name darkgreen ; + +// for Prolog, Perl... +variable darkgreen ; + +// explicit for Latex +italics darkgreen i; +bold darkgreen b; +underline darkgreen u; +fixed green f; +argument darkgreen; +optionalargument purple b; +math orange; +bibtex blue; + +// for diffs +oldfile orange; +newfile darkgreen; +difflines blue; + +// for css +selector purple; +property blue; +value darkgreen i; + +// for oz +atom orange; +meta i; + +// for file system +path orange; + +// for C (or other language) labels +label teal b; + +// for errors +error purple; +warning darkgreen; + + + diff --git a/docs/book/dev.eps b/docs/book/dev.eps new file mode 100644 index 00000000..c945bc2f --- /dev/null +++ b/docs/book/dev.eps @@ -0,0 +1,719 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: inkscape 0.46 +%%Pages: 1 +%%Orientation: Portrait +%%BoundingBox: 20 6 469 366 +%%HiResBoundingBox: 20.371133 6.367207 468.24456 365.78906 +%%EndComments +%%BeginSetup +%%EndSetup +%%Page: 1 1 +0 384 translate +0.8 -0.8 scale +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +gsave [1 0 0 1 0 0] concat +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +71.9 415.4 moveto +80.9 415.4 lineto +579.2 415.4 moveto +570.2 415.4 lineto +stroke +gsave [1 0 0 1 63.6 419.9] concat +gsave [1 0 0 1 -6.6737 0.0003662109] concat +gsave [1 0 0 -1 0 0] concat +gsave +/newlatin1font {findfont dup length dict copy dup /Encoding ISOLatin1Encoding put definefont} def +/ArialMT-ISOLatin1 /ArialMT newlatin1font +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(0) show +grestore +grestore +grestore +grestore +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +71.9 345.9 moveto +80.9 345.9 lineto +579.2 345.9 moveto +570.2 345.9 lineto +stroke +gsave [1 0 0 1 63.6 350.4] concat +gsave [1 0 0 1 -6.6737 0.0003662109] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(1) show +grestore +grestore +grestore +grestore +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +71.9 276.4 moveto +80.9 276.4 lineto +579.2 276.4 moveto +570.2 276.4 lineto +stroke +gsave [1 0 0 1 63.6 280.9] concat +gsave [1 0 0 1 -6.6737 0.0003662109] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(2) show +grestore +grestore +grestore +grestore +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +71.9 206.9 moveto +80.9 206.9 lineto +579.2 206.9 moveto +570.2 206.9 lineto +stroke +gsave [1 0 0 1 63.6 211.4] concat +gsave [1 0 0 1 -6.6737 0.0003662109] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(3) show +grestore +grestore +grestore +grestore +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +71.9 137.4 moveto +80.9 137.4 lineto +579.2 137.4 moveto +570.2 137.4 lineto +stroke +gsave [1 0 0 1 63.6 141.9] concat +gsave [1 0 0 1 -6.6737 0.0004882812] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(4) show +grestore +grestore +grestore +grestore +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +71.9 67.9 moveto +80.9 67.9 lineto +579.2 67.9 moveto +570.2 67.9 lineto +stroke +gsave [1 0 0 1 63.6 72.4] concat +gsave [1 0 0 1 -6.6737 0.0004882812] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(5) show +grestore +grestore +grestore +grestore +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +71.9 422.4 moveto +71.9 413.4 lineto +71.9 54 moveto +71.9 63 lineto +stroke +gsave [1 0 0 1 71.9 444.9] concat +gsave [1 0 0 1 -3.337 0.0003662109] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(0) show +grestore +grestore +grestore +grestore +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +122.6 422.4 moveto +122.6 413.4 lineto +122.6 54 moveto +122.6 63 lineto +stroke +gsave [1 0 0 1 122.6 444.9] concat +gsave [1 0 0 1 -3.3368 0.0003662109] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(2) show +grestore +grestore +grestore +grestore +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +173.4 422.4 moveto +173.4 413.4 lineto +173.4 54 moveto +173.4 63 lineto +stroke +gsave [1 0 0 1 173.4 444.9] concat +gsave [1 0 0 1 -3.337 0.0003662109] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(4) show +grestore +grestore +grestore +grestore +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +224.1 422.4 moveto +224.1 413.4 lineto +224.1 54 moveto +224.1 63 lineto +stroke +gsave [1 0 0 1 224.1 444.9] concat +gsave [1 0 0 1 -3.3368 0.0003662109] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(6) show +grestore +grestore +grestore +grestore +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +274.8 422.4 moveto +274.8 413.4 lineto +274.8 54 moveto +274.8 63 lineto +stroke +gsave [1 0 0 1 274.8 444.9] concat +gsave [1 0 0 1 -3.3372 0.0003662109] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(8) show +grestore +grestore +grestore +grestore +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +325.6 422.4 moveto +325.6 413.4 lineto +325.6 54 moveto +325.6 63 lineto +stroke +gsave [1 0 0 1 325.6 444.9] concat +gsave [1 0 0 1 -6.6742 0.0003662109] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(10) show +grestore +grestore +grestore +grestore +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +376.3 422.4 moveto +376.3 413.4 lineto +376.3 54 moveto +376.3 63 lineto +stroke +gsave [1 0 0 1 376.3 444.9] concat +gsave [1 0 0 1 -6.6741 0.0003662109] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(12) show +grestore +grestore +grestore +grestore +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +427 422.4 moveto +427 413.4 lineto +427 54 moveto +427 63 lineto +stroke +gsave [1 0 0 1 427 444.9] concat +gsave [1 0 0 1 -6.6738 0.0003662109] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(14) show +grestore +grestore +grestore +grestore +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +477.7 422.4 moveto +477.7 413.4 lineto +477.7 54 moveto +477.7 63 lineto +stroke +gsave [1 0 0 1 477.7 444.9] concat +gsave [1 0 0 1 -6.6736 0.0003662109] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(16) show +grestore +grestore +grestore +grestore +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +528.5 422.4 moveto +528.5 413.4 lineto +528.5 54 moveto +528.5 63 lineto +stroke +gsave [1 0 0 1 528.5 444.9] concat +gsave [1 0 0 1 -6.6738 0.0003662109] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(18) show +grestore +grestore +grestore +grestore +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +579.2 422.4 moveto +579.2 413.4 lineto +579.2 54 moveto +579.2 63 lineto +stroke +gsave [1 0 0 1 579.2 444.9] concat +gsave [1 0 0 1 -6.6736 0.0003662109] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(20) show +grestore +grestore +grestore +grestore +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +71.9 54 moveto +71.9 422.4 lineto +579.2 422.4 lineto +579.2 54 lineto +71.9 54 lineto +closepath +stroke +gsave [0 -1 1 0 34.2 238.2] concat +gsave [1 0 0 1 -89.054 0.0002441406] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(Amount of jobs running in parallel) show +grestore +grestore +grestore +grestore +gsave [1 0 0 1 325.5 471.9] concat +gsave [1 0 0 1 -43.4619 0.0003662109] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(Time in seconds) show +grestore +grestore +grestore +grestore +gsave [1 0 0 1 325.5 31.5] concat +gsave [1 0 0 1 -112.3887 0] concat +gsave [1 0 0 -1 0 0] concat +gsave +/ArialMT-ISOLatin1 findfont +12 scalefont +setfont +0 0 0 setrgbcolor +newpath +0 0 moveto +(Amount of jobs running in parallel \(waf -j5\)) show +grestore +grestore +grestore +grestore +0 0.50196081 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +71.9 415.4 moveto +74.4 415.4 lineto +77 415.4 lineto +79.5 415.4 lineto +82.1 415.4 lineto +84.6 415.4 lineto +87.2 415.4 lineto +90 67.9 lineto +92.5 67.9 lineto +95.1 67.9 lineto +97.7 67.9 lineto +100.2 67.9 lineto +102.9 137.4 lineto +106.1 137.4 lineto +108.7 67.9 lineto +111.3 67.9 lineto +114.6 67.9 lineto +117.1 67.9 lineto +119.7 206.9 lineto +122.2 67.9 lineto +126.1 137.4 lineto +128.6 67.9 lineto +131.6 137.4 lineto +134.1 67.9 lineto +136.9 137.4 lineto +140.1 206.9 lineto +143 67.9 lineto +145.9 67.9 lineto +148.4 206.9 lineto +151 67.9 lineto +154 67.9 lineto +156.8 67.9 lineto +159.7 67.9 lineto +162.9 67.9 lineto +165.6 67.9 lineto +168.2 67.9 lineto +170.7 67.9 lineto +173.5 67.9 lineto +176.1 67.9 lineto +178.7 67.9 lineto +181.2 67.9 lineto +184.4 67.9 lineto +187 67.9 lineto +189.5 67.9 lineto +192.1 67.9 lineto +195.7 67.9 lineto +198.4 67.9 lineto +201.6 67.9 lineto +204.2 206.9 lineto +206.9 137.4 lineto +209.7 67.9 lineto +212.6 137.4 lineto +215.3 206.9 lineto +218.5 137.4 lineto +221 67.9 lineto +223.7 67.9 lineto +226.2 137.4 lineto +228.7 67.9 lineto +231.3 206.9 lineto +233.9 67.9 lineto +236.4 67.9 lineto +239.4 67.9 lineto +242.4 137.4 lineto +245 67.9 lineto +247.6 67.9 lineto +250.1 137.4 lineto +252.7 67.9 lineto +255.2 67.9 lineto +257.8 67.9 lineto +260.3 67.9 lineto +262.9 67.9 lineto +266.1 67.9 lineto +268.7 67.9 lineto +271.8 67.9 lineto +274.5 67.9 lineto +277.4 67.9 lineto +280 67.9 lineto +282.5 67.9 lineto +285 67.9 lineto +288.1 67.9 lineto +290.7 67.9 lineto +294 67.9 lineto +296.5 67.9 lineto +299.1 67.9 lineto +301.7 137.4 lineto +304.2 67.9 lineto +307.3 67.9 lineto +310 137.4 lineto +312.6 137.4 lineto +315.1 67.9 lineto +318.1 67.9 lineto +320.701 67.9 lineto +323.201 67.9 lineto +325.801 137.4 lineto +328.402 67.9 lineto +331.601 67.9 lineto +334.202 415.4 lineto +336.702 415.4 lineto +339.302 415.4 lineto +341.802 415.4 lineto +344.302 415.4 lineto +346.903 415.4 lineto +349.403 67.9 lineto +352.003 67.9 lineto +354.503 67.9 lineto +357.103 67.9 lineto +360.303 67.9 lineto +363.203 137.4 lineto +365.803 137.4 lineto +368.404 67.9 lineto +371.704 137.4 lineto +374.204 67.9 lineto +376.904 137.4 lineto +379.404 67.9 lineto +382.204 67.9 lineto +385.004 67.9 lineto +387.704 67.9 lineto +390.404 67.9 lineto +392.904 67.9 lineto +395.504 67.9 lineto +399.004 67.9 lineto +401.504 67.9 lineto +404.104 137.4 lineto +408.304 137.4 lineto +410.804 67.9 lineto +413.604 67.9 lineto +416.104 67.9 lineto +418.705 67.9 lineto +421.205 67.9 lineto +423.805 67.9 lineto +426.906 67.9 lineto +429.406 67.9 lineto +432.006 67.9 lineto +434.706 276.4 lineto +437.306 67.9 lineto +439.806 67.9 lineto +442.407 67.9 lineto +445.407 67.9 lineto +447.907 276.4 lineto +450.507 67.9 lineto +453.507 67.9 lineto +456.507 67.9 lineto +459.107 67.9 lineto +462.007 67.9 lineto +464.507 206.9 lineto +467.607 206.9 lineto +470.408 137.4 lineto +473.008 67.9 lineto +476.208 67.9 lineto +478.708 67.9 lineto +481.808 67.9 lineto +484.508 67.9 lineto +487.108 67.9 lineto +490.209 67.9 lineto +492.709 67.9 lineto +495.309 67.9 lineto +498.109 67.9 lineto +501.109 67.9 lineto +503.71 67.9 lineto +506.609 67.9 lineto +509.509 137.4 lineto +512.009 67.9 lineto +514.509 67.9 lineto +518.309 137.4 lineto +521.41 67.9 lineto +524.01 67.9 lineto +526.71 67.9 lineto +529.41 206.9 lineto +532.21 67.9 lineto +534.71 137.4 lineto +537.31 67.9 lineto +539.911 206.9 lineto +542.511 276.4 lineto +545.111 67.9 lineto +547.611 67.9 lineto +550.712 137.4 lineto +553.412 137.4 lineto +stroke +0 0 0 setrgbcolor +[] 0 setdash +1 setlinewidth +0 setlinejoin +0 setlinecap +newpath +71.9 54 moveto +71.9 422.4 lineto +579.2 422.4 lineto +579.2 54 lineto +71.9 54 lineto +closepath +stroke +grestore +showpage +%%EOF diff --git a/docs/book/development.txt b/docs/book/development.txt new file mode 100644 index 00000000..3320b1d3 --- /dev/null +++ b/docs/book/development.txt @@ -0,0 +1,235 @@ +== Using the development version + +A few notes on the waf development follow. + +=== Execution traces + +==== Logging + +The generic flags to add more information to the stack traces or to the messages is '-v' (verbosity), it is used to display the command-lines executed during a build: + +[source,shishell] +--------------- +$ waf -v +--------------- + +To display all the traces (useful for bug reports), use the following flag: + +[source,shishell] +--------------- +$ waf -vvv +--------------- + +Debugging information can be filtered easily with the flag 'zones': + +[source,shishell] +--------------- +$ waf --zones=action +--------------- + +Tracing zones must be comma-separated, for example: + +[source,shishell] +--------------- +$ waf --zones=action,envhash,task_gen +--------------- + +The Waf module 'Logs' replaces the Python module logging. In the source code, traces are provided by using the 'debug' function, they must obey the format "zone: message" like in the following: + +[source,python] +--------------- +Logs.debug("task: executing %r - it was never run before or its class changed" % self) +--------------- + +The following zones are used in Waf: + +.Debugging zones +[options="header",cols='1,5'] +|================= +|Zone | Description +|runner | command-lines executed (enabled when -v is provided without debugging zones) +|deps | implicit dependencies found (task scanners) +|task_gen| task creation (from task generators) and task generator method execution +|action | functions to execute for building the targets +|env | environment contents +|envhash | hashes of the environment objects - helps seeing what changes +|build | build context operations such as filesystem access +|preproc | preprocessor execution +|group | groups and task generators +|================= + +WARNING: Debugging information can be displayed only after the command-line has been parsed. For example, no debugging information will be displayed when a waf tool is being by for the command-line options 'opt.load()' or by the global init method function 'init.tool()' + +==== Build visualization + +The Waf tool named _parallel_debug_ is used to inject code in Waf modules and to obtain a detailed execution trace. This module is provided in the folder +waflib/extras+ and must be imported in one's project before use: + +[source,python] +--------------- +def options(ctx): + ctx.load('parallel_debug', tooldir='.') + +def configure(ctx): + ctx.load('parallel_debug', tooldir='.') + +def build(ctx): + bld(rule='touch ${TGT}', target='foo') +--------------- + +The execution will generate a diagram of the tasks executed during the build in the file +pdebug.svg+: + +image::pdebug{PIC}["Parallel execution diagram"{backend@docbook:,width=500:},align="center"] + +The details will be generated in the file +pdebug.dat+ as space-separated values. The file can be processed by other applications such as Gnuplot to obtain other diagrams: + +[source,shishell] +--------------- +#! /usr/bin/env gnuplot +set terminal png +set output "output.png" +set ylabel "Amount of active threads" +set xlabel "Time in seconds" +set title "Active threads on a parallel build (waf -j5)" +unset label +set yrange [-0.1:5.2] +set ytic 1 +plot 'pdebug.dat' using 3:7 with lines title "" lt 2 +--------------- + +image::dev{PIC}["Thread activity during the build"{backend@docbook:,width=410:},align="center"] + +The data file columns are the following: + +.pdebug file format +[options="header", cols="1,2,6"] +|================= +|Column | Type | Description +|1 |int| Identifier of the thread which has started or finished processing a task +|2 |int| Identifier of the task processed +|3 |float| Event time +|4 |string| Type of the task processed +|5 |int| Amount of tasks processed +|6 |int| Amount of tasks waiting to be processed by the task consumers +|7 |int| Amount of active threads +|================= + + +=== Profiling + +==== Benchmark projects + +The script +utils/genbench.py+ is used as a base to create large c-like project files. The habitual use is the following: + +[source,shishell] +--------------- +$ utils/genbench.py /tmp/build 50 100 15 5 +$ cd /tmp/build +$ waf configure +$ waf -p -j2 +--------------- + +The C++ project created will generate 50 libraries from 100 class files for each, each source file having 15 include headers pointing at the same library and 5 headers pointing at other headers randomly chosen. + +The compilation time may be discarded easily by disabling the actual compilation, for example: + +[source,python] +--------------- +def build(bld): + from waflib import Task + def touch_func(task): + for x in task.outputs: + x.write('') + for x in Task.TaskBase.classes.keys(): + cls = Task.TaskBase.classes[x] + cls.func = touch_func + cls.color = 'CYAN' +--------------- + +==== Profile traces + +Profiling information is obtained by calling the module cProfile and by injecting specific code. The most interesting methods to profile is 'waflib.Build.BuildContext.compile'. The amount of function calls is usually a bottleneck, and reducing it results in noticeable speedups. Here is an example on the method compile: + +[source,python] +--------------- +from waflib.Build import BuildContext +def ncomp(self): + import cProfile, pstats + cProfile.runctx('self.orig_compile()', {}, {'self': self}, 'profi.txt') + p = pstats.Stats('profi.txt') + p.sort_stats('time').print_stats(45) + +BuildContext.orig_compile = BuildContext.compile +BuildContext.compile = ncomp +--------------- + +Here the output obtained on a benchmark build created as explained in the previous section: + +[source,shishell] +--------------- +Fri Jul 23 15:11:15 2010 profi.txt + + 1114979 function calls (1099879 primitive calls) in 5.768 CPU seconds + + Ordered by: internal time + List reduced from 139 to 45 due to restriction 45 + + ncalls tottime percall cumtime percall filename:lineno(function) + 109500 0.523 0.000 1.775 0.000 /comp/waf/waflib/Node.py:615(get_bld_sig) + 5000 0.381 0.000 1.631 0.000 /comp/waf/waflib/Task.py:475(compute_sig_implicit_deps) + 154550 0.286 0.000 0.286 0.000 {method 'update' of '_hashlib.HASH' objects} + 265350 0.232 0.000 0.232 0.000 {id} +40201/25101 0.228 0.000 0.228 0.000 /comp/waf/waflib/Node.py:319(abspath) + 10000 0.223 0.000 0.223 0.000 {open} + 20000 0.197 0.000 0.197 0.000 {method 'read' of 'file' objects} + 15000 0.193 0.000 0.349 0.000 /comp/waf/waflib/Task.py:270(uid) + 10000 0.189 0.000 0.850 0.000 /comp/waf/waflib/Utils.py:96(h_file) +--------------- + +A few known hot spots are present in the library: + +. The persistence implemented by the cPickle module (the cache file to serialize may take a few megabytes) +. Accessing configuration data from the Environment instances +. Computing implicit dependencies in general + +==== Optimizations tips + +The Waf source code has already been optimized in various ways. In practice, the projects may use additional assumptions to replace certain methods or parameters from its build scripts. For example, if a project is always executed on Windows, then the _framework_ and _rpath_ variables may be removed: + +[source,python] +--------------- +from waflib.Tools.ccroot import USELIB_VARS +USELIB_VARS['cprogram'] = USELIB_VARS['cxxprogram'] = \ + set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'LINKDEPS']) +--------------- + +=== Waf programming + +==== Setting up a Waf directory for development + +Waf is hosted on http://code.google.com/p/waf/source[Google code], and uses Subversion for source control. To obtain the development copy, use: + +[source,shishell] +--------------- +$ svn checkout http://waf.googlecode.com/svn/trunk/ waf-read-only +$ cd waf-read-only +$ ./waf-light --make-waf +--------------- + +To avoid regenerating Waf each time, the environment variable *WAFDIR* should be used to point at the directory containing _waflib_: + +[source,shishell] +--------------- +$ export WAFDIR=/path/to/directory/ +--------------- + +==== Specific guidelines + +Though Waf is written in Python, additional restrictions apply to the source code: + +. Identation is tab-only, and the maximum line length should be about 200 characters +. The development code is kept compatible with Python 2.3, to the exception of decorators in the Tools directory. In particular, the Waf binary can be generated using Python 2.3 +. The _waflib_ modules must be insulated from the _Tools_ modules to keep the Waf core small and language independent +. Api compatibility is maintained in the cycle of a minor version (from 1.5.0 to 1.5.n) + +NOTE: More code always means more bugs. Whenever possible, unnecessary code must be removed, and the existing code base should be simplified. + diff --git a/docs/book/download.txt b/docs/book/download.txt new file mode 100644 index 00000000..947a757a --- /dev/null +++ b/docs/book/download.txt @@ -0,0 +1,154 @@ +== Download and installation + +=== Obtaining the Waf file + +The Waf project is located on http://code.google.com/p/waf[Google Code]. +The current Waf version requires an interpreter for the Python programming language such as http://www.python.org[cPython] 2.3 to 3.1 or http://www.jython.org[Jython] >= 2.5. + +==== Downloading and using the Waf binary + +The Waf binary is a python script which does not require any installation whatsoever. It may be executed directly from a writeable folder. Just rename it as +waf+ if necessary: + +[source,shishell] +--------------- +$ wget http://waf.googlecode.com/files/waf-1.6.8 +$ mv waf-1.6.8 waf +$ python waf --version +waf 1.6.8 (11500) +--------------- + +The +waf+ file has its own library compressed in a binary stream in the same file. Upon execution, the library is uncompressed in a hidden folder in the current directory. The folder will be re-created if removed. This scheme enables different Waf versions to be executed from the same folders: + +[source,shishell] +--------------- +$ ls -ld .waf* +.waf-1.6.8-2c924e3f453eb715218b9cc852291170 +--------------- + +NOTE: The binary file requires http://docs.python.org/library/bz2.html[bzip2] compression support, which may be unavailable in some self-compiled cPython installations. + +==== Building Waf from the source code + +Building Waf requires a Python interpreter having a version number in the range 2.6-3.1. The source code is then processed to support Python 2.3, 2.4 and 2.5. + +[source,shishell] +--------------- +$ wget http://waf.googlecode.com/files/waf-1.6.8.tar.bz2 +$ tar xjvf waf-1.6.8.tar.bz2 +$ cd waf-1.6.8 +$ python waf-light +Configuring the project +'build' finished successfully (0.001s) +Checking for program python : /usr/bin/python +Checking for python version : (2, 6, 5, 'final', 0) +'configure' finished successfully (0.176s) +Waf: Entering directory `/waf-1.6.8/build' +[1/1] create_waf: -> waf +Waf: Leaving directory `/waf-1.6.8/build' +'build' finished successfully (2.050s) +--------------- + +For older interpreters, it is possible to build the +waf+ file with gzip compression instead of bzip2: + +[source,shishell] +--------------- +$ python waf-light --zip-type=gz +--------------- + +The files present in the folder _waflib/extras_ represent extensions (Waf tools) that are in a testing phase. They may be added to the Waf binary by using the _--tools_ switch: + +[source,shishell] +--------------- +$ python waf-light --tools=compat15,swig,doxygen +--------------- + +The tool _compat15_ is required to provide some compatibility with previous Waf versions. +To remove it, it is necessary to modify the initialization by changing the _--prelude_ switch: + +[source,shishell] +--------------- +$ python waf-light --make-waf --prelude='' --tools=swig +--------------- + +Finally, here is how to import an external tool and load it in the initialization. Assuming the file `aba.py` is present in the current directory: + +[source,python] +--------------- +def foo(): + from waflib.Context import WAFVERSION + print("This is Waf %s" % WAFVERSION) +--------------- + +The following will create a custom waf file which will import and execute 'foo' whenever it is executed: + +[source,shishell] +--------------- +$ python waf-light --make-waf --tools=compat15,$PWD/aba.py + --prelude=$'\tfrom waflib.extras import aba\n\taba.foo()' +$ ./waf --help +This is Waf 1.6.8 +[...] +--------------- + +Foreign files to add into the folder 'extras' must be given by absolute paths in the _--tools_ switch. +Such files do not have to be Python files, yet, a typical scenario is to add an initializer to modify existing +functions and classes from the Waf modules. Various from the http://code.google.com/p/waf/source/browse/trunk/build_system_kit/[build system kit] illustrate how to create custom +build systems derived from Waf. + +=== Using the waf file + +==== Permissions and aliases + +Because the waf script is a python script, it is usually executed by calling +python+ on it: + +[source,shishell] +--------------- +$ python waf +--------------- + +On unix-like systems, it is usually much more convenient to set the executable permissions and avoid calling +python+ each time: + +[source,shishell] +--------------- +$ chmod 755 waf +$ ./waf --version +waf 1.6.8 (11500) +--------------- + +If the command-line interpreter supports aliases, it is recommended to set the alias once: + +[source,shishell] +--------------- +$ alias waf=$PWD/waf +$ waf --version +waf 1.6.8 (11500) +--------------- + +Or, the execution path may be modified to point at the location of the waf binary: + +[source,shishell] +--------------- +$ export PATH=$PWD:$PATH +$ waf --version +waf 1.6.8 (11500) +--------------- + +In the next sections of the book, we assume that either an alias or the execution path have been set in a way that +waf+ may be called directly. + +==== Local waflib folders + +Although the waf library is unpacked automatically from the waf binary file, it is sometimes necessary to keep the files in a visible folder, which may even be kept in a source control tool (subversion, git, etc). For example, the +waf-light+ script does not contain the waf library, yet it is used to create the +waf+ script by using the directory +waflib+. + +The following diagram represents the process used to find the +waflib+ directory: + +image::waflib{PIC}["Waflib discovery"{backend@docbook:,width=220:},align="center"] + + +==== Portability concerns + +By default, the recommended Python interpreter is cPython, for which the supported versions are 2.3 to 3.1. For maximum convenience for the user, a copy of the http://www.jython.org[Jython] interpreter in the version 2.5 may be redistributed along with a copy of the Waf executable. + +NOTE: A project containing 'waf', 'jython2.5.jar' and the source code may be used almost anywhere. + +WARNING: The 'waf' script must reside in a writable folder to unpack its cache files. + diff --git a/docs/book/examples/advbuild_cmdtool/some_tool.py b/docs/book/examples/advbuild_cmdtool/some_tool.py new file mode 100644 index 00000000..aad90b78 --- /dev/null +++ b/docs/book/examples/advbuild_cmdtool/some_tool.py @@ -0,0 +1,10 @@ +#! /usr/bin/env python + +from waflib import Context + +def cnt(ctx): + """do something""" + print('just a test') + +Context.g_module.__dict__['cnt'] = cnt + diff --git a/docs/book/examples/advbuild_cmdtool/wscript b/docs/book/examples/advbuild_cmdtool/wscript new file mode 100644 index 00000000..c2001ea7 --- /dev/null +++ b/docs/book/examples/advbuild_cmdtool/wscript @@ -0,0 +1,17 @@ +#! /usr/bin/env python + +""" +Execute +$ 'waf cnt' +the cnt context is defined in 'some_tool.py' +""" + +top = '.' +out = 'build' + +def options(opt): + opt.load('some_tool', tooldir='.') + +def configure(conf): + pass + diff --git a/docs/book/examples/advbuild_composition/wscript b/docs/book/examples/advbuild_composition/wscript new file mode 100644 index 00000000..1f6caf9f --- /dev/null +++ b/docs/book/examples/advbuild_composition/wscript @@ -0,0 +1,17 @@ +#! /usr/bin/env python + +""" +Calling 'waf clean build' can be shortened to 'waf cleanbuild' +The cleanbuild command is defined below +""" + +def configure(ctx): + pass + +def build(ctx): + pass + +def cleanbuild(ctx): + from waflib import Options + Options.commands = ['clean', 'build'] + Options.commands + diff --git a/docs/book/examples/advbuild_confdata/wscript b/docs/book/examples/advbuild_confdata/wscript new file mode 100644 index 00000000..9ec249dd --- /dev/null +++ b/docs/book/examples/advbuild_confdata/wscript @@ -0,0 +1,20 @@ +#! /usr/bin/env python + +""" +the command 'foo' uses a build context subclass internally +try calling 'waf configure foo' +""" + +def configure(ctx): + ctx.env.FOO = 'some data' + +def build(ctx): + print('build command') + +def foo(ctx): + print(ctx.env.FOO) + +from waflib.Build import BuildContext +class one(BuildContext): + cmd = 'foo' + fun = 'foo' diff --git a/docs/book/examples/advbuild_subclass/wscript b/docs/book/examples/advbuild_subclass/wscript new file mode 100644 index 00000000..985076e3 --- /dev/null +++ b/docs/book/examples/advbuild_subclass/wscript @@ -0,0 +1,27 @@ +#! /usr/bin/env python + +""" +Context commands usually derive from different classe. +The command 'foo' uses a normal context, while bar uses +a different class. Try executing +$ waf configure foo bar tak +""" + +def configure(ctx): + print(type(ctx)) + +def foo(ctx): + print(type(ctx)) + +def bar(ctx): + print(type(ctx)) + +from waflib.Context import Context + +class one(Context): + cmd = 'foo' + +class two(Context): + cmd = 'tak' + fun = 'bar' + diff --git a/docs/book/examples/advbuild_testcase/wscript b/docs/book/examples/advbuild_testcase/wscript new file mode 100644 index 00000000..c2f8130a --- /dev/null +++ b/docs/book/examples/advbuild_testcase/wscript @@ -0,0 +1,36 @@ +#! /usr/bin/env python + +""" +Commands may aggregate other commands + +Try for example +$ waf configure +$ waf test +""" + +def options(ctx): + ctx.load('compiler_c') + +def configure(ctx): + ctx.load('compiler_c') + +def setup(ctx): + n = ctx.path.make_node('main.c') + n.write('#include "foo.h"\nint main() {return 0;}\n') + + global v + m = ctx.path.make_node('foo.h') + m.write('int k = %d;\n' % v) + v += 1 + +def build(ctx): + ctx.program(source='main.c', target='app') + +def test(ctx): + global v + v = 12 + + from waflib import Options + lst = ['configure', 'setup', 'build', 'setup', 'build'] + Options.commands = lst + Options.commands + diff --git a/docs/book/examples/advbuild_variant/wscript b/docs/book/examples/advbuild_variant/wscript new file mode 100644 index 00000000..3a102ba5 --- /dev/null +++ b/docs/book/examples/advbuild_variant/wscript @@ -0,0 +1,20 @@ +#! /usr/bin/env python + +""" +Typical projects are build by using: +$ waf configure build +Other build commands may build the same project in a different output directory +$ waf configure debug +""" + +def configure(ctx): + pass + +def build(ctx): + ctx(rule='touch ${TGT}', target=ctx.cmd + '.txt') + +from waflib.Build import BuildContext +class debug(BuildContext): + cmd = 'debug' + variant = 'debug' + diff --git a/docs/book/examples/advbuild_variant_env/main.c b/docs/book/examples/advbuild_variant_env/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/docs/book/examples/advbuild_variant_env/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/docs/book/examples/advbuild_variant_env/wscript b/docs/book/examples/advbuild_variant_env/wscript new file mode 100644 index 00000000..abcc7104 --- /dev/null +++ b/docs/book/examples/advbuild_variant_env/wscript @@ -0,0 +1,43 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +""" +Define build commands for several variants at once +Try executing +$ waf clean_debug build_debug clean_release build_release +""" + +VERSION='0.0.1' +APPNAME='cc_test' + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.setenv('debug') + conf.load('compiler_c') + conf.env.CFLAGS = ['-g'] + + conf.setenv('release') + conf.load('compiler_c') + conf.env.CFLAGS = ['-O2'] + +def build(bld): + if not bld.variant: + bld.fatal('call "waf build_debug" or "waf build_release", and try "waf --help"') + bld.program(source='main.c', target='app', includes='.') + +from waflib.Build import BuildContext, CleanContext, \ + InstallContext, UninstallContext + +for x in 'debug release'.split(): + for y in (BuildContext, CleanContext, InstallContext, UninstallContext): + name = y.__name__.replace('Context','').lower() + class tmp(y): + cmd = name + '_' + x + variant = x + diff --git a/docs/book/examples/advbuild_waflock/wscript b/docs/book/examples/advbuild_waflock/wscript new file mode 100644 index 00000000..74f5ed4a --- /dev/null +++ b/docs/book/examples/advbuild_waflock/wscript @@ -0,0 +1,18 @@ +#! /usr/bin/env python + +""" +Here we do not define the top or the out folders +The build directory may be controlled by setting +the WAFLOCK variable with the name convention .lock-wafNAME +where NAME will be the build directory + +WAFLOCK=.lock-waffoo waf configure build +WAFLOCK=.lock-wafbar waf configure build +""" + +def configure(conf): + pass + +def build(bld): + bld(rule='touch ${TGT}', target='foo.txt') + diff --git a/docs/book/examples/architecture_link/faa.ext b/docs/book/examples/architecture_link/faa.ext new file mode 100644 index 00000000..2b3145e1 --- /dev/null +++ b/docs/book/examples/architecture_link/faa.ext @@ -0,0 +1,2 @@ +gibberish there + diff --git a/docs/book/examples/architecture_link/foo.ext b/docs/book/examples/architecture_link/foo.ext new file mode 100644 index 00000000..83599ef3 --- /dev/null +++ b/docs/book/examples/architecture_link/foo.ext @@ -0,0 +1 @@ +gibberish here diff --git a/docs/book/examples/architecture_link/wscript b/docs/book/examples/architecture_link/wscript new file mode 100644 index 00000000..b3eb15f4 --- /dev/null +++ b/docs/book/examples/architecture_link/wscript @@ -0,0 +1,36 @@ +#! /usr/bin/env python + +""" +This example demonstrates how to create custom compilation and link tasks + +NOTE: to avoid redefining the method call_apply_link, you could use this: +import waflib.TaskGen +waflib.TaskGen.feats['mylink'] = ['apply_link'] +""" + +def configure(ctx): + pass + +def build(ctx): + ctx(features='mylink', source='foo.ext faa.ext', target='bingo') + +from waflib.Task import Task +from waflib.TaskGen import feature, extension, after_method +from waflib.Tools import ccroot + +@after_method('process_source') +@feature('mylink') +def call_apply_link(self): + self.apply_link() + +class mylink(ccroot.link_task): + run_str = 'cat ${SRC} > ${TGT}' + +class ext2o(Task): + run_str = 'cp ${SRC} ${TGT}' + +@extension('.ext') +def process_ext(self, node): + self.create_compiled_task('ext2o', node) + + diff --git a/docs/book/examples/architecture_subcontext/wscript b/docs/book/examples/architecture_subcontext/wscript new file mode 100644 index 00000000..0c26b64a --- /dev/null +++ b/docs/book/examples/architecture_subcontext/wscript @@ -0,0 +1,43 @@ +#! /usr/bin/env python + +""" +Task generators may create any kind of task + +In this example, the tasks create configuration contexts +and execute configuration tests. + +Execute +$ waf configure build +""" + +import os +from waflib.Configure import conf, ConfigurationContext +from waflib import Task, Build, Logs + +def run_test(self): + top = self.generator.bld.srcnode.abspath() + out = self.generator.bld.bldnode.abspath() + + ctx = ConfigurationContext(top_dir=top, out_dir=out) + ctx.init_dirs() + + ctx.in_msg = 1 + ctx.env = self.env.derive() + + header = self.generator.header_name + logfile = self.generator.path.get_bld().abspath() + os.sep \ + + header + '.log' + ctx.logger = Logs.make_logger(logfile, header) + + ctx.check(header_name=header) + +def options(ctx): + ctx.load('compiler_c') + +def configure(ctx): + ctx.load('compiler_c') + +def build(ctx): + ctx(rule=run_test, always=True, header_name='stdio.h') + ctx(rule=run_test, always=True, header_name='unistd.h') + diff --git a/docs/book/examples/build_lazy_tg/wscript b/docs/book/examples/build_lazy_tg/wscript new file mode 100644 index 00000000..cdc0f73f --- /dev/null +++ b/docs/book/examples/build_lazy_tg/wscript @@ -0,0 +1,20 @@ +#! /usr/bin/env python + +""" +Task generators do not create their tasks immediately +Here is an illustration + +$ waf configure clean build +""" + +def configure(ctx): + pass + +def build(ctx): + tg = ctx(rule='touch ${TGT}', target='foo') + print(type(tg)) + print(tg.tasks) + tg.post() + print(tg.tasks) + print(type(tg.tasks[0])) + diff --git a/docs/book/examples/build_list/wscript b/docs/book/examples/build_list/wscript new file mode 100644 index 00000000..c036015e --- /dev/null +++ b/docs/book/examples/build_list/wscript @@ -0,0 +1,20 @@ +#! /usr/bin/env python + +""" +To list the targets to build, use 'waf list' + +Try the following +$ waf configure clean build +$ waf list +$ waf clean build --targets=bar +""" + +top = '.' +out = 'build' + +def configure(ctx): + pass + +def build(ctx): + ctx(source='wscript', target='foo.txt', rule='cp ${SRC} ${TGT}') + ctx(target='bar.txt', rule='touch ${TGT}', name='bar') diff --git a/docs/book/examples/build_manual_tasks/wscript b/docs/book/examples/build_manual_tasks/wscript new file mode 100644 index 00000000..b511cccc --- /dev/null +++ b/docs/book/examples/build_manual_tasks/wscript @@ -0,0 +1,53 @@ +#! /usr/bin/env python + +""" +Task objects may be created manually +This is tedious and leads to spaghetti code + +Yet, it it interesting to see this to understand +why the task generator abstraction is necessary + +$ waf configure build +""" + +def configure(ctx): + pass + +from waflib.Task import Task +class cp(Task): + def run(self): + return self.exec_command('cp %s %s' % ( + self.inputs[0].abspath(), + self.outputs[0].abspath() + ) + ) + +class cat(Task): + def run(self): + return self.exec_command('cat %s %s > %s' % ( + self.inputs[0].abspath(), + self.inputs[1].abspath(), + self.outputs[0].abspath() + ) + ) + +def build(ctx): + + cp_1 = cp(env=ctx.env) + cp_1.set_inputs(ctx.path.find_resource('wscript')) + cp_1.set_outputs(ctx.path.find_or_declare('foo.txt')) + ctx.add_to_group(cp_1) + + cp_2 = cp(env=ctx.env) + cp_2.set_inputs(ctx.path.find_resource('wscript')) + cp_2.set_outputs(ctx.path.find_or_declare('bar.txt')) + ctx.add_to_group(cp_2) + + cat_1 = cat(env=ctx.env) + cat_1.set_inputs(cp_1.outputs + cp_2.outputs) + cat_1.set_outputs(ctx.path.find_or_declare('foobar.txt')) + ctx.add_to_group(cat_1) + + cat_1.set_run_after(cp_1) + cat_1.set_run_after(cp_2) + diff --git a/docs/book/examples/build_manual_tasks/wscript-noshell b/docs/book/examples/build_manual_tasks/wscript-noshell new file mode 100644 index 00000000..8502b642 --- /dev/null +++ b/docs/book/examples/build_manual_tasks/wscript-noshell @@ -0,0 +1,42 @@ +#! /usr/bin/env python + +""" +The wscript file in this directory uses external commands in the +task execution. We may as well use node objects to write to them +directly. +""" + +def configure(ctx): + pass + +from waflib.Task import Task +class cp(Task): + def run(self): + txt = self.inputs[0].read() + self.outputs[0].write(txt) + +class cat(Task): + def run(self): + txt = self.inputs[0].read() + self.inputs[1].read() + self.outputs[0].write(txt) + +def build(ctx): + + cp_1 = cp(env=ctx.env) + cp_1.set_inputs(ctx.path.find_resource('wscript')) + cp_1.set_outputs(ctx.path.find_or_declare('foo.txt')) + ctx.add_to_group(cp_1) + + cp_2 = cp(env=ctx.env) + cp_2.set_inputs(ctx.path.find_resource('wscript')) + cp_2.set_outputs(ctx.path.find_or_declare('bar.txt')) + ctx.add_to_group(cp_2) + + cat_1 = cat(env=ctx.env) + cat_1.set_inputs(cp_1.outputs + cp_2.outputs) + cat_1.set_outputs(ctx.path.find_or_declare('foobar.txt')) + ctx.add_to_group(cat_1) + + cat_1.set_run_after(cp_1) + cat_1.set_run_after(cp_2) + diff --git a/docs/book/examples/build_pre_post/wscript b/docs/book/examples/build_pre_post/wscript new file mode 100644 index 00000000..73a4d169 --- /dev/null +++ b/docs/book/examples/build_pre_post/wscript @@ -0,0 +1,35 @@ +#! /usr/bin/env python + +""" +Demonstrate how to bind functions to be executed before or after the build + +Try executing: +$ waf configure clean build +""" + +top = '.' +out = 'build' + +def options(ctx): + ctx.add_option('--exe', action='store_true', default=False, + help='execute the program after it is built') + +def configure(ctx): + pass + +def pre(ctx): + print('before the build is started') + +def post(ctx): + print('after the build is complete') + if ctx.cmd == 'install': + from waflib import Options, Utils + if Options.options.exe: + ctx.exec_command('/sbin/ldconfig') + +def build(ctx): + ctx(rule='touch ${TGT}', target='bar.txt', always=True) + + ctx.add_pre_fun(pre) + ctx.add_post_fun(post) + diff --git a/docs/book/examples/build_task_gen/wscript b/docs/book/examples/build_task_gen/wscript new file mode 100644 index 00000000..0d26ba6e --- /dev/null +++ b/docs/book/examples/build_task_gen/wscript @@ -0,0 +1,17 @@ +#! /usr/bin/env python + +""" +Create task generators to create tasks when necessary, +it is equivalent to the example 'build_manual_tasks' + +$ waf configure clean build +""" + +def configure(ctx): + pass + +def build(ctx): + ctx(rule='cp ${SRC} ${TGT}', source='wscript', target='foo.txt') + ctx(rule='cp ${SRC} ${TGT}', source='wscript', target='bar.txt') + ctx(rule='cat ${SRC} > ${TGT}', source='foo.txt bar.txt', target='foobar.txt') + diff --git a/docs/book/examples/configuration_build/wscript b/docs/book/examples/configuration_build/wscript new file mode 100644 index 00000000..5a065ec8 --- /dev/null +++ b/docs/book/examples/configuration_build/wscript @@ -0,0 +1,24 @@ +#! /usr/bin/env python + +""" +The variable conf.env.TOUCH (set by conf.find_program) is re-used during the build + +Try: +$ waf configure clean build +""" + +top = '.' +out = 'build' + +def options(ctx): + ctx.add_option('--foo', action='store', default=False, help='Silly test') + +def configure(ctx): + ctx.env.FOO = ctx.options.foo + ctx.find_program('touch', var='TOUCH', mandatory=True) # a configuration helper + +def build(ctx): + print(ctx.env.TOUCH) + print(ctx.env.FOO) + ctx(rule='${TOUCH} ${TGT}', target='bar.txt') + diff --git a/docs/book/examples/configuration_copysets/wscript b/docs/book/examples/configuration_copysets/wscript new file mode 100644 index 00000000..97328aa8 --- /dev/null +++ b/docs/book/examples/configuration_copysets/wscript @@ -0,0 +1,24 @@ +#! /usr/bin/env python + +""" +The conf.env object is an instance of waflib.ConfigSet.ConfigSet + +It is used as a serializable dict to hold any kind of useful data +""" + +top = '.' +out = 'build' + +def configure(ctx): + ctx.env.FOO = 'TEST' + node = ctx.path.make_node('test.txt') + + env_copy = ctx.env.derive() + env_copy.store(node.abspath()) + + from waflib.ConfigSet import ConfigSet + env2 = ConfigSet() + env2.load(node.abspath()) + + print(node.read()) + diff --git a/docs/book/examples/configuration_dang/dang.py b/docs/book/examples/configuration_dang/dang.py new file mode 100644 index 00000000..4c7f6ea5 --- /dev/null +++ b/docs/book/examples/configuration_dang/dang.py @@ -0,0 +1,21 @@ +#! /usr/bin/env python +# encoding: utf-8 + +print('→ loading the dang tool') + +from waflib.Configure import conf + +def options(opt): + opt.add_option('--dang', action='store', default='', dest='dang') + +@conf +def read_dang(ctx): + ctx.start_msg('Checking for DANG') + if ctx.options.dang: + ctx.env.DANG = ctx.options.dang + ctx.end_msg(ctx.env.DANG) + else: + ctx.end_msg('DANG is not set') + +def configure(ctx): + ctx.read_dang() diff --git a/docs/book/examples/configuration_dang/wscript b/docs/book/examples/configuration_dang/wscript new file mode 100644 index 00000000..8ed9577c --- /dev/null +++ b/docs/book/examples/configuration_dang/wscript @@ -0,0 +1,23 @@ +#! /usr/bin/env python + +""" +The context methods may execute the same methods from waf tools + +observe how the dang.py file is used and compare + +$ waf configure build +$ waf configure build --dang=test +""" + +top = '.' +out = 'build' + +def options(ctx): + ctx.load('dang', tooldir='.') + +def configure(ctx): + ctx.load('dang', tooldir='.') + +def build(ctx): + print(ctx.env.DANG) + diff --git a/docs/book/examples/configuration_deco/wscript b/docs/book/examples/configuration_deco/wscript new file mode 100644 index 00000000..e4edf743 --- /dev/null +++ b/docs/book/examples/configuration_deco/wscript @@ -0,0 +1,25 @@ +#! /usr/bin/env python + +""" +The @conf is a python decorator - decorators are used to replace functions + +This particular decorator will bind the function to +the configuration context + +Try: +$ waf configure +""" + +top = '.' +out = 'build' + +from waflib.Configure import conf + +@conf +def hi(ctx): + print('→ hello, world!') + +# hi = conf(hi) + +def configure(ctx): + ctx.hi() diff --git a/docs/book/examples/configuration_exception/wscript b/docs/book/examples/configuration_exception/wscript new file mode 100644 index 00000000..1df947e3 --- /dev/null +++ b/docs/book/examples/configuration_exception/wscript @@ -0,0 +1,15 @@ +#! /usr/bin/env python + +""" +Tests should not rely on return codes and will return exceptions +in case of errors +""" + +top = '.' +out = 'build' + +def configure(ctx): + try: + ctx.find_program('some_app') + except ctx.errors.ConfigurationError: + ctx.to_log('some_app was not found (ignoring)') diff --git a/docs/book/examples/configuration_methods/wscript b/docs/book/examples/configuration_methods/wscript new file mode 100644 index 00000000..b82debed --- /dev/null +++ b/docs/book/examples/configuration_methods/wscript @@ -0,0 +1,17 @@ +#! /usr/bin/env python + +""" +This example illustrates several configuration methods: +. find_program for finding executables +. check_waf_version to throw a configuration error if the waf version is too old/too new +. find_file for finding files in particular folders +""" + +top = '.' +out = 'build' + +def configure(ctx): + ctx.find_program('touch', var='TOUCH') + ctx.check_waf_version(mini='1.6.0') + ctx.find_file('fstab', ['/opt/', '/etc']) + diff --git a/docs/book/examples/configuration_sets/wscript b/docs/book/examples/configuration_sets/wscript new file mode 100644 index 00000000..ebed2344 --- /dev/null +++ b/docs/book/examples/configuration_sets/wscript @@ -0,0 +1,25 @@ +#! /usr/bin/env python + +""" +The configuration set such as conf.env behave like dicts +Lists are usually stored in them, and may be shared by several +configuration sets. + +For this reason, the methods append_unique, append_value +and prepend_value should be used whenever possible +""" + +top = '.' +out = 'build' + +def configure(ctx): + ctx.env['CFLAGS'] = ['-g'] + ctx.env.CFLAGS = ['-g'] + ctx.env.append_value('CXXFLAGS', ['-O2', '-g']) + ctx.env.append_unique('CFLAGS', ['-g', '-O2']) + ctx.env.prepend_value('CFLAGS', ['-O3']) + + print(type(ctx.env)) + print(ctx.env) + print(ctx.env.FOO) + diff --git a/docs/book/examples/cprog_attributes/main.c b/docs/book/examples/cprog_attributes/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/docs/book/examples/cprog_attributes/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/docs/book/examples/cprog_attributes/wscript b/docs/book/examples/cprog_attributes/wscript new file mode 100644 index 00000000..83a0fd2f --- /dev/null +++ b/docs/book/examples/cprog_attributes/wscript @@ -0,0 +1,35 @@ +#! /usr/bin/env python + +""" +This example demonstrates a few attributes that may be passed to +bld.program, bld.shlib and bld.stlib +""" + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + bld.program( + source = 'main.c', + target = 'appname', + features = ['more', 'features', 'here'], + + includes = ['.'], + defines = ['LINUX=1', 'BIDULE'], + + lib = ['m'], + libpath = ['/usr/lib64'], + stlib = ['dl'], + stlibpath = ['/usr/local/lib'], + linkflags = ['-g'], + + install_path = '${SOME_PATH}/bin', # None to disable + vnum = '1.2.3', + ccflags = ['-O2', '-Wall'], + cxxflags = ['-O3'], + rpath = ['/opt/kde/lib'] + ) + diff --git a/docs/book/examples/cprog_cfg_advanced/wscript b/docs/book/examples/cprog_cfg_advanced/wscript new file mode 100644 index 00000000..155df9cc --- /dev/null +++ b/docs/book/examples/cprog_cfg_advanced/wscript @@ -0,0 +1,22 @@ +#! /usr/bin/env python + +""" +The configuration tests such as conf.check* do not have to execute +c/c++ tests. By changing the features, it is possible to turn +almost any kind of build into a configuration test + +Try 'waf configure' and look at the config.log file +""" + +from waflib.TaskGen import feature, before_method + +@feature('special_test') +@before_method('process_source') +def my_special_test(self): + self.bld(rule='touch ${TGT}', target='foo') + self.bld(rule='cp ${SRC} ${TGT}', source='foo', target='bar') + self.source = [] + +def configure(conf): + conf.check_cc(features='special_test', msg='my test!') + diff --git a/docs/book/examples/cprog_conf/wscript b/docs/book/examples/cprog_conf/wscript new file mode 100644 index 00000000..ad569088 --- /dev/null +++ b/docs/book/examples/cprog_conf/wscript @@ -0,0 +1,22 @@ +#! /usr/bin/env python + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + conf.check(header_name='time.h', features='c cprogram') + conf.check_cc(function_name='printf', header_name="stdio.h") + conf.check_cc(fragment='int main() {2+2==4;}\n', define_name="boobah") + conf.check_cc(lib='m', cflags='-Wall', defines=['var=foo', 'x=y'], uselib_store='M') + #conf.check_cxx(lib='linux', use='M', cxxflags='-O2') + + conf.check_cc(fragment=''' + #include + int main() { printf("4"); return 0; } ''', + define_name = "booeah", + execute = True, + define_ret = True, + msg = "Checking for something") + + conf.write_config_header('config.h') diff --git a/docs/book/examples/cprog_fakelibs/main.c b/docs/book/examples/cprog_fakelibs/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/docs/book/examples/cprog_fakelibs/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/docs/book/examples/cprog_fakelibs/wscript b/docs/book/examples/cprog_fakelibs/wscript new file mode 100644 index 00000000..c6770df7 --- /dev/null +++ b/docs/book/examples/cprog_fakelibs/wscript @@ -0,0 +1,18 @@ +#! /usr/bin/env python + +""" +Use a system library as if it were part of the project +Among others: +- it may be present in the 'use' argument +- the program will be rebuilt if the library changes (hash) +""" + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + bld.read_shlib('m', paths=['/usr/lib64']) + bld.program(source='main.c', target='app', use='m') diff --git a/docs/book/examples/cprog_incdirs/a.c b/docs/book/examples/cprog_incdirs/a.c new file mode 100644 index 00000000..a91cc3b1 --- /dev/null +++ b/docs/book/examples/cprog_incdirs/a.c @@ -0,0 +1,5 @@ +#include "a.h" + +int foo() { + return ke; +} diff --git a/docs/book/examples/cprog_incdirs/main.c b/docs/book/examples/cprog_incdirs/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/docs/book/examples/cprog_incdirs/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/docs/book/examples/cprog_incdirs/src/a.h b/docs/book/examples/cprog_incdirs/src/a.h new file mode 100644 index 00000000..6082b66c --- /dev/null +++ b/docs/book/examples/cprog_incdirs/src/a.h @@ -0,0 +1 @@ +int ke = 55; diff --git a/docs/book/examples/cprog_incdirs/wscript b/docs/book/examples/cprog_incdirs/wscript new file mode 100644 index 00000000..c34c8a7d --- /dev/null +++ b/docs/book/examples/cprog_incdirs/wscript @@ -0,0 +1,29 @@ +#! /usr/bin/env python + +""" +include paths added by export_includes are propagated to other targets +""" + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + bld( + includes = '. src', + export_includes = 'src', + name = 'com_includes') + + bld.stlib( + source = 'a.c', + target = 'shlib1', + use = 'com_includes') + + bld.program( + source = 'main.c', + target = 'app', + use = 'shlib1', + ) + diff --git a/docs/book/examples/cprog_objects/a.c b/docs/book/examples/cprog_objects/a.c new file mode 100644 index 00000000..d4e4ecb6 --- /dev/null +++ b/docs/book/examples/cprog_objects/a.c @@ -0,0 +1 @@ +int k = 44; diff --git a/docs/book/examples/cprog_objects/main.c b/docs/book/examples/cprog_objects/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/docs/book/examples/cprog_objects/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/docs/book/examples/cprog_objects/test.c b/docs/book/examples/cprog_objects/test.c new file mode 100644 index 00000000..a1b614d5 --- /dev/null +++ b/docs/book/examples/cprog_objects/test.c @@ -0,0 +1,3 @@ +int foo() { + return 0; +} diff --git a/docs/book/examples/cprog_objects/wscript b/docs/book/examples/cprog_objects/wscript new file mode 100644 index 00000000..4095250f --- /dev/null +++ b/docs/book/examples/cprog_objects/wscript @@ -0,0 +1,28 @@ +#! /usr/bin/env python + +""" +Compile some files with -O2 and others with -O3 +""" + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + bld.objects( + source = 'test.c', + ccflags = '-O3', + target = 'my_objs') + + bld.shlib( + source = 'a.c', + ccflags = '-O2', + target = 'lib1', + use = 'my_objs') + + bld.program( + source = 'main.c', + target = 'test_c_program', + use = 'lib1') diff --git a/docs/book/examples/cprog_pkgconfig/wscript b/docs/book/examples/cprog_pkgconfig/wscript new file mode 100644 index 00000000..db6220bc --- /dev/null +++ b/docs/book/examples/cprog_pkgconfig/wscript @@ -0,0 +1,24 @@ +#! /usr/bin/env python + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + + conf.check_cfg(atleast_pkgconfig_version='0.0.0') + pango_version = conf.check_cfg(modversion='pango') + + conf.check_cfg(package='pango') + conf.check_cfg(package='pango', uselib_store='MYPANGO', + args=['--cflags', '--libs']) + + conf.check_cfg(package='pango', + args=['pango >= 0.1.0', 'pango < 9.9.9', '--cflags', '--libs'], + msg="Checking for 'pango 0.1.0'") + + conf.check_cfg(path='sdl-config', args='--cflags --libs', + package='', uselib_store='SDL') + conf.check_cfg(path='mpicc', args='--showme:compile --showme:link', + package='', uselib_store='OPEN_MPI', mandatory=False) + diff --git a/docs/book/examples/cprog_propagation/a.c b/docs/book/examples/cprog_propagation/a.c new file mode 100644 index 00000000..d4e4ecb6 --- /dev/null +++ b/docs/book/examples/cprog_propagation/a.c @@ -0,0 +1 @@ +int k = 44; diff --git a/docs/book/examples/cprog_propagation/b.c b/docs/book/examples/cprog_propagation/b.c new file mode 100644 index 00000000..67ea476d --- /dev/null +++ b/docs/book/examples/cprog_propagation/b.c @@ -0,0 +1,3 @@ +int c() { + return 34; +} diff --git a/docs/book/examples/cprog_propagation/c.c b/docs/book/examples/cprog_propagation/c.c new file mode 100644 index 00000000..8a50df44 --- /dev/null +++ b/docs/book/examples/cprog_propagation/c.c @@ -0,0 +1 @@ +int kde = 4.5; diff --git a/docs/book/examples/cprog_propagation/main.c b/docs/book/examples/cprog_propagation/main.c new file mode 100644 index 00000000..a23183e1 --- /dev/null +++ b/docs/book/examples/cprog_propagation/main.c @@ -0,0 +1,5 @@ +int k; + +int main() { + return k; +} diff --git a/docs/book/examples/cprog_propagation/wscript b/docs/book/examples/cprog_propagation/wscript new file mode 100644 index 00000000..ecc5203d --- /dev/null +++ b/docs/book/examples/cprog_propagation/wscript @@ -0,0 +1,50 @@ +#! /usr/bin/env python + +""" +The program below will link against all other libraries (except the static one) +""" + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + bld.shlib( + source = 'a.c', + target = 'lib1') + + bld.stlib( + source = 'b.c', + use = 'cshlib', # add the shared library flags + target = 'lib2') + + bld.shlib( + source = 'c.c', + target = 'lib3', + use = 'lib1 lib2') + + bld.program( + source = 'main.c', + target = 'app', + use = 'lib3') + + """ + The static library from this example is completely useless, and will add the -fPIC + flags to the program which might be annoying. It will be much better + to get rid of those static libraries but if you cannot live without them, use the following: + """ + + from waflib.TaskGen import feature, after_method + @feature('c', 'cxx') + @after_method('propagate_uselib_vars', 'process_use') + def skip_cshlib_or_cxxshlib(self): + self.uselib = self.to_list(getattr(self, 'uselib', [])) + self.use = self.to_list(getattr(self, 'use', [])) + for x in ('cshlib', 'cxxshlib', 'dshlib'): + while x in self.uselib: + self.uselib.remove(x) + while x in self.use: + self.use.remove(x) + diff --git a/docs/book/examples/cprog_system/test_staticlib.c b/docs/book/examples/cprog_system/test_staticlib.c new file mode 100644 index 00000000..e590264d --- /dev/null +++ b/docs/book/examples/cprog_system/test_staticlib.c @@ -0,0 +1 @@ +int k = 334; diff --git a/docs/book/examples/cprog_system/wscript b/docs/book/examples/cprog_system/wscript new file mode 100644 index 00000000..fae5244b --- /dev/null +++ b/docs/book/examples/cprog_system/wscript @@ -0,0 +1,34 @@ +#! /usr/bin/env python + +""" +System libraries may be linked by setting flags into the variables XYZ_NAME +where NAME is added to the use keyword, and XYZ can be DEFINES, INCLUDES, LINKFLAGS, etc +""" + +import sys + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + + conf.env.INCLUDES_TEST = ['/usr/include'] + + if sys.platform != 'win32': + conf.env.DEFINES_TEST = ['TEST'] + conf.env.CFLAGS_TEST = ['-O0'] + conf.env.LIB_TEST = ['m'] + conf.env.LIBPATH_TEST = ['/usr/lib'] + conf.env.LINKFLAGS_TEST = ['-g'] + conf.env.INCLUDES_TEST = ['/opt/gnome/include'] + +def build(bld): + mylib = bld.stlib( + source = 'test_staticlib.c', + target = 'teststaticlib', + use = 'TEST') + + if mylib.env.CC_NAME == 'gcc': + mylib.cxxflags = ['-O2'] + diff --git a/docs/book/examples/cprog_use/main.c b/docs/book/examples/cprog_use/main.c new file mode 100644 index 00000000..a23183e1 --- /dev/null +++ b/docs/book/examples/cprog_use/main.c @@ -0,0 +1,5 @@ +int k; + +int main() { + return k; +} diff --git a/docs/book/examples/cprog_use/test_staticlib.c b/docs/book/examples/cprog_use/test_staticlib.c new file mode 100644 index 00000000..75b29341 --- /dev/null +++ b/docs/book/examples/cprog_use/test_staticlib.c @@ -0,0 +1 @@ +int k = 32; diff --git a/docs/book/examples/cprog_use/wscript b/docs/book/examples/cprog_use/wscript new file mode 100644 index 00000000..484b551e --- /dev/null +++ b/docs/book/examples/cprog_use/wscript @@ -0,0 +1,23 @@ +#! /usr/bin/env python + +""" +Link against existing libraries from the project +""" + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + bld.stlib( + source = 'test_staticlib.c', + target = 'mylib', + name = 'stlib1') + + bld.program( + source = 'main.c', + target = 'app', + includes = '.', + use = ['stlib1']) diff --git a/docs/book/examples/cprog_wrappers/a.c b/docs/book/examples/cprog_wrappers/a.c new file mode 100644 index 00000000..2b64da6b --- /dev/null +++ b/docs/book/examples/cprog_wrappers/a.c @@ -0,0 +1 @@ +int a = 32; diff --git a/docs/book/examples/cprog_wrappers/b.c b/docs/book/examples/cprog_wrappers/b.c new file mode 100644 index 00000000..bf54fb27 --- /dev/null +++ b/docs/book/examples/cprog_wrappers/b.c @@ -0,0 +1 @@ +int b = 32; diff --git a/docs/book/examples/cprog_wrappers/c.c b/docs/book/examples/cprog_wrappers/c.c new file mode 100644 index 00000000..aa920a12 --- /dev/null +++ b/docs/book/examples/cprog_wrappers/c.c @@ -0,0 +1 @@ +int c = 32; diff --git a/docs/book/examples/cprog_wrappers/main.c b/docs/book/examples/cprog_wrappers/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/docs/book/examples/cprog_wrappers/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/docs/book/examples/cprog_wrappers/wscript b/docs/book/examples/cprog_wrappers/wscript new file mode 100644 index 00000000..c28478ae --- /dev/null +++ b/docs/book/examples/cprog_wrappers/wscript @@ -0,0 +1,20 @@ +#! /usr/bin/env python + +""" +The 4 wrappers program, stlib, shlib and objects are aliases for bld(features='..', ..) +where the features can be +c, cshlib, cstlib, cprogram, cxx, cxxshlib, cxxstlib, cxxprogram, d, dshlib, ... +""" + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + bld.program(source='main.c', target='app', use='myshlib mystlib') + bld.stlib(source='a.c', target='mystlib') + bld.shlib(source='b.c', target='myshlib', use='myobjects') + bld.objects(source='c.c', target='myobjects') + diff --git a/docs/book/examples/execution_build/wscript b/docs/book/examples/execution_build/wscript new file mode 100644 index 00000000..9de0436a --- /dev/null +++ b/docs/book/examples/execution_build/wscript @@ -0,0 +1,19 @@ +#! /usr/bin/env python + +""" +A simple build - the file foo.txt is used both as target and as source +Note that the correct build order is computed automatically + +$ waf configure clean build +""" + +top = '.' +out = 'build_directory' + +def configure(ctx): + pass + +def build(ctx): + ctx(rule='touch ${TGT}', target='foo.txt') + ctx(rule='cp ${SRC} ${TGT}', source='foo.txt', target='bar.txt') + diff --git a/docs/book/examples/execution_cmd/wscript b/docs/book/examples/execution_cmd/wscript new file mode 100644 index 00000000..e0facb76 --- /dev/null +++ b/docs/book/examples/execution_cmd/wscript @@ -0,0 +1,19 @@ +#! /usr/bin/env python + +""" +Several build commands are defined by default, for example: +$ waf configure clean build list install uninstall +""" + +top = '.' +out = 'build_directory' + +def configure(ctx): + print(ctx.cmd) + +def build(ctx): + if ctx.cmd == 'clean': + print('cleaning!') + else: + print(ctx.cmd) + diff --git a/docs/book/examples/execution_configure/src/wscript b/docs/book/examples/execution_configure/src/wscript new file mode 100644 index 00000000..981fe128 --- /dev/null +++ b/docs/book/examples/execution_configure/src/wscript @@ -0,0 +1,4 @@ +#! /usr/bin/env python + +def ping(ctx): + print('→ ping from ' + ctx.path.abspath()) diff --git a/docs/book/examples/execution_configure/wscript b/docs/book/examples/execution_configure/wscript new file mode 100644 index 00000000..d1cbb336 --- /dev/null +++ b/docs/book/examples/execution_configure/wscript @@ -0,0 +1,15 @@ +#! /usr/bin/env python + +""" +$ waf configure ping +""" + +top = '.' +out = 'build_directory' + +def configure(ctx): + print('→ configuring the project in ' + ctx.path.abspath()) + +def ping(ctx): + print('→ ping from ' + ctx.path.abspath()) + ctx.recurse('src') diff --git a/docs/book/examples/execution_dist/wscript b/docs/book/examples/execution_dist/wscript new file mode 100644 index 00000000..c2889340 --- /dev/null +++ b/docs/book/examples/execution_dist/wscript @@ -0,0 +1,22 @@ +#! /usr/bin/env python + +""" +$ waf configure dist +""" + +APPNAME = 'webe' +VERSION = '1.0' + +top = '.' +out = 'build_directory' + +def configure(ctx): + print('→ configuring the project in ' + ctx.path.abspath()) + +def dist(ctx): + ctx.base_name = 'foo_2.0' + #ctx.base_path = ctx.path.find_node('build_directory') + ctx.algo = 'zip' + ctx.excl = ' **/.waf-1* **/*~ **/*.pyc **/*.swp **/.lock-w*' + ctx.files = ctx.path.ant_glob('**/wscript') + diff --git a/docs/book/examples/execution_hello/wscript b/docs/book/examples/execution_hello/wscript new file mode 100644 index 00000000..f2c1a8db --- /dev/null +++ b/docs/book/examples/execution_hello/wscript @@ -0,0 +1,10 @@ +#! /usr/bin/env python + +""" +Move the folder to /tmp for example, and execute +$ waf hello +""" + +def hello(ctx): + print('hello, world!') + diff --git a/docs/book/examples/execution_ping/wscript b/docs/book/examples/execution_ping/wscript new file mode 100644 index 00000000..f2971e55 --- /dev/null +++ b/docs/book/examples/execution_ping/wscript @@ -0,0 +1,14 @@ +#! /usr/bin/env python + +""" +Move the folder to /tmp for example and call +$ waf ping pong + +The context objects 'ctx' are different instances +""" + +def ping(ctx): + print(' ping! %d' % id(ctx)) + +def pong(ctx): + print(' pong! %d' % id(ctx)) diff --git a/docs/book/examples/execution_recurse/src/wscript b/docs/book/examples/execution_recurse/src/wscript new file mode 100644 index 00000000..c2857340 --- /dev/null +++ b/docs/book/examples/execution_recurse/src/wscript @@ -0,0 +1,4 @@ +#! /usr/bin/env python + +def ping(ctx): + print('→ ping from ' + ctx.path.abspath()) diff --git a/docs/book/examples/execution_recurse/wscript b/docs/book/examples/execution_recurse/wscript new file mode 100644 index 00000000..d53e019a --- /dev/null +++ b/docs/book/examples/execution_recurse/wscript @@ -0,0 +1,16 @@ +#! /usr/bin/env python + +""" +The following command: +$ waf ping +Will not give the expected result because this folder belongs to a bigger project. +Call 'configure' first: +$ waf configure +Then after: +$ waf ping +""" + +def ping(ctx): + print('→ ping from ' + ctx.path.abspath()) + ctx.recurse('src') + diff --git a/docs/book/examples/nodes_ant_glob/wscript b/docs/book/examples/nodes_ant_glob/wscript new file mode 100644 index 00000000..628a639e --- /dev/null +++ b/docs/book/examples/nodes_ant_glob/wscript @@ -0,0 +1,19 @@ +#! /usr/bin/env python + +""" +A few examples of ant_glob. Try +$ waf configure dosomething +""" + +top = '.' +out = 'build' + +def configure(ctx): + pass + +def dosomething(ctx): + print(ctx.path.ant_glob('wsc*')) + print(ctx.path.ant_glob('w?cr?p?')) + print(ctx.root.ant_glob('usr/include/**/zlib*', dir=False, src=True)) + print(ctx.path.ant_glob(['**/*py'], excl=['**/default*'])) + diff --git a/docs/book/examples/nodes_cache/wscript b/docs/book/examples/nodes_cache/wscript new file mode 100644 index 00000000..6472dc7d --- /dev/null +++ b/docs/book/examples/nodes_cache/wscript @@ -0,0 +1,18 @@ +#! /usr/bin/env python + +""" +The node objects behave like singletons (one node <-> one path) + +Try: +$ waf configure dosomething +""" + +top = '.' +out = 'build' + +def configure(ctx): + pass + +def dosomething(ctx): + print(ctx.root.children) + diff --git a/docs/book/examples/nodes_search/wscript b/docs/book/examples/nodes_search/wscript new file mode 100644 index 00000000..eacf4026 --- /dev/null +++ b/docs/book/examples/nodes_search/wscript @@ -0,0 +1,33 @@ +#! /usr/bin/env python + +""" +find_node -> create or return a node corresponding to an existing path +make_node -> create a node object, even if there is no folder or file +search -> search for a node in the existing node hierarchy (do not ask the file system) + +Try: +$ waf configure dosomething +""" + +top = '.' +out = 'build' + +def configure(ctx): + pass + +def dosomething(ctx): + print(ctx.path.find_node('wscript')) + + nd1 = ctx.path.make_node('foo.txt') + print(nd1) + + nd2 = ctx.path.search('foo.txt') + print(nd2) + + nd3 = ctx.path.search('bar.txt') + print(nd3) + + nd2.write('some text') + print(nd2.read()) + + print(ctx.path.listdir()) diff --git a/docs/book/examples/nodes_tree/wscript b/docs/book/examples/nodes_tree/wscript new file mode 100644 index 00000000..e8f4f252 --- /dev/null +++ b/docs/book/examples/nodes_tree/wscript @@ -0,0 +1,21 @@ +#! /usr/bin/env python + +""" +This example illustrates how to navigate in the node tree + +Try: +$ waf configure dosomething +""" + +top = '.' +out = 'build' + +def configure(ctx): + pass + +def dosomething(ctx): + print(ctx.path.abspath()) + print(ctx.root.abspath()) + print("ctx.path contents %r" % (ctx.path.children)) + print("ctx.path parent %r" % ctx.path.parent.abspath()) + print("ctx.root parent %r" % ctx.root.parent) diff --git a/docs/book/examples/rules_function/wscript b/docs/book/examples/rules_function/wscript new file mode 100644 index 00000000..84f62319 --- /dev/null +++ b/docs/book/examples/rules_function/wscript @@ -0,0 +1,32 @@ +#! /usr/bin/env python +# encoding: utf-8 + +""" +Task generators with a rule= attribute will create a single task +that will execute the corresponding function. The rule below is +equivalent to rule='cp ${SRC} ${TGT}' + +Try: +$ waf configure clean build +""" + +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + def run(task): + src = task.inputs[0].abspath() + tgt = task.outputs[0].abspath() + cmd = 'cp %s %s' % (src, tgt) + print(cmd) + return task.generator.bld.exec_command(cmd) + + bld( + rule = run, + source = 'wscript', + target = 'same.txt', + ) + diff --git a/docs/book/examples/rules_simple/wscript b/docs/book/examples/rules_simple/wscript new file mode 100644 index 00000000..66d2e779 --- /dev/null +++ b/docs/book/examples/rules_simple/wscript @@ -0,0 +1,23 @@ +#! /usr/bin/env python + +""" +A task generator that will copy a file from the source directory +to another file in the build directory + +Try: +$ waf configure clean build +""" + +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + bld( + rule = 'cp ${SRC} ${TGT}', + source = 'wscript', + target = 'foobar.txt', + ) + diff --git a/docs/book/examples/scenarios_compiler/a.src b/docs/book/examples/scenarios_compiler/a.src new file mode 100644 index 00000000..75b29341 --- /dev/null +++ b/docs/book/examples/scenarios_compiler/a.src @@ -0,0 +1 @@ +int k = 32; diff --git a/docs/book/examples/scenarios_compiler/comp.cpp b/docs/book/examples/scenarios_compiler/comp.cpp new file mode 100644 index 00000000..13520919 --- /dev/null +++ b/docs/book/examples/scenarios_compiler/comp.cpp @@ -0,0 +1,40 @@ +#include +#include + +using namespace std; + +int main(int argc, char**argv) +{ + if (argc != 3) { + cout<<"usage ./comp in out"< +#include + +void ping() { + printf("Project compiled: %s %s\n", __DATE__, __TIME__); +} diff --git a/docs/book/examples/scenarios_end/main.c b/docs/book/examples/scenarios_end/main.c new file mode 100644 index 00000000..f4be97cf --- /dev/null +++ b/docs/book/examples/scenarios_end/main.c @@ -0,0 +1,8 @@ +#include "a.h" + +int main() { + ping(); + return 0; +} + + diff --git a/docs/book/examples/scenarios_end/test_staticlib.c b/docs/book/examples/scenarios_end/test_staticlib.c new file mode 100644 index 00000000..a2449ef5 --- /dev/null +++ b/docs/book/examples/scenarios_end/test_staticlib.c @@ -0,0 +1,3 @@ +void foo() {} + + diff --git a/docs/book/examples/scenarios_end/wscript b/docs/book/examples/scenarios_end/wscript new file mode 100644 index 00000000..09fa1fec --- /dev/null +++ b/docs/book/examples/scenarios_end/wscript @@ -0,0 +1,62 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +""" +Compile 'about.c' after all other c tasks have been compiled + +$ waf configure clean build +""" + +VERSION='1.0.1' +APPNAME='cc_test' + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + bld.program( + source = 'main.c about.c', + target = 'app', + includes = '.', + use = 'my_static_lib') + + bld.stlib( + source = 'test_staticlib.c', + target = 'my_static_lib') + +import os +from waflib import Task +def runnable_status(self): + if self.inputs[0].name == 'about.c': + h = 0 + for g in self.generator.bld.groups: + for tg in g: + if isinstance(tg, Task.TaskBase): + continue + h = hash((self.generator.bld.hash_env_vars(self.generator.env, ['LINKFLAGS']), h)) + for tsk in getattr(tg, 'compiled_tasks', []): # all .c or .cpp compilations + if id(tsk) == id(self): + # but not 'about.c' (skip other tasks too if necessary) + continue + if not tsk.hasrun: + return Task.ASK_LATER + h = hash((tsk.signature(), h)) + self.env.CCDEPS = h + + try: + os.stat(self.generator.link_task.outputs[0].abspath()) + except: + return Task.RUN_ME + + return Task.Task.runnable_status(self) + +from waflib.Tools.c import c +c.runnable_status = runnable_status + diff --git a/docs/book/examples/scenarios_expansion/main.c b/docs/book/examples/scenarios_expansion/main.c new file mode 100644 index 00000000..a46866d9 --- /dev/null +++ b/docs/book/examples/scenarios_expansion/main.c @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/docs/book/examples/scenarios_expansion/wscript b/docs/book/examples/scenarios_expansion/wscript new file mode 100644 index 00000000..12beb8bc --- /dev/null +++ b/docs/book/examples/scenarios_expansion/wscript @@ -0,0 +1,44 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +""" +Add a task generator method that will expand ${} expressons from various +attributes such as includes, target, source, etc +""" + +VERSION='0.0.1' +APPNAME='cc_test' + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + bld.env.FOO = '/usr/includes' + bld.env.MAIN = 'main.c' + bld( + features = 'c cprogram', + source = '${MAIN}', + target = 'app', + includes = '. ${FOO}') + +from waflib import Utils, TaskGen +@TaskGen.feature('*') +@TaskGen.before('process_source', 'process_rule', 'apply_incpaths', 'apply_link') +def transform_strings(self): + for x in 'includes target source use libpath linkflags'.split(): + val = getattr(self, x, None) + if val: + if isinstance(val, str): + setattr(self, x, Utils.subst_vars(val, self.env)) + elif isinstance(val, list): + for i in xrange(len(val)): + if isinstance(val[i], str): + val[i] = Utils.subst_vars(val[i], self.env) + diff --git a/docs/book/examples/scenarios_idl/foo.idl b/docs/book/examples/scenarios_idl/foo.idl new file mode 100644 index 00000000..c2b636b9 --- /dev/null +++ b/docs/book/examples/scenarios_idl/foo.idl @@ -0,0 +1,3 @@ +// not a real idl file +int k = 31492; + diff --git a/docs/book/examples/scenarios_idl/main.cpp b/docs/book/examples/scenarios_idl/main.cpp new file mode 100644 index 00000000..b1f1eb6e --- /dev/null +++ b/docs/book/examples/scenarios_idl/main.cpp @@ -0,0 +1,6 @@ +#include "foo.hpp" + +int main() { + return 0; +} + diff --git a/docs/book/examples/scenarios_idl/wscript b/docs/book/examples/scenarios_idl/wscript new file mode 100644 index 00000000..c8abcf99 --- /dev/null +++ b/docs/book/examples/scenarios_idl/wscript @@ -0,0 +1,35 @@ +#! /usr/bin/env python + +""" +An idl task +$ waf configure clean build +""" + +top = '.' +out = 'build' + +def configure(conf): + conf.load('g++') + +def build(bld): + bld.program( + source = 'foo.idl main.cpp', + target = 'myapp', + includes = '.' + ) + +from waflib.Task import Task +from waflib.TaskGen import extension + +class idl(Task): + run_str = 'cp ${SRC} ${TGT[0].abspath()} && touch ${TGT[1].abspath()}' + color = 'BLUE' + ext_out = ['.h'] + +@extension('.idl') +def process_idl(self, node): + cpp_node = node.change_ext('.cpp') + hpp_node = node.change_ext('.hpp') + self.create_task('idl', node, [cpp_node, hpp_node]) + self.source.append(cpp_node) + diff --git a/docs/book/examples/scenarios_idl2/foo.idl b/docs/book/examples/scenarios_idl2/foo.idl new file mode 100644 index 00000000..c2b636b9 --- /dev/null +++ b/docs/book/examples/scenarios_idl2/foo.idl @@ -0,0 +1,3 @@ +// not a real idl file +int k = 31492; + diff --git a/docs/book/examples/scenarios_idl2/main.cpp b/docs/book/examples/scenarios_idl2/main.cpp new file mode 100644 index 00000000..b1f1eb6e --- /dev/null +++ b/docs/book/examples/scenarios_idl2/main.cpp @@ -0,0 +1,6 @@ +#include "foo.hpp" + +int main() { + return 0; +} + diff --git a/docs/book/examples/scenarios_idl2/wscript b/docs/book/examples/scenarios_idl2/wscript new file mode 100644 index 00000000..bfc608ec --- /dev/null +++ b/docs/book/examples/scenarios_idl2/wscript @@ -0,0 +1,48 @@ +#! /usr/bin/env python + +""" +share the idl outputs by other task generators +$ waf configure clean build +""" + +top = '.' +out = 'build' + +def configure(ctx): + ctx.load('g++') + +def build(ctx): + ctx( + source = 'foo.idl', + name = 'idl_gen') + + ctx.program( + source = ['main.cpp'], + target = 'testprog', + includes = '.', + add_idl = 'idl_gen') + +from waflib.Task import Task +from waflib.TaskGen import feature, before_method, extension + +class idl(Task): + run_str = 'cp ${SRC} ${TGT[0].abspath()} && touch ${TGT[1].abspath()}' + color = 'BLUE' + ext_out = ['.h'] + +@extension('.idl') +def process_idl(self, node): + cpp_node = node.change_ext('.cpp') + hpp_node = node.change_ext('.hpp') + self.create_task('idl', node, [cpp_node, hpp_node]) + self.more_source = [cpp_node] + +@feature('*') +@before_method('process_source') +def process_add_source(self): + for x in self.to_list(getattr(self, 'add_idl', [])): + y = self.bld.get_tgen_by_name(x) + y.post() + if getattr(y, 'more_source', None): + self.source.extend(y.more_source) + diff --git a/docs/book/examples/scenarios_impfile/wscript b/docs/book/examples/scenarios_impfile/wscript new file mode 100644 index 00000000..da4348b9 --- /dev/null +++ b/docs/book/examples/scenarios_impfile/wscript @@ -0,0 +1,22 @@ +#! /usr/bin/env python + +""" +Create a file in the build directory before the build starts +""" + +cfg_file = 'somedir/foo.txt' + +def configure(conf): + + orig = conf.root.find_node('/etc/fstab') + txt = orig.read() + + dest = conf.bldnode.make_node(cfg_file) + dest.parent.mkdir() + dest.write(txt) + + conf.env.append_value('cfg_files', dest.abspath()) + +def build(ctx): + ctx(rule='cp ${SRC} ${TGT}', source=cfg_file, target='bar.txt') + diff --git a/docs/book/examples/scenarios_incflags/main.cpp b/docs/book/examples/scenarios_incflags/main.cpp new file mode 100644 index 00000000..a46866d9 --- /dev/null +++ b/docs/book/examples/scenarios_incflags/main.cpp @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/docs/book/examples/scenarios_incflags/wscript b/docs/book/examples/scenarios_incflags/wscript new file mode 100644 index 00000000..1fa37e19 --- /dev/null +++ b/docs/book/examples/scenarios_incflags/wscript @@ -0,0 +1,29 @@ +#! /usr/bin/env python + +""" +special include flags +$ waf configure clean build +""" + +top = '.' +out = 'build' + +def configure(conf): + conf.load('g++') + +def build(bld): + bld.program(features='cxx cxxprogram', source='main.cpp', target='test') + +from waflib.TaskGen import after, feature + +@feature('cxx') +@after('apply_incpaths') +def insert_blddir(self): + self.env.prepend_value('INCPATHS', '.') + +@feature('cxx') +@after('apply_incpaths', 'insert_blddir') +def insert_srcdir(self): + path = self.bld.srcnode.abspath() + self.env.prepend_value('INCPATHS', path) + diff --git a/docs/book/examples/scenarios_unknown/evil_comp.py b/docs/book/examples/scenarios_unknown/evil_comp.py new file mode 100755 index 00000000..2d2ed21e --- /dev/null +++ b/docs/book/examples/scenarios_unknown/evil_comp.py @@ -0,0 +1,42 @@ +#! /usr/bin/env python + +""" +example of an ill-behaving compiler +* the output files cannot be known in advance +* the output file names are written to stdout +""" + +import sys, os + +def write_file(filename, contents): + a_file = None + try: + a_file = open(filename, 'w') + a_file.write(contents) + finally: + if a_file: + a_file.close() + +name = sys.argv[1] +file = open(name, 'r') +txt = file.read() +file.close() + +lst = txt.split('\n') +for line in lst: + source_filename = line.strip() + if not source_filename: continue + (dirs, name) = os.path.split(source_filename) + try: + os.makedirs(dirs) + except: + pass + + header_filename = os.path.splitext(source_filename)[0] + '.h' + varname = name.replace('.', '_') + write_file(header_filename, 'int %s=4;\n' % varname) + write_file(source_filename, '#include "%s"\nint get_%s() {return %s;}\n' % (os.path.split(header_filename)[1], varname, varname)) + + print (source_filename) + print (header_filename) + diff --git a/docs/book/examples/scenarios_unknown/foo.src b/docs/book/examples/scenarios_unknown/foo.src new file mode 100644 index 00000000..ef4f68cd --- /dev/null +++ b/docs/book/examples/scenarios_unknown/foo.src @@ -0,0 +1,2 @@ +shpip/a12.c +shpop/a13.c diff --git a/docs/book/examples/scenarios_unknown/mytool.py b/docs/book/examples/scenarios_unknown/mytool.py new file mode 100644 index 00000000..7a8e3488 --- /dev/null +++ b/docs/book/examples/scenarios_unknown/mytool.py @@ -0,0 +1,64 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2008-2010 (ita) + +import os +from waflib import Task, Utils, Context +from waflib.Utils import subprocess +from waflib.TaskGen import extension + +@extension('.src') +def process_shpip(self, node): + self.create_task('src2c', node) + +class src2c(Task.Task): + color = 'PINK' + quiet = 1 + ext_out = ['.h'] + + def run(self): + cmd = '%s %s' % (self.env.COMP, self.inputs[0].abspath()) + cwd = self.inputs[0].parent.get_bld().abspath() + out = self.generator.bld.cmd_and_log(cmd, cwd=cwd, quiet=Context.STDOUT) + + out = Utils.to_list(out) + self.outputs = [self.generator.path.find_or_declare(x) for x in out] + self.generator.bld.raw_deps[self.uid()] = [self.signature()] + self.outputs + self.add_c_tasks(self.outputs) + + def add_c_tasks(self, lst): + self.more_tasks = [] + for node in lst: + if node.name.endswith('.h'): + continue + tsk = self.generator.create_compiled_task('c', node) + self.more_tasks.append(tsk) + + tsk.env.append_value('INCPATHS', [node.parent.abspath()]) + + if getattr(self.generator, 'link_task', None): + self.generator.link_task.set_run_after(tsk) + self.generator.link_task.inputs.append(tsk.outputs[0]) + + def runnable_status(self): + + ret = super(src2c, self).runnable_status() + if ret == Task.SKIP_ME: + + lst = self.generator.bld.raw_deps[self.uid()] + if lst[0] != self.signature(): + return Task.RUN_ME + + nodes = lst[1:] + for x in nodes: + try: + os.stat(x.abspath()) + except: + return Task.RUN_ME + + nodes = lst[1:] + self.set_outputs(nodes) + self.add_c_tasks(nodes) + + return ret + diff --git a/docs/book/examples/scenarios_unknown/wscript b/docs/book/examples/scenarios_unknown/wscript new file mode 100644 index 00000000..6bf400ff --- /dev/null +++ b/docs/book/examples/scenarios_unknown/wscript @@ -0,0 +1,20 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2008-2010 (ita) + +""" +a compiler that creates files unknown in advance +see mytool.py +""" + +top = '.' +out = 'build' + +def configure(conf): + conf.load('gcc') + conf.load('mytool', tooldir='.') + +def build(bld): + bld.env.COMP = bld.path.find_resource('evil_comp.py').abspath() + bld.stlib(source='x.c foo.src', target='astaticlib') + diff --git a/docs/book/examples/scenarios_unknown/x.c b/docs/book/examples/scenarios_unknown/x.c new file mode 100644 index 00000000..e5588a62 --- /dev/null +++ b/docs/book/examples/scenarios_unknown/x.c @@ -0,0 +1 @@ +int k=3; diff --git a/docs/book/examples/tasks_copy/wscript b/docs/book/examples/tasks_copy/wscript new file mode 100644 index 00000000..0cd3350c --- /dev/null +++ b/docs/book/examples/tasks_copy/wscript @@ -0,0 +1,22 @@ +#! /usr/bin/env python + +""" +Simple copy task + +The attribute 'run_str' is compiled into the task method 'run' by a metaclass +""" + +def configure(ctx): + pass + +def build(ctx): + from waflib import Task + class copy(Task.Task): + run_str = 'cp ${SRC} ${TGT}' + copy = Task.always_run(copy) + + tsk = copy(env=ctx.env) + tsk.set_inputs(ctx.path.find_resource('wscript')) + tsk.set_outputs(ctx.path.find_or_declare('b.out')) + ctx.add_to_group(tsk) + diff --git a/docs/book/examples/tasks_groups/wscript b/docs/book/examples/tasks_groups/wscript new file mode 100644 index 00000000..2deeb003 --- /dev/null +++ b/docs/book/examples/tasks_groups/wscript @@ -0,0 +1,38 @@ +#! /usr/bin/env python + +""" +waf configure clean build -j4 --dwidth=800 --dtitle='Parallel build representation for "waf -j4"' + +for this to work, make sure to either use waf svn or to create waf with './waf-light --make-waf --tools=compat15,parallel_debug' +""" + +def options(ctx): + ctx.load('parallel_debug') + +def configure(ctx): + ctx.load('parallel_debug') + +def build(ctx): + for i in range(8): + ctx(rule='cp ${SRC} ${TGT}', source='wscript', target='wscript_a_%d' % i, + color='YELLOW', name='tasks a') + ctx(rule='cp ${SRC} ${TGT}', source='wscript_a_%d' % i, target='wscript_b_%d' % i, + color='GREEN', name='tasks b') + ctx.add_group() + for i in range(8): + ctx(rule='cp ${SRC} ${TGT}', source='wscript', target='wscript_c_%d' % i, + color='BLUE', name='tasks c') + ctx(rule='cp ${SRC} ${TGT}', source='wscript_c_%d' % i, target='wscript_d_%d' % i, + color='PINK', name='tasks d') + +# just to make the diagrams more interesting, shuffle the tasks + +from waflib import Task + +old = Task.set_file_constraints +def meth(lst): + import random + random.shuffle(lst) + old(lst) +Task.set_file_constraints = meth + diff --git a/docs/book/examples/tasks_groups2/wscript b/docs/book/examples/tasks_groups2/wscript new file mode 100644 index 00000000..0e521af5 --- /dev/null +++ b/docs/book/examples/tasks_groups2/wscript @@ -0,0 +1,32 @@ +#! /usr/bin/env python + +""" +waf configure clean build -j4 --dwidth=800 --dtitle='Parallel build representation for "waf -j4"' + +for this to work, make sure to either use waf svn or to create waf with './waf-light --make-waf --tools=compat15,parallel_debug' +""" + +def options(ctx): + ctx.load('parallel_debug') + +def configure(ctx): + ctx.load('parallel_debug') + +def build(ctx): + + ctx.add_group('group1') + ctx.add_group('group2') + + for i in range(8): + ctx.set_group('group1') + ctx(rule='cp ${SRC} ${TGT}', source='wscript', target='wscript_a_%d' % i, + color='YELLOW', name='tasks a') + ctx(rule='cp ${SRC} ${TGT}', source='wscript_a_%d' % i, target='wscript_b_%d' % i, + color='GREEN', name='tasks b') + + ctx.set_group('group2') + ctx(rule='cp ${SRC} ${TGT}', source='wscript', target='wscript_c_%d' % i, + color='BLUE', name='tasks c') + ctx(rule='cp ${SRC} ${TGT}', source='wscript_c_%d' % i, target='wscript_d_%d' % i, + color='PINK', name='tasks d') + diff --git a/docs/book/examples/tasks_manual_deps/testfile b/docs/book/examples/tasks_manual_deps/testfile new file mode 100644 index 00000000..163b4d3f --- /dev/null +++ b/docs/book/examples/tasks_manual_deps/testfile @@ -0,0 +1 @@ + diff --git a/docs/book/examples/tasks_manual_deps/wscript b/docs/book/examples/tasks_manual_deps/wscript new file mode 100644 index 00000000..20573499 --- /dev/null +++ b/docs/book/examples/tasks_manual_deps/wscript @@ -0,0 +1,15 @@ +#! /usr/bin/env python + +""" +ctx.add_manual_dependency + +change the file 'testfile' and see how it affects the rebuild of 'somecopy' +""" + +def configure(ctx): + pass + +def build(ctx): + ctx(rule='cp ${SRC} ${TGT}', source='wscript', target='somecopy') + ctx.add_manual_dependency(ctx.path.find_node('wscript'), ctx.path.find_node('testfile')) + diff --git a/docs/book/examples/tasks_scan/a.in b/docs/book/examples/tasks_scan/a.in new file mode 100644 index 00000000..8d1c8b69 --- /dev/null +++ b/docs/book/examples/tasks_scan/a.in @@ -0,0 +1 @@ + diff --git a/docs/book/examples/tasks_scan/wscript b/docs/book/examples/tasks_scan/wscript new file mode 100644 index 00000000..a36db174 --- /dev/null +++ b/docs/book/examples/tasks_scan/wscript @@ -0,0 +1,37 @@ +#! /usr/bin/env python +# encoding: utf-8 + +""" +A simple scanner method +""" + +import time +from waflib.Task import Task +class copy(Task): + + def scan(self): + print('→ calling the scanner method') + node = self.inputs[0].parent.find_resource('wscript') + return ([node], time.time()) + + def run(self): + return self.exec_command('cp %s %s' % (self.inputs[0].abspath(), self.outputs[0].abspath()) + ) + + def runnable_status(self): + ret = super(copy, self).runnable_status() + bld = self.generator.bld + print('nodes: %r' % bld.node_deps[self.uid()]) + print('custom data: %r' % bld.raw_deps[self.uid()]) + return ret + +def configure(ctx): + pass + +def build(ctx): + tsk = copy(env=ctx.env) + tsk.set_inputs(ctx.path.find_resource('a.in')) + tsk.set_outputs(ctx.path.find_or_declare('b.out')) + ctx.add_to_group(tsk) + + diff --git a/docs/book/examples/tasks_values/wscript b/docs/book/examples/tasks_values/wscript new file mode 100644 index 00000000..85e89d15 --- /dev/null +++ b/docs/book/examples/tasks_values/wscript @@ -0,0 +1,25 @@ +#! /usr/bin/env python +# encoding: utf-8 + +""" +The variables from cls.vars are then used to compute the task signature +and may trigger rebuilds +""" + +from waflib.Task import Task +class foo(Task): + vars = ['FLAGS'] + def run(self): + print('the flags are %r' % self.env.FLAGS) + +def options(ctx): + ctx.add_option('--flags', default='-f', dest='flags', type='string') + +def configure(ctx): + pass + +def build(ctx): + ctx.env.FLAGS = ctx.options.flags + tsk = foo(env=ctx.env) + ctx.add_to_group(tsk) + diff --git a/docs/book/examples/tasks_values2/wscript b/docs/book/examples/tasks_values2/wscript new file mode 100644 index 00000000..560796fa --- /dev/null +++ b/docs/book/examples/tasks_values2/wscript @@ -0,0 +1,23 @@ +#! /usr/bin/env python +# encoding: utf-8 + +""" +The attribute run_str is assumed to be a string and will be compiled into +a method 'run' by a metaclass (that is, when the class is defined) +""" + +def configure(ctx): + ctx.env.COPY = '/bin/cp' + ctx.env.COPYFLAGS = ['-f'] + +def build(ctx): + from waflib.Task import Task + class copy(Task): + run_str = '${COPY} ${COPYFLAGS} ${SRC} ${TGT}' + print(copy.vars) + + tsk = copy(env=ctx.env) + tsk.set_inputs(ctx.path.find_resource('wscript')) + tsk.set_outputs(ctx.path.find_or_declare('b.out')) + ctx.add_to_group(tsk) + diff --git a/docs/book/examples/tasks_weak/wscript b/docs/book/examples/tasks_weak/wscript new file mode 100644 index 00000000..78159d8c --- /dev/null +++ b/docs/book/examples/tasks_weak/wscript @@ -0,0 +1,30 @@ +#! /usr/bin/env python + +""" +Illustrate the effect of weak order constraints for the build (generates diagrams) + +waf configure clean build -j2 --dwidth=800 --dtitle='No particular order for "waf -j2"' --dmaxtime=7.08 +waf configure clean build -j2 --dwidth=800 --dtitle='Weak order for "waf -j2"' --dmaxtime=7.08 +""" + +def options(ctx): + ctx.load('parallel_debug') + +def configure(ctx): + ctx.load('parallel_debug') + +def build(ctx): + for x in range(5): + ctx(rule='sleep 1', color='GREEN', name='short task') + ctx(rule='sleep 5', color='RED', name='long task') + +import random +from waflib import Task + +old = Task.set_file_constraints +def meth(lst): + lst.sort(cmp=lambda x, y: cmp(x.__class__.__name__, y.__class__.__name__)) + #random.shuffle(lst) + old(lst) +Task.set_file_constraints = meth + diff --git a/docs/book/execution.txt b/docs/book/execution.txt new file mode 100644 index 00000000..08b801fe --- /dev/null +++ b/docs/book/execution.txt @@ -0,0 +1,584 @@ +== Projects and commands + +The _waf_ script is meant to build software projects, and is of little use when taken alone. This chapter describes what is necessary to set up a waf project and how to use the _waf_ script. + +=== Waf commands + +Waf projects use description files of the name _wscript_ which are python scripts containing functions and variables that may be used by Waf. Particular functions named _waf commands_ may be used by Waf on the command-line. + +==== Declaring Waf commands + +Waf commands are really simple functions and may execute arbitrary python code such as calling other functions. +They take a single parameter as input and do not have to return any particular value as in the following example: + +// execution_hello +[source,python] +--------------- +#! /usr/bin/env python +# encoding: utf-8 + +def <1> hello(ctx <2>): + print('hello world') +--------------- + +<1> The _waf command_ *hello* +<2> A waf context, used to share data between scripts + +And here is how to have +waf+ call the function hello from the command-line: + +[source,shishell] +--------------- +$ waf hello +hello world +'hello' finished successfully (0.001s) +--------------- + +==== Chaining Waf commands + +Several commands may be declared in the same _wscript_ file: + +// execution_ping +[source,python] +--------------- +def ping(ctx): + print(' ping! %d' % id(ctx)) + +def pong(ctx): + print(' pong! %d' % id(ctx)) +--------------- + +And may be chained for execution by Waf: + +[source,shishell] +--------------- +$ waf ping pong ping ping + ping! 140704847272272 +'ping' finished successfully (0.001s) + pong! 140704847271376 +'pong' finished successfully (0.001s) + ping! 140704847272336 +'ping' finished successfully (0.001s) + ping! 140704847272528 +'ping' finished successfully (0.001s) +--------------- + +NOTE: The context parameter is a new object for each command executed. The classes are also different: ConfigureContext for configure, BuildContext for build, OptionContext for option, and Context for any other command. + +==== Using several scripts and folders + +Although a Waf project must contain a top-level _wscript_ file, the contents may be split into several sub-project files. We will now illustrate this concept on a small project: + +[source,shishell] +--------------- +$ tree +|-- src +| `-- wscript +`-- wscript +--------------- + +The commands in the top-level _wscript_ will call the same commands from a subproject _wscript_ file by calling a context method named _recurse_: + +// execution_recurse +[source,python] +--------------- +def ping(ctx): + print('→ ping from ' + ctx.path.abspath()) + ctx.recurse('src') +--------------- + +And here is the contents of 'src/wscript' + +[source,python] +--------------- +def ping(ctx): + print('→ ping from ' + ctx.path.abspath()) +--------------- + +Upon execution, the results will be: + +[source,shishell] +--------------- +$ cd /tmp/execution_recurse + +$ waf ping +→ ping from /tmp/execution_recurse +→ ping from /tmp/execution_recurse/src +'ping' finished successfully (0.002s) + +$ cd src + +$ waf ping +→ ping from /tmp/execution_recurse/src +'ping' finished successfully (0.001s) +--------------- + +NOTE: The method _recurse_, and the attribute _path_ are available on all waf context classes + +=== Waf project definition + +==== Configuring a project (the _configure_ command) + +Although Waf may be called from any folder containing a 'wscript' file, it is usually a good idea to have a single entry point in the scripts. +Besides ensuring a consistent behaviour, it also saves the redefinition of the same imports and function redefinitions in all wscript files. +The following concepts help to structure a Waf project: + +. Project directory: directory containing the source files that will be packaged and redistributed to other developers or to end users +. Build directory: directory containing the files generated by the project (configuration sets, build files, logs, etc) +. System files: files and folders which do not belong to the project (operating system files, etc) + +The predefined command named _configure_ is used to gather and store the information about these folders. +We will now extend the example from the previous section with the following top-level wscript file: + +// execution_configure +[source,python] +--------------- +top = '.' <1> +out = 'build_directory' <2> + +def configure(ctx): <3> + print('→ configuring the project in ' + ctx.path.abspath()) + +def ping(ctx): + print('→ ping from ' + ctx.path.abspath()) + ctx.recurse('src') +--------------- + +<1> string representing the project directory. In general, top is set to '.', except for some proprietary projects where the wscript cannot be added to the top-level, top may be set to '../..' or even some other folder such as '/checkout/perforce/project' +<2> string representing the build directory. In general, it is set to 'build', except for some proprietary projects where the build directory may be set to an absolute path such as '/tmp/build'. It is important to be able to remove the build directory safely, so it should never be given as '.' or '..'. +<3> the _configure_ function is called by the 'configure' command + +The script in 'src/wscript' is left unchanged: + +[source,python] +--------------- +def ping(ctx): + print('→ ping from ' + ctx.path.abspath()) +--------------- + +The execution output will be the following: + +//// +$ waf ping +→ ping from /tmp/execution_configure +→ ping from /tmp/execution_configure/src +'ping' finished successfully (0.001s) + +$ cd src + +$ waf ping +→ ping from /tmp/execution_configure/src +'ping' finished successfully (0.001s) + +$ cd .. + +//// + +[source,shishell] +--------------- +$ cd /tmp/execution_configure <1> +$ tree +|-- src +| `-- wscript +`-- wscript + +$ waf configure <2> +→ configuring the project in /tmp/execution_configure +'configure' finished successfully (0.021s) + +$ tree -a +|-- build_directory/ <3> +| |-- c4che/ <4> +| | |-- build.config.py <5> +| | `-- _cache.py <6> +| `-- config.log <7> +|--.lock-wafbuild <8> +|-- src +| `-- wscript +`-- wscript + +$ waf ping +→ ping from /tmp/execution_configure +→ ping from /tmp/execution_configure/src +'ping' finished successfully (0.001s) + +$ cd src +$ waf ping <9> +→ ping from /tmp/execution_configure +→ ping from /tmp/execution_configure/src +'ping' finished successfully (0.001s) +--------------- + +<1> To configure the project, change to the directory containing the top-level project file +<2> The execution is called by calling _waf configure_ +<3> The build directory was created +<4> The configuration data is stored in the folder 'c4che/' +<5> The command-line options and environment variables in use are stored in 'build.config.py' +<6> The user configuration set is stored in '_cache.py' +<7> Configuration log (duplicate of the output generated during the configuration) +<8> Hidden file pointing at the relevant project file and build directory +<9> Calling _waf_ from a subfolder will execute the commands from the same wscript file used for the configuration + +NOTE: _waf configure_ is always called from the directory containing the wscript file + +==== Removing generated files (the _distclean_ command) + +A command named _distclean_ is provided to remove the build directory and the lock file created during the configuration. On the example from the previous section: + +[source,shishell] +--------------- +$ waf configure +→ configuring the project in /tmp/execution_configure +'configure' finished successfully (0.001s) + +$ tree -a +|-- build_directory/ +| |-- c4che/ +| | |-- build.config.py +| | `-- _cache.py +| `-- config.log +|--.lock-wafbuild +`-- wscript + +$ waf distclean <1> +'distclean' finished successfully (0.001s) + +$ tree <2> +|-- src +| `-- wscript +`-- wscript +--------------- + +<1> The _distclean_ command definition is implicit (no declaration in the wscript file) +<2> The tree is reverted to its original state: no build directory and no lock file + +The behaviour of _distclean_ is fairly generic and the corresponding function does not have to be defined in the wscript files. It may be defined to alter its behaviour though, see for example the following: + +[source,python] +--------------- +top = '.' +out = 'build_directory' + +def configure(ctx): + print('→ configuring the project') + +def distclean(ctx): + print(' Not cleaning anything!') +--------------- + +Upon execution: + +[source,shishell] +--------------- +$ waf distclean + Not cleaning anything! +'distclean' finished successfully (0.000s) +--------------- + +==== Packaging the project sources (the _dist_ command) + +The _dist_ command is provided to create an archive of the project. By using the script presented previously: + +// execution_dist + +[source,python] +--------------- +top = '.' +out = 'build_directory' + +def configure(ctx): + print('→ configuring the project in ' + ctx.path.abspath()) +--------------- + +Execute the _dist_ command to get: + +[source,shishell] +--------------- +$ cd /tmp/execution_dist + +$ waf configure +→ configuring the project in /tmp/execution_dist +'configure' finished successfully (0.005s) + +$ waf dist +New archive created: noname-1.0.tar.bz2 (sha='a4543bb438456b56d6c89a6695f17e6cb69061f5') +'dist' finished successfully (0.035s) +--------------- + +By default, the project name and version are set to 'noname' and '1.0'. To change them, it is necessary to provide two additional variables in the top-level project file: + +[source,python] +--------------- +APPNAME = 'webe' +VERSION = '2.0' + +top = '.' +out = 'build_directory' + +def configure(ctx): + print('→ configuring the project in ' + ctx.path.abspath()) +--------------- + +Because the project was configured once, it is not necessary to configure it once again: + +[source,shishell] +--------------- +$ waf dist +New archive created: webe-2.0.tar.bz2 (sha='7ccc338e2ff99b46d97e5301793824e5941dd2be') +'dist' finished successfully (0.006s) +--------------- + +More parameters may be given to alter the archive by adding a function 'dist' in the script: + +[source,python] +--------------- +def dist(ctx): + ctx.base_name = 'foo_2.0' <1> + ctx.algo = 'zip' <2> + ctx.excl = ' **/.waf-1* **/*~ **/*.pyc **/*.swp **/.lock-w*' <3> + ctx.files = ctx.path.ant_glob('**/wscript') <4> +--------------- + +<1> The archive name may be given directly instead of computing from 'APPNAME' and 'VERSION' +<2> The default compression format is 'tar.bz2'. Other valid formats are 'zip' and 'tar.gz' +<3> Exclude patterns passed to give to 'ctx.path.ant_glob()' which is used to find the files +<4> The files to add to the archive may be given as Waf node objects ('excl' is therefore ignored) + +==== Defining command-line options (the _options_ command) + +The Waf script provides various default command-line options, which may be consulted by executing +waf --help+: + +[source,shishell] +--------------- +$ waf --help +waf [command] [options] + +Main commands (example: ./waf build -j4) + build : executes the build + clean : cleans the project + configure: configures the project + dist : makes a tarball for redistributing the sources + distcheck: checks if the project compiles (tarball from 'dist') + distclean: removes the build directory + install : installs the targets on the system + list : lists the targets to execute + step : executes tasks in a step-by-step fashion, for debugging + uninstall: removes the targets installed + +Options: + --version show program's version number and exit + -h, --help show this help message and exit + -j JOBS, --jobs=JOBS amount of parallel jobs (2) + -k, --keep keep running happily even if errors are found + -v, --verbose verbosity level -v -vv or -vvv [default: 0] + --nocache ignore the WAFCACHE (if set) + --zones=ZONES debugging zones (task_gen, deps, tasks, etc) + + configure options: + -o OUT, --out=OUT build dir for the project + -t TOP, --top=TOP src dir for the project + --prefix=PREFIX installation prefix [default: '/usr/local/'] + --download try to download the tools if missing + + build and install options: + -p, --progress -p: progress bar; -pp: ide output + --targets=TARGETS task generators, e.g. "target1,target2" + + step options: + --files=FILES files to process, by regexp, e.g. "*/main.c,*/test/main.o" + + install/uninstall options: + --destdir=DESTDIR installation root [default: ''] + -f, --force force file installation +--------------- + +Accessing a command-line option is possible from any command. Here is how to access the value _prefix_: + +[source,python] +--------------- +top = '.' +out = 'build_directory' + +def configure(ctx): + print('→ prefix is ' + ctx.options.prefix) +--------------- + +Upon execution, the following will be observed: + +[source,shishell] +--------------- +$ waf configure +→ prefix is /usr/local/ +'configure' finished successfully (0.001s) +--------------- + +To define project command-line options, a special command named _options_ may be defined in user scripts. This command will be called once before any other command executes. + +[source,python] +--------------- +top = '.' +out = 'build_directory' + +def options(ctx): + ctx.add_option('--foo', action='store', default=False, help='Silly test') + +def configure(ctx): + print('→ the value of foo is %r' % ctx.options.foo) +--------------- + +Upon execution, the following will be observed: + +[source,shishell] +--------------- +$ waf configure --foo=test +→ the value of foo is 'test' +'configure' finished successfully (0.001s) +--------------- + +The command context for options is a shortcut to access the optparse functionality. For more information on the optparse module, consult the http://docs.python.org/library/optparse.html[Python documentation] + + +=== The _build_ commands + +==== Building targets (the _build_ command) + +The 'build' command is used for building targets. We will now create a new project in '/tmp/execution_build/', and add a script to create an empty file +foo.txt+ and then copy it into another file +bar.txt+: + +// execution_build +[source,python] +--------------- +top = '.' +out = 'build_directory' + +def configure(ctx): + pass + +def build(ctx): + ctx(rule='touch ${TGT}', target='foo.txt') + ctx(rule='cp ${SRC} ${TGT}', source='foo.txt', target='bar.txt') +--------------- + +Calling _waf build_ directly results in an error: + +[source,shishell] +--------------- +$ cd /tmp/execution_build/ + +$ waf build +The project was not configured: run "waf configure" first! +--------------- + +The build requires a configured folder to know where to look for source files and where to output the created files. Let's try again: + +[source,shishell] +--------------- +$ waf configure build +'configure' finished successfully (0.007s) +Waf: Entering directory `/tmp/execution_build/build_directory' +[1/2] foo.txt: -> build_directory/foo.txt <1> +[2/2] bar.txt: build_directory/foo.txt -> build_directory/bar.txt +Waf: Leaving directory `/tmp/examples/execution_build/build_directory' +'build' finished successfully (0.041s) + +$ tree -a +|-- build_directory/ +| |-- bar.txt <2> +| |-- c4che/ +| | |-- build.config.py +| | `-- _cache.py +| |-- foo.txt +| |-- config.log +| `-- .wafpickle <3> +|--.lock-wafbuild +`-- wscript + +$ waf build +Waf: Entering directory `/tmp/execution_build/build_directory' +Waf: Leaving directory `/tmp/execution_build/build_directory' +'build' finished successfully (0.008s) <4> +--------------- + +<1> Note that the build _deduced_ that +bar.txt+ has to be created after +foo.txt+ +<2> The targets are created in the build directory +<3> A pickle file is used to store the information about the targets +<4> Since the targets are up-to-date, they do not have to be created once again + +Since the command _waf build_ is usually executed very often, a shortcut is provided to call it implicitly: + +[source,shishell] +--------------- +$ waf +Waf: Entering directory `/tmp/execution_build/build_directory' +Waf: Leaving directory `/tmp/execution_build/build_directory' +--------------- + +==== Cleaning the targets (the _clean_ command) + +The _clean_ command is used to remove the information about the files and targets created during the build. It uses the same function _build_ from the wscript files so there is no need to add a function named _clean_ in the wscript file. + +After cleaning, the targets will be created once again even if they were up-to-date: + +[source,shishell] +--------------- +$ waf clean build -v +'clean' finished successfully (0.003s) +Waf: Entering directory `/tmp/execution_build/build_directory' <1> +[1/2] foo.txt: -> build_directory/foo.txt <2> +14:58:34 runner 'touch foo.txt' <3> +[2/2] bar.txt: build_directory/foo.txt -> build_directory/bar.txt +14:58:34 runner 'cp foo.txt bar.txt' +Waf: Leaving directory `/tmp/execution_build/build_directory' +'build' finished successfully (0.040s) +--------------- + +<1> All commands are executed from the build directory by default +<2> The information about the files +foo.txt+ was lost so it is rebuilt +<3> By using the _-v_ flag, the command-lines executed are displayed + +==== More build commands + +The following commands all use the same function _build_ from the wscript file: + +. +build:+ process the source code to create the object files +. +clean:+ remove the object files that were created during a build (unlike distclean, do not remove the configuration) +. +install:+ check that all object files have been generated and copy them on the system (programs, libraries, data files, etc) +. +uninstall:+ undo the installation, remove the object files from the system without touching the ones in the build directory +. +list:+ list the task generators in the build section (to use with waf --targets=name) +. +step:+ force the rebuild of particular files for debugging purposes + +The attribute 'cmd' holds the name of the command being executed: + +// execution_cmd +[source,python] +--------------- +top = '.' +out = 'build_directory' + +def configure(ctx): + print(ctx.cmd) + +def build(ctx): + if ctx.cmd == 'clean': + print('cleaning!') + else: + print(ctx.cmd) +--------------- + +The execution will produce the following output: + +[source,shishell] +--------------- +$ waf configure clean build +Setting top to : /tmp/execution_cmd +Setting out to : /tmp/execution_cmd/build_directory +configure +'configure' finished successfully (0.002s) +cleaning! +'clean' finished successfully (0.002s) +Waf: Entering directory `/tmp/execution_cmd/build_directory' +build +Waf: Leaving directory `/tmp/execution_cmd/build_directory' +'build' finished successfully (0.001s) +--------------- + +The build command usage will be described in details in the next chapters. + diff --git a/docs/book/glossary.txt b/docs/book/glossary.txt new file mode 100644 index 00000000..ee86eaef --- /dev/null +++ b/docs/book/glossary.txt @@ -0,0 +1,20 @@ + +== Glossary + +[glossary] +Build Order:: + The build order is the sequence in which tasks must be executed. Because tasks can be executed in parallel, several build orders can be computed depending on the constraints between the tasks. When a build order cannot be computed (usually by contradictory order constraints), the build is said to be in a deadlock. +Dependency:: + A dependency represents the conditions by which a task can be considered up-to-date or not (execution status). The dependencies can be explicit (file inputs and outputs) or abstract (dependency on a value for example). +Task generator:: + A task generator is an object instance of the class Task.task_gen. The task generators encapsulate the creation of various task instances at a time, and simplify the creation of ordering constraints between them (for example, compilation tasks are executed before link tasks). +Task:: + A Waf task is an object instance of the class Task.TaskBase. Waf tasks may be simple (Task.TaskBase) or related to the filesystem (Task.Task). Tasks represent the production of something during the build (files in general), and may be executed in sequence (with ordering constraints) or in parallel. +Tool:: + A Waf tool is a python module containing Waf-specific extensions. The Waf tools are located in the folder +waflib/Tools/+ and usually contain a global variable 'configure' which may reference functions to execute in the configuration. +Node:: + The Node class is a data structure used to represent the filesystem in an efficient manner. The node objects may represent files or folders. File nodes are associated to signatures objects. The signature can be hashes of the file contents (source files) or task signatures (build files). +Command:: + Function present in the top-level project file (wscript) and accepting a 'waflib.Context.Context' instance as unique input parameter. The function is executed when its name is given on the command-line (for example running 'waf configure' will execute the function 'configure') +Variant:: + Additional output directory used to enable several (build) commands to create the same targets with different compilation flags. diff --git a/docs/book/intro_waf_1.6.txt b/docs/book/intro_waf_1.6.txt new file mode 100644 index 00000000..ae5d30d0 --- /dev/null +++ b/docs/book/intro_waf_1.6.txt @@ -0,0 +1,644 @@ +Introduction to Waf 1.6 (draft) +=============================== +:author: Thomas Nagy +:quotes.++: + +[preface] +== Introduction + +Copyright (C) 2010 Thomas Nagy + +Copies of this document may be redistributed, verbatim, and for non-commercial +purposes. The license for this document is + http://creativecommons.org/licenses/by-nc-nd/3.0/[by-nc-nd license]. + +=== Waf 1.5 limitations + +The Waf build system was first created to provide an alternative for the autotool-based build systems based on a single programming language. To ease the development, a few assumptions were made, and over the time it became clear that they had become a limitation for specific corner cases, among others: + +- in some projects, files are generated in the source directory and are kept in the source control system +- there may be more than one kind of build command +- some configuration API may be useful in the build section +- it may be interesting to use Waf as a library in another system +- some API was private (node objects) + +=== Goals of Waf 1.6 + +The main goals of Waf 1.6 are: + +. to remove most assumptions/restrictions present in Waf 1.5 (nodes, build directory, reduce the coupling between the modules) +. to optimize the code base (execution time, source code size, compatibility with different python versions) +. to improve the user experience (make the API more intuitive, expose more classes and functions, improve the error handling) + +=== Purpose of this document + +The Waf API (modules, classes. functions) had to be changed to make the changes described previously. +This document describes the main changes compared to the previous version, Waf 1.5. +The Waf book for Waf 1.6 should be the reference for new users and for projects upgrading from previous Waf versions such as Waf 1.4. + + + + + + +== Main changes in user scripts + +=== The attributes 'srcdir' and 'blddir' + +The attributes 'srcdir' and 'blddir' have been renamed to 'top' and 'out' respectively: + +[source,python] +--------------- +#srcdir = '.' # old +#blddir = 'build' # old +top = '.' +out = 'build' + +def configure(ctx): + pass +--------------- + +=== The command 'options' + +The method set_options has been renamed to 'options': + +[source,python] +--------------- +top = '.' +out = 'build' + +def options(opt): + pass + +def configure(ctx): + pass +--------------- + +=== The method 'recurse' + +The context class for all commands has a unique method named 'recurse'. The methods 'add_subdirs', 'sub_config' and 'sub_options' have been removed: + +[source,python] +--------------- +def options(ctx): + #ctx.sub_options('src') # old + ctx.recurse('src') + +def configure(ctx): + #ctx.sub_config('src') # old + ctx.recurse('src') + +def build(ctx): + #ctx.add_subdirs('src') # old + ctx.recurse('src') +--------------- + +=== Tool declaration + +For consistency with the user scripts, the method 'detect' in Waf tools has been renamed to 'configure'. The method 'set_options' is now called 'options' and is consistent with Waf scripts. + +=== Task generator declaration + +The build context method 'new_task_gen' disappears, and is replaced by '__call__'. The task generator subclasses have been removed completely, so only keyword arguments are accepted. For example: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + pass + +def build(ctx): + # ctx.new_task_gen('cc', 'cprogram', source='main.c', target='app') # old + ctx(features='c cprogram', source='main.c', target='app') +--------------- + + + + + +== Waf modules refactoring + +=== Syntax and namespaces + +Python 3 is now the default syntax. Waf now runs unmodified for 2.6, 2.7, 3.0 and 3.1. Upon execution, a detection is performed and the code +is then converted for python 2.3, 2.4 and 2.5 if necessary. It is far easier to modify Python3 code to run on Python2 than porting Python 2 code to Python 3. + +The Waf tools and modules have been reorganized in the following directory structure: + +[source,shishell] +--------------- +waflib +|- extras +`- Tools +--------------- + +To avoid conflicts with other projects, python namespaces are now used in the Waf library. This means that the import system has changed: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + # import Options, Utils # old + from waflib import Options, Utils + + print(Options.options) + print(Utils.to_list("a b c")) +--------------- + +=== Core modules added and removed + +The following modules have been removed: + +- py3fixes: the code uses the python 3 syntax by default +- Constants.py: the constants have been moved to the modules that use them +- pproc.py: the system subprocess module is now used by default + +The following modules have been added: + +- Context.py: base class for Waf commands (build context, configuration context, etc) +- Errors.py: exceptions used in the Waf code +- fixpy2.py: routines to make the Waf code executable on python 2.3, 2.4 and 2.6 + +The module Environment.py has been renamed to ConfigSet.py + +=== Module dependencies + +The core modules have been refactored to remove the cyclic imports. The following diagram represents the global +dependencies between the core modules. The non-essential dependencies - such as the module 'Scripting' using the module 'Utils' are hidden. + +image::core.eps["Dependencies between Waf core modules",height=600,align="center"] + + + + +== New context classes + +=== Context subclasses + +The following will create a build context subclass that will be instantiated when calling 'waf build'. It will execute the user function 'build' from the scripts. + +[source,python] +--------------- +from waflib.Build import BuildContext +class debug_context(BuildContext): + cmd = 'debug' + fun = 'build' +--------------- + +=== Context subclasses and nodes + +Nodes objects are now accessible from the command contexts. + +[source,python] +--------------- +from waflib.Context import Context +class package_class(Context): + cmd = 'package' + fun = 'package' + +def package(ctx): + print(ctx.path.ant_glob('wscript')) +--------------- + +The output will be: + +[source,shishell] +--------------- +$ waf package +[/disk/comp/waf-1.6/demos/c/wscript] +'package' finished successfully (0.006s) +--------------- + +=== New variant system + +The variant system present in the previous Waf versions has been removed. The new system makes it easier to declare an output directory for the tasks: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + ctx.load('gcc') + +def build(ctx): + tg = ctx(features='c', source='main.c') + if ctx.cmd == 'debug': + tg.cflags = ['-g'] + +from waflib.Build import BuildContext, CleanContext +class debug_build(BuildContext): + cmd = 'debug' + variant = 'debug_' + +class debug_clean(CleanContext): + cmd = 'clean_debug' + variant = 'debug_' +--------------- + +The outputs from the default build will be written to 'build/' but the outputs from the debug one will go into 'build/debug_': + +[source,shishell] +--------------- +waf clean clean_debug build debug -v +'clean' finished successfully (0.034s) +'clean_debug' finished successfully (0.007s) +Waf: Entering directory `/disk/comp/waf-1.6/demos/c/gnu/build' +[1/1] c: main.c -> build/main.c.0.o +00:36:41 runner ['/usr/bin/gcc', '../main.c', '-c', '-o', 'main.c.0.o'] +Waf: Leaving directory `/disk/comp/waf-1.6/demos/c/gnu/build' +'build' finished successfully (0.075s) +Waf: Entering directory `/disk/comp/waf-1.6/demos/c/gnu/build/debug_' +[1/1] c: main.c -> build/debug_/main.c.0.o +00:36:41 runner ['/usr/bin/gcc', '-g', '../../main.c', '-c', '-o', 'main.c.0.o'] +Waf: Leaving directory `/disk/comp/waf-1.6/demos/c/gnu/build/debug_' +'debug' finished successfully (0.082s) +--------------- + + + +== The 'Node' class + +=== New node class design + +The node objects are used to to represent files or and folders. In Waf 1.6 the build folders are no more virtual and do not depend on the variant anymore. They are represented by exactly one node: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + pass + +def build(ctx): + #print(ctx.path.abspath(ctx.env)) # old + print(ctx.path.get_bld().abspath()) # Waf 1.6 +--------------- + +The method 'ant_glob' no longer accepts the 'bld' argument to enumerate the build files, and must be called with the appropriate node: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + pass + +def build(ctx): + # ctx.path.ant_glob('*.o', src=False, bld=True, dir=False) # old + ctx.path.get_bld().ant_glob('*.o', src=True, dir=False) # equivalent in Waf 1.6 +--------------- + +The method 'ant_glob' is now the default for finding files and folders. As a consequence, the methods 'Node.find_iter', 'task_gen.find_sources_in_dirs' and the glob functionality in 'bld.install_files' have been removed. + + +=== Targets in the source directory or in any location + +The build may now update files under the source directory (versioned files). In this case, the task signature may not be used as node signature (source files signatures are computed from a hash of the file). Here is an example: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + pass + +def build(ctx): + # create the node next to the current wscript file - no build directory + node = ctx.path.make_node('foo.txt') + ctx(rule='touch ${TARGET}', always=True, update_outputs=True, target=node) +--------------- + +For nodes present out of the build directory, the node signature must be set to the file hash immediately after the file is created or modified (not the task signature set by default): + +[source,python] +--------------- +import Task + +@Task.update_outputs +class copy(Task.Task): + run_str = '${CP} ${SRC} ${TGT}' +--------------- + +Although discouraged, it is even possible to avoid the use of a build directory: + +[source,python] +--------------- +top = '.' +out = '.' + +def configure(ctx): + pass +--------------- + +=== Use nodes in various attributes + +Nodes are now accepted in the task generator source and target attributes + +[source,python] +--------------- +def configure(ctx): + pass + +def build(ctx): + src = ctx.path.find_resource('foo.a') + tgt = ctx.path.find_or_declare('foo.b') + + ctx(rule='cp ${SRC} ${TGT}', source=[src], target=[tgt]) +--------------- + +In the case of includes, the node may be used to control exactly the include path to add: + +[source,python] +--------------- +def configure(ctx): + ctx.load('gcc') + +def build(ctx): + ctx(features='c', source='main.c', includes=[ctx.path]) + ctx(features='c', source='main.c', includes=[ctx.path.get_bld()]) +--------------- + +The output will be: + +[source,shishell] +--------------- +$ waf -v +Waf: Entering directory `/foo/test/build' +[1/2] c: main.c -> build/main.c.0.o +03:01:20 runner ['/usr/bin/gcc', '-I/foo/test', '../main.c', '-c', '-o', 'main.c.0.o'] +[2/2] c: main.c -> build/main.c.1.o +03:01:20 runner ['/usr/bin/gcc', '-I/foo/test/bld', '../main.c', '-c', '-o', 'main.c.1.o'] +Waf: Leaving directory `/foo/test/build' +'build' finished successfully (0.476s) +--------------- + +Because node objects are accepted directly, the attribute 'allnodes' has been removed, and the nodes should be added to the list of source directly: + +[source,python] +--------------- +from waflib.TaskGen import extension +@extension('.c.in') +def some_method(self, node): + out_node = node.change_ext('.c') + task = self.create_task('in2c', node, out_node) + + #self.allnodes.append(out_node) # old + self.source.append(out_node) +--------------- + + + + + + + +== c/cxx/d/fortran/assembly/go + +The support for the languages 'c', 'cxx', 'd', 'fortran', 'assembly', and 'go' is now based on 4 methods executed in the following order: 'apply_link', 'apply_uselib_local', 'propagate_uselib_vars' and 'apply_incpaths'. + +=== apply_link + +The task generator method apply_link scans the task generator attribute for the feature names and tries to find a corresponding task class, if possible. If a task class is found and has the attribute 'inst_to' set, it will be used to create a link task. The tasks stored in the attribute 'compiled_tasks' will be used to set the input object files (.o) to the link task. + +The following classes are now used and match the names of the task generator attribute 'features': cprogram, cshlib, cstlib, cxxprogram, cxxshlib, cxxstlib, dprogram, dshlib and dstlib. + +The method 'create_compiled_tasks' should be used to create tasks that provide '.o' files, as it adds the tasks to the attribute 'compiled_tasks', for use with the method 'apply_link'. This is of course optional. + +=== apply_uselib_local + +The method apply_uselib_local has been changed to take into account static and shared library types. Two base classes for link tasks have been added in the module 'waflib.Tools.ccroot': 'link_task' and 'stlink_task'. The names for the subclasses correspond to 'features' names to use for the method 'apply_link' from the previous section: + +image::cls.eps["Class hierarchy",width=420,align="center"] + +=== propagate_uselib_vars + +The uselib attribute processing has been split from the local library processing. The main operations are the following: + +. the variables names are extracted from the task generator attribute 'features', for example 'CFLAGS' and 'LINKFLAGS' +. for each variable, the task generator attributes in lowercase are processed, for example the contents of 'tg.cflags' is added to 'tg.env.CFLAGS' +. for each feature, the corresponding variables are added if they are set, for example, if 'tg.features = "c cprogram"' is set, then 'c_CFLAGS' and 'cshlib_CFLAGS' are added to 'tg.env.CFLAGS' +. for each variable defined in the task generator attribute 'uselib', add the corresponding variable, for example, if 'tg.uselib = "M"' is set, then 'tg.env.CFLAGS_M' is added to 'tg.env.CFLAGS' + +The variable names are defined in 'waflib.Tools.ccroot.USELIB_VARS' and are bound to the feature names. + +=== apply_incpaths + +The task generator method apply_incpaths no longer processes the attribute 'CPPPATH' but the attribute 'INCLUDES' which is consistent with the task generator attribute 'tg.includes'. After execution, two variables are set: + +. 'tg.env.INCPATHS' contains the list of paths to add to the command-line (without the '-I' flags) +. 'tg.includes_nodes' contains the list of paths as node objects for use by other methods or scanners + +=== New wrappers for programs and libraries + +For convenience, the following wrappers 'program', 'shlib' and 'stlib' have been added. The following declarations are equivalent: + +[source,python] +--------------- +def build(ctx): + ctx(features='cxx cxxprogram', source='main.cpp', target='app1') + ctx.program(source='main.cpp', target='app2') + + ctx(features='c cstlib', source='main.cpp', target='app3') + ctx.stlib(source='main.cpp', target='app4') +--------------- + + + + +== New features + +=== Task dependencies + +The method 'BuildContext.use_the_magic' disappears, and a heuristic based on the task input and outputs is used to set the compilation +order over the tasks. For compilation chains and indirect dependencies it will still be necessary to provide the extension symbols, but this +will be the exception. For example: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + ctx.load('gcc') + +def build(ctx): + ctx(rule='touch ${TGT}', target='foo.h', ext_out=['.h']) + ctx(features='c cprogram', source='main.c', target='app') +--------------- + +In this case, the '.h' extension is used to indicate that the unique task produced by this rule-based task generator must be processed before the c/cxx compilation (the c and cxx tasks have ext_in=['.h']). + +=== Configuration API + +The configuration methods bound to the ConfigurationContext by the decorator '@conf' are now bound to the BuildContext class too. +This means that the configuration API may be used during the build phase. + +[source,python] +--------------- +def options(ctx): + ctx.load('compiler_cxx') + +def configure(ctx): + ctx.load('compiler_cxx') + ctx.check(header_name='stdio.h', features='cxx cxxprogram') + +def build(bld): + bld.program(source='main.cpp', target='app') + if bld.cmd != 'clean': + bld.check(header_name='sadlib.h', features='cxx cprogram') +--------------- + +To redirect the output, a logger (from the python logging module) must be provided. Here is an example demonstrating a convenience method for creating the logger: + +[source,python] +--------------- +def options(ctx): + ctx.load('compiler_cxx') + +def configure(ctx): + ctx.load('compiler_cxx') + ctx.check(header_name='stdio.h', features='cxx cxxprogram') + +def build(bld): + if bld.cmd != 'clean': + from waflib import Logs + bld.logger = Logs.make_logger('test.log', 'build') + bld.check(header_name='sadlib.h', features='cxx cprogram') + bld.logger = None +--------------- + +Note that the logger must be removed after the configuration API is used, else the build output will be redirected as well. + +=== Downloading Waf tools + +The call to 'conf.load' will normally fail if a tool cannot be found. + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + conf.load('swig') +--------------- + +By configuring with the option '--download', it is possible to go and download the missing tools +from a remote repository. + +[source,shishell] +--------------- +$ ./waf configure --download +Checking for Setting top to : /comp/test +Checking for Setting out to : /comp/test/build +http://waf.googlecode.com/svn//branches/waf-1.6/waflib/extras/swig.py +downloaded swig from http://waf.googlecode.com/svn//branches/waf-1.6/waflib/extras/swig.py +Checking for Checking for program swig : /usr/bin/swig +'configure' finished successfully (0.408s) +--------------- + +The list of remote and locations may be configured from the user scripts. It is also +recommended to change the function 'Configure.download_check' to check the downloaded tool +from a white list and to avoid potential security issues. This may be unnecessary on local +networks. + +[source,python] +--------------- +top = '.' +out = 'build' + +from waflib import Configure, Utils, Context + +Context.remote_repo = ['http://waf.googlecode.com/svn/'] +Context.remote_locs = ['branches/waf1.6/waflib/extras'] + +def check(path): + if not Utils.h_file(path) in whitelist: + raise ValueError('corrupt file, try again') +Configure.download_check = check + +def configure(conf): + conf.load('swig') +--------------- + +=== The command 'list' + +A new command named 'list' is used to list the valid targets (task generators) for "waf build --targets=x" + +[source,shishell] +--------------- +$ waf list +foo.exe +my_shared_lib +my_static_lib +test_shared_link +test_static_link +'list' finished successfully (0.029s) + +$ waf clean --targets=foo.exe +Waf: Entering directory `/disk/comp/waf-1.6/demos/c/build' +[1/2] c: program/main.c -> build/program/main.c.0.o +[2/2] cprogram: build/program/main.c.0.o -> build/foo.exe +Waf: Leaving directory `/disk/comp/waf-1.6/demos/c/build' +'build' finished successfully (0.154s) +--------------- + +=== The command 'step' + +The new command 'step' is used to execute specific tasks and to return the exit status or any error message. It is particularly useful for debugging: + +[source,shishell] +--------------- +$ waf clean step --file=test_shlib.c +'clean' finished successfully (0.017s) +Waf: Entering directory `/disk/comp/waf-1.6/demos/c/build' +c: shlib/test_shlib.c -> build/shlib/test_shlib.c.1.o + -> 0 +cshlib: build/shlib/test_shlib.c.1.o -> build/shlib/libmy_shared_lib.so + -> 0 +Waf: Leaving directory `/disk/comp/waf-1.6/demos/c/build' +'step' finished successfully (0.146s) +--------------- + +To restrict the tasks to execute, just prefix the names by 'in:' or 'out:': + +[source,shishell] +--------------- +$ waf step --file=out:build/shlib/test_shlib.c.1.o +Waf: Entering directory `/disk/comp/waf-1.6/demos/c/build' +c: shlib/test_shlib.c -> build/shlib/test_shlib.c.1.o + -> 0 +Waf: Leaving directory `/disk/comp/waf-1.6/demos/c/build' +'step' finished successfully (0.091s) +--------------- + + + + +== Compatibility layer + +The module 'waflib.extras.compat15' provides a compatibility layer so that existing project scripts should not require too many modifications. + + +//// +* Environment -> ConfigSet +* only lists are allowed in ConfigSet +* only the build-related commands require a configured project +* rename apply_core -> process_source +* rename apply_rule -> process_rule +* rename Task.TaskBase.classes -> Task.classes +* remove Task.TaskManager and Build.BuildContext.all_task_gen to improve the build group handling +* infer the build directory from the lock filename +* post task generators in a lazy manner +* use tasks for target installation +* Utils.load_tool -> Context.load_tool +* improve the exception handling (WscriptError was removed, use WafError) +//// + diff --git a/docs/book/lang.map b/docs/book/lang.map new file mode 100644 index 00000000..bd587565 --- /dev/null +++ b/docs/book/lang.map @@ -0,0 +1,146 @@ +java = java.lang +moc = cpp.lang +cpp = cpp.lang +c = c.lang +C = cpp.lang +cc = cpp.lang +cs = csharp.lang +csharp = csharp.lang +h = cpp.lang +hh = cpp.lang +H = cpp.lang +hpp = cpp.lang +javascript = javascript.lang +js = javascript.lang +prolog = prolog.lang +pl = prolog.lang +perl = perl.lang +pm = perl.lang +php3 = php.lang +php4 = php.lang +php5 = php.lang +php = php.lang +ctp = php.lang +protobuf = proto.lang +proto = proto.lang +python = python.lang +py = python.lang +ruby = ruby.lang +rb = ruby.lang +flex = flex.lang +lex = flex.lang +l = flex.lang +ll = flex.lang +bison = bison.lang +yacc = bison.lang +y = bison.lang +yy = bison.lang +changelog = changelog.lang +lua = lua.lang +ml = caml.lang +caml = caml.lang +mli = caml.lang +sml = sml.lang +sig = sml.lang +syslog = log.lang +log = log.lang +pas = pascal.lang +pascal = pascal.lang +fortran = fortran.lang +free-fortran = fortran.lang +fixed-fortran = fixed-fortran.lang +html = html.lang +htm = html.lang +tex = latex.lang +latex = latex.lang +cls = latex.lang +sty = latex.lang +dtx = latex.lang +lgt = logtalk.lang +logtalk = logtalk.lang +diff = diff.lang +patch = diff.lang +lang = langdef.lang +langdef = langdef.lang +outlang = outlang.lang +style = style.lang +ps = postscript.lang +eps = postscript.lang +postscript = postscript.lang +kcfg = xml.lang +ui = xml.lang +kdevelop = xml.lang +rc = xml.lang +docbook = xml.lang +kidl = xml.lang +xml = xml.lang +xhtml = xml.lang +bash = sh.lang +sh = sh.lang +csh = sh.lang +ksh = sh.lang +tcsh = sh.lang +shell = sh.lang +tcl = tcl.lang +tk = tcl.lang +txt = nohilite.lang +sql = sql.lang +bib = bib.lang +makefile = makefile.lang +make = makefile.lang +am = makefile.lang +in = makefile.lang +css = css.lang +m4 = m4.lang +ac = m4.lang +autoconf = m4.lang +sl = slang.lang +slsh = slang.lang +slang = slang.lang +properties = properties.lang +desktop = desktop.lang +ini = desktop.lang +conf = conf.lang +lsm = lsm.lang +spec = spec.lang +haxe = haxe.lang +hx = haxe.lang +ldap = ldap.lang +ldif = ldap.lang +glsl = glsl.lang +xorg = xorg.lang +scala = scala.lang +ada = ada.lang +adb = ada.lang +pc = pc.lang +pkgconfig = pc.lang +oz = oz.lang +texinfo = texinfo.lang +texi = texinfo.lang +hs = haskell.lang +hs = haskell.lang +lhs = haskell_literate.lang +haskell = haskell.lang +manifest = manifest.lang +mf = manifest.lang +asm = asm.lang +s = asm.lang +applescript = applescript.lang +scpt = applescript.lang +vbscript = vbscript.lang +vbs = vbscript.lang +awk = awk.lang +bat = bat.lang +batch = bat.lang +clipper = clipper.lang +prg = clipper.lang +cbl = cobol.lang +cobol = cobol.lang +dmd = d.lang +d = d.lang +errors = errors.lang +erl = erlang.lang +erlang = erlang.lang +vala = vala.lang +lisp = lisp.lang +shishell = shishell.lang diff --git a/docs/book/make_like_rules.txt b/docs/book/make_like_rules.txt new file mode 100644 index 00000000..4c3ea53c --- /dev/null +++ b/docs/book/make_like_rules.txt @@ -0,0 +1,406 @@ + +== Task generators + +=== Rule-based task generators (Make-like) + +This chapter illustrates the use of rule-based task generators for building simple targets. + +==== Declaration and usage + +Rule-based task generators are a particular category of task generators producing exactly one task. + +The following example shows a task generator producing the file 'foobar.txt' from the project file 'wscript' by executing the command _cp_ to perform a copy: + +// rule_simple +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + bld( <1> + rule = 'cp ${SRC} ${TGT}', <2> + source = 'wscript', <3> + target = 'foobar.txt', <4> + ) +--------------- + +<1> To instantiate a new task generator, remember that all arguments have the form 'key=value' +<2> The attribute _rule_ represents the command to execute in a readable manner (more on this in the next chapters). +<3> Source files, either in a space-delimited string, or in a list of python strings +<4> Target files, either in a space-delimited string, or in a list of python strings + +Upon execution, the following output will be observed: + +// rules_simple +[source,shishell] +--------------- +$ waf distclean configure build -v +'distclean' finished successfully (0.000s) +'configure' finished successfully (0.021s) +Waf: Entering directory `/tmp/rules_simple/build' +[1/1] foobar.txt: wscript -> build/foobar.txt <1> +10:57:33 runner 'cp ../wscript foobar.txt' <2> +Waf: Leaving directory `/tmp/rules_simple/build' +'build' finished successfully (0.016s) + +$ tree +. +|-- build +| |-- c4che +| | |-- build.config.py +| | `-- _cache.py +| |-- config.log +| `-- foobar.txt +`-- wscript + +$ waf <3> +Waf: Entering directory `/tmp/rules_simple/build' +Waf: Leaving directory `/tmp/rules_simple/build' +'build' finished successfully (0.006s) + +$ echo " " >> wscript <4> + +$ waf +Waf: Entering directory `/tmp/rules_simple/build' +[1/1] foobar.txt: wscript → build/foobar.txt <5> +Waf: Leaving directory `/tmp/rules_simple/build' +'build' finished successfully (0.013s) +--------------- + +<1> In the first execution, the target is correctly created +<2> Command-lines are only displayed in 'verbose mode' by using the option '-v' +<3> The target is up-to-date, so the task is not executed +<4> Modify the source file in place by appending a space character +<5> Since the source has changed, the target is created once again + +The string for the rule also enters in the dependency calculation. If the rule changes, then the task will be recompiled. + +==== Rule functions + +Rules may be given as expression strings or as python function. The function is assigned to the task class created: + +// rule_function +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + def run(task): <1> + src = task.inputs[0].abspath() <2> + tgt = task.outputs[0].abspath() <3> + cmd = 'cp %s %s' % (src, tgt) + print(cmd) + return task.exec_command(cmd) <4> + + bld( + rule = run, <5> + source = 'wscript', + target = 'same.txt', + ) +--------------- + +<1> Rule functions take the task instance as parameter. +<2> Sources and targets are represented internally as Node objects bound to the task instance. +<3> Commands are executed from the root of the build directory. Node methods such as 'bldpath' ease the command line creation. +<4> The task class holds a wrapper around subprocess.Popen(...) to execute commands. +<5> Use a function instead of a string expression + +The execution trace will be similar to the following: + +[source,shishell] +--------------- +$ waf distclean configure build +'distclean' finished successfully (0.001s) +'configure' finished successfully (0.001s) +Waf: Entering directory `/tmp/rule_function/out' +[1/1] same.txt: wscript -> out/same.txt +cp /tmp/rule_function/wscript /tmp/rule_function/build/same.txt +Waf: Leaving directory `/tmp/rule_function/out' +'build' finished successfully (0.010s) +--------------- + +The rule function must return a null value (0, None or False) to indicate success, and must generate the files corresponding to the outputs. The rule function is executed by threads internally so it is important to write thread-safe code (cannot search or create node objects). + +Unlike string expressions, functions may execute several commands at once. + +==== Shell usage + +The attribute 'shell' is used to enable the system shell for command execution. A few points are worth keeping in mind when declaring rule-based task generators: + +. The Waf tools do not use the shell for executing commands +. The shell is used by default for user commands and custom task generators +. String expressions containing the following symbols `>', `<' or `&' cannot be transformed into functions to execute commands without a shell, even if told to +. In general, it is better to avoid the shell whenever possible to avoid quoting problems (paths having blank characters in the name for example) +. The shell is creating a performance penalty which is more visible on win32 systems. + +Here is an example: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + bld(rule='cp ${SRC} ${TGT}', source='wscript', target='f1.txt', shell=False) + bld(rule='cp ${SRC} ${TGT}', source='wscript', target='f2.txt', shell=True) +--------------- + +Upon execution, the results will be similar to the following: + +[source,shishell] +--------------- +$ waf distclean configure build --zones=runner,action +'distclean' finished successfully (0.004s) +'configure' finished successfully (0.001s) +Waf: Entering directory `/tmp/rule/out' +23:11:23 action <1> +def f(task): + env = task.env + wd = getattr(task, 'cwd', None) + def to_list(xx): + if isinstance(xx, str): return [xx] + return xx + lst = [] + lst.extend(['cp']) + lst.extend([a.srcpath(env) for a in task.inputs]) + lst.extend([a.bldpath(env) for a in task.outputs]) + lst = [x for x in lst if x] + return task.exec_command(lst, cwd=wd) + +23:11:23 action +def f(task): + env = task.env + wd = getattr(task, 'cwd', None) + p = env.get_flat + cmd = ''' cp %s %s ''' % (" ".join([a.srcpath(env) for a in task.inputs]), <2> + " ".join([a.bldpath(env) for a in task.outputs])) + return task.exec_command(cmd, cwd=wd) + +[1/2] f1.txt: wscript -> out/f1.txt +23:11:23 runner system command -> ['cp', '../wscript', 'f1.txt'] <3> +[2/2] f2.txt: wscript -> out/f2.txt +23:11:23 runner system command -> cp ../wscript f2.txt +Waf: Leaving directory `/tmp/rule/out' +'build' finished successfully (0.017s) +--------------- + +<1> String expressions are converted to functions (here, without the shell). +<2> Command execution by the shell. Notice the heavy use of string concatenation. +<3> Commands to execute are displayed by calling 'waf --zones=runner'. When called without the shell, the arguments are displayed as a list. + +NOTE: For performance and maintainability, try avoiding the shell whenever possible + +==== Inputs and outputs + +Source and target arguments are optional for make-like task generators, and may point at one or several files at once. Here are a few examples: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + bld( <1> + rule = 'cp ${SRC} ${TGT[0].abspath()} && cp ${SRC} ${TGT[1].abspath()}', + source = 'wscript', + target = 'f1.txt f2.txt', + shell = True + ) + + bld( <2> + source = 'wscript', + rule = 'echo ${SRC}' + ) + + bld( <3> + target = 'test.k3', + rule = 'echo "test" > ${TGT}', + ) + + bld( <4> + rule = 'echo 1337' + ) + + bld( <5> + rule = "echo 'task always run'", + always = True + ) +--------------- + +<1> Generate 'two files' whenever the input or the rule change. Likewise, a rule-based task generator may have multiple input files. +<2> The command is executed whenever the input or the rule change. There are no declared outputs. +<3> No input, the command is executed whenever it changes +<4> No input and no output, the command is executed only when the string expression changes +<5> No input and no output, the command is executed each time the build is called + +For the record, here is the output of the build: + +[source,shishell] +--------------- +$ waf distclean configure build +'distclean' finished successfully (0.002s) +'configure' finished successfully (0.093s) +Waf: Entering directory `/tmp/rule/out' +[1/5] echo 1337: +1337 +[2/5] echo 'task always run': +[3/5] echo ${SRC}: wscript +../wscript +[4/5] f1.txt f2.txt: wscript -> out/f1.txt out/f2.txt +task always run +[5/5] test.k3: -> out/test.k3 +Waf: Leaving directory `/tmp/rule/out' +'build' finished successfully (0.049s) + +$ waf +Waf: Entering directory `/tmp/rule/out' +[2/5] echo 'task always run': +task always run +Waf: Leaving directory `/tmp/rule/out' +'build' finished successfully (0.014s) +--------------- + +==== Dependencies on file contents + +As a second example, we will create a file named 'r1.txt' from the current date. It will be updated each time the build is executed. A second file named 'r2.txt' will be created from 'r1.txt'. + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + bld( + name = 'r1', <1> + target = 'r1.txt', + rule = '(date > ${TGT}) && cat ${TGT}', <2> + always = True, <3> + ) + + bld( + name = 'r2', <4> + target = 'r2.txt', + rule = 'cp ${SRC} ${TGT}', + source = 'r1.txt', <5> + after = 'r1', <6> + ) +--------------- + +<1> Give the task generator a name, it will create a task class of the same name to execute the command +<2> Create 'r1.txt' with the date +<3> There is no source file to depend on and the rule never changes. The task is then set to be executed each time the build is started by using the attribute 'always' +<4> If no name is provided, the rule is used as a name for the task class +<5> Use 'r1.txt' as a source for 'r2.txt'. Since 'r1.txt' was declared before, the dependency will be added automatically ('r2.txt' will be re-created whenever 'r1.txt' changes) +<6> Set the command generating 'r2.txt' to be executed after the command generating 'r1.txt'. The attribute 'after' references task class names, not task generators. Here it will work because rule-based task generator tasks inherit the 'name' attribute + +The execution output will be the following: + +[source,shishell] +--------------- +$ waf distclean configure build -v +'distclean' finished successfully (0.003s) +'configure' finished successfully (0.001s) +Waf: Entering directory `/tmp/rule/out' +[1/2] r1: -> out/r1.txt +16:44:39 runner system command -> (date > r1.txt) && cat r1.txt +dom ene 31 16:44:39 CET 2010 +[2/2] r2: out/r1.txt -> out/r2.txt +16:44:39 runner system command -> cp r1.txt r2.txt +Waf: Leaving directory `/tmp/rule/out' +'build' finished successfully (0.021s) + +$ waf -v +Waf: Entering directory `/tmp/rule/out' +[1/2] r1: -> out/r1.txt +16:44:41 runner system command -> (date > r1.txt) && cat r1.txt +dom ene 31 16:44:41 CET 2010 +Waf: Leaving directory `/tmp/rule/out' +'build' finished successfully (0.016s) +--------------- + +Although r2 *depends* on 'r1.txt', r2 was not executed in the second build. As a matter of fact, the signature of the task r1 has not changed, and r1 was only set to be executed each time, regardless of its signature. Since the signature of the 'r1.txt' does not change, the signature of r2 will not change either, and 'r2.txt' is considered up-to-date. + +We will now illustrate how to make certain that the outputs reflect the file contents and trigger the rebuild for dependent tasks by enabling the attribute 'on_results': + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + bld( + name = 'r1', + target = 'r1.txt', + rule = '(date > ${TGT}) && cat ${TGT}', + always = True, + on_results = True, + ) + + bld( + target = 'r2.txt', + rule = 'cp ${SRC} ${TGT}', + source = 'r1.txt', + after = 'r1', + ) +--------------- + +Here 'r2.txt' will be re-created each time: + +[source,shishell] +--------------- +$ waf distclean configure build -v +'distclean' finished successfully (0.003s) +'configure' finished successfully (0.001s) +Waf: Entering directory `/tmp/rule/out' +[1/2] r1: -> out/r1.txt +16:59:49 runner system command -> (date > r1.txt) && cat r1.txt <1> +dom ene 31 16:59:49 CET 2010 <2> +[2/2] r2: out/r1.txt -> out/r2.txt +16:59:49 runner system command -> cp r1.txt r2.txt +Waf: Leaving directory `/tmp/rule/out' +'build' finished successfully (0.020s) + +$ waf -v +Waf: Entering directory `/tmp/rule/out' +[1/2] r1: -> out/r1.txt +16:59:49 runner system command -> (date > r1.txt) && cat r1.txt +dom ene 31 16:59:49 CET 2010 <3> +Waf: Leaving directory `/tmp/rule/out' +'build' finished successfully (0.016s) + +$ waf -v +Waf: Entering directory `/tmp/rule/out' +[1/2] r1: -> out/r1.txt +16:59:53 runner system command -> (date > r1.txt) && cat r1.txt +dom ene 31 16:59:53 CET 2010 <4> +[2/2] r2: out/r1.txt -> out/r2.txt +16:59:53 runner system command -> cp r1.txt r2.txt +Waf: Leaving directory `/tmp/rule/out' +'build' finished successfully (0.022s) +--------------- + +<1> Start with a clean build, both 'r1.txt' and 'r2.txt' are created +<2> Notice the date and time +<3> The second build was executed at the same date and time, so 'r1.txt' has not changed, therefore 'r2.txt' is up to date +<4> The third build is executed at another date and time. Since 'r1.txt' has changed, 'r2.txt' is created once again + diff --git a/docs/book/nodes.txt b/docs/book/nodes.txt new file mode 100644 index 00000000..2f1ef12c --- /dev/null +++ b/docs/book/nodes.txt @@ -0,0 +1,334 @@ +== Node objects + +Node objects represent files or folders and are used to ease the operations dealing with the file system. This chapter provides an overview of their usage. + +=== Design of the node class + +==== The node tree + +The Waf nodes inherit the class _waflib.Node.Node_ and provide a tree structure to represent the file system: + +. *parent*: parent node +. *children*: folder contents - or empty if the node is a file + +In practice, the reference to the filesystem tree is bound to the context classes for access from Waf commands. Here is an illustration: + +// nodes_tree +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + pass + +def dosomething(ctx): + print(ctx.path.abspath()) <1> + print(ctx.root.abspath()) <2> + print("ctx.path contents %r" % ctx.path.children) + print("ctx.path parent %r" % ctx.path.parent.abspath()) + print("ctx.root parent %r" % ctx.root.parent) +--------------- + +<1> *ctx.path* represents the path to the +wscript+ file being executed +<2> *ctx.root* is the root of the file system or the folder containing the drive letters (win32 systems) + +The execution output will be the following: + +[source,shishell] +--------------- +$ waf configure dosomething +Setting top to : /tmp/node_tree +Setting out to : /tmp/node_tree/build +'configure' finished successfully (0.007s) +/tmp/node_tree <1> +/ +ctx.path contents {'wscript': /tmp/node_tree/wscript} <2> +ctx.path parent '/tmp' <3> +ctx.root parent None <4> +'dosomething' finished successfully (0.001s) +--------------- + +<1> Absolute paths are used frequently +<2> The folder contents are stored in the dict _children_ which maps names to node objects +<3> Each node keeps reference to his _parent_ node +<4> The root node has no _parent_ + +NOTE: There is a strict correspondance between nodes and filesystem elements: a node represents exactly one file or one folder, and only one node can represent a file or a folder. + +==== Node caching + +By default, only the necessary nodes are created: + +// nodes_cache +[source,python] +--------------- +def configure(ctx): + pass + +def dosomething(ctx): + print(ctx.root.children) +--------------- + +The filesystem root appears to only contain one node, although the real filesystem root contains more folders than just +/tmp+: + +[source,shishell] +--------------- +$ waf configure dosomething +Setting top to : /tmp/nodes_cache +Setting out to : /tmp/nodes_cache/build +'configure' finished successfully (0.086s) +{'tmp': /tmp} +'dosomething' finished successfully (0.001s) + +$ ls / +bin boot dev etc home tmp usr var +--------------- + +This means in particular that some nodes may have to be read from the file system or created before being used. + +// ==== nodes and signatures TODO + + +=== General usage + +==== Searching and creating nodes + +Nodes may be created manually or read from the file system. Three methods are provided for this purpose: + +// nodes_search +[source,python] +--------------- +def configure(ctx): + pass + +def dosomething(ctx): + print(ctx.path.find_node('wscript')) <1> + + nd1 = ctx.path.make_node('foo.txt') <2> + print(nd1) + + nd2 = ctx.path.search('foo.txt') <3> + print(nd2) + + nd3 = ctx.path.search('bar.txt') <4> + print(nd3) + + nd2.write('some text') <5> + print(nd2.read()) + + print(ctx.path.listdir()) +--------------- + +<1> Search for a node by reading the file system +<2> Search for a node or create it if it does not exist +<3> Search for a node but do not try to create it +<4> Search for a file which does not exist +<5> Write to the file pointed by the node, creating or overwriting the file + +The output will be the following: + +[source,shishell] +--------------- +$ waf distclean configure dosomething +'distclean' finished successfully (0.005s) +Setting top to : /tmp/nodes_search +Setting out to : /tmp/nodes_search/build +'configure' finished successfully (0.006s) +wscript +foo.txt +foo.txt +None +some text +['.lock-wafbuild', 'foo.txt', 'build', 'wscript', '.svn'] +--------------- + +NOTE: More methods may be found in the http://waf.googlecode.com/svn/docs/apidocs/index.html[API documentation] + +WARNING: These methods are not safe for concurrent access. The node class methods are not meant to be thread-safe. + +==== Listing files and folders + +The method *ant_glob* is used to list files and folders recursively: + +// nodes_ant_glob +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + pass + +def dosomething(ctx): + print(ctx.path.ant_glob('wsc*')) <1> + print(ctx.path.ant_glob('w?cr?p?')) <2> + print(ctx.root.ant_glob('usr/include/**/zlib*', <3> dir=False, src=True)) <4> + print(ctx.path.ant_glob(['**/*py', '**/*p'], excl=['**/default*'])) <5> +--------------- + +<1> The method ant_glob is called on a node object, and not on the build context, it returns only files by default +<2> Patterns may contain wildcards such as '*' or '?', but they are http://ant.apache.org/manual/dirtasks.html[Ant patterns], not regular expressions +<3> The symbol '**' enable recursion. Complex folder hierarchies may take a lot of time, so use with care. +<4> Even though recursion is enabled, only files are returned by default. To turn directory listing on, use 'dir=True' +<5> Patterns are either lists of strings or space-delimited values. Patterns to exclude are defined in 'waflib.Node.exclude_regs'. + +The execution output will be the following: + +[source,shishell] +--------------- +$ waf configure dosomething +Setting top to : /tmp/nodes_ant_glob +Setting out to : /tmp/nodes_ant_glob/build +'configure' finished successfully (0.006s) +[/tmp/nodes_ant_glob/wscript] +[/tmp/nodes_ant_glob/wscript] +[/usr/include/zlib.h] +[/tmp/nodes_ant_glob/build/c4che/build.config.py] +--------------- + +The sequence '..' represents exactly two dot characters, and not the parent directory. This is used to guarantee that the search will terminate, and that the same files will not be listed multiple times. Consider the following: + +[source,python] +--------------- +ctx.path.ant_glob('../wscript') <1> +ctx.path.parent.ant_glob('wscript') <2> +--------------- + +<1> Invalid, this pattern will never return anything +<2> Call 'ant_glob' from the parent directory + +==== Path manipulation: abspath, path_from + +The method 'abspath' is used to obtain the absolute path for a node. In the following example, three nodes are used: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(ctx): + dir = ctx.path <1> + src = ctx.path.find_resource('wscript') + bld = ctx.path.find_or_declare('out.out') + + print(src.abspath(ctx.env)) <2> + print(bld.abspath(ctx.env)) + print(dir.abspath(ctx.env)) <3> + print(dir.abspath()) +--------------- + +<1> Directory node, source node and build node +<2> Computing the absolute path for source node or a build node takes a configuration set as parameter +<3> Computing the absolute path for a directory may use a configuration set or not + +Here is the execution trace: + +[source,shishell] +--------------- +$ waf distclean configure build +'distclean' finished successfully (0.002s) +'configure' finished successfully (0.005s) +Waf: Entering directory `/tmp/nested/build' +/tmp/nested/wscript <1> +/tmp/nested/build/out.out <2> +/tmp/nested/build/ <3> +/tmp/nested <4> +Waf: Leaving directory `/tmp/nested/build' +'build' finished successfully (0.003s) +--------------- + +<1> Absolute path for the source node +<2> The absolute path for the build node depends on the variant in use +<3> When a configuration set is provided, the absolute path for a directory node is the build directory representation including the variant +<4> When no configuration set is provided, the directory node absolute path is the one for the source directory + +NOTE: Several other methods such as 'relpath_gen' or 'srcpath' are provided. See the http://waf.googlecode.com/svn/docs/apidocs/index.html[API documentation] + +=== BuildContext-specific methods + +==== Source and build nodes + +Although the _sources_ and _targets_ in the +wscript+ files are declared as if they were in the current directory, the target files are output into the build directory. To enable this behaviour, the directory structure below the _top_ directory must be replicated in the _out_ directory. For example, the folder *program* from +demos/c+ has its equivalent in the build directory: + +[source,shishell] +--------------- +$ cd demos/c +$ tree +. +|-- build +| |-- c4che +| | |-- build.config.py +| | `-- _cache.py +| |-- config.h +| |-- config.log +| `-- program +| |-- main.c.0.o +| `-- myprogram +|-- program +| |-- a.h +| |-- main.c +| `-- wscript_build +`-- wscript +--------------- + +To support this, the build context provides two additional nodes: + +. srcnode: node representing the top-level directory +. bldnode: node representing the build directory + +To obtain a build node from a src node and vice-versa, the following methods may be used: + +. Node.get_src() +. Node.get_bld() + +==== Using Nodes during the build phase + +Although using _srcnode_ and _bldnode_ directly is possible, the three following wrapper methods are much easier to use. They accept a string representing the target as input and return a single node: + +. *find_dir*: returns a node or None if the folder cannot be found on the system. +. *find_resource*: returns a node under the source directory, a node under the corresponding build directory, or None if no such a node exists. If the file is not in the build directory, the node signature is computed and put into a cache (file contents hash). +. *find_or_declare*: returns a node or create the corresponding node in the build directory. + +Besides, they all use _find_dir_ internally which will create the required directory structure in the build directory. Because the folders may be replicated in the build directory before the build starts, it is recommended to use it whenever possible: + +[source,python] +--------------- +def build(bld): + p = bld.path.parent.find_dir('src') <1> + p = bld.path.find_dir('../src') <2> +--------------- + +<1> Not recommended, use _find_dir_ instead +<2> Path separators are converted automatically according to the platform. + +==== Nodes, tasks, and task generators + +As seen in the previous chapter, Task objects can process files represented as lists of input and output nodes. The task generators +will usually process the input files given as strings to obtain such nodes and bind them to the tasks. + +Since the build directory can be enabled or disabled, the following file copy is invalid: footnote:[When file copies cannot be avoided, the best practice is to change the file names] + +[source,python] +--------------- +def build(bld): + bld(rule='cp ${SRC}' ${TGT}, source='foo.txt', target='foo.txt') +--------------- + +To actually copy a file into the corresponding build directory with the same name, the ambiguity must be removed: + +[source,python] +--------------- +def build(bld): + bld( + rule = 'cp ${SRC}' ${TGT}, + source = bld.path.make_node('foo.txt'), + target = bld.path.get_bld().make_node('foo.txt') + ) +--------------- + +// ==== Serialization concerns + diff --git a/docs/book/pdebug.eps b/docs/book/pdebug.eps new file mode 100644 index 00000000..fe7afdb0 --- /dev/null +++ b/docs/book/pdebug.eps @@ -0,0 +1,5787 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.8.10 (http://cairographics.org) +%%CreationDate: Fri Jul 23 18:31:56 2010 +%%Pages: 1 +%%BoundingBox: 0 0 640 167 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%EndComments +%%BeginProlog +/cairo_eps_state save def +/dict_count countdictstack def +/op_count count 1 sub def +userdict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/B { fill stroke } bind def +/B* { eofill stroke } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +%%EndProlog +11 dict begin +/FontType 42 def +/FontName /f-0-0 def +/PaintType 0 def +/FontMatrix [ 1 0 0 1 0 0 ] def +/FontBBox [ 0 0 0 0 ] def +/Encoding 256 array def +0 1 255 { Encoding exch /.notdef put } for +Encoding 1 /uni0050 put +Encoding 2 /uni0061 put +Encoding 3 /uni0072 put +Encoding 4 /uni006C put +Encoding 5 /uni0065 put +Encoding 6 /uni0020 put +Encoding 7 /uni0070 put +Encoding 8 /uni0073 put +Encoding 9 /uni006E put +Encoding 10 /uni0074 put +Encoding 11 /uni0069 put +Encoding 12 /uni006F put +Encoding 13 /uni0066 put +Encoding 14 /uni0027 put +Encoding 15 /uni0077 put +Encoding 16 /uni0062 put +Encoding 17 /uni0075 put +Encoding 18 /uni0064 put +Encoding 19 /uni002D put +Encoding 20 /uni006A put +Encoding 21 /uni0036 put +Encoding 22 /uni0043 put +Encoding 23 /uni006D put +Encoding 24 /uni006B put +Encoding 25 /uni004F put +Encoding 26 /uni0068 put +Encoding 27 /uni004C put +/CharStrings 28 dict dup begin +/.notdef 0 def +/uni0050 1 def +/uni0061 2 def +/uni0072 3 def +/uni006C 4 def +/uni0065 5 def +/uni0020 6 def +/uni0070 7 def +/uni0073 8 def +/uni006E 9 def +/uni0074 10 def +/uni0069 11 def +/uni006F 12 def +/uni0066 13 def +/uni0027 14 def +/uni0077 15 def +/uni0062 16 def +/uni0075 17 def +/uni0064 18 def +/uni002D 19 def +/uni006A 20 def +/uni0036 21 def +/uni0043 22 def +/uni006D 23 def +/uni006B 24 def +/uni004F 25 def +/uni0068 26 def +/uni004C 27 def +end readonly def +/sfnts [ +<00010000000a008000030020636d617000f2f182000011dc000000766376742000691d390000 +1254000001fe6670676d7134766a00001454000000ab676c796676060f25000000ac00001130 +68656164f1f329920000150000000036686865610cb8066d0000153800000024686d74787ae6 +0eb00000155c000000706c6f63610000f634000015cc000000746d6178700489067100001640 +00000020707265703b07f100000016600000056800020066fe96046605a400030007001a400c +04fb0006fb0108057f0204002fc4d4ec310010d4ecd4ec301311211125211121660400fc7303 +1bfce5fe96070ef8f2720629000200c90000048d05d500080013003a40180195100095098112 +100a0802040005190d3f11001c09041410fcec32fcec11173931002ff4ecd4ec30400b0f151f +153f155f15af1505015d011133323635342623252132041514042b0111230193fe8d9a9a8dfe +3801c8fb0101fefffbfeca052ffdcf92878692a6e3dbdde2fda80002007bffe3042d047b000a +002500bc4027191f0b17090e00a91706b90e1120861fba1cb923b8118c170c001703180d0908 +0b1f030814452610fcecccd4ec323211393931002fc4e4f4fcf4ec10c6ee10ee113911391239 +30406e301d301e301f3020302130223f27401d401e401f402040214022501d501e501f502050 +21502250277027851d871e871f8720872185229027a027f0271e301e301f30203021401e401f +40204021501e501f50205021601e601f60206021701e701f70207021801e801f80208021185d +015d0122061514163332363d01371123350e01232226353436332135342623220607353e0133 +321602bedfac816f99b9b8b83fbc88accbfdfb0102a79760b65465be5af3f00233667b6273d9 +b4294cfd81aa6661c1a2bdc0127f8b2e2eaa2727fc00000100ba0000034a047b001100304014 +060b0700110b03870eb809bc070a06080008461210fcc4ec3231002fe4f4ecc4d4cc11123930 +b450139f1302015d012e012322061511231133153e0133321617034a1f492c9ca7b9b93aba85 +132e1c03b41211cbbefdb20460ae666305050000000100c100000179061400030022b7009702 +010800460410fcec31002fec30400d10054005500560057005f00506015d13331123c1b8b806 +14f9ec0000020071ffe3047f047b0014001b00704024001501098608880515a90105b90c01bb +18b912b80c8c1c1b1502081508004b02120f451c10fcecf4ecc4111239310010e4f4ece410ee +10ee10f4ee1112393040293f1d701da01dd01df01d053f003f013f023f153f1b052c072f082f +092c0a6f006f016f026f156f1b095d71015d0115211e0133323637150e012320001110003332 +00072e0123220607047ffcb20ccdb76ac76263d06bfef4fec70129fce20107b802a5889ab90e +025e5abec73434ae2a2c0138010a01130143feddc497b4ae9e00000200bafe5604a4047b0010 +001c003e401b1ab9000e14b90508b80e8c01bd03bc1d11120b471704000802461d10fcec3232 +f4ec310010e4e4e4f4c4ec10c4ee304009601e801ea01ee01e04015d2511231133153e013332 +001110022322260134262322061514163332360173b9b93ab17bcc00ffffcc7bb10238a79292 +a7a79292a7a8fdae060aaa6461febcfef8fef8febc6101ebcbe7e7cbcbe7e70000000001006f +ffe303c7047b002700e7403c0d0c020e0b531f1e080902070a531f1f1e420a0b1e1f04150086 +0189041486158918b91104b925b8118c281e0a0b1f1b0700521b080e07081422452810fcc4ec +d4ece4111239393939310010e4f4ec10fef5ee10f5ee121739304b535807100eed111739070e +ed1117395922b2002701015d406d1c0a1c0b1c0c2e092c0a2c0b2c0c3b093b0a3b0b3b0c0b20 +0020012402280a280b2a132f142f152a16281e281f292029212427860a860b860c860d120000 +00010202060a060b030c030d030e030f03100319031a031b031c041d09272f293f295f297f29 +80299029a029f029185d005d7101152e012322061514161f011e0115140623222627351e0133 +32363534262f012e01353436333216038b4ea85a898962943fc4a5f7d85ac36c66c661828c65 +ab40ab98e0ce66b4043fae282854544049210e2a99899cb62323be353559514b50250f249582 +9eac1e000000000100ba00000464047b001300364019030900030e0106870e11b80cbc0a0102 +08004e0d09080b461410fcec32f4ec31002f3ce4f4c4ec1112173930b46015cf1502015d0111 +231134262322061511231133153e013332160464b87c7c95acb9b942b375c1c602a4fd5c029e +9f9ebea4fd870460ae6564ef00010037000002f2059e0013003840190e05080f03a9001101bc +08870a0b08090204000810120e461410fc3cc4fc3cc432393931002fecf43cc4ec3211393930 +b2af1501015d01112115211114163b01152322263511233533110177017bfe854b73bdbdd5a2 +8787059efec28ffda0894e9a9fd202608f013e000000000200c100000179061400030007002b +400e06be04b100bc020501080400460810fc3cec3231002fe4fcec30400b1009400950096009 +700905015d1333112311331523c1b8b8b8b80460fba00614e90000020071ffe30475047b000b +0017004a401306b91200b90cb8128c1809120f51031215451810fcecf4ec310010e4f4ec10ee +3040233f197b007b067f077f087f097f0a7f0b7b0c7f0d7f0e7f0f7f107f117b12a019f01911 +015d012206151416333236353426273200111000232200111000027394acab9593acac93f001 +12feeef0f1feef011103dfe7c9c9e7e8c8c7e99cfec8feecfeedfec701390113011401380000 +0001002f000002f8061400130059401c0510010c08a906018700970e06bc0a02130700070905 +080d0f0b4c1410fc4bb00a5458b9000b004038594bb00e5458b9000bffc038593cc4fc3cc4c4 +12393931002fe432fcec10ee321239393001b640155015a015035d01152322061d0121152111 +23112335333534363302f8b0634d012ffed1b9b0b0aebd0614995068638ffc2f03d18f4ebbab +000100c503aa016f05d500030037400a0184008104000502040410fc4bb012544bb013545b58 +b90002ffc03859ec310010f4ec3001400d40055005600570059005a005065d01112311016faa +05d5fdd5022b000000010056000006350460000c01eb404905550605090a0904550a0903550a +0b0a025501020b0b0a061107080705110405080807021103020c000c011100000c420a050203 +060300bf0b080c0b0a09080605040302010b07000d10d44bb00a544bb011545b4bb012545b4b +b013545b4bb00b545b58b9000000403859014bb00c544bb00d545b4bb010545b58b90000ffc0 +3859cc173931002f3cec32321739304b5358071005ed071008ed071008ed071005ed071008ed +071005ed0705ed071008ed59220140ff050216021605220a350a49024905460a400a5b025b05 +550a500a6e026e05660a79027f0279057f05870299029805940abc02bc05ce02c703cf051d05 +02090306040b050a080b09040b050c1502190316041a051b081b09140b150c25002501230227 +03210425052206220725082709240a210b230c390336043608390c300e460248034604400442 +054006400740084409440a440b400e400e560056015602500451055206520750085309540a55 +0b6300640165026a0365046a056a066a076e09610b670c6f0e7500750179027d0378047d057a +067f067a077f07780879097f097b0a760b7d0c870288058f0e97009701940293039c049b0598 +0698079908402f960c9f0ea600a601a402a403ab04ab05a906a907ab08a40caf0eb502b103bd +04bb05b809bf0ec402c303cc04ca05795d005d13331b01331b013301230b012356b8e6e5d9e6 +e5b8fedbd9f1f2d90460fc96036afc96036afba00396fc6a000200baffe304a40614000b001c +0038401903b90c0f09b918158c0fb81b971900121247180c06081a461d10fcec3232f4ec3100 +2fece4f4c4ec10c6ee30b6601e801ea01e03015d013426232206151416333236013e01333200 +111002232226271523113303e5a79292a7a79292a7fd8e3ab17bcc00ffffcc7bb13ab9b9022f +cbe7e7cbcbe7e702526461febcfef8fef8febc6164a80614000200aeffe30458047b00130014 +003b401c030900030e0106870e118c0a01bc14b80c0d0908140b4e020800461510fcecf439ec +3231002fe4e432f4c4ec1112173930b46f15c01502015d131133111416333236351133112335 +0e0123222601aeb87c7c95adb8b843b175c1c801cf01ba02a6fd619f9fbea4027bfba0ac6663 +f003a80000020071ffe3045a06140010001c003840191ab9000e14b905088c0eb80197031704 +0008024711120b451d10fcecf4ec323231002fece4f4c4ec10c4ee30b6601e801ea01e03015d +0111331123350e0123220211100033321601141633323635342623220603a2b8b83ab17ccbff +00ffcb7cb1fdc7a79292a8a89292a703b6025ef9eca86461014401080108014461fe15cbe7e7 +cbcbe7e70001006401df027f028300030011b6009c020401000410dccc310010d4ec30132115 +2164021bfde50283a4000002ffdbfe5601790614000b000f0044401c0b0207000ebe0c078705 +bd00bc0cb110081005064f0d01080c00461010fc3cec32e4391239310010ece4f4ec10ee1112 +393930400b1011401150116011701105015d13331114062b01353332363511331523c1b8a3b5 +4631694cb8b80460fb8cd6c09c61990628e900000002008fffe3049605f0000b002400584024 +1306000d860c00a01606a01c16a510a00c8922911c8c250c22091c191e131c03211f1b2510fc +ececf4ece4310010e4f4e4fce410ee10ee10ee111239304014cb00cb01cd02cd03cd04cb05cb +0607a41eb21e025d015d01220615141633323635342601152e01232202033e01333200151400 +23200011100021321602a4889f9f88889f9f01094c9b4cc8d30f3bb26be10105fef0e2fefdfe +ee0150011b4c9b033bbaa2a1bbbba1a2ba0279b82426fef2feef575dfeefebe6feea018d0179 +016201a51e00000000010073ffe3052705f000190036401a0da10eae0a951101a100ae049517 +91118c1a07190d003014101a10fcec32ec310010e4f4ecf4ec10eef6ee30b40f1b1f1b02015d +01152e0123200011100021323637150e01232000111000213216052766e782ff00fef0011001 +0082e7666aed84feadfe7a0186015386ed0562d55f5efec7fed8fed9fec75e5fd34848019f01 +670168019f470000000100ba0000071d047b0022005a4026061209180f00061d07150c871d20 +03b81bbc19100700110f0808065011080f501c18081a462310fcec32fcfcfcec11123931002f +3c3ce4f43cc4ec32111217393040133024502470249024a024a024bf24df24ff2409015d013e +013332161511231134262322061511231134262322061511231133153e01333216042945c082 +afbeb972758fa6b972778da6b9b93fb0797aab03897c76f5e2fd5c029ea19cbea4fd87029ea2 +9bbfa3fd870460ae67627c000000000100ba0000049c0614000a00bc40290811050605071106 +060503110405040211050504420805020303bc009709060501040608010800460b10fcec32d4 +c4113931002f3cece41739304b5358071004ed071005ed071005ed071004ed5922b2100c0101 +5d405f04020a081602270229052b0856026602670873027705820289058e08930296059708a3 +021209050906020b030a072803270428052b062b07400c6803600c8903850489058d068f079a +039707aa03a705b607c507d607f703f003f704f0041a5d71005d1333110133090123011123ba +b90225ebfdae026bf0fdc7b90614fc6901e3fdf4fdac0223fddd00020073ffe305d905f0000b +00170023401306951200950c91128c1809190f33031915101810fcecfcec310010e4f4ec10ee +300122001110003332001110002720001110002120001110000327dcfefd0103dcdc0101feff +dc013a0178fe88fec6fec5fe870179054cfeb8fee5fee6feb80148011a011b0148a4fe5bfe9e +fe9ffe5b01a40162016201a50000000100ba000004640614001300344019030900030e010687 +0e11b80c970a010208004e0d09080b461410fcec32f4ec31002f3cecf4c4ec1112173930b260 +1501015d0111231134262322061511231133113e013332160464b87c7c95acb9b942b375c1c6 +02a4fd5c029e9f9ebea4fd870614fd9e6564ef00000100c90000046a05d500050025400c0295 +008104011c033a00040610fcecec31002fe4ec304009300750078003800404015d1333112115 +21c9ca02d7fc5f05d5fad5aa0000000200030000000000140001000000000034000400200000 +0004000400010000f01bffff0000f000ffff10000001000000000006004200000000001c0000 +000100020003000400050006000700080009000a000b000c000d000e000f0010001100120013 +001400150016001700180019001a001b0000013500b800cb00cb00c100aa009c01a600b80066 +0000007100cb00a002b20085007500b800c301cb0189022d00cb00a600f000d300aa008700cb +03aa0400014a003300cb000000d9050200f4015400b4009c01390114013907060400044e04b4 +045204b804e704cd0037047304cd04600473013303a2055605a60556053903c5021200c9001f +00b801df007300ba03e9033303bc0444040e00df03cd03aa00e503aa0404000000cb008f00a4 +007b00b80014016f007f027b0252008f00c705cd009a009a006f00cb00cd019e01d300f000ba +018300d5009803040248009e01d500c100cb00f600830354027f00000333026600d300c700a4 +00cd008f009a0073040005d5010a00fe022b00a400b4009c00000062009c0000001d032d05d5 +05d505d505f0007f007b005400a406b80614072301d300b800cb00a601c301ec069300a000d3 +035c037103db0185042304a80448008f0139011401390360008f05d5019a0614072306660179 +046004600460047b009c00000277046001aa00e904600762007b00c5007f027b000000b40252 +05cd006600bc00660077061000cd013b01850389008f007b0000001d00cd074a042f009c009c +0000077d006f0000006f0335006a006f007b00ae00b2002d0396008f027b00f6008303540637 +05f6008f009c04e10266008f018d02f600cd03440029006604ee00730000140000960000b707 +060504030201002c2010b002254964b040515820c859212d2cb002254964b040515820c85921 +2d2c20100720b00050b00d7920b8ffff5058041b0559b0051cb0032508b0042523e120b00050 +b00d7920b8ffff5058041b0559b0051cb0032508e12d2c4b505820b0fd454459212d2cb00225 +4560442d2c4b5358b00225b0022545445921212d2c45442d2cb00225b0022549b00525b00525 +4960b0206368208a108a233a8a10653a2d000001000000024ccc55ef24b85f0f3cf5001f0800 +00000000c6bc48a000000000c6bc48a0f7d6fd330d7209550000000800000001000000000001 +0000076dfe1d00000de2f7d6fa510d7200010000000000000000000000000000001c04cd0066 +04d300c904e7007b034a00ba023900c104ec0071028b0000051400ba042b006f051200ba0323 +0037023900c104e5007102d1002f023300c5068b0056051400ba051200ae0514007102e30064 +0239ffdb0517008f0596007307cb00ba04a200ba064c0073051200ba047500c9000000000000 +0044000000c4000001f0000002600000029c00000370000003700000041000000570000005e8 +00000664000006b400000758000007f00000084400000a6800000b0000000b8400000c1c0000 +0c4800000cc400000d9c00000e3400000ef800000fe800001074000010ec0000113000010000 +001c0354002b0068000c000200100099000800000415021600080004b8028040fffbfe03fa14 +03f92503f83203f79603f60e03f5fe03f4fe03f32503f20e03f19603f02503ef8a4105effe03 +ee9603ed9603ecfa03ebfa03eafe03e93a03e84203e7fe03e63203e5e45305e59603e48a4105 +e45303e3e22f05e3fa03e22f03e1fe03e0fe03df3203de1403dd9603dcfe03db1203da7d03d9 +bb03d8fe03d68a4105d67d03d5d44705d57d03d44703d3d21b05d3fe03d21b03d1fe03d0fe03 +cffe03cefe03cd9603cccb1e05ccfe03cb1e03ca3203c9fe03c6851105c61c03c51603c4fe03 +c3fe03c2fe03c1fe03c0fe03bffe03befe03bdfe03bcfe03bbfe03ba1103b9862505b9fe03b8 +b7bb05b8fe03b7b65d05b7bb03b78004b6b52505b65d40ff03b64004b52503b4fe03b39603b2 +fe03b1fe03b0fe03affe03ae6403ad0e03acab2505ac6403abaa1205ab2503aa1203a98a4105 +a9fa03a8fe03a7fe03a6fe03a51203a4fe03a3a20e05a33203a20e03a16403a08a4105a09603 +9ffe039e9d0c059efe039d0c039c9b19059c64039b9a10059b19039a1003990a0398fe039796 +0d0597fe03960d03958a410595960394930e05942803930e0392fa039190bb0591fe03908f5d +0590bb039080048f8e25058f5d038f40048e25038dfe038c8b2e058cfe038b2e038a8625058a +410389880b05891403880b03878625058764038685110586250385110384fe038382110583fe +0382110381fe0380fe037ffe0340ff7e7d7d057efe037d7d037c64037b5415057b25037afe03 +79fe03780e03770c03760a0375fe0374fa0373fa0372fa0371fa0370fe036ffe036efe036c21 +036bfe036a1142056a530369fe03687d036711420566fe0365fe0364fe0363fe0362fe03613a +0360fa035e0c035dfe035bfe035afe0359580a0559fa03580a035716190557320356fe035554 +150555420354150353011005531803521403514a130551fe03500b034ffe034e4d10054efe03 +4d10034cfe034b4a13054bfe034a4910054a1303491d0d05491003480d0347fe034696034596 +0344fe0343022d0543fa0342bb03414b0340fe033ffe033e3d12053e14033d3c0f053d12033c +3b0d053c40ff0f033b0d033afe0339fe033837140538fa033736100537140336350b05361003 +350b03341e03330d0332310b0532fe03310b03302f0b05300d032f0b032e2d09052e10032d09 +032c32032b2a25052b64032a2912052a25032912032827250528410327250326250b05260f03 +250b0324fe0323fe03220f03210110052112032064031ffa031e1d0d051e64031d0d031c1142 +051cfe031bfa031a42031911420519fe031864031716190517fe031601100516190315fe0314 +fe0313fe031211420512fe0311022d05114203107d030f64030efe030d0c16050dfe030c0110 +050c16030bfe030a100309fe0308022d0508fe030714030664030401100504fe03401503022d +0503fe0302011005022d0301100300fe0301b80164858d012b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b00 +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b1d00> +] def +FontName currentdict end definefont pop +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 640 167 +%%EndPageSetup +q +0 g +BT +12 0 0 12 200.282813 2.4961 Tm +/f-0-0 1 Tf +[<01>44<0203>-1<02>-1<04>1<040504>1<06>-1<03>21<050703>20<05080509>-1<0a +02>-1<0a0b>]TJ +10.056641 0 Td +[<0c0906>-1<0d>1<0c03>-1<060e>-1<0f02>-1<0d>1<06>-1<1011>-1<0b>1<041206 +13>-1<14>1<15>-1<0e>]TJ +ET +0.301961 0.654902 0.301961 rg +0.398 165.698 m 3.848 165.698 l 3.848 148.096 l 0.398 148.096 l 0.398 +165.698 l h +0.398 165.698 m f* +0 g +0.8 w +0 J +0 j +[] 0.0 d +4 M q 1 0 0 -1 0 166.0961 cm +0.398 0.398 m 3.848 0.398 l 3.848 18 l 0.398 18 l 0.398 0.398 l h +0.398 0.398 m S Q +0.301961 0.654902 0.301961 rg +0.746 148.096 m 6.031 148.096 l 6.031 130.495 l 0.746 130.495 l 0.746 +148.096 l h +0.746 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +0.746 18 m 6.031 18 l 6.031 35.602 l 0.746 35.602 l 0.746 18 l h +0.746 18 m S Q +0.301961 0.654902 0.301961 rg +1.074 130.495 m 5.875 130.495 l 5.875 112.897 l 1.074 112.897 l 1.074 +130.495 l h +1.074 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +1.074 35.602 m 5.875 35.602 l 5.875 53.199 l 1.074 53.199 l 1.074 +35.602 l h +1.074 35.602 m S Q +0.301961 0.654902 0.301961 rg +1.566 112.897 m 7.105 112.897 l 7.105 95.295 l 1.566 95.295 l 1.566 +112.897 l h +1.566 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +1.566 53.199 m 7.105 53.199 l 7.105 70.801 l 1.566 70.801 l 1.566 +53.199 l h +1.566 53.199 m S Q +0.301961 0.654902 0.301961 rg +1.848 95.295 m 7.109 95.295 l 7.109 77.698 l 1.848 77.698 l 1.848 +95.295 l h +1.848 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +1.848 70.801 m 7.109 70.801 l 7.109 88.398 l 1.848 88.398 l 1.848 +70.801 l h +1.848 70.801 m S Q +0.301961 0.654902 0.301961 rg +3.852 165.698 m 9.012 165.698 l 9.012 148.096 l 3.852 148.096 l 3.852 +165.698 l h +3.852 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +3.852 0.398 m 9.012 0.398 l 9.012 18 l 3.852 18 l 3.852 0.398 l h +3.852 0.398 m S Q +0.301961 0.654902 0.301961 rg +5.879 130.495 m 10.711 130.495 l 10.711 112.897 l 5.879 112.897 l 5.879 +130.495 l h +5.879 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +5.879 35.602 m 10.711 35.602 l 10.711 53.199 l 5.879 53.199 l 5.879 +35.602 l h +5.879 35.602 m S Q +0.301961 0.654902 0.301961 rg +6.047 148.096 m 11.812 148.096 l 11.812 130.495 l 6.047 130.495 l 6.047 +148.096 l h +6.047 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +6.047 18 m 11.812 18 l 11.812 35.602 l 6.047 35.602 l 6.047 18 l h +6.047 18 m S Q +0.301961 0.654902 0.301961 rg +7.117 112.897 m 13.332 112.897 l 13.332 95.295 l 7.117 95.295 l 7.117 +112.897 l h +7.117 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +7.117 53.199 m 13.332 53.199 l 13.332 70.801 l 7.117 70.801 l 7.117 +53.199 l h +7.117 53.199 m S Q +0.301961 0.654902 0.301961 rg +7.125 95.295 m 12.566 95.295 l 12.566 77.698 l 7.125 77.698 l 7.125 +95.295 l h +7.125 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +7.125 70.801 m 12.566 70.801 l 12.566 88.398 l 7.125 88.398 l 7.125 +70.801 l h +7.125 70.801 m S Q +0.301961 0.654902 0.301961 rg +9.016 165.698 m 13.074 165.698 l 13.074 148.096 l 9.016 148.096 l 9.016 +165.698 l h +9.016 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +9.016 0.398 m 13.074 0.398 l 13.074 18 l 9.016 18 l 9.016 0.398 l h +9.016 0.398 m S Q +0.301961 0.654902 0.301961 rg +10.715 130.495 m 16.02 130.495 l 16.02 112.897 l 10.715 112.897 l +10.715 130.495 l h +10.715 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +10.715 35.602 m 16.02 35.602 l 16.02 53.199 l 10.715 53.199 l 10.715 +35.602 l h +10.715 35.602 m S Q +0.301961 0.654902 0.301961 rg +11.816 148.096 m 16.785 148.096 l 16.785 130.495 l 11.816 130.495 l +11.816 148.096 l h +11.816 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +11.816 18 m 16.785 18 l 16.785 35.602 l 11.816 35.602 l 11.816 18 l h +11.816 18 m S Q +0.301961 0.654902 0.301961 rg +12.574 95.295 m 17.582 95.295 l 17.582 77.698 l 12.574 77.698 l 12.574 +95.295 l h +12.574 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +12.574 70.801 m 17.582 70.801 l 17.582 88.398 l 12.574 88.398 l 12.574 +70.801 l h +12.574 70.801 m S Q +0.301961 0.654902 0.301961 rg +13.078 165.698 m 18.496 165.698 l 18.496 148.096 l 13.078 148.096 l +13.078 165.698 l h +13.078 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +13.078 0.398 m 18.496 0.398 l 18.496 18 l 13.078 18 l 13.078 0.398 l h +13.078 0.398 m S Q +0.301961 0.654902 0.301961 rg +13.336 112.897 m 19.203 112.897 l 19.203 95.295 l 13.336 95.295 l +13.336 112.897 l h +13.336 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +13.336 53.199 m 19.203 53.199 l 19.203 70.801 l 13.336 70.801 l 13.336 +53.199 l h +13.336 53.199 m S Q +0.301961 0.654902 0.301961 rg +16.031 130.495 m 21.137 130.495 l 21.137 112.897 l 16.031 112.897 l +16.031 130.495 l h +16.031 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +16.031 35.602 m 21.137 35.602 l 21.137 53.199 l 16.031 53.199 l 16.031 +35.602 l h +16.031 35.602 m S Q +0.301961 0.654902 0.301961 rg +16.789 148.096 m 21.699 148.096 l 21.699 130.495 l 16.789 130.495 l +16.789 148.096 l h +16.789 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +16.789 18 m 21.699 18 l 21.699 35.602 l 16.789 35.602 l 16.789 18 l h +16.789 18 m S Q +0.301961 0.654902 0.301961 rg +17.586 95.295 m 23.246 95.295 l 23.246 77.698 l 17.586 77.698 l 17.586 +95.295 l h +17.586 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +17.586 70.801 m 23.246 70.801 l 23.246 88.398 l 17.586 88.398 l 17.586 +70.801 l h +17.586 70.801 m S Q +0.301961 0.654902 0.301961 rg +18.5 165.698 m 24.012 165.698 l 24.012 148.096 l 18.5 148.096 l 18.5 +165.698 l h +18.5 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +18.5 0.398 m 24.012 0.398 l 24.012 18 l 18.5 18 l 18.5 0.398 l h +18.5 0.398 m S Q +0.301961 0.654902 0.301961 rg +19.207 112.897 m 22.973 112.897 l 22.973 95.295 l 19.207 95.295 l +19.207 112.897 l h +19.207 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +19.207 53.199 m 22.973 53.199 l 22.973 70.801 l 19.207 70.801 l 19.207 +53.199 l h +19.207 53.199 m S Q +0.301961 0.654902 0.301961 rg +21.141 130.495 m 27.18 130.495 l 27.18 112.897 l 21.141 112.897 l +21.141 130.495 l h +21.141 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +21.141 35.602 m 27.18 35.602 l 27.18 53.199 l 21.141 53.199 l 21.141 +35.602 l h +21.141 35.602 m S Q +0.301961 0.654902 0.301961 rg +21.707 148.096 m 26.477 148.096 l 26.477 130.495 l 21.707 130.495 l +21.707 148.096 l h +21.707 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +21.707 18 m 26.477 18 l 26.477 35.602 l 21.707 35.602 l 21.707 18 l h +21.707 18 m S Q +0.301961 0.654902 0.301961 rg +22.977 112.897 m 27.543 112.897 l 27.543 95.295 l 22.977 95.295 l +22.977 112.897 l h +22.977 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +22.977 53.199 m 27.543 53.199 l 27.543 70.801 l 22.977 70.801 l 22.977 +53.199 l h +22.977 53.199 m S Q +0.301961 0.654902 0.301961 rg +23.25 95.295 m 28.098 95.295 l 28.098 77.698 l 23.25 77.698 l 23.25 +95.295 l h +23.25 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +23.25 70.801 m 28.098 70.801 l 28.098 88.398 l 23.25 88.398 l 23.25 +70.801 l h +23.25 70.801 m S Q +0.301961 0.654902 0.301961 rg +24.016 165.698 m 29.434 165.698 l 29.434 148.096 l 24.016 148.096 l +24.016 165.698 l h +24.016 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +24.016 0.398 m 29.434 0.398 l 29.434 18 l 24.016 18 l 24.016 0.398 l h +24.016 0.398 m S Q +0.301961 0.654902 0.301961 rg +26.484 148.096 m 30.824 148.096 l 30.824 130.495 l 26.484 130.495 l +26.484 148.096 l h +26.484 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +26.484 18 m 30.824 18 l 30.824 35.602 l 26.484 35.602 l 26.484 18 l h +26.484 18 m S Q +0.301961 0.654902 0.301961 rg +27.184 130.495 m 32.43 130.495 l 32.43 112.897 l 27.184 112.897 l +27.184 130.495 l h +27.184 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +27.184 35.602 m 32.43 35.602 l 32.43 53.199 l 27.184 53.199 l 27.184 +35.602 l h +27.184 35.602 m S Q +0.301961 0.654902 0.301961 rg +27.547 112.897 m 34.059 112.897 l 34.059 95.295 l 27.547 95.295 l +27.547 112.897 l h +27.547 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +27.547 53.199 m 34.059 53.199 l 34.059 70.801 l 27.547 70.801 l 27.547 +53.199 l h +27.547 53.199 m S Q +0.301961 0.654902 0.301961 rg +28.102 95.295 m 32.883 95.295 l 32.883 77.698 l 28.102 77.698 l 28.102 +95.295 l h +28.102 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +28.102 70.801 m 32.883 70.801 l 32.883 88.398 l 28.102 88.398 l 28.102 +70.801 l h +28.102 70.801 m S Q +0.301961 0.654902 0.301961 rg +29.438 165.698 m 34.82 165.698 l 34.82 148.096 l 29.438 148.096 l +29.438 165.698 l h +29.438 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +29.438 0.398 m 34.82 0.398 l 34.82 18 l 29.438 18 l 29.438 0.398 l h +29.438 0.398 m S Q +0.301961 0.654902 0.301961 rg +30.828 148.096 m 36.594 148.096 l 36.594 130.495 l 30.828 130.495 l +30.828 148.096 l h +30.828 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +30.828 18 m 36.594 18 l 36.594 35.602 l 30.828 35.602 l 30.828 18 l h +30.828 18 m S Q +0.301961 0.654902 0.301961 rg +32.438 130.495 m 37.125 130.495 l 37.125 112.897 l 32.438 112.897 l +32.438 130.495 l h +32.438 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +32.438 35.602 m 37.125 35.602 l 37.125 53.199 l 32.438 53.199 l 32.438 +35.602 l h +32.438 35.602 m S Q +0.301961 0.654902 0.301961 rg +32.887 95.295 m 37.848 95.295 l 37.848 77.698 l 32.887 77.698 l 32.887 +95.295 l h +32.887 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +32.887 70.801 m 37.848 70.801 l 37.848 88.398 l 32.887 88.398 l 32.887 +70.801 l h +32.887 70.801 m S Q +0.301961 0.654902 0.301961 rg +34.062 112.897 m 39.445 112.897 l 39.445 95.295 l 34.062 95.295 l +34.062 112.897 l h +34.062 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +34.062 53.199 m 39.445 53.199 l 39.445 70.801 l 34.062 70.801 l 34.062 +53.199 l h +34.062 53.199 m S Q +0.301961 0.654902 0.301961 rg +34.824 165.698 m 39.062 165.698 l 39.062 148.096 l 34.824 148.096 l +34.824 165.698 l h +34.824 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +34.824 0.398 m 39.062 0.398 l 39.062 18 l 34.824 18 l 34.824 0.398 l h +34.824 0.398 m S Q +0.301961 0.654902 0.301961 rg +36.602 148.096 m 42.637 148.096 l 42.637 130.495 l 36.602 130.495 l +36.602 148.096 l h +36.602 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +36.602 18 m 42.637 18 l 42.637 35.602 l 36.602 35.602 l 36.602 18 l h +36.602 18 m S Q +0.301961 0.654902 0.301961 rg +37.133 130.495 m 42.102 130.495 l 42.102 112.897 l 37.133 112.897 l +37.133 130.495 l h +37.133 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +37.133 35.602 m 42.102 35.602 l 42.102 53.199 l 37.133 53.199 l 37.133 +35.602 l h +37.133 35.602 m S Q +0.301961 0.654902 0.301961 rg +37.855 95.295 m 42.184 95.295 l 42.184 77.698 l 37.855 77.698 l 37.855 +95.295 l h +37.855 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +37.855 70.801 m 42.184 70.801 l 42.184 88.398 l 37.855 88.398 l 37.855 +70.801 l h +37.855 70.801 m S Q +0.301961 0.654902 0.301961 rg +39.066 165.698 m 43.633 165.698 l 43.633 148.096 l 39.066 148.096 l +39.066 165.698 l h +39.066 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +39.066 0.398 m 43.633 0.398 l 43.633 18 l 39.066 18 l 39.066 0.398 l h +39.066 0.398 m S Q +0.301961 0.654902 0.301961 rg +39.48 112.897 m 44.773 112.897 l 44.773 95.295 l 39.48 95.295 l 39.48 +112.897 l h +39.48 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +39.48 53.199 m 44.773 53.199 l 44.773 70.801 l 39.48 70.801 l 39.48 +53.199 l h +39.48 53.199 m S Q +0.301961 0.654902 0.301961 rg +42.105 130.495 m 46.477 130.495 l 46.477 112.897 l 42.105 112.897 l +42.105 130.495 l h +42.105 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +42.105 35.602 m 46.477 35.602 l 46.477 53.199 l 42.105 53.199 l 42.105 +35.602 l h +42.105 35.602 m S Q +0.301961 0.654902 0.301961 rg +42.289 95.295 m 47.82 95.295 l 47.82 77.698 l 42.289 77.698 l 42.289 +95.295 l h +42.289 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +42.289 70.801 m 47.82 70.801 l 47.82 88.398 l 42.289 88.398 l 42.289 +70.801 l h +42.289 70.801 m S Q +0.301961 0.654902 0.301961 rg +42.641 148.096 m 49.34 148.096 l 49.34 130.495 l 42.641 130.495 l +42.641 148.096 l h +42.641 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +42.641 18 m 49.34 18 l 49.34 35.602 l 42.641 35.602 l 42.641 18 l h +42.641 18 m S Q +0.301961 0.654902 0.301961 rg +43.637 165.698 m 48.129 165.698 l 48.129 148.096 l 43.637 148.096 l +43.637 165.698 l h +43.637 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +43.637 0.398 m 48.129 0.398 l 48.129 18 l 43.637 18 l 43.637 0.398 l h +43.637 0.398 m S Q +0.301961 0.654902 0.301961 rg +44.777 112.897 m 50.113 112.897 l 50.113 95.295 l 44.777 95.295 l +44.777 112.897 l h +44.777 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +44.777 53.199 m 50.113 53.199 l 50.113 70.801 l 44.777 70.801 l 44.777 +53.199 l h +44.777 53.199 m S Q +0.301961 0.654902 0.301961 rg +46.48 130.495 m 52.559 130.495 l 52.559 112.897 l 46.48 112.897 l 46.48 +130.495 l h +46.48 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +46.48 35.602 m 52.559 35.602 l 52.559 53.199 l 46.48 53.199 l 46.48 +35.602 l h +46.48 35.602 m S Q +0.301961 0.654902 0.301961 rg +47.828 95.295 m 51.926 95.295 l 51.926 77.698 l 47.828 77.698 l 47.828 +95.295 l h +47.828 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +47.828 70.801 m 51.926 70.801 l 51.926 88.398 l 47.828 88.398 l 47.828 +70.801 l h +47.828 70.801 m S Q +0.301961 0.654902 0.301961 rg +48.133 165.698 m 52.402 165.698 l 52.402 148.096 l 48.133 148.096 l +48.133 165.698 l h +48.133 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +48.133 0.398 m 52.402 0.398 l 52.402 18 l 48.133 18 l 48.133 0.398 l h +48.133 0.398 m S Q +0.301961 0.654902 0.301961 rg +49.348 148.096 m 54.555 148.096 l 54.555 130.495 l 49.348 130.495 l +49.348 148.096 l h +49.348 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +49.348 18 m 54.555 18 l 54.555 35.602 l 49.348 35.602 l 49.348 18 l h +49.348 18 m S Q +0.301961 0.654902 0.301961 rg +50.121 112.897 m 55.582 112.897 l 55.582 95.295 l 50.121 95.295 l +50.121 112.897 l h +50.121 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +50.121 53.199 m 55.582 53.199 l 55.582 70.801 l 50.121 70.801 l 50.121 +53.199 l h +50.121 53.199 m S Q +0.301961 0.654902 0.301961 rg +51.93 95.295 m 56.754 95.295 l 56.754 77.698 l 51.93 77.698 l 51.93 +95.295 l h +51.93 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +51.93 70.801 m 56.754 70.801 l 56.754 88.398 l 51.93 88.398 l 51.93 +70.801 l h +51.93 70.801 m S Q +0.301961 0.654902 0.301961 rg +52.41 165.698 m 58.242 165.698 l 58.242 148.096 l 52.41 148.096 l 52.41 +165.698 l h +52.41 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +52.41 0.398 m 58.242 0.398 l 58.242 18 l 52.41 18 l 52.41 0.398 l h +52.41 0.398 m S Q +0.301961 0.654902 0.301961 rg +52.57 130.495 m 57.148 130.495 l 57.148 112.897 l 52.57 112.897 l 52.57 +130.495 l h +52.57 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +52.57 35.602 m 57.148 35.602 l 57.148 53.199 l 52.57 53.199 l 52.57 +35.602 l h +52.57 35.602 m S Q +0.301961 0.654902 0.301961 rg +54.559 148.096 m 59.785 148.096 l 59.785 130.495 l 54.559 130.495 l +54.559 148.096 l h +54.559 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +54.559 18 m 59.785 18 l 59.785 35.602 l 54.559 35.602 l 54.559 18 l h +54.559 18 m S Q +0.301961 0.654902 0.301961 rg +55.59 112.897 m 60.59 112.897 l 60.59 95.295 l 55.59 95.295 l 55.59 +112.897 l h +55.59 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +55.59 53.199 m 60.59 53.199 l 60.59 70.801 l 55.59 70.801 l 55.59 +53.199 l h +55.59 53.199 m S Q +0.301961 0.654902 0.301961 rg +56.762 95.295 m 62.945 95.295 l 62.945 77.698 l 56.762 77.698 l 56.762 +95.295 l h +56.762 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +56.762 70.801 m 62.945 70.801 l 62.945 88.398 l 56.762 88.398 l 56.762 +70.801 l h +56.762 70.801 m S Q +0.301961 0.654902 0.301961 rg +57.156 130.495 m 62.598 130.495 l 62.598 112.897 l 57.156 112.897 l +57.156 130.495 l h +57.156 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +57.156 35.602 m 62.598 35.602 l 62.598 53.199 l 57.156 53.199 l 57.156 +35.602 l h +57.156 35.602 m S Q +0.301961 0.654902 0.301961 rg +58.25 165.698 m 62.156 165.698 l 62.156 148.096 l 58.25 148.096 l 58.25 +165.698 l h +58.25 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +58.25 0.398 m 62.156 0.398 l 62.156 18 l 58.25 18 l 58.25 0.398 l h +58.25 0.398 m S Q +0.301961 0.654902 0.301961 rg +59.793 148.096 m 65.645 148.096 l 65.645 130.495 l 59.793 130.495 l +59.793 148.096 l h +59.793 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +59.793 18 m 65.645 18 l 65.645 35.602 l 59.793 35.602 l 59.793 18 l h +59.793 18 m S Q +0.301961 0.654902 0.301961 rg +60.594 112.897 m 65.926 112.897 l 65.926 95.295 l 60.594 95.295 l +60.594 112.897 l h +60.594 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +60.594 53.199 m 65.926 53.199 l 65.926 70.801 l 60.594 70.801 l 60.594 +53.199 l h +60.594 53.199 m S Q +0.301961 0.654902 0.301961 rg +62.16 165.698 m 67.809 165.698 l 67.809 148.096 l 62.16 148.096 l 62.16 +165.698 l h +62.16 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +62.16 0.398 m 67.809 0.398 l 67.809 18 l 62.16 18 l 62.16 0.398 l h +62.16 0.398 m S Q +0.301961 0.654902 0.301961 rg +62.605 130.495 m 67.117 130.495 l 67.117 112.897 l 62.605 112.897 l +62.605 130.495 l h +62.605 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +62.605 35.602 m 67.117 35.602 l 67.117 53.199 l 62.605 53.199 l 62.605 +35.602 l h +62.605 35.602 m S Q +0.301961 0.654902 0.301961 rg +62.953 95.295 m 67.809 95.295 l 67.809 77.698 l 62.953 77.698 l 62.953 +95.295 l h +62.953 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +62.953 70.801 m 67.809 70.801 l 67.809 88.398 l 62.953 88.398 l 62.953 +70.801 l h +62.953 70.801 m S Q +0.301961 0.654902 0.301961 rg +65.648 148.096 m 70.516 148.096 l 70.516 130.495 l 65.648 130.495 l +65.648 148.096 l h +65.648 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +65.648 18 m 70.516 18 l 70.516 35.602 l 65.648 35.602 l 65.648 18 l h +65.648 18 m S Q +0.301961 0.654902 0.301961 rg +65.93 112.897 m 70.051 112.897 l 70.051 95.295 l 65.93 95.295 l 65.93 +112.897 l h +65.93 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +65.93 53.199 m 70.051 53.199 l 70.051 70.801 l 65.93 70.801 l 65.93 +53.199 l h +65.93 53.199 m S Q +0.301961 0.654902 0.301961 rg +67.121 130.495 m 72.699 130.495 l 72.699 112.897 l 67.121 112.897 l +67.121 130.495 l h +67.121 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +67.121 35.602 m 72.699 35.602 l 72.699 53.199 l 67.121 53.199 l 67.121 +35.602 l h +67.121 35.602 m S Q +0.301961 0.654902 0.301961 rg +67.949 95.295 m 73.281 95.295 l 73.281 77.698 l 67.949 77.698 l 67.949 +95.295 l h +67.949 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +67.949 70.801 m 73.281 70.801 l 73.281 88.398 l 67.949 88.398 l 67.949 +70.801 l h +67.949 70.801 m S Q +0.301961 0.654902 0.301961 rg +67.973 165.698 m 73.789 165.698 l 73.789 148.096 l 67.973 148.096 l +67.973 165.698 l h +67.973 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +67.973 0.398 m 73.789 0.398 l 73.789 18 l 67.973 18 l 67.973 0.398 l h +67.973 0.398 m S Q +0.301961 0.654902 0.301961 rg +70.055 112.897 m 74.332 112.897 l 74.332 95.295 l 70.055 95.295 l +70.055 112.897 l h +70.055 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +70.055 53.199 m 74.332 53.199 l 74.332 70.801 l 70.055 70.801 l 70.055 +53.199 l h +70.055 53.199 m S Q +0.301961 0.654902 0.301961 rg +70.52 148.096 m 76.457 148.096 l 76.457 130.495 l 70.52 130.495 l 70.52 +148.096 l h +70.52 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +70.52 18 m 76.457 18 l 76.457 35.602 l 70.52 35.602 l 70.52 18 l h +70.52 18 m S Q +0.301961 0.654902 0.301961 rg +72.707 130.495 m 78.004 130.495 l 78.004 112.897 l 72.707 112.897 l +72.707 130.495 l h +72.707 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +72.707 35.602 m 78.004 35.602 l 78.004 53.199 l 72.707 53.199 l 72.707 +35.602 l h +72.707 35.602 m S Q +0.301961 0.654902 0.301961 rg +73.535 95.295 m 79.438 95.295 l 79.438 77.698 l 73.535 77.698 l 73.535 +95.295 l h +73.535 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +73.535 70.801 m 79.438 70.801 l 79.438 88.398 l 73.535 88.398 l 73.535 +70.801 l h +73.535 70.801 m S Q +0.301961 0.654902 0.301961 rg +73.797 165.698 m 77.355 165.698 l 77.355 148.096 l 73.797 148.096 l +73.797 165.698 l h +73.797 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +73.797 0.398 m 77.355 0.398 l 77.355 18 l 73.797 18 l 73.797 0.398 l h +73.797 0.398 m S Q +0.301961 0.654902 0.301961 rg +74.336 112.897 m 79.168 112.897 l 79.168 95.295 l 74.336 95.295 l +74.336 112.897 l h +74.336 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +74.336 53.199 m 79.168 53.199 l 79.168 70.801 l 74.336 70.801 l 74.336 +53.199 l h +74.336 53.199 m S Q +0.301961 0.654902 0.301961 rg +76.461 148.096 m 82.152 148.096 l 82.152 130.495 l 76.461 130.495 l +76.461 148.096 l h +76.461 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +76.461 18 m 82.152 18 l 82.152 35.602 l 76.461 35.602 l 76.461 18 l h +76.461 18 m S Q +0.301961 0.654902 0.301961 rg +77.359 165.698 m 82.52 165.698 l 82.52 148.096 l 77.359 148.096 l +77.359 165.698 l h +77.359 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +77.359 0.398 m 82.52 0.398 l 82.52 18 l 77.359 18 l 77.359 0.398 l h +77.359 0.398 m S Q +0.301961 0.654902 0.301961 rg +78.02 130.495 m 83.625 130.495 l 83.625 112.897 l 78.02 112.897 l 78.02 +130.495 l h +78.02 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +78.02 35.602 m 83.625 35.602 l 83.625 53.199 l 78.02 53.199 l 78.02 +35.602 l h +78.02 35.602 m S Q +0.301961 0.654902 0.301961 rg +79.172 112.897 m 83.199 112.897 l 83.199 95.295 l 79.172 95.295 l +79.172 112.897 l h +79.172 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +79.172 53.199 m 83.199 53.199 l 83.199 70.801 l 79.172 70.801 l 79.172 +53.199 l h +79.172 53.199 m S Q +0.301961 0.654902 0.301961 rg +79.445 95.295 m 85.027 95.295 l 85.027 77.698 l 79.445 77.698 l 79.445 +95.295 l h +79.445 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +79.445 70.801 m 85.027 70.801 l 85.027 88.398 l 79.445 88.398 l 79.445 +70.801 l h +79.445 70.801 m S Q +0.301961 0.654902 0.301961 rg +82.16 148.096 m 86.355 148.096 l 86.355 130.495 l 82.16 130.495 l 82.16 +148.096 l h +82.16 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +82.16 18 m 86.355 18 l 86.355 35.602 l 82.16 35.602 l 82.16 18 l h +82.16 18 m S Q +0.301961 0.654902 0.301961 rg +82.539 165.698 m 88.559 165.698 l 88.559 148.096 l 82.539 148.096 l +82.539 165.698 l h +82.539 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +82.539 0.398 m 88.559 0.398 l 88.559 18 l 82.539 18 l 82.539 0.398 l h +82.539 0.398 m S Q +0.301961 0.654902 0.301961 rg +83.215 112.897 m 88.875 112.897 l 88.875 95.295 l 83.215 95.295 l +83.215 112.897 l h +83.215 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +83.215 53.199 m 88.875 53.199 l 88.875 70.801 l 83.215 70.801 l 83.215 +53.199 l h +83.215 53.199 m S Q +0.301961 0.654902 0.301961 rg +83.629 130.495 m 89.035 130.495 l 89.035 112.897 l 83.629 112.897 l +83.629 130.495 l h +83.629 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +83.629 35.602 m 89.035 35.602 l 89.035 53.199 l 83.629 53.199 l 83.629 +35.602 l h +83.629 35.602 m S Q +0.301961 0.654902 0.301961 rg +85.031 95.295 m 90.258 95.295 l 90.258 77.698 l 85.031 77.698 l 85.031 +95.295 l h +85.031 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +85.031 70.801 m 90.258 70.801 l 90.258 88.398 l 85.031 88.398 l 85.031 +70.801 l h +85.031 70.801 m S Q +0.301961 0.654902 0.301961 rg +86.359 148.096 m 91.672 148.096 l 91.672 130.495 l 86.359 130.495 l +86.359 148.096 l h +86.359 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +86.359 18 m 91.672 18 l 91.672 35.602 l 86.359 35.602 l 86.359 18 l h +86.359 18 m S Q +0.301961 0.654902 0.301961 rg +88.566 165.698 m 94.184 165.698 l 94.184 148.096 l 88.566 148.096 l +88.566 165.698 l h +88.566 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +88.566 0.398 m 94.184 0.398 l 94.184 18 l 88.566 18 l 88.566 0.398 l h +88.566 0.398 m S Q +0.301961 0.654902 0.301961 rg +88.887 112.897 m 93.629 112.897 l 93.629 95.295 l 88.887 95.295 l +88.887 112.897 l h +88.887 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +88.887 53.199 m 93.629 53.199 l 93.629 70.801 l 88.887 70.801 l 88.887 +53.199 l h +88.887 53.199 m S Q +0.301961 0.654902 0.301961 rg +89.051 130.495 m 94.887 130.495 l 94.887 112.897 l 89.051 112.897 l +89.051 130.495 l h +89.051 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +89.051 35.602 m 94.887 35.602 l 94.887 53.199 l 89.051 53.199 l 89.051 +35.602 l h +89.051 35.602 m S Q +0.301961 0.654902 0.301961 rg +90.266 95.295 m 96.402 95.295 l 96.402 77.698 l 90.266 77.698 l 90.266 +95.295 l h +90.266 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +90.266 70.801 m 96.402 70.801 l 96.402 88.398 l 90.266 88.398 l 90.266 +70.801 l h +90.266 70.801 m S Q +0.301961 0.654902 0.301961 rg +91.688 148.096 m 96.633 148.096 l 96.633 130.495 l 91.688 130.495 l +91.688 148.096 l h +91.688 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +91.688 18 m 96.633 18 l 96.633 35.602 l 91.688 35.602 l 91.688 18 l h +91.688 18 m S Q +0.301961 0.654902 0.301961 rg +93.637 112.897 m 98.789 112.897 l 98.789 95.295 l 93.637 95.295 l +93.637 112.897 l h +93.637 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +93.637 53.199 m 98.789 53.199 l 98.789 70.801 l 93.637 70.801 l 93.637 +53.199 l h +93.637 53.199 m S Q +0.301961 0.654902 0.301961 rg +94.188 165.698 m 99.293 165.698 l 99.293 148.096 l 94.188 148.096 l +94.188 165.698 l h +94.188 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +94.188 0.398 m 99.293 0.398 l 99.293 18 l 94.188 18 l 94.188 0.398 l h +94.188 0.398 m S Q +0.301961 0.654902 0.301961 rg +94.891 130.495 m 99.922 130.495 l 99.922 112.897 l 94.891 112.897 l +94.891 130.495 l h +94.891 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +94.891 35.602 m 99.922 35.602 l 99.922 53.199 l 94.891 53.199 l 94.891 +35.602 l h +94.891 35.602 m S Q +0.301961 0.654902 0.301961 rg +96.41 95.295 m 101.477 95.295 l 101.477 77.698 l 96.41 77.698 l 96.41 +95.295 l h +96.41 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +96.41 70.801 m 101.477 70.801 l 101.477 88.398 l 96.41 88.398 l 96.41 +70.801 l h +96.41 70.801 m S Q +0.301961 0.654902 0.301961 rg +96.645 148.096 m 103.281 148.096 l 103.281 130.495 l 96.645 130.495 l +96.645 148.096 l h +96.645 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +96.645 18 m 103.281 18 l 103.281 35.602 l 96.645 35.602 l 96.645 18 l h +96.645 18 m S Q +0.301961 0.654902 0.301961 rg +98.797 112.897 m 103.91 112.897 l 103.91 95.295 l 98.797 95.295 l +98.797 112.897 l h +98.797 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +98.797 53.199 m 103.91 53.199 l 103.91 70.801 l 98.797 70.801 l 98.797 +53.199 l h +98.797 53.199 m S Q +0.301961 0.654902 0.301961 rg +99.301 165.698 m 102.922 165.698 l 102.922 148.096 l 99.301 148.096 l +99.301 165.698 l h +99.301 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +99.301 0.398 m 102.922 0.398 l 102.922 18 l 99.301 18 l 99.301 0.398 l +h +99.301 0.398 m S Q +0.301961 0.654902 0.301961 rg +99.926 130.495 m 104.75 130.495 l 104.75 112.897 l 99.926 112.897 l +99.926 130.495 l h +99.926 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +99.926 35.602 m 104.75 35.602 l 104.75 53.199 l 99.926 53.199 l 99.926 +35.602 l h +99.926 35.602 m S Q +0.301961 0.654902 0.301961 rg +101.48 95.295 m 107.34 95.295 l 107.34 77.698 l 101.48 77.698 l 101.48 +95.295 l h +101.48 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +101.48 70.801 m 107.34 70.801 l 107.34 88.398 l 101.48 88.398 l 101.48 +70.801 l h +101.48 70.801 m S Q +0.301961 0.654902 0.301961 rg +102.926 165.698 m 108.281 165.698 l 108.281 148.096 l 102.926 148.096 l +102.926 165.698 l h +102.926 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +102.926 0.398 m 108.281 0.398 l 108.281 18 l 102.926 18 l 102.926 0.398 +l h +102.926 0.398 m S Q +0.301961 0.654902 0.301961 rg +103.285 148.096 m 110.113 148.096 l 110.113 130.495 l 103.285 130.495 l +103.285 148.096 l h +103.285 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +103.285 18 m 110.113 18 l 110.113 35.602 l 103.285 35.602 l 103.285 18 +l h +103.285 18 m S Q +0.301961 0.654902 0.301961 rg +103.914 112.897 m 109.707 112.897 l 109.707 95.295 l 103.914 95.295 l +103.914 112.897 l h +103.914 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +103.914 53.199 m 109.707 53.199 l 109.707 70.801 l 103.914 70.801 l +103.914 53.199 l h +103.914 53.199 m S Q +0.301961 0.654902 0.301961 rg +104.754 130.495 m 108.836 130.495 l 108.836 112.897 l 104.754 112.897 l +104.754 130.495 l h +104.754 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +104.754 35.602 m 108.836 35.602 l 108.836 53.199 l 104.754 53.199 l +104.754 35.602 l h +104.754 35.602 m S Q +0.301961 0.654902 0.301961 rg +107.344 95.295 m 112.473 95.295 l 112.473 77.698 l 107.344 77.698 l +107.344 95.295 l h +107.344 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +107.344 70.801 m 112.473 70.801 l 112.473 88.398 l 107.344 88.398 l +107.344 70.801 l h +107.344 70.801 m S Q +0.301961 0.654902 0.301961 rg +108.285 165.698 m 113.188 165.698 l 113.188 148.096 l 108.285 148.096 l +108.285 165.698 l h +108.285 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +108.285 0.398 m 113.188 0.398 l 113.188 18 l 108.285 18 l 108.285 0.398 +l h +108.285 0.398 m S Q +0.301961 0.654902 0.301961 rg +108.84 130.495 m 114.105 130.495 l 114.105 112.897 l 108.84 112.897 l +108.84 130.495 l h +108.84 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +108.84 35.602 m 114.105 35.602 l 114.105 53.199 l 108.84 53.199 l +108.84 35.602 l h +108.84 35.602 m S Q +0.301961 0.654902 0.301961 rg +109.711 112.897 m 114.754 112.897 l 114.754 95.295 l 109.711 95.295 l +109.711 112.897 l h +109.711 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +109.711 53.199 m 114.754 53.199 l 114.754 70.801 l 109.711 70.801 l +109.711 53.199 l h +109.711 53.199 m S Q +0.301961 0.654902 0.301961 rg +110.117 148.096 m 115.02 148.096 l 115.02 130.495 l 110.117 130.495 l +110.117 148.096 l h +110.117 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +110.117 18 m 115.02 18 l 115.02 35.602 l 110.117 35.602 l 110.117 18 l +h +110.117 18 m S Q +0.301961 0.654902 0.301961 rg +112.477 95.295 m 118.445 95.295 l 118.445 77.698 l 112.477 77.698 l +112.477 95.295 l h +112.477 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +112.477 70.801 m 118.445 70.801 l 118.445 88.398 l 112.477 88.398 l +112.477 70.801 l h +112.477 70.801 m S Q +0.301961 0.654902 0.301961 rg +113.191 165.698 m 120.262 165.698 l 120.262 148.096 l 113.191 148.096 l +113.191 165.698 l h +113.191 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +113.191 0.398 m 120.262 0.398 l 120.262 18 l 113.191 18 l 113.191 0.398 +l h +113.191 0.398 m S Q +0.301961 0.654902 0.301961 rg +114.109 130.495 m 120.117 130.495 l 120.117 112.897 l 114.109 112.897 l +114.109 130.495 l h +114.109 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +114.109 35.602 m 120.117 35.602 l 120.117 53.199 l 114.109 53.199 l +114.109 35.602 l h +114.109 35.602 m S Q +0.301961 0.654902 0.301961 rg +114.762 112.897 m 121.793 112.897 l 121.793 95.295 l 114.762 95.295 l +114.762 112.897 l h +114.762 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +114.762 53.199 m 121.793 53.199 l 121.793 70.801 l 114.762 70.801 l +114.762 53.199 l h +114.762 53.199 m S Q +0.301961 0.654902 0.301961 rg +115.062 148.096 m 119.652 148.096 l 119.652 130.495 l 115.062 130.495 l +115.062 148.096 l h +115.062 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +115.062 18 m 119.652 18 l 119.652 35.602 l 115.062 35.602 l 115.062 18 +l h +115.062 18 m S Q +0.301961 0.654902 0.301961 rg +118.449 95.295 m 123.613 95.295 l 123.613 77.698 l 118.449 77.698 l +118.449 95.295 l h +118.449 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +118.449 70.801 m 123.613 70.801 l 123.613 88.398 l 118.449 88.398 l +118.449 70.801 l h +118.449 70.801 m S Q +0.301961 0.654902 0.301961 rg +119.66 148.096 m 125.012 148.096 l 125.012 130.495 l 119.66 130.495 l +119.66 148.096 l h +119.66 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +119.66 18 m 125.012 18 l 125.012 35.602 l 119.66 35.602 l 119.66 18 l h +119.66 18 m S Q +0.301961 0.654902 0.301961 rg +120.125 130.495 m 124.156 130.495 l 124.156 112.897 l 120.125 112.897 l +120.125 130.495 l h +120.125 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +120.125 35.602 m 124.156 35.602 l 124.156 53.199 l 120.125 53.199 l +120.125 35.602 l h +120.125 35.602 m S Q +0.301961 0.654902 0.301961 rg +120.352 165.698 m 125.594 165.698 l 125.594 148.096 l 120.352 148.096 l +120.352 165.698 l h +120.352 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +120.352 0.398 m 125.594 0.398 l 125.594 18 l 120.352 18 l 120.352 0.398 +l h +120.352 0.398 m S Q +0.301961 0.654902 0.301961 rg +121.797 112.897 m 127.527 112.897 l 127.527 95.295 l 121.797 95.295 l +121.797 112.897 l h +121.797 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +121.797 53.199 m 127.527 53.199 l 127.527 70.801 l 121.797 70.801 l +121.797 53.199 l h +121.797 53.199 m S Q +0.301961 0.654902 0.301961 rg +123.621 95.295 m 129.496 95.295 l 129.496 77.698 l 123.621 77.698 l +123.621 95.295 l h +123.621 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +123.621 70.801 m 129.496 70.801 l 129.496 88.398 l 123.621 88.398 l +123.621 70.801 l h +123.621 70.801 m S Q +0.301961 0.654902 0.301961 rg +124.16 130.495 m 129.984 130.495 l 129.984 112.897 l 124.16 112.897 l +124.16 130.495 l h +124.16 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +124.16 35.602 m 129.984 35.602 l 129.984 53.199 l 124.16 53.199 l +124.16 35.602 l h +124.16 35.602 m S Q +0.301961 0.654902 0.301961 rg +125.02 148.096 m 130.129 148.096 l 130.129 130.495 l 125.02 130.495 l +125.02 148.096 l h +125.02 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +125.02 18 m 130.129 18 l 130.129 35.602 l 125.02 35.602 l 125.02 18 l h +125.02 18 m S Q +0.301961 0.654902 0.301961 rg +125.602 165.698 m 130.703 165.698 l 130.703 148.096 l 125.602 148.096 l +125.602 165.698 l h +125.602 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +125.602 0.398 m 130.703 0.398 l 130.703 18 l 125.602 18 l 125.602 0.398 +l h +125.602 0.398 m S Q +0.301961 0.654902 0.301961 rg +127.531 112.897 m 132.812 112.897 l 132.812 95.295 l 127.531 95.295 l +127.531 112.897 l h +127.531 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +127.531 53.199 m 132.812 53.199 l 132.812 70.801 l 127.531 70.801 l +127.531 53.199 l h +127.531 53.199 m S Q +0.301961 0.654902 0.301961 rg +129.504 95.295 m 133.828 95.295 l 133.828 77.698 l 129.504 77.698 l +129.504 95.295 l h +129.504 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +129.504 70.801 m 133.828 70.801 l 133.828 88.398 l 129.504 88.398 l +129.504 70.801 l h +129.504 70.801 m S Q +0.301961 0.654902 0.301961 rg +129.992 130.495 m 134.836 130.495 l 134.836 112.897 l 129.992 112.897 l +129.992 130.495 l h +129.992 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +129.992 35.602 m 134.836 35.602 l 134.836 53.199 l 129.992 53.199 l +129.992 35.602 l h +129.992 35.602 m S Q +0.301961 0.654902 0.301961 rg +130.145 148.096 m 135.719 148.096 l 135.719 130.495 l 130.145 130.495 l +130.145 148.096 l h +130.145 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +130.145 18 m 135.719 18 l 135.719 35.602 l 130.145 35.602 l 130.145 18 +l h +130.145 18 m S Q +0.301961 0.654902 0.301961 rg +130.707 165.698 m 136.168 165.698 l 136.168 148.096 l 130.707 148.096 l +130.707 165.698 l h +130.707 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +130.707 0.398 m 136.168 0.398 l 136.168 18 l 130.707 18 l 130.707 0.398 +l h +130.707 0.398 m S Q +0.301961 0.654902 0.301961 rg +132.816 112.897 m 137.516 112.897 l 137.516 95.295 l 132.816 95.295 l +132.816 112.897 l h +132.816 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +132.816 53.199 m 137.516 53.199 l 137.516 70.801 l 132.816 70.801 l +132.816 53.199 l h +132.816 53.199 m S Q +0.301961 0.654902 0.301961 rg +133.836 95.295 m 138.859 95.295 l 138.859 77.698 l 133.836 77.698 l +133.836 95.295 l h +133.836 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +133.836 70.801 m 138.859 70.801 l 138.859 88.398 l 133.836 88.398 l +133.836 70.801 l h +133.836 70.801 m S Q +0.301961 0.654902 0.301961 rg +134.844 130.495 m 141.012 130.495 l 141.012 112.897 l 134.844 112.897 l +134.844 130.495 l h +134.844 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +134.844 35.602 m 141.012 35.602 l 141.012 53.199 l 134.844 53.199 l +134.844 35.602 l h +134.844 35.602 m S Q +0.301961 0.654902 0.301961 rg +135.723 148.096 m 140.102 148.096 l 140.102 130.495 l 135.723 130.495 l +135.723 148.096 l h +135.723 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +135.723 18 m 140.102 18 l 140.102 35.602 l 135.723 35.602 l 135.723 18 +l h +135.723 18 m S Q +0.301961 0.654902 0.301961 rg +136.172 165.698 m 141.668 165.698 l 141.668 148.096 l 136.172 148.096 l +136.172 165.698 l h +136.172 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +136.172 0.398 m 141.668 0.398 l 141.668 18 l 136.172 18 l 136.172 0.398 +l h +136.172 0.398 m S Q +0.301961 0.654902 0.301961 rg +137.523 112.897 m 144.516 112.897 l 144.516 95.295 l 137.523 95.295 l +137.523 112.897 l h +137.523 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +137.523 53.199 m 144.516 53.199 l 144.516 70.801 l 137.523 70.801 l +137.523 53.199 l h +137.523 53.199 m S Q +0.301961 0.654902 0.301961 rg +138.867 95.295 m 143.078 95.295 l 143.078 77.698 l 138.867 77.698 l +138.867 95.295 l h +138.867 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +138.867 70.801 m 143.078 70.801 l 143.078 88.398 l 138.867 88.398 l +138.867 70.801 l h +138.867 70.801 m S Q +0.301961 0.654902 0.301961 rg +140.109 148.096 m 144.969 148.096 l 144.969 130.495 l 140.109 130.495 l +140.109 148.096 l h +140.109 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +140.109 18 m 144.969 18 l 144.969 35.602 l 140.109 35.602 l 140.109 18 +l h +140.109 18 m S Q +0.301961 0.654902 0.301961 rg +141.016 130.495 m 145.992 130.495 l 145.992 112.897 l 141.016 112.897 l +141.016 130.495 l h +141.016 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +141.016 35.602 m 145.992 35.602 l 145.992 53.199 l 141.016 53.199 l +141.016 35.602 l h +141.016 35.602 m S Q +0.301961 0.654902 0.301961 rg +141.672 165.698 m 148.57 165.698 l 148.57 148.096 l 141.672 148.096 l +141.672 165.698 l h +141.672 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +141.672 0.398 m 148.57 0.398 l 148.57 18 l 141.672 18 l 141.672 0.398 l +h +141.672 0.398 m S Q +0.301961 0.654902 0.301961 rg +143.082 95.295 m 148.062 95.295 l 148.062 77.698 l 143.082 77.698 l +143.082 95.295 l h +143.082 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +143.082 70.801 m 148.062 70.801 l 148.062 88.398 l 143.082 88.398 l +143.082 70.801 l h +143.082 70.801 m S Q +0.301961 0.654902 0.301961 rg +144.52 112.897 m 149.242 112.897 l 149.242 95.295 l 144.52 95.295 l +144.52 112.897 l h +144.52 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +144.52 53.199 m 149.242 53.199 l 149.242 70.801 l 144.52 70.801 l +144.52 53.199 l h +144.52 53.199 m S Q +0.301961 0.654902 0.301961 rg +144.977 148.096 m 150.586 148.096 l 150.586 130.495 l 144.977 130.495 l +144.977 148.096 l h +144.977 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +144.977 18 m 150.586 18 l 150.586 35.602 l 144.977 35.602 l 144.977 18 +l h +144.977 18 m S Q +0.301961 0.654902 0.301961 rg +145.996 130.495 m 150.387 130.495 l 150.387 112.897 l 145.996 112.897 l +145.996 130.495 l h +145.996 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +145.996 35.602 m 150.387 35.602 l 150.387 53.199 l 145.996 53.199 l +145.996 35.602 l h +145.996 35.602 m S Q +0.301961 0.654902 0.301961 rg +148.066 95.295 m 153.406 95.295 l 153.406 77.698 l 148.066 77.698 l +148.066 95.295 l h +148.066 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +148.066 70.801 m 153.406 70.801 l 153.406 88.398 l 148.066 88.398 l +148.066 70.801 l h +148.066 70.801 m S Q +0.301961 0.654902 0.301961 rg +148.578 165.698 m 153.715 165.698 l 153.715 148.096 l 148.578 148.096 l +148.578 165.698 l h +148.578 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +148.578 0.398 m 153.715 0.398 l 153.715 18 l 148.578 18 l 148.578 0.398 +l h +148.578 0.398 m S Q +0.301961 0.654902 0.301961 rg +149.344 112.897 m 154.184 112.897 l 154.184 95.295 l 149.344 95.295 l +149.344 112.897 l h +149.344 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +149.344 53.199 m 154.184 53.199 l 154.184 70.801 l 149.344 70.801 l +149.344 53.199 l h +149.344 53.199 m S Q +0.301961 0.654902 0.301961 rg +150.391 130.495 m 155.188 130.495 l 155.188 112.897 l 150.391 112.897 l +150.391 130.495 l h +150.391 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +150.391 35.602 m 155.188 35.602 l 155.188 53.199 l 150.391 53.199 l +150.391 35.602 l h +150.391 35.602 m S Q +0.301961 0.654902 0.301961 rg +150.598 148.096 m 157.035 148.096 l 157.035 130.495 l 150.598 130.495 l +150.598 148.096 l h +150.598 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +150.598 18 m 157.035 18 l 157.035 35.602 l 150.598 35.602 l 150.598 18 +l h +150.598 18 m S Q +0.301961 0.654902 0.301961 rg +153.41 95.295 m 160.547 95.295 l 160.547 77.698 l 153.41 77.698 l +153.41 95.295 l h +153.41 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +153.41 70.801 m 160.547 70.801 l 160.547 88.398 l 153.41 88.398 l +153.41 70.801 l h +153.41 70.801 m S Q +0.301961 0.654902 0.301961 rg +153.719 165.698 m 159.973 165.698 l 159.973 148.096 l 153.719 148.096 l +153.719 165.698 l h +153.719 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +153.719 0.398 m 159.973 0.398 l 159.973 18 l 153.719 18 l 153.719 0.398 +l h +153.719 0.398 m S Q +0.301961 0.654902 0.301961 rg +154.188 112.897 m 160.879 112.897 l 160.879 95.295 l 154.188 95.295 l +154.188 112.897 l h +154.188 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +154.188 53.199 m 160.879 53.199 l 160.879 70.801 l 154.188 70.801 l +154.188 53.199 l h +154.188 53.199 m S Q +0.301961 0.654902 0.301961 rg +155.195 130.495 m 161.508 130.495 l 161.508 112.897 l 155.195 112.897 l +155.195 130.495 l h +155.195 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +155.195 35.602 m 161.508 35.602 l 161.508 53.199 l 155.195 53.199 l +155.195 35.602 l h +155.195 35.602 m S Q +0.301961 0.654902 0.301961 rg +157.039 148.096 m 162.434 148.096 l 162.434 130.495 l 157.039 130.495 l +157.039 148.096 l h +157.039 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +157.039 18 m 162.434 18 l 162.434 35.602 l 157.039 35.602 l 157.039 18 +l h +157.039 18 m S Q +0.301961 0.654902 0.301961 rg +159.977 165.698 m 165.613 165.698 l 165.613 148.096 l 159.977 148.096 l +159.977 165.698 l h +159.977 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +159.977 0.398 m 165.613 0.398 l 165.613 18 l 159.977 18 l 159.977 0.398 +l h +159.977 0.398 m S Q +0.301961 0.654902 0.301961 rg +160.555 95.295 m 165.785 95.295 l 165.785 77.698 l 160.555 77.698 l +160.555 95.295 l h +160.555 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +160.555 70.801 m 165.785 70.801 l 165.785 88.398 l 160.555 88.398 l +160.555 70.801 l h +160.555 70.801 m S Q +0.301961 0.654902 0.301961 rg +160.883 112.897 m 165.289 112.897 l 165.289 95.295 l 160.883 95.295 l +160.883 112.897 l h +160.883 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +160.883 53.199 m 165.289 53.199 l 165.289 70.801 l 160.883 70.801 l +160.883 53.199 l h +160.883 53.199 m S Q +0.301961 0.654902 0.301961 rg +161.512 130.495 m 168.016 130.495 l 168.016 112.897 l 161.512 112.897 l +161.512 130.495 l h +161.512 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +161.512 35.602 m 168.016 35.602 l 168.016 53.199 l 161.512 53.199 l +161.512 35.602 l h +161.512 35.602 m S Q +0.301961 0.654902 0.301961 rg +162.438 148.096 m 166.648 148.096 l 166.648 130.495 l 162.438 130.495 l +162.438 148.096 l h +162.438 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +162.438 18 m 166.648 18 l 166.648 35.602 l 162.438 35.602 l 162.438 18 +l h +162.438 18 m S Q +0.301961 0.654902 0.301961 rg +165.297 112.897 m 170.445 112.897 l 170.445 95.295 l 165.297 95.295 l +165.297 112.897 l h +165.297 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +165.297 53.199 m 170.445 53.199 l 170.445 70.801 l 165.297 70.801 l +165.297 53.199 l h +165.297 53.199 m S Q +0.301961 0.654902 0.301961 rg +165.617 165.698 m 170.777 165.698 l 170.777 148.096 l 165.617 148.096 l +165.617 165.698 l h +165.617 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +165.617 0.398 m 170.777 0.398 l 170.777 18 l 165.617 18 l 165.617 0.398 +l h +165.617 0.398 m S Q +0.301961 0.654902 0.301961 rg +165.809 95.295 m 169.406 95.295 l 169.406 77.698 l 165.809 77.698 l +165.809 95.295 l h +165.809 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +165.809 70.801 m 169.406 70.801 l 169.406 88.398 l 165.809 88.398 l +165.809 70.801 l h +165.809 70.801 m S Q +0.301961 0.654902 0.301961 rg +166.977 148.096 m 173.207 148.096 l 173.207 130.495 l 166.977 130.495 l +166.977 148.096 l h +166.977 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +166.977 18 m 173.207 18 l 173.207 35.602 l 166.977 35.602 l 166.977 18 +l h +166.977 18 m S Q +0.301961 0.654902 0.301961 rg +168.02 130.495 m 173.055 130.495 l 173.055 112.897 l 168.02 112.897 l +168.02 130.495 l h +168.02 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +168.02 35.602 m 173.055 35.602 l 173.055 53.199 l 168.02 53.199 l +168.02 35.602 l h +168.02 35.602 m S Q +0.301961 0.654902 0.301961 rg +169.5 95.295 m 175.043 95.295 l 175.043 77.698 l 169.5 77.698 l 169.5 +95.295 l h +169.5 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +169.5 70.801 m 175.043 70.801 l 175.043 88.398 l 169.5 88.398 l 169.5 +70.801 l h +169.5 70.801 m S Q +0.301961 0.654902 0.301961 rg +170.453 112.897 m 175.188 112.897 l 175.188 95.295 l 170.453 95.295 l +170.453 112.897 l h +170.453 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +170.453 53.199 m 175.188 53.199 l 175.188 70.801 l 170.453 70.801 l +170.453 53.199 l h +170.453 53.199 m S Q +0.301961 0.654902 0.301961 rg +170.785 165.698 m 175.719 165.698 l 175.719 148.096 l 170.785 148.096 l +170.785 165.698 l h +170.785 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +170.785 0.398 m 175.719 0.398 l 175.719 18 l 170.785 18 l 170.785 0.398 +l h +170.785 0.398 m S Q +0.301961 0.654902 0.301961 rg +173.062 130.495 m 177.137 130.495 l 177.137 112.897 l 173.062 112.897 l +173.062 130.495 l h +173.062 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +173.062 35.602 m 177.137 35.602 l 177.137 53.199 l 173.062 53.199 l +173.062 35.602 l h +173.062 35.602 m S Q +0.301961 0.654902 0.301961 rg +173.359 148.096 m 178.574 148.096 l 178.574 130.495 l 173.359 130.495 l +173.359 148.096 l h +173.359 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +173.359 18 m 178.574 18 l 178.574 35.602 l 173.359 35.602 l 173.359 18 +l h +173.359 18 m S Q +0.301961 0.654902 0.301961 rg +175.051 95.295 m 180.414 95.295 l 180.414 77.698 l 175.051 77.698 l +175.051 95.295 l h +175.051 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +175.051 70.801 m 180.414 70.801 l 180.414 88.398 l 175.051 88.398 l +175.051 70.801 l h +175.051 70.801 m S Q +0.301961 0.654902 0.301961 rg +175.207 112.897 m 180.898 112.897 l 180.898 95.295 l 175.207 95.295 l +175.207 112.897 l h +175.207 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +175.207 53.199 m 180.898 53.199 l 180.898 70.801 l 175.207 70.801 l +175.207 53.199 l h +175.207 53.199 m S Q +0.301961 0.654902 0.301961 rg +175.723 165.698 m 181.039 165.698 l 181.039 148.096 l 175.723 148.096 l +175.723 165.698 l h +175.723 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +175.723 0.398 m 181.039 0.398 l 181.039 18 l 175.723 18 l 175.723 0.398 +l h +175.723 0.398 m S Q +0.301961 0.654902 0.301961 rg +177.141 130.495 m 183.777 130.495 l 183.777 112.897 l 177.141 112.897 l +177.141 130.495 l h +177.141 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +177.141 35.602 m 183.777 35.602 l 183.777 53.199 l 177.141 53.199 l +177.141 35.602 l h +177.141 35.602 m S Q +0.301961 0.654902 0.301961 rg +178.578 148.096 m 183.199 148.096 l 183.199 130.495 l 178.578 130.495 l +178.578 148.096 l h +178.578 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +178.578 18 m 183.199 18 l 183.199 35.602 l 178.578 35.602 l 178.578 18 +l h +178.578 18 m S Q +0.301961 0.654902 0.301961 rg +180.422 95.295 m 185.637 95.295 l 185.637 77.698 l 180.422 77.698 l +180.422 95.295 l h +180.422 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +180.422 70.801 m 185.637 70.801 l 185.637 88.398 l 180.422 88.398 l +180.422 70.801 l h +180.422 70.801 m S Q +0.301961 0.654902 0.301961 rg +180.906 112.897 m 185.953 112.897 l 185.953 95.295 l 180.906 95.295 l +180.906 112.897 l h +180.906 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +180.906 53.199 m 185.953 53.199 l 185.953 70.801 l 180.906 70.801 l +180.906 53.199 l h +180.906 53.199 m S Q +0.301961 0.654902 0.301961 rg +181.199 165.698 m 186.617 165.698 l 186.617 148.096 l 181.199 148.096 l +181.199 165.698 l h +181.199 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +181.199 0.398 m 186.617 0.398 l 186.617 18 l 181.199 18 l 181.199 0.398 +l h +181.199 0.398 m S Q +0.301961 0.654902 0.301961 rg +183.207 148.096 m 188.664 148.096 l 188.664 130.495 l 183.207 130.495 l +183.207 148.096 l h +183.207 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +183.207 18 m 188.664 18 l 188.664 35.602 l 183.207 35.602 l 183.207 18 +l h +183.207 18 m S Q +0.301961 0.654902 0.301961 rg +183.785 130.495 m 188.062 130.495 l 188.062 112.897 l 183.785 112.897 l +183.785 130.495 l h +183.785 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +183.785 35.602 m 188.062 35.602 l 188.062 53.199 l 183.785 53.199 l +183.785 35.602 l h +183.785 35.602 m S Q +0.301961 0.654902 0.301961 rg +185.645 95.295 m 190.102 95.295 l 190.102 77.698 l 185.645 77.698 l +185.645 95.295 l h +185.645 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +185.645 70.801 m 190.102 70.801 l 190.102 88.398 l 185.645 88.398 l +185.645 70.801 l h +185.645 70.801 m S Q +0.301961 0.654902 0.301961 rg +185.961 112.897 m 191.109 112.897 l 191.109 95.295 l 185.961 95.295 l +185.961 112.897 l h +185.961 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +185.961 53.199 m 191.109 53.199 l 191.109 70.801 l 185.961 70.801 l +185.961 53.199 l h +185.961 53.199 m S Q +0.301961 0.654902 0.301961 rg +186.625 165.698 m 191.461 165.698 l 191.461 148.096 l 186.625 148.096 l +186.625 165.698 l h +186.625 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +186.625 0.398 m 191.461 0.398 l 191.461 18 l 186.625 18 l 186.625 0.398 +l h +186.625 0.398 m S Q +0.301961 0.654902 0.301961 rg +188.066 130.495 m 193.305 130.495 l 193.305 112.897 l 188.066 112.897 l +188.066 130.495 l h +188.066 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +188.066 35.602 m 193.305 35.602 l 193.305 53.199 l 188.066 53.199 l +188.066 35.602 l h +188.066 35.602 m S Q +0.301961 0.654902 0.301961 rg +188.668 148.096 m 194.469 148.096 l 194.469 130.495 l 188.668 130.495 l +188.668 148.096 l h +188.668 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +188.668 18 m 194.469 18 l 194.469 35.602 l 188.668 35.602 l 188.668 18 +l h +188.668 18 m S Q +0.301961 0.654902 0.301961 rg +190.109 95.295 m 197.328 95.295 l 197.328 77.698 l 190.109 77.698 l +190.109 95.295 l h +190.109 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +190.109 70.801 m 197.328 70.801 l 197.328 88.398 l 190.109 88.398 l +190.109 70.801 l h +190.109 70.801 m S Q +0.301961 0.654902 0.301961 rg +191.113 112.897 m 196.43 112.897 l 196.43 95.295 l 191.113 95.295 l +191.113 112.897 l h +191.113 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +191.113 53.199 m 196.43 53.199 l 196.43 70.801 l 191.113 70.801 l +191.113 53.199 l h +191.113 53.199 m S Q +0.301961 0.654902 0.301961 rg +191.469 165.698 m 195.23 165.698 l 195.23 148.096 l 191.469 148.096 l +191.469 165.698 l h +191.469 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +191.469 0.398 m 195.23 0.398 l 195.23 18 l 191.469 18 l 191.469 0.398 l +h +191.469 0.398 m S Q +0.301961 0.654902 0.301961 rg +193.309 130.495 m 199.141 130.495 l 199.141 112.897 l 193.309 112.897 l +193.309 130.495 l h +193.309 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +193.309 35.602 m 199.141 35.602 l 199.141 53.199 l 193.309 53.199 l +193.309 35.602 l h +193.309 35.602 m S Q +0.301961 0.654902 0.301961 rg +194.477 148.096 m 200.102 148.096 l 200.102 130.495 l 194.477 130.495 l +194.477 148.096 l h +194.477 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +194.477 18 m 200.102 18 l 200.102 35.602 l 194.477 35.602 l 194.477 18 +l h +194.477 18 m S Q +0.301961 0.654902 0.301961 rg +195.234 165.698 m 199.715 165.698 l 199.715 148.096 l 195.234 148.096 l +195.234 165.698 l h +195.234 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +195.234 0.398 m 199.715 0.398 l 199.715 18 l 195.234 18 l 195.234 0.398 +l h +195.234 0.398 m S Q +0.301961 0.654902 0.301961 rg +196.434 112.897 m 201.898 112.897 l 201.898 95.295 l 196.434 95.295 l +196.434 112.897 l h +196.434 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +196.434 53.199 m 201.898 53.199 l 201.898 70.801 l 196.434 70.801 l +196.434 53.199 l h +196.434 53.199 m S Q +0.301961 0.654902 0.301961 rg +197.332 95.295 m 202.449 95.295 l 202.449 77.698 l 197.332 77.698 l +197.332 95.295 l h +197.332 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +197.332 70.801 m 202.449 70.801 l 202.449 88.398 l 197.332 88.398 l +197.332 70.801 l h +197.332 70.801 m S Q +0.301961 0.654902 0.301961 rg +199.16 130.495 m 205.215 130.495 l 205.215 112.897 l 199.16 112.897 l +199.16 130.495 l h +199.16 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +199.16 35.602 m 205.215 35.602 l 205.215 53.199 l 199.16 53.199 l +199.16 35.602 l h +199.16 35.602 m S Q +0.301961 0.654902 0.301961 rg +199.723 165.698 m 205.371 165.698 l 205.371 148.096 l 199.723 148.096 l +199.723 165.698 l h +199.723 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +199.723 0.398 m 205.371 0.398 l 205.371 18 l 199.723 18 l 199.723 0.398 +l h +199.723 0.398 m S Q +0.301961 0.654902 0.301961 rg +200.113 148.096 m 204.422 148.096 l 204.422 130.495 l 200.113 130.495 l +200.113 148.096 l h +200.113 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +200.113 18 m 204.422 18 l 204.422 35.602 l 200.113 35.602 l 200.113 18 +l h +200.113 18 m S Q +0.301961 0.654902 0.301961 rg +201.902 112.897 m 207.344 112.897 l 207.344 95.295 l 201.902 95.295 l +201.902 112.897 l h +201.902 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +201.902 53.199 m 207.344 53.199 l 207.344 70.801 l 201.902 70.801 l +201.902 53.199 l h +201.902 53.199 m S Q +0.301961 0.654902 0.301961 rg +202.469 95.295 m 208.328 95.295 l 208.328 77.698 l 202.469 77.698 l +202.469 95.295 l h +202.469 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +202.469 70.801 m 208.328 70.801 l 208.328 88.398 l 202.469 88.398 l +202.469 70.801 l h +202.469 70.801 m S Q +0.301961 0.654902 0.301961 rg +204.43 148.096 m 210.023 148.096 l 210.023 130.495 l 204.43 130.495 l +204.43 148.096 l h +204.43 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +204.43 18 m 210.023 18 l 210.023 35.602 l 204.43 35.602 l 204.43 18 l h +204.43 18 m S Q +0.301961 0.654902 0.301961 rg +205.219 130.495 m 210.332 130.495 l 210.332 112.897 l 205.219 112.897 l +205.219 130.495 l h +205.219 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +205.219 35.602 m 210.332 35.602 l 210.332 53.199 l 205.219 53.199 l +205.219 35.602 l h +205.219 35.602 m S Q +0.301961 0.654902 0.301961 rg +205.383 165.698 m 208.527 165.698 l 208.527 148.096 l 205.383 148.096 l +205.383 165.698 l h +205.383 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +205.383 0.398 m 208.527 0.398 l 208.527 18 l 205.383 18 l 205.383 0.398 +l h +205.383 0.398 m S Q +0.301961 0.654902 0.301961 rg +207.352 112.897 m 212.77 112.897 l 212.77 95.295 l 207.352 95.295 l +207.352 112.897 l h +207.352 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +207.352 53.199 m 212.77 53.199 l 212.77 70.801 l 207.352 70.801 l +207.352 53.199 l h +207.352 53.199 m S Q +0.301961 0.654902 0.301961 rg +208.332 95.295 m 214.891 95.295 l 214.891 77.698 l 208.332 77.698 l +208.332 95.295 l h +208.332 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +208.332 70.801 m 214.891 70.801 l 214.891 88.398 l 208.332 88.398 l +208.332 70.801 l h +208.332 70.801 m S Q +0.301961 0.654902 0.301961 rg +208.535 165.698 m 213.246 165.698 l 213.246 148.096 l 208.535 148.096 l +208.535 165.698 l h +208.535 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +208.535 0.398 m 213.246 0.398 l 213.246 18 l 208.535 18 l 208.535 0.398 +l h +208.535 0.398 m S Q +0.301961 0.654902 0.301961 rg +210.027 148.096 m 216.301 148.096 l 216.301 130.495 l 210.027 130.495 l +210.027 148.096 l h +210.027 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +210.027 18 m 216.301 18 l 216.301 35.602 l 210.027 35.602 l 210.027 18 +l h +210.027 18 m S Q +0.301961 0.654902 0.301961 rg +210.34 130.495 m 216.703 130.495 l 216.703 112.897 l 210.34 112.897 l +210.34 130.495 l h +210.34 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +210.34 35.602 m 216.703 35.602 l 216.703 53.199 l 210.34 53.199 l +210.34 35.602 l h +210.34 35.602 m S Q +0.301961 0.654902 0.301961 rg +212.773 112.897 m 216.277 112.897 l 216.277 95.295 l 212.773 95.295 l +212.773 112.897 l h +212.773 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +212.773 53.199 m 216.277 53.199 l 216.277 70.801 l 212.773 70.801 l +212.773 53.199 l h +212.773 53.199 m S Q +0.301961 0.654902 0.301961 rg +213.254 165.698 m 218.5 165.698 l 218.5 148.096 l 213.254 148.096 l +213.254 165.698 l h +213.254 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +213.254 0.398 m 218.5 0.398 l 218.5 18 l 213.254 18 l 213.254 0.398 l h +213.254 0.398 m S Q +0.301961 0.654902 0.301961 rg +214.895 95.295 m 219.586 95.295 l 219.586 77.698 l 214.895 77.698 l +214.895 95.295 l h +214.895 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +214.895 70.801 m 219.586 70.801 l 219.586 88.398 l 214.895 88.398 l +214.895 70.801 l h +214.895 70.801 m S Q +0.301961 0.654902 0.301961 rg +216.281 112.897 m 220.941 112.897 l 220.941 95.295 l 216.281 95.295 l +216.281 112.897 l h +216.281 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +216.281 53.199 m 220.941 53.199 l 220.941 70.801 l 216.281 70.801 l +216.281 53.199 l h +216.281 53.199 m S Q +0.301961 0.654902 0.301961 rg +216.312 148.096 m 221.941 148.096 l 221.941 130.495 l 216.312 130.495 l +216.312 148.096 l h +216.312 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +216.312 18 m 221.941 18 l 221.941 35.602 l 216.312 35.602 l 216.312 18 +l h +216.312 18 m S Q +0.301961 0.654902 0.301961 rg +216.723 130.495 m 222.188 130.495 l 222.188 112.897 l 216.723 112.897 l +216.723 130.495 l h +216.723 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +216.723 35.602 m 222.188 35.602 l 222.188 53.199 l 216.723 53.199 l +216.723 35.602 l h +216.723 35.602 m S Q +0.301961 0.654902 0.301961 rg +218.508 165.698 m 224.621 165.698 l 224.621 148.096 l 218.508 148.096 l +218.508 165.698 l h +218.508 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +218.508 0.398 m 224.621 0.398 l 224.621 18 l 218.508 18 l 218.508 0.398 +l h +218.508 0.398 m S Q +0.301961 0.654902 0.301961 rg +219.59 95.295 m 225.305 95.295 l 225.305 77.698 l 219.59 77.698 l +219.59 95.295 l h +219.59 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +219.59 70.801 m 225.305 70.801 l 225.305 88.398 l 219.59 88.398 l +219.59 70.801 l h +219.59 70.801 m S Q +0.301961 0.654902 0.301961 rg +220.945 112.897 m 227.617 112.897 l 227.617 95.295 l 220.945 95.295 l +220.945 112.897 l h +220.945 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +220.945 53.199 m 227.617 53.199 l 227.617 70.801 l 220.945 70.801 l +220.945 53.199 l h +220.945 53.199 m S Q +0.301961 0.654902 0.301961 rg +221.945 148.096 m 226.426 148.096 l 226.426 130.495 l 221.945 130.495 l +221.945 148.096 l h +221.945 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +221.945 18 m 226.426 18 l 226.426 35.602 l 221.945 35.602 l 221.945 18 +l h +221.945 18 m S Q +0.301961 0.654902 0.301961 rg +222.203 130.495 m 227.824 130.495 l 227.824 112.897 l 222.203 112.897 l +222.203 130.495 l h +222.203 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +222.203 35.602 m 227.824 35.602 l 227.824 53.199 l 222.203 53.199 l +222.203 35.602 l h +222.203 35.602 m S Q +0.301961 0.654902 0.301961 rg +224.625 165.698 m 230.289 165.698 l 230.289 148.096 l 224.625 148.096 l +224.625 165.698 l h +224.625 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +224.625 0.398 m 230.289 0.398 l 230.289 18 l 224.625 18 l 224.625 0.398 +l h +224.625 0.398 m S Q +0.301961 0.654902 0.301961 rg +225.312 95.295 m 229.695 95.295 l 229.695 77.698 l 225.312 77.698 l +225.312 95.295 l h +225.312 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +225.312 70.801 m 229.695 70.801 l 229.695 88.398 l 225.312 88.398 l +225.312 70.801 l h +225.312 70.801 m S Q +0.301961 0.654902 0.301961 rg +226.43 148.096 m 231.98 148.096 l 231.98 130.495 l 226.43 130.495 l +226.43 148.096 l h +226.43 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +226.43 18 m 231.98 18 l 231.98 35.602 l 226.43 35.602 l 226.43 18 l h +226.43 18 m S Q +0.301961 0.654902 0.301961 rg +227.621 112.897 m 232.746 112.897 l 232.746 95.295 l 227.621 95.295 l +227.621 112.897 l h +227.621 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +227.621 53.199 m 232.746 53.199 l 232.746 70.801 l 227.621 70.801 l +227.621 53.199 l h +227.621 53.199 m S Q +0.301961 0.654902 0.301961 rg +227.832 130.495 m 233.504 130.495 l 233.504 112.897 l 227.832 112.897 l +227.832 130.495 l h +227.832 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +227.832 35.602 m 233.504 35.602 l 233.504 53.199 l 227.832 53.199 l +227.832 35.602 l h +227.832 35.602 m S Q +0.301961 0.654902 0.301961 rg +229.699 95.295 m 234.57 95.295 l 234.57 77.698 l 229.699 77.698 l +229.699 95.295 l h +229.699 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +229.699 70.801 m 234.57 70.801 l 234.57 88.398 l 229.699 88.398 l +229.699 70.801 l h +229.699 70.801 m S Q +0.301961 0.654902 0.301961 rg +230.297 165.698 m 235.898 165.698 l 235.898 148.096 l 230.297 148.096 l +230.297 165.698 l h +230.297 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +230.297 0.398 m 235.898 0.398 l 235.898 18 l 230.297 18 l 230.297 0.398 +l h +230.297 0.398 m S Q +0.301961 0.654902 0.301961 rg +231.992 148.096 m 237.957 148.096 l 237.957 130.495 l 231.992 130.495 l +231.992 148.096 l h +231.992 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +231.992 18 m 237.957 18 l 237.957 35.602 l 231.992 35.602 l 231.992 18 +l h +231.992 18 m S Q +0.301961 0.654902 0.301961 rg +232.754 112.897 m 238.359 112.897 l 238.359 95.295 l 232.754 95.295 l +232.754 112.897 l h +232.754 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +232.754 53.199 m 238.359 53.199 l 238.359 70.801 l 232.754 70.801 l +232.754 53.199 l h +232.754 53.199 m S Q +0.301961 0.654902 0.301961 rg +233.508 130.495 m 238.656 130.495 l 238.656 112.897 l 233.508 112.897 l +233.508 130.495 l h +233.508 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +233.508 35.602 m 238.656 35.602 l 238.656 53.199 l 233.508 53.199 l +233.508 35.602 l h +233.508 35.602 m S Q +0.301961 0.654902 0.301961 rg +234.574 95.295 m 240.152 95.295 l 240.152 77.698 l 234.574 77.698 l +234.574 95.295 l h +234.574 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +234.574 70.801 m 240.152 70.801 l 240.152 88.398 l 234.574 88.398 l +234.574 70.801 l h +234.574 70.801 m S Q +0.301961 0.654902 0.301961 rg +235.902 165.698 m 241.793 165.698 l 241.793 148.096 l 235.902 148.096 l +235.902 165.698 l h +235.902 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +235.902 0.398 m 241.793 0.398 l 241.793 18 l 235.902 18 l 235.902 0.398 +l h +235.902 0.398 m S Q +0.301961 0.654902 0.301961 rg +237.961 148.096 m 242.301 148.096 l 242.301 130.495 l 237.961 130.495 l +237.961 148.096 l h +237.961 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +237.961 18 m 242.301 18 l 242.301 35.602 l 237.961 35.602 l 237.961 18 +l h +237.961 18 m S Q +0.301961 0.654902 0.301961 rg +238.363 112.897 m 243.129 112.897 l 243.129 95.295 l 238.363 95.295 l +238.363 112.897 l h +238.363 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +238.363 53.199 m 243.129 53.199 l 243.129 70.801 l 238.363 70.801 l +238.363 53.199 l h +238.363 53.199 m S Q +0.301961 0.654902 0.301961 rg +238.664 130.495 m 244.484 130.495 l 244.484 112.897 l 238.664 112.897 l +238.664 130.495 l h +238.664 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +238.664 35.602 m 244.484 35.602 l 244.484 53.199 l 238.664 53.199 l +238.664 35.602 l h +238.664 35.602 m S Q +0.301961 0.654902 0.301961 rg +240.156 95.295 m 246.93 95.295 l 246.93 77.698 l 240.156 77.698 l +240.156 95.295 l h +240.156 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +240.156 70.801 m 246.93 70.801 l 246.93 88.398 l 240.156 88.398 l +240.156 70.801 l h +240.156 70.801 m S Q +0.301961 0.654902 0.301961 rg +241.797 165.698 m 247.633 165.698 l 247.633 148.096 l 241.797 148.096 l +241.797 165.698 l h +241.797 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +241.797 0.398 m 247.633 0.398 l 247.633 18 l 241.797 18 l 241.797 0.398 +l h +241.797 0.398 m S Q +0.301961 0.654902 0.301961 rg +242.309 148.096 m 246.59 148.096 l 246.59 130.495 l 242.309 130.495 l +242.309 148.096 l h +242.309 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +242.309 18 m 246.59 18 l 246.59 35.602 l 242.309 35.602 l 242.309 18 l +h +242.309 18 m S Q +0.301961 0.654902 0.301961 rg +243.133 112.897 m 248.375 112.897 l 248.375 95.295 l 243.133 95.295 l +243.133 112.897 l h +243.133 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +243.133 53.199 m 248.375 53.199 l 248.375 70.801 l 243.133 70.801 l +243.133 53.199 l h +243.133 53.199 m S Q +0.301961 0.654902 0.301961 rg +244.492 130.495 m 249.531 130.495 l 249.531 112.897 l 244.492 112.897 l +244.492 130.495 l h +244.492 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +244.492 35.602 m 249.531 35.602 l 249.531 53.199 l 244.492 53.199 l +244.492 35.602 l h +244.492 35.602 m S Q +0.301961 0.654902 0.301961 rg +246.594 148.096 m 251.383 148.096 l 251.383 130.495 l 246.594 130.495 l +246.594 148.096 l h +246.594 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +246.594 18 m 251.383 18 l 251.383 35.602 l 246.594 35.602 l 246.594 18 +l h +246.594 18 m S Q +0.301961 0.654902 0.301961 rg +246.934 95.295 m 252.223 95.295 l 252.223 77.698 l 246.934 77.698 l +246.934 95.295 l h +246.934 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +246.934 70.801 m 252.223 70.801 l 252.223 88.398 l 246.934 88.398 l +246.934 70.801 l h +246.934 70.801 m S Q +0.301961 0.654902 0.301961 rg +247.637 165.698 m 253.512 165.698 l 253.512 148.096 l 247.637 148.096 l +247.637 165.698 l h +247.637 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +247.637 0.398 m 253.512 0.398 l 253.512 18 l 247.637 18 l 247.637 0.398 +l h +247.637 0.398 m S Q +0.301961 0.654902 0.301961 rg +248.383 112.897 m 253.191 112.897 l 253.191 95.295 l 248.383 95.295 l +248.383 112.897 l h +248.383 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +248.383 53.199 m 253.191 53.199 l 253.191 70.801 l 248.383 70.801 l +248.383 53.199 l h +248.383 53.199 m S Q +0.301961 0.654902 0.301961 rg +249.535 130.495 m 254.566 130.495 l 254.566 112.897 l 249.535 112.897 l +249.535 130.495 l h +249.535 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +249.535 35.602 m 254.566 35.602 l 254.566 53.199 l 249.535 53.199 l +249.535 35.602 l h +249.535 35.602 m S Q +0.301961 0.654902 0.301961 rg +251.387 148.096 m 256.684 148.096 l 256.684 130.495 l 251.387 130.495 l +251.387 148.096 l h +251.387 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +251.387 18 m 256.684 18 l 256.684 35.602 l 251.387 35.602 l 251.387 18 +l h +251.387 18 m S Q +0.301961 0.654902 0.301961 rg +252.227 95.295 m 257.953 95.295 l 257.953 77.698 l 252.227 77.698 l +252.227 95.295 l h +252.227 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +252.227 70.801 m 257.953 70.801 l 257.953 88.398 l 252.227 88.398 l +252.227 70.801 l h +252.227 70.801 m S Q +0.301961 0.654902 0.301961 rg +253.199 112.897 m 257.625 112.897 l 257.625 95.295 l 253.199 95.295 l +253.199 112.897 l h +253.199 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +253.199 53.199 m 257.625 53.199 l 257.625 70.801 l 253.199 70.801 l +253.199 53.199 l h +253.199 53.199 m S Q +0.301961 0.654902 0.301961 rg +253.52 165.698 m 259.605 165.698 l 259.605 148.096 l 253.52 148.096 l +253.52 165.698 l h +253.52 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +253.52 0.398 m 259.605 0.398 l 259.605 18 l 253.52 18 l 253.52 0.398 l +h +253.52 0.398 m S Q +0.301961 0.654902 0.301961 rg +254.57 130.495 m 260.035 130.495 l 260.035 112.897 l 254.57 112.897 l +254.57 130.495 l h +254.57 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +254.57 35.602 m 260.035 35.602 l 260.035 53.199 l 254.57 53.199 l +254.57 35.602 l h +254.57 35.602 m S Q +0.301961 0.654902 0.301961 rg +256.688 148.096 m 262.238 148.096 l 262.238 130.495 l 256.688 130.495 l +256.688 148.096 l h +256.688 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +256.688 18 m 262.238 18 l 262.238 35.602 l 256.688 35.602 l 256.688 18 +l h +256.688 18 m S Q +0.301961 0.654902 0.301961 rg +257.629 112.897 m 263.336 112.897 l 263.336 95.295 l 257.629 95.295 l +257.629 112.897 l h +257.629 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +257.629 53.199 m 263.336 53.199 l 263.336 70.801 l 257.629 70.801 l +257.629 53.199 l h +257.629 53.199 m S Q +0.301961 0.654902 0.301961 rg +257.957 95.295 m 263.055 95.295 l 263.055 77.698 l 257.957 77.698 l +257.957 95.295 l h +257.957 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +257.957 70.801 m 263.055 70.801 l 263.055 88.398 l 257.957 88.398 l +257.957 70.801 l h +257.957 70.801 m S Q +0.301961 0.654902 0.301961 rg +259.609 165.698 m 265.039 165.698 l 265.039 148.096 l 259.609 148.096 l +259.609 165.698 l h +259.609 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +259.609 0.398 m 265.039 0.398 l 265.039 18 l 259.609 18 l 259.609 0.398 +l h +259.609 0.398 m S Q +0.301961 0.654902 0.301961 rg +260.043 130.495 m 264.324 130.495 l 264.324 112.897 l 260.043 112.897 l +260.043 130.495 l h +260.043 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +260.043 35.602 m 264.324 35.602 l 264.324 53.199 l 260.043 53.199 l +260.043 35.602 l h +260.043 35.602 m S Q +0.301961 0.654902 0.301961 rg +262.246 148.096 m 269.133 148.096 l 269.133 130.495 l 262.246 130.495 l +262.246 148.096 l h +262.246 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +262.246 18 m 269.133 18 l 269.133 35.602 l 262.246 35.602 l 262.246 18 +l h +262.246 18 m S Q +0.301961 0.654902 0.301961 rg +263.062 95.295 m 268.488 95.295 l 268.488 77.698 l 263.062 77.698 l +263.062 95.295 l h +263.062 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +263.062 70.801 m 268.488 70.801 l 268.488 88.398 l 263.062 88.398 l +263.062 70.801 l h +263.062 70.801 m S Q +0.301961 0.654902 0.301961 rg +263.352 112.897 m 267.625 112.897 l 267.625 95.295 l 263.352 95.295 l +263.352 112.897 l h +263.352 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +263.352 53.199 m 267.625 53.199 l 267.625 70.801 l 263.352 70.801 l +263.352 53.199 l h +263.352 53.199 m S Q +0.301961 0.654902 0.301961 rg +264.332 130.495 m 268.812 130.495 l 268.812 112.897 l 264.332 112.897 l +264.332 130.495 l h +264.332 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +264.332 35.602 m 268.812 35.602 l 268.812 53.199 l 264.332 53.199 l +264.332 35.602 l h +264.332 35.602 m S Q +0.301961 0.654902 0.301961 rg +265.043 165.698 m 270.113 165.698 l 270.113 148.096 l 265.043 148.096 l +265.043 165.698 l h +265.043 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +265.043 0.398 m 270.113 0.398 l 270.113 18 l 265.043 18 l 265.043 0.398 +l h +265.043 0.398 m S Q +0.301961 0.654902 0.301961 rg +267.629 112.897 m 272.32 112.897 l 272.32 95.295 l 267.629 95.295 l +267.629 112.897 l h +267.629 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +267.629 53.199 m 272.32 53.199 l 272.32 70.801 l 267.629 70.801 l +267.629 53.199 l h +267.629 53.199 m S Q +0.301961 0.654902 0.301961 rg +268.496 95.295 m 273.285 95.295 l 273.285 77.698 l 268.496 77.698 l +268.496 95.295 l h +268.496 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +268.496 70.801 m 273.285 70.801 l 273.285 88.398 l 268.496 88.398 l +268.496 70.801 l h +268.496 70.801 m S Q +0.301961 0.654902 0.301961 rg +268.816 130.495 m 274.035 130.495 l 274.035 112.897 l 268.816 112.897 l +268.816 130.495 l h +268.816 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +268.816 35.602 m 274.035 35.602 l 274.035 53.199 l 268.816 53.199 l +268.816 35.602 l h +268.816 35.602 m S Q +0.301961 0.654902 0.301961 rg +269.141 148.096 m 274.867 148.096 l 274.867 130.495 l 269.141 130.495 l +269.141 148.096 l h +269.141 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +269.141 18 m 274.867 18 l 274.867 35.602 l 269.141 35.602 l 269.141 18 +l h +269.141 18 m S Q +0.301961 0.654902 0.301961 rg +270.117 165.698 m 276.32 165.698 l 276.32 148.096 l 270.117 148.096 l +270.117 165.698 l h +270.117 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +270.117 0.398 m 276.32 0.398 l 276.32 18 l 270.117 18 l 270.117 0.398 l +h +270.117 0.398 m S Q +0.301961 0.654902 0.301961 rg +272.328 112.897 m 277.434 112.897 l 277.434 95.295 l 272.328 95.295 l +272.328 112.897 l h +272.328 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +272.328 53.199 m 277.434 53.199 l 277.434 70.801 l 272.328 70.801 l +272.328 53.199 l h +272.328 53.199 m S Q +0.301961 0.654902 0.301961 rg +273.289 95.295 m 278.852 95.295 l 278.852 77.698 l 273.289 77.698 l +273.289 95.295 l h +273.289 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +273.289 70.801 m 278.852 70.801 l 278.852 88.398 l 273.289 88.398 l +273.289 70.801 l h +273.289 70.801 m S Q +0.301961 0.654902 0.301961 rg +274.039 130.495 m 279.227 130.495 l 279.227 112.897 l 274.039 112.897 l +274.039 130.495 l h +274.039 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +274.039 35.602 m 279.227 35.602 l 279.227 53.199 l 274.039 53.199 l +274.039 35.602 l h +274.039 35.602 m S Q +0.301961 0.654902 0.301961 rg +274.871 148.096 m 280.34 148.096 l 280.34 130.495 l 274.871 130.495 l +274.871 148.096 l h +274.871 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +274.871 18 m 280.34 18 l 280.34 35.602 l 274.871 35.602 l 274.871 18 l +h +274.871 18 m S Q +0.301961 0.654902 0.301961 rg +276.324 165.698 m 281.297 165.698 l 281.297 148.096 l 276.324 148.096 l +276.324 165.698 l h +276.324 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +276.324 0.398 m 281.297 0.398 l 281.297 18 l 276.324 18 l 276.324 0.398 +l h +276.324 0.398 m S Q +0.301961 0.654902 0.301961 rg +277.438 112.897 m 282.809 112.897 l 282.809 95.295 l 277.438 95.295 l +277.438 112.897 l h +277.438 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +277.438 53.199 m 282.809 53.199 l 282.809 70.801 l 277.438 70.801 l +277.438 53.199 l h +277.438 53.199 m S Q +0.301961 0.654902 0.301961 rg +278.855 95.295 m 283.504 95.295 l 283.504 77.698 l 278.855 77.698 l +278.855 95.295 l h +278.855 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +278.855 70.801 m 283.504 70.801 l 283.504 88.398 l 278.855 88.398 l +278.855 70.801 l h +278.855 70.801 m S Q +0.301961 0.654902 0.301961 rg +279.234 130.495 m 284.672 130.495 l 284.672 112.897 l 279.234 112.897 l +279.234 130.495 l h +279.234 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +279.234 35.602 m 284.672 35.602 l 284.672 53.199 l 279.234 53.199 l +279.234 35.602 l h +279.234 35.602 m S Q +0.301961 0.654902 0.301961 rg +280.348 148.096 m 286.465 148.096 l 286.465 130.495 l 280.348 130.495 l +280.348 148.096 l h +280.348 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +280.348 18 m 286.465 18 l 286.465 35.602 l 280.348 35.602 l 280.348 18 +l h +280.348 18 m S Q +0.301961 0.654902 0.301961 rg +281.305 165.698 m 286.453 165.698 l 286.453 148.096 l 281.305 148.096 l +281.305 165.698 l h +281.305 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +281.305 0.398 m 286.453 0.398 l 286.453 18 l 281.305 18 l 281.305 0.398 +l h +281.305 0.398 m S Q +0.301961 0.654902 0.301961 rg +282.812 112.897 m 289.047 112.897 l 289.047 95.295 l 282.812 95.295 l +282.812 112.897 l h +282.812 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +282.812 53.199 m 289.047 53.199 l 289.047 70.801 l 282.812 70.801 l +282.812 53.199 l h +282.812 53.199 m S Q +0.301961 0.654902 0.301961 rg +283.512 95.295 m 287.535 95.295 l 287.535 77.698 l 283.512 77.698 l +283.512 95.295 l h +283.512 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +283.512 70.801 m 287.535 70.801 l 287.535 88.398 l 283.512 88.398 l +283.512 70.801 l h +283.512 70.801 m S Q +0.301961 0.654902 0.301961 rg +284.676 130.495 m 289.887 130.495 l 289.887 112.897 l 284.676 112.897 l +284.676 130.495 l h +284.676 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +284.676 35.602 m 289.887 35.602 l 289.887 53.199 l 284.676 53.199 l +284.676 35.602 l h +284.676 35.602 m S Q +0.301961 0.654902 0.301961 rg +286.461 165.698 m 291.602 165.698 l 291.602 148.096 l 286.461 148.096 l +286.461 165.698 l h +286.461 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +286.461 0.398 m 291.602 0.398 l 291.602 18 l 286.461 18 l 286.461 0.398 +l h +286.461 0.398 m S Q +0.301961 0.654902 0.301961 rg +286.477 148.096 m 291.074 148.096 l 291.074 130.495 l 286.477 130.495 l +286.477 148.096 l h +286.477 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +286.477 18 m 291.074 18 l 291.074 35.602 l 286.477 35.602 l 286.477 18 +l h +286.477 18 m S Q +0.301961 0.654902 0.301961 rg +287.543 95.295 m 293.098 95.295 l 293.098 77.698 l 287.543 77.698 l +287.543 95.295 l h +287.543 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +287.543 70.801 m 293.098 70.801 l 293.098 88.398 l 287.543 88.398 l +287.543 70.801 l h +287.543 70.801 m S Q +0.301961 0.654902 0.301961 rg +289.055 112.897 m 293.73 112.897 l 293.73 95.295 l 289.055 95.295 l +289.055 112.897 l h +289.055 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +289.055 53.199 m 293.73 53.199 l 293.73 70.801 l 289.055 70.801 l +289.055 53.199 l h +289.055 53.199 m S Q +0.301961 0.654902 0.301961 rg +289.891 130.495 m 295.184 130.495 l 295.184 112.897 l 289.891 112.897 l +289.891 130.495 l h +289.891 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +289.891 35.602 m 295.184 35.602 l 295.184 53.199 l 289.891 53.199 l +289.891 35.602 l h +289.891 35.602 m S Q +0.301961 0.654902 0.301961 rg +291.082 148.096 m 296.277 148.096 l 296.277 130.495 l 291.082 130.495 l +291.082 148.096 l h +291.082 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +291.082 18 m 296.277 18 l 296.277 35.602 l 291.082 35.602 l 291.082 18 +l h +291.082 18 m S Q +0.301961 0.654902 0.301961 rg +291.609 165.698 m 297.926 165.698 l 297.926 148.096 l 291.609 148.096 l +291.609 165.698 l h +291.609 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +291.609 0.398 m 297.926 0.398 l 297.926 18 l 291.609 18 l 291.609 0.398 +l h +291.609 0.398 m S Q +0.301961 0.654902 0.301961 rg +293.105 95.295 m 297.527 95.295 l 297.527 77.698 l 293.105 77.698 l +293.105 95.295 l h +293.105 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +293.105 70.801 m 297.527 70.801 l 297.527 88.398 l 293.105 88.398 l +293.105 70.801 l h +293.105 70.801 m S Q +0.301961 0.654902 0.301961 rg +293.738 112.897 m 300.902 112.897 l 300.902 95.295 l 293.738 95.295 l +293.738 112.897 l h +293.738 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +293.738 53.199 m 300.902 53.199 l 300.902 70.801 l 293.738 70.801 l +293.738 53.199 l h +293.738 53.199 m S Q +0.301961 0.654902 0.301961 rg +295.188 130.495 m 300.234 130.495 l 300.234 112.897 l 295.188 112.897 l +295.188 130.495 l h +295.188 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +295.188 35.602 m 300.234 35.602 l 300.234 53.199 l 295.188 53.199 l +295.188 35.602 l h +295.188 35.602 m S Q +0.301961 0.654902 0.301961 rg +296.285 148.096 m 300.727 148.096 l 300.727 130.495 l 296.285 130.495 l +296.285 148.096 l h +296.285 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +296.285 18 m 300.727 18 l 300.727 35.602 l 296.285 35.602 l 296.285 18 +l h +296.285 18 m S Q +0.301961 0.654902 0.301961 rg +297.531 95.295 m 301.559 95.295 l 301.559 77.698 l 297.531 77.698 l +297.531 95.295 l h +297.531 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +297.531 70.801 m 301.559 70.801 l 301.559 88.398 l 297.531 88.398 l +297.531 70.801 l h +297.531 70.801 m S Q +0.301961 0.654902 0.301961 rg +297.93 165.698 m 304.039 165.698 l 304.039 148.096 l 297.93 148.096 l +297.93 165.698 l h +297.93 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +297.93 0.398 m 304.039 0.398 l 304.039 18 l 297.93 18 l 297.93 0.398 l +h +297.93 0.398 m S Q +0.301961 0.654902 0.301961 rg +300.238 130.495 m 306.344 130.495 l 306.344 112.897 l 300.238 112.897 l +300.238 130.495 l h +300.238 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +300.238 35.602 m 306.344 35.602 l 306.344 53.199 l 300.238 53.199 l +300.238 35.602 l h +300.238 35.602 m S Q +0.301961 0.654902 0.301961 rg +300.734 148.096 m 306.105 148.096 l 306.105 130.495 l 300.734 130.495 l +300.734 148.096 l h +300.734 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +300.734 18 m 306.105 18 l 306.105 35.602 l 300.734 35.602 l 300.734 18 +l h +300.734 18 m S Q +0.301961 0.654902 0.301961 rg +300.922 112.897 m 305.457 112.897 l 305.457 95.295 l 300.922 95.295 l +300.922 112.897 l h +300.922 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +300.922 53.199 m 305.457 53.199 l 305.457 70.801 l 300.922 70.801 l +300.922 53.199 l h +300.922 53.199 m S Q +0.301961 0.654902 0.301961 rg +301.566 95.295 m 307.422 95.295 l 307.422 77.698 l 301.566 77.698 l +301.566 95.295 l h +301.566 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +301.566 70.801 m 307.422 70.801 l 307.422 88.398 l 301.566 88.398 l +301.566 70.801 l h +301.566 70.801 m S Q +0.301961 0.654902 0.301961 rg +304.043 165.698 m 309.945 165.698 l 309.945 148.096 l 304.043 148.096 l +304.043 165.698 l h +304.043 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +304.043 0.398 m 309.945 0.398 l 309.945 18 l 304.043 18 l 304.043 0.398 +l h +304.043 0.398 m S Q +0.301961 0.654902 0.301961 rg +305.465 112.897 m 310.605 112.897 l 310.605 95.295 l 305.465 95.295 l +305.465 112.897 l h +305.465 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +305.465 53.199 m 310.605 53.199 l 310.605 70.801 l 305.465 70.801 l +305.465 53.199 l h +305.465 53.199 m S Q +0.301961 0.654902 0.301961 rg +306.113 148.096 m 311.551 148.096 l 311.551 130.495 l 306.113 130.495 l +306.113 148.096 l h +306.113 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +306.113 18 m 311.551 18 l 311.551 35.602 l 306.113 35.602 l 306.113 18 +l h +306.113 18 m S Q +0.301961 0.654902 0.301961 rg +306.352 130.495 m 311.688 130.495 l 311.688 112.897 l 306.352 112.897 l +306.352 130.495 l h +306.352 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +306.352 35.602 m 311.688 35.602 l 311.688 53.199 l 306.352 53.199 l +306.352 35.602 l h +306.352 35.602 m S Q +0.301961 0.654902 0.301961 rg +307.426 95.295 m 312.832 95.295 l 312.832 77.698 l 307.426 77.698 l +307.426 95.295 l h +307.426 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +307.426 70.801 m 312.832 70.801 l 312.832 88.398 l 307.426 88.398 l +307.426 70.801 l h +307.426 70.801 m S Q +0.301961 0.654902 0.301961 rg +309.949 165.698 m 314.418 165.698 l 314.418 148.096 l 309.949 148.096 l +309.949 165.698 l h +309.949 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +309.949 0.398 m 314.418 0.398 l 314.418 18 l 309.949 18 l 309.949 0.398 +l h +309.949 0.398 m S Q +0.301961 0.654902 0.301961 rg +310.609 112.897 m 315.875 112.897 l 315.875 95.295 l 310.609 95.295 l +310.609 112.897 l h +310.609 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +310.609 53.199 m 315.875 53.199 l 315.875 70.801 l 310.609 70.801 l +310.609 53.199 l h +310.609 53.199 m S Q +0.301961 0.654902 0.301961 rg +311.555 148.096 m 318.098 148.096 l 318.098 130.495 l 311.555 130.495 l +311.555 148.096 l h +311.555 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +311.555 18 m 318.098 18 l 318.098 35.602 l 311.555 35.602 l 311.555 18 +l h +311.555 18 m S Q +0.301961 0.654902 0.301961 rg +311.703 130.495 m 315.52 130.495 l 315.52 112.897 l 311.703 112.897 l +311.703 130.495 l h +311.703 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +311.703 35.602 m 315.52 35.602 l 315.52 53.199 l 311.703 53.199 l +311.703 35.602 l h +311.703 35.602 m S Q +0.301961 0.654902 0.301961 rg +312.836 95.295 m 318.664 95.295 l 318.664 77.698 l 312.836 77.698 l +312.836 95.295 l h +312.836 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +312.836 70.801 m 318.664 70.801 l 318.664 88.398 l 312.836 88.398 l +312.836 70.801 l h +312.836 70.801 m S Q +0.301961 0.654902 0.301961 rg +314.422 165.698 m 320.969 165.698 l 320.969 148.096 l 314.422 148.096 l +314.422 165.698 l h +314.422 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +314.422 0.398 m 320.969 0.398 l 320.969 18 l 314.422 18 l 314.422 0.398 +l h +314.422 0.398 m S Q +0.301961 0.654902 0.301961 rg +315.523 130.495 m 319.949 130.495 l 319.949 112.897 l 315.523 112.897 l +315.523 130.495 l h +315.523 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +315.523 35.602 m 319.949 35.602 l 319.949 53.199 l 315.523 53.199 l +315.523 35.602 l h +315.523 35.602 m S Q +0.301961 0.654902 0.301961 rg +315.891 112.897 m 321.113 112.897 l 321.113 95.295 l 315.891 95.295 l +315.891 112.897 l h +315.891 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +315.891 53.199 m 321.113 53.199 l 321.113 70.801 l 315.891 70.801 l +315.891 53.199 l h +315.891 53.199 m S Q +0.301961 0.654902 0.301961 rg +318.102 148.096 m 324.02 148.096 l 324.02 130.495 l 318.102 130.495 l +318.102 148.096 l h +318.102 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +318.102 18 m 324.02 18 l 324.02 35.602 l 318.102 35.602 l 318.102 18 l +h +318.102 18 m S Q +0.301961 0.654902 0.301961 rg +318.672 95.295 m 322.668 95.295 l 322.668 77.698 l 318.672 77.698 l +318.672 95.295 l h +318.672 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +318.672 70.801 m 322.668 70.801 l 322.668 88.398 l 318.672 88.398 l +318.672 70.801 l h +318.672 70.801 m S Q +0.301961 0.654902 0.301961 rg +319.957 130.495 m 325.051 130.495 l 325.051 112.897 l 319.957 112.897 l +319.957 130.495 l h +319.957 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +319.957 35.602 m 325.051 35.602 l 325.051 53.199 l 319.957 53.199 l +319.957 35.602 l h +319.957 35.602 m S Q +0.301961 0.654902 0.301961 rg +320.977 165.698 m 325.43 165.698 l 325.43 148.096 l 320.977 148.096 l +320.977 165.698 l h +320.977 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +320.977 0.398 m 325.43 0.398 l 325.43 18 l 320.977 18 l 320.977 0.398 l +h +320.977 0.398 m S Q +0.301961 0.654902 0.301961 rg +321.129 112.897 m 326.555 112.897 l 326.555 95.295 l 321.129 95.295 l +321.129 112.897 l h +321.129 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +321.129 53.199 m 326.555 53.199 l 326.555 70.801 l 321.129 70.801 l +321.129 53.199 l h +321.129 53.199 m S Q +0.301961 0.654902 0.301961 rg +322.672 95.295 m 327.84 95.295 l 327.84 77.698 l 322.672 77.698 l +322.672 95.295 l h +322.672 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +322.672 70.801 m 327.84 70.801 l 327.84 88.398 l 322.672 88.398 l +322.672 70.801 l h +322.672 70.801 m S Q +0.301961 0.654902 0.301961 rg +324.023 148.096 m 329.047 148.096 l 329.047 130.495 l 324.023 130.495 l +324.023 148.096 l h +324.023 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +324.023 18 m 329.047 18 l 329.047 35.602 l 324.023 35.602 l 324.023 18 +l h +324.023 18 m S Q +0.301961 0.654902 0.301961 rg +325.059 130.495 m 330.922 130.495 l 330.922 112.897 l 325.059 112.897 l +325.059 130.495 l h +325.059 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +325.059 35.602 m 330.922 35.602 l 330.922 53.199 l 325.059 53.199 l +325.059 35.602 l h +325.059 35.602 m S Q +0.301961 0.654902 0.301961 rg +325.434 165.698 m 331.176 165.698 l 331.176 148.096 l 325.434 148.096 l +325.434 165.698 l h +325.434 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +325.434 0.398 m 331.176 0.398 l 331.176 18 l 325.434 18 l 325.434 0.398 +l h +325.434 0.398 m S Q +0.301961 0.654902 0.301961 rg +326.559 112.897 m 332.18 112.897 l 332.18 95.295 l 326.559 95.295 l +326.559 112.897 l h +326.559 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +326.559 53.199 m 332.18 53.199 l 332.18 70.801 l 326.559 70.801 l +326.559 53.199 l h +326.559 53.199 m S Q +0.301961 0.654902 0.301961 rg +327.848 95.295 m 333.355 95.295 l 333.355 77.698 l 327.848 77.698 l +327.848 95.295 l h +327.848 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +327.848 70.801 m 333.355 70.801 l 333.355 88.398 l 327.848 88.398 l +327.848 70.801 l h +327.848 70.801 m S Q +0.301961 0.654902 0.301961 rg +329.055 148.096 m 333.527 148.096 l 333.527 130.495 l 329.055 130.495 l +329.055 148.096 l h +329.055 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +329.055 18 m 333.527 18 l 333.527 35.602 l 329.055 35.602 l 329.055 18 +l h +329.055 18 m S Q +0.301961 0.654902 0.301961 rg +330.93 130.495 m 336.262 130.495 l 336.262 112.897 l 330.93 112.897 l +330.93 130.495 l h +330.93 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +330.93 35.602 m 336.262 35.602 l 336.262 53.199 l 330.93 53.199 l +330.93 35.602 l h +330.93 35.602 m S Q +0.301961 0.654902 0.301961 rg +331.199 165.698 m 335.457 165.698 l 335.457 148.096 l 331.199 148.096 l +331.199 165.698 l h +331.199 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +331.199 0.398 m 335.457 0.398 l 335.457 18 l 331.199 18 l 331.199 0.398 +l h +331.199 0.398 m S Q +0.301961 0.654902 0.301961 rg +332.184 112.897 m 337.988 112.897 l 337.988 95.295 l 332.184 95.295 l +332.184 112.897 l h +332.184 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +332.184 53.199 m 337.988 53.199 l 337.988 70.801 l 332.184 70.801 l +332.184 53.199 l h +332.184 53.199 m S Q +0.301961 0.654902 0.301961 rg +333.359 95.295 m 338.301 95.295 l 338.301 77.698 l 333.359 77.698 l +333.359 95.295 l h +333.359 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +333.359 70.801 m 338.301 70.801 l 338.301 88.398 l 333.359 88.398 l +333.359 70.801 l h +333.359 70.801 m S Q +0.301961 0.654902 0.301961 rg +333.535 148.096 m 340.637 148.096 l 340.637 130.495 l 333.535 130.495 l +333.535 148.096 l h +333.535 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +333.535 18 m 340.637 18 l 340.637 35.602 l 333.535 35.602 l 333.535 18 +l h +333.535 18 m S Q +0.301961 0.654902 0.301961 rg +335.465 165.698 m 339.855 165.698 l 339.855 148.096 l 335.465 148.096 l +335.465 165.698 l h +335.465 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +335.465 0.398 m 339.855 0.398 l 339.855 18 l 335.465 18 l 335.465 0.398 +l h +335.465 0.398 m S Q +0.301961 0.654902 0.301961 rg +336.27 130.495 m 341.691 130.495 l 341.691 112.897 l 336.27 112.897 l +336.27 130.495 l h +336.27 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +336.27 35.602 m 341.691 35.602 l 341.691 53.199 l 336.27 53.199 l +336.27 35.602 l h +336.27 35.602 m S Q +0.301961 0.654902 0.301961 rg +337.992 112.897 m 343.98 112.897 l 343.98 95.295 l 337.992 95.295 l +337.992 112.897 l h +337.992 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +337.992 53.199 m 343.98 53.199 l 343.98 70.801 l 337.992 70.801 l +337.992 53.199 l h +337.992 53.199 m S Q +0.301961 0.654902 0.301961 rg +338.309 95.295 m 344.422 95.295 l 344.422 77.698 l 338.309 77.698 l +338.309 95.295 l h +338.309 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +338.309 70.801 m 344.422 70.801 l 344.422 88.398 l 338.309 88.398 l +338.309 70.801 l h +338.309 70.801 m S Q +0.301961 0.654902 0.301961 rg +339.859 165.698 m 343.223 165.698 l 343.223 148.096 l 339.859 148.096 l +339.859 165.698 l h +339.859 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +339.859 0.398 m 343.223 0.398 l 343.223 18 l 339.859 18 l 339.859 0.398 +l h +339.859 0.398 m S Q +0.301961 0.654902 0.301961 rg +340.641 148.096 m 346.348 148.096 l 346.348 130.495 l 340.641 130.495 l +340.641 148.096 l h +340.641 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +340.641 18 m 346.348 18 l 346.348 35.602 l 340.641 35.602 l 340.641 18 +l h +340.641 18 m S Q +0.301961 0.654902 0.301961 rg +341.695 130.495 m 346.918 130.495 l 346.918 112.897 l 341.695 112.897 l +341.695 130.495 l h +341.695 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +341.695 35.602 m 346.918 35.602 l 346.918 53.199 l 341.695 53.199 l +341.695 35.602 l h +341.695 35.602 m S Q +0.301961 0.654902 0.301961 rg +343.227 165.698 m 349.203 165.698 l 349.203 148.096 l 343.227 148.096 l +343.227 165.698 l h +343.227 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +343.227 0.398 m 349.203 0.398 l 349.203 18 l 343.227 18 l 343.227 0.398 +l h +343.227 0.398 m S Q +0.301961 0.654902 0.301961 rg +343.984 112.897 m 348.469 112.897 l 348.469 95.295 l 343.984 95.295 l +343.984 112.897 l h +343.984 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +343.984 53.199 m 348.469 53.199 l 348.469 70.801 l 343.984 70.801 l +343.984 53.199 l h +343.984 53.199 m S Q +0.301961 0.654902 0.301961 rg +344.426 95.295 m 349.871 95.295 l 349.871 77.698 l 344.426 77.698 l +344.426 95.295 l h +344.426 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +344.426 70.801 m 349.871 70.801 l 349.871 88.398 l 344.426 88.398 l +344.426 70.801 l h +344.426 70.801 m S Q +0.301961 0.654902 0.301961 rg +346.352 148.096 m 352.148 148.096 l 352.148 130.495 l 346.352 130.495 l +346.352 148.096 l h +346.352 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +346.352 18 m 352.148 18 l 352.148 35.602 l 346.352 35.602 l 346.352 18 +l h +346.352 18 m S Q +0.301961 0.654902 0.301961 rg +346.926 130.495 m 352.57 130.495 l 352.57 112.897 l 346.926 112.897 l +346.926 130.495 l h +346.926 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +346.926 35.602 m 352.57 35.602 l 352.57 53.199 l 346.926 53.199 l +346.926 35.602 l h +346.926 35.602 m S Q +0.301961 0.654902 0.301961 rg +348.477 112.897 m 353.129 112.897 l 353.129 95.295 l 348.477 95.295 l +348.477 112.897 l h +348.477 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +348.477 53.199 m 353.129 53.199 l 353.129 70.801 l 348.477 70.801 l +348.477 53.199 l h +348.477 53.199 m S Q +0.301961 0.654902 0.301961 rg +349.211 165.698 m 354.969 165.698 l 354.969 148.096 l 349.211 148.096 l +349.211 165.698 l h +349.211 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +349.211 0.398 m 354.969 0.398 l 354.969 18 l 349.211 18 l 349.211 0.398 +l h +349.211 0.398 m S Q +0.301961 0.654902 0.301961 rg +349.875 95.295 m 354.199 95.295 l 354.199 77.698 l 349.875 77.698 l +349.875 95.295 l h +349.875 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +349.875 70.801 m 354.199 70.801 l 354.199 88.398 l 349.875 88.398 l +349.875 70.801 l h +349.875 70.801 m S Q +0.301961 0.654902 0.301961 rg +352.152 148.096 m 357.5 148.096 l 357.5 130.495 l 352.152 130.495 l +352.152 148.096 l h +352.152 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +352.152 18 m 357.5 18 l 357.5 35.602 l 352.152 35.602 l 352.152 18 l h +352.152 18 m S Q +0.301961 0.654902 0.301961 rg +352.578 130.495 m 358.523 130.495 l 358.523 112.897 l 352.578 112.897 l +352.578 130.495 l h +352.578 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +352.578 35.602 m 358.523 35.602 l 358.523 53.199 l 352.578 53.199 l +352.578 35.602 l h +352.578 35.602 m S Q +0.301961 0.654902 0.301961 rg +353.137 112.897 m 357.941 112.897 l 357.941 95.295 l 353.137 95.295 l +353.137 112.897 l h +353.137 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +353.137 53.199 m 357.941 53.199 l 357.941 70.801 l 353.137 70.801 l +353.137 53.199 l h +353.137 53.199 m S Q +0.301961 0.654902 0.301961 rg +354.207 95.295 m 359.285 95.295 l 359.285 77.698 l 354.207 77.698 l +354.207 95.295 l h +354.207 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +354.207 70.801 m 359.285 70.801 l 359.285 88.398 l 354.207 88.398 l +354.207 70.801 l h +354.207 70.801 m S Q +0.301961 0.654902 0.301961 rg +354.973 165.698 m 360.238 165.698 l 360.238 148.096 l 354.973 148.096 l +354.973 165.698 l h +354.973 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +354.973 0.398 m 360.238 0.398 l 360.238 18 l 354.973 18 l 354.973 0.398 +l h +354.973 0.398 m S Q +0.301961 0.654902 0.301961 rg +357.504 148.096 m 361.523 148.096 l 361.523 130.495 l 357.504 130.495 l +357.504 148.096 l h +357.504 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +357.504 18 m 361.523 18 l 361.523 35.602 l 357.504 35.602 l 357.504 18 +l h +357.504 18 m S Q +0.301961 0.654902 0.301961 rg +357.949 112.897 m 363.43 112.897 l 363.43 95.295 l 357.949 95.295 l +357.949 112.897 l h +357.949 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +357.949 53.199 m 363.43 53.199 l 363.43 70.801 l 357.949 70.801 l +357.949 53.199 l h +357.949 53.199 m S Q +0.301961 0.654902 0.301961 rg +358.527 130.495 m 364.281 130.495 l 364.281 112.897 l 358.527 112.897 l +358.527 130.495 l h +358.527 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +358.527 35.602 m 364.281 35.602 l 364.281 53.199 l 358.527 53.199 l +358.527 35.602 l h +358.527 35.602 m S Q +0.301961 0.654902 0.301961 rg +359.289 95.295 m 364.566 95.295 l 364.566 77.698 l 359.289 77.698 l +359.289 95.295 l h +359.289 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +359.289 70.801 m 364.566 70.801 l 364.566 88.398 l 359.289 88.398 l +359.289 70.801 l h +359.289 70.801 m S Q +0.301961 0.654902 0.301961 rg +360.242 165.698 m 365.504 165.698 l 365.504 148.096 l 360.242 148.096 l +360.242 165.698 l h +360.242 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +360.242 0.398 m 365.504 0.398 l 365.504 18 l 360.242 18 l 360.242 0.398 +l h +360.242 0.398 m S Q +0.301961 0.654902 0.301961 rg +361.527 148.096 m 367.316 148.096 l 367.316 130.495 l 361.527 130.495 l +361.527 148.096 l h +361.527 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +361.527 18 m 367.316 18 l 367.316 35.602 l 361.527 35.602 l 361.527 18 +l h +361.527 18 m S Q +0.301961 0.654902 0.301961 rg +363.434 112.897 m 369.348 112.897 l 369.348 95.295 l 363.434 95.295 l +363.434 112.897 l h +363.434 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +363.434 53.199 m 369.348 53.199 l 369.348 70.801 l 363.434 70.801 l +363.434 53.199 l h +363.434 53.199 m S Q +0.301961 0.654902 0.301961 rg +364.289 130.495 m 369.926 130.495 l 369.926 112.897 l 364.289 112.897 l +364.289 130.495 l h +364.289 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +364.289 35.602 m 369.926 35.602 l 369.926 53.199 l 364.289 53.199 l +364.289 35.602 l h +364.289 35.602 m S Q +0.301961 0.654902 0.301961 rg +364.582 95.295 m 370.09 95.295 l 370.09 77.698 l 364.582 77.698 l +364.582 95.295 l h +364.582 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +364.582 70.801 m 370.09 70.801 l 370.09 88.398 l 364.582 88.398 l +364.582 70.801 l h +364.582 70.801 m S Q +0.301961 0.654902 0.301961 rg +365.508 165.698 m 370.93 165.698 l 370.93 148.096 l 365.508 148.096 l +365.508 165.698 l h +365.508 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +365.508 0.398 m 370.93 0.398 l 370.93 18 l 365.508 18 l 365.508 0.398 l +h +365.508 0.398 m S Q +0.301961 0.654902 0.301961 rg +367.32 148.096 m 372.34 148.096 l 372.34 130.495 l 367.32 130.495 l +367.32 148.096 l h +367.32 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +367.32 18 m 372.34 18 l 372.34 35.602 l 367.32 35.602 l 367.32 18 l h +367.32 18 m S Q +0.301961 0.654902 0.301961 rg +369.352 112.897 m 374.969 112.897 l 374.969 95.295 l 369.352 95.295 l +369.352 112.897 l h +369.352 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +369.352 53.199 m 374.969 53.199 l 374.969 70.801 l 369.352 70.801 l +369.352 53.199 l h +369.352 53.199 m S Q +0.301961 0.654902 0.301961 rg +369.938 130.495 m 374.996 130.495 l 374.996 112.897 l 369.938 112.897 l +369.938 130.495 l h +369.938 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +369.938 35.602 m 374.996 35.602 l 374.996 53.199 l 369.938 53.199 l +369.938 35.602 l h +369.938 35.602 m S Q +0.301961 0.654902 0.301961 rg +370.105 95.295 m 374.051 95.295 l 374.051 77.698 l 370.105 77.698 l +370.105 95.295 l h +370.105 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +370.105 70.801 m 374.051 70.801 l 374.051 88.398 l 370.105 88.398 l +370.105 70.801 l h +370.105 70.801 m S Q +0.301961 0.654902 0.301961 rg +370.938 165.698 m 375.922 165.698 l 375.922 148.096 l 370.938 148.096 l +370.938 165.698 l h +370.938 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +370.938 0.398 m 375.922 0.398 l 375.922 18 l 370.938 18 l 370.938 0.398 +l h +370.938 0.398 m S Q +0.301961 0.654902 0.301961 rg +372.348 148.096 m 376.801 148.096 l 376.801 130.495 l 372.348 130.495 l +372.348 148.096 l h +372.348 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +372.348 18 m 376.801 18 l 376.801 35.602 l 372.348 35.602 l 372.348 18 +l h +372.348 18 m S Q +0.301961 0.654902 0.301961 rg +374.055 95.295 m 379.402 95.295 l 379.402 77.698 l 374.055 77.698 l +374.055 95.295 l h +374.055 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +374.055 70.801 m 379.402 70.801 l 379.402 88.398 l 374.055 88.398 l +374.055 70.801 l h +374.055 70.801 m S Q +0.301961 0.654902 0.301961 rg +374.973 112.897 m 381.484 112.897 l 381.484 95.295 l 374.973 95.295 l +374.973 112.897 l h +374.973 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +374.973 53.199 m 381.484 53.199 l 381.484 70.801 l 374.973 70.801 l +374.973 53.199 l h +374.973 53.199 m S Q +0.301961 0.654902 0.301961 rg +375 130.495 m 381.34 130.495 l 381.34 112.897 l 375 112.897 l 375 +130.495 l h +375 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +375 35.602 m 381.34 35.602 l 381.34 53.199 l 375 53.199 l 375 35.602 l +h +375 35.602 m S Q +0.301961 0.654902 0.301961 rg +375.926 165.698 m 382.609 165.698 l 382.609 148.096 l 375.926 148.096 l +375.926 165.698 l h +375.926 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +375.926 0.398 m 382.609 0.398 l 382.609 18 l 375.926 18 l 375.926 0.398 +l h +375.926 0.398 m S Q +0.301961 0.654902 0.301961 rg +376.805 148.096 m 380.543 148.096 l 380.543 130.495 l 376.805 130.495 l +376.805 148.096 l h +376.805 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +376.805 18 m 380.543 18 l 380.543 35.602 l 376.805 35.602 l 376.805 18 +l h +376.805 18 m S Q +0.301961 0.654902 0.301961 rg +379.406 95.295 m 383.664 95.295 l 383.664 77.698 l 379.406 77.698 l +379.406 95.295 l h +379.406 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +379.406 70.801 m 383.664 70.801 l 383.664 88.398 l 379.406 88.398 l +379.406 70.801 l h +379.406 70.801 m S Q +0.301961 0.654902 0.301961 rg +380.551 148.096 m 384.777 148.096 l 384.777 130.495 l 380.551 130.495 l +380.551 148.096 l h +380.551 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +380.551 18 m 384.777 18 l 384.777 35.602 l 380.551 35.602 l 380.551 18 +l h +380.551 18 m S Q +0.301961 0.654902 0.301961 rg +381.344 130.495 m 387.941 130.495 l 387.941 112.897 l 381.344 112.897 l +381.344 130.495 l h +381.344 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +381.344 35.602 m 387.941 35.602 l 387.941 53.199 l 381.344 53.199 l +381.344 35.602 l h +381.344 35.602 m S Q +0.301961 0.654902 0.301961 rg +381.5 112.897 m 387.762 112.897 l 387.762 95.295 l 381.5 95.295 l 381.5 +112.897 l h +381.5 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +381.5 53.199 m 387.762 53.199 l 387.762 70.801 l 381.5 70.801 l 381.5 +53.199 l h +381.5 53.199 m S Q +0.301961 0.654902 0.301961 rg +382.617 165.698 m 386.82 165.698 l 386.82 148.096 l 382.617 148.096 l +382.617 165.698 l h +382.617 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +382.617 0.398 m 386.82 0.398 l 386.82 18 l 382.617 18 l 382.617 0.398 l +h +382.617 0.398 m S Q +0.301961 0.654902 0.301961 rg +383.668 95.295 m 389.879 95.295 l 389.879 77.698 l 383.668 77.698 l +383.668 95.295 l h +383.668 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +383.668 70.801 m 389.879 70.801 l 389.879 88.398 l 383.668 88.398 l +383.668 70.801 l h +383.668 70.801 m S Q +0.301961 0.654902 0.301961 rg +384.781 148.096 m 389.156 148.096 l 389.156 130.495 l 384.781 130.495 l +384.781 148.096 l h +384.781 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +384.781 18 m 389.156 18 l 389.156 35.602 l 384.781 35.602 l 384.781 18 +l h +384.781 18 m S Q +0.301961 0.654902 0.301961 rg +386.824 165.698 m 391.402 165.698 l 391.402 148.096 l 386.824 148.096 l +386.824 165.698 l h +386.824 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +386.824 0.398 m 391.402 0.398 l 391.402 18 l 386.824 18 l 386.824 0.398 +l h +386.824 0.398 m S Q +0.301961 0.654902 0.301961 rg +387.77 112.897 m 392.789 112.897 l 392.789 95.295 l 387.77 95.295 l +387.77 112.897 l h +387.77 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +387.77 53.199 m 392.789 53.199 l 392.789 70.801 l 387.77 70.801 l +387.77 53.199 l h +387.77 53.199 m S Q +0.301961 0.654902 0.301961 rg +387.953 130.495 m 392.648 130.495 l 392.648 112.897 l 387.953 112.897 l +387.953 130.495 l h +387.953 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +387.953 35.602 m 392.648 35.602 l 392.648 53.199 l 387.953 53.199 l +387.953 35.602 l h +387.953 35.602 m S Q +0.301961 0.654902 0.301961 rg +389.16 148.096 m 396.477 148.096 l 396.477 130.495 l 389.16 130.495 l +389.16 148.096 l h +389.16 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +389.16 18 m 396.477 18 l 396.477 35.602 l 389.16 35.602 l 389.16 18 l h +389.16 18 m S Q +0.301961 0.654902 0.301961 rg +389.883 95.295 m 395.508 95.295 l 395.508 77.698 l 389.883 77.698 l +389.883 95.295 l h +389.883 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +389.883 70.801 m 395.508 70.801 l 395.508 88.398 l 389.883 88.398 l +389.883 70.801 l h +389.883 70.801 m S Q +0.301961 0.654902 0.301961 rg +391.41 165.698 m 396.699 165.698 l 396.699 148.096 l 391.41 148.096 l +391.41 165.698 l h +391.41 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +391.41 0.398 m 396.699 0.398 l 396.699 18 l 391.41 18 l 391.41 0.398 l +h +391.41 0.398 m S Q +0.301961 0.654902 0.301961 rg +392.656 130.495 m 398.441 130.495 l 398.441 112.897 l 392.656 112.897 l +392.656 130.495 l h +392.656 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +392.656 35.602 m 398.441 35.602 l 398.441 53.199 l 392.656 53.199 l +392.656 35.602 l h +392.656 35.602 m S Q +0.301961 0.654902 0.301961 rg +392.809 112.897 m 396.27 112.897 l 396.27 95.295 l 392.809 95.295 l +392.809 112.897 l h +392.809 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +392.809 53.199 m 396.27 53.199 l 396.27 70.801 l 392.809 70.801 l +392.809 53.199 l h +392.809 53.199 m S Q +0.301961 0.654902 0.301961 rg +395.512 95.295 m 399.785 95.295 l 399.785 77.698 l 395.512 77.698 l +395.512 95.295 l h +395.512 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +395.512 70.801 m 399.785 70.801 l 399.785 88.398 l 395.512 88.398 l +395.512 70.801 l h +395.512 70.801 m S Q +0.301961 0.654902 0.301961 rg +396.273 112.897 m 401.012 112.897 l 401.012 95.295 l 396.273 95.295 l +396.273 112.897 l h +396.273 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +396.273 53.199 m 401.012 53.199 l 401.012 70.801 l 396.273 70.801 l +396.273 53.199 l h +396.273 53.199 m S Q +0.301961 0.654902 0.301961 rg +396.605 148.096 m 402.613 148.096 l 402.613 130.495 l 396.605 130.495 l +396.605 148.096 l h +396.605 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +396.605 18 m 402.613 18 l 402.613 35.602 l 396.605 35.602 l 396.605 18 +l h +396.605 18 m S Q +0.301961 0.654902 0.301961 rg +396.703 165.698 m 402.613 165.698 l 402.613 148.096 l 396.703 148.096 l +396.703 165.698 l h +396.703 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +396.703 0.398 m 402.613 0.398 l 402.613 18 l 396.703 18 l 396.703 0.398 +l h +396.703 0.398 m S Q +0.301961 0.654902 0.301961 rg +398.445 130.495 m 403.062 130.495 l 403.062 112.897 l 398.445 112.897 l +398.445 130.495 l h +398.445 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +398.445 35.602 m 403.062 35.602 l 403.062 53.199 l 398.445 53.199 l +398.445 35.602 l h +398.445 35.602 m S Q +0.301961 0.654902 0.301961 rg +399.793 95.295 m 405.102 95.295 l 405.102 77.698 l 399.793 77.698 l +399.793 95.295 l h +399.793 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +399.793 70.801 m 405.102 70.801 l 405.102 88.398 l 399.793 88.398 l +399.793 70.801 l h +399.793 70.801 m S Q +0.301961 0.654902 0.301961 rg +401.02 112.897 m 406.348 112.897 l 406.348 95.295 l 401.02 95.295 l +401.02 112.897 l h +401.02 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +401.02 53.199 m 406.348 53.199 l 406.348 70.801 l 401.02 70.801 l +401.02 53.199 l h +401.02 53.199 m S Q +0.301961 0.654902 0.301961 rg +402.621 165.698 m 409.629 165.698 l 409.629 148.096 l 402.621 148.096 l +402.621 165.698 l h +402.621 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +402.621 0.398 m 409.629 0.398 l 409.629 18 l 402.621 18 l 402.621 0.398 +l h +402.621 0.398 m S Q +0.301961 0.654902 0.301961 rg +402.715 148.096 m 407.617 148.096 l 407.617 130.495 l 402.715 130.495 l +402.715 148.096 l h +402.715 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +402.715 18 m 407.617 18 l 407.617 35.602 l 402.715 35.602 l 402.715 18 +l h +402.715 18 m S Q +0.301961 0.654902 0.301961 rg +403.07 130.495 m 409.461 130.495 l 409.461 112.897 l 403.07 112.897 l +403.07 130.495 l h +403.07 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +403.07 35.602 m 409.461 35.602 l 409.461 53.199 l 403.07 53.199 l +403.07 35.602 l h +403.07 35.602 m S Q +0.301961 0.654902 0.301961 rg +405.105 95.295 m 410.918 95.295 l 410.918 77.698 l 405.105 77.698 l +405.105 95.295 l h +405.105 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +405.105 70.801 m 410.918 70.801 l 410.918 88.398 l 405.105 88.398 l +405.105 70.801 l h +405.105 70.801 m S Q +0.301961 0.654902 0.301961 rg +406.352 112.897 m 412.539 112.897 l 412.539 95.295 l 406.352 95.295 l +406.352 112.897 l h +406.352 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +406.352 53.199 m 412.539 53.199 l 412.539 70.801 l 406.352 70.801 l +406.352 53.199 l h +406.352 53.199 m S Q +0.301961 0.654902 0.301961 rg +407.621 148.096 m 415.152 148.096 l 415.152 130.495 l 407.621 130.495 l +407.621 148.096 l h +407.621 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +407.621 18 m 415.152 18 l 415.152 35.602 l 407.621 35.602 l 407.621 18 +l h +407.621 18 m S Q +0.301961 0.654902 0.301961 rg +409.469 130.495 m 415.945 130.495 l 415.945 112.897 l 409.469 112.897 l +409.469 130.495 l h +409.469 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +409.469 35.602 m 415.945 35.602 l 415.945 53.199 l 409.469 53.199 l +409.469 35.602 l h +409.469 35.602 m S Q +0.301961 0.654902 0.301961 rg +409.648 165.698 m 416.27 165.698 l 416.27 148.096 l 409.648 148.096 l +409.648 165.698 l h +409.648 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +409.648 0.398 m 416.27 0.398 l 416.27 18 l 409.648 18 l 409.648 0.398 l +h +409.648 0.398 m S Q +0.301961 0.654902 0.301961 rg +410.926 95.295 m 416.762 95.295 l 416.762 77.698 l 410.926 77.698 l +410.926 95.295 l h +410.926 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +410.926 70.801 m 416.762 70.801 l 416.762 88.398 l 410.926 88.398 l +410.926 70.801 l h +410.926 70.801 m S Q +0.301961 0.654902 0.301961 rg +412.543 112.897 m 417.902 112.897 l 417.902 95.295 l 412.543 95.295 l +412.543 112.897 l h +412.543 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +412.543 53.199 m 417.902 53.199 l 417.902 70.801 l 412.543 70.801 l +412.543 53.199 l h +412.543 53.199 m S Q +0.301961 0.654902 0.301961 rg +415.156 148.096 m 421.727 148.096 l 421.727 130.495 l 415.156 130.495 l +415.156 148.096 l h +415.156 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +415.156 18 m 421.727 18 l 421.727 35.602 l 415.156 35.602 l 415.156 18 +l h +415.156 18 m S Q +0.301961 0.654902 0.301961 rg +415.953 130.495 m 421.102 130.495 l 421.102 112.897 l 415.953 112.897 l +415.953 130.495 l h +415.953 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +415.953 35.602 m 421.102 35.602 l 421.102 53.199 l 415.953 53.199 l +415.953 35.602 l h +415.953 35.602 m S Q +0.301961 0.654902 0.301961 rg +416.289 165.698 m 421.883 165.698 l 421.883 148.096 l 416.289 148.096 l +416.289 165.698 l h +416.289 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +416.289 0.398 m 421.883 0.398 l 421.883 18 l 416.289 18 l 416.289 0.398 +l h +416.289 0.398 m S Q +0.301961 0.654902 0.301961 rg +416.777 95.295 m 422.227 95.295 l 422.227 77.698 l 416.777 77.698 l +416.777 95.295 l h +416.777 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +416.777 70.801 m 422.227 70.801 l 422.227 88.398 l 416.777 88.398 l +416.777 70.801 l h +416.777 70.801 m S Q +0.301961 0.654902 0.301961 rg +417.906 112.897 m 423.371 112.897 l 423.371 95.295 l 417.906 95.295 l +417.906 112.897 l h +417.906 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +417.906 53.199 m 423.371 53.199 l 423.371 70.801 l 417.906 70.801 l +417.906 53.199 l h +417.906 53.199 m S Q +0.301961 0.654902 0.301961 rg +421.109 130.495 m 426.191 130.495 l 426.191 112.897 l 421.109 112.897 l +421.109 130.495 l h +421.109 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +421.109 35.602 m 426.191 35.602 l 426.191 53.199 l 421.109 53.199 l +421.109 35.602 l h +421.109 35.602 m S Q +0.301961 0.654902 0.301961 rg +421.734 148.096 m 426.789 148.096 l 426.789 130.495 l 421.734 130.495 l +421.734 148.096 l h +421.734 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +421.734 18 m 426.789 18 l 426.789 35.602 l 421.734 35.602 l 421.734 18 +l h +421.734 18 m S Q +0.301961 0.654902 0.301961 rg +421.898 165.698 m 427.605 165.698 l 427.605 148.096 l 421.898 148.096 l +421.898 165.698 l h +421.898 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +421.898 0.398 m 427.605 0.398 l 427.605 18 l 421.898 18 l 421.898 0.398 +l h +421.898 0.398 m S Q +0.301961 0.654902 0.301961 rg +422.234 95.295 m 427.234 95.295 l 427.234 77.698 l 422.234 77.698 l +422.234 95.295 l h +422.234 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +422.234 70.801 m 427.234 70.801 l 427.234 88.398 l 422.234 88.398 l +422.234 70.801 l h +422.234 70.801 m S Q +0.301961 0.654902 0.301961 rg +423.379 112.897 m 428.297 112.897 l 428.297 95.295 l 423.379 95.295 l +423.379 112.897 l h +423.379 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +423.379 53.199 m 428.297 53.199 l 428.297 70.801 l 423.379 70.801 l +423.379 53.199 l h +423.379 53.199 m S Q +0.301961 0.654902 0.301961 rg +426.195 130.495 m 432.477 130.495 l 432.477 112.897 l 426.195 112.897 l +426.195 130.495 l h +426.195 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +426.195 35.602 m 432.477 35.602 l 432.477 53.199 l 426.195 53.199 l +426.195 35.602 l h +426.195 35.602 m S Q +0.301961 0.654902 0.301961 rg +426.793 148.096 m 431.66 148.096 l 431.66 130.495 l 426.793 130.495 l +426.793 148.096 l h +426.793 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +426.793 18 m 431.66 18 l 431.66 35.602 l 426.793 35.602 l 426.793 18 l +h +426.793 18 m S Q +0.301961 0.654902 0.301961 rg +427.242 95.295 m 431.309 95.295 l 431.309 77.698 l 427.242 77.698 l +427.242 95.295 l h +427.242 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +427.242 70.801 m 431.309 70.801 l 431.309 88.398 l 427.242 88.398 l +427.242 70.801 l h +427.242 70.801 m S Q +0.301961 0.654902 0.301961 rg +427.613 165.698 m 432.746 165.698 l 432.746 148.096 l 427.613 148.096 l +427.613 165.698 l h +427.613 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +427.613 0.398 m 432.746 0.398 l 432.746 18 l 427.613 18 l 427.613 0.398 +l h +427.613 0.398 m S Q +0.301961 0.654902 0.301961 rg +428.301 112.897 m 433.621 112.897 l 433.621 95.295 l 428.301 95.295 l +428.301 112.897 l h +428.301 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +428.301 53.199 m 433.621 53.199 l 433.621 70.801 l 428.301 70.801 l +428.301 53.199 l h +428.301 53.199 m S Q +0.301961 0.654902 0.301961 rg +431.312 95.295 m 437.934 95.295 l 437.934 77.698 l 431.312 77.698 l +431.312 95.295 l h +431.312 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +431.312 70.801 m 437.934 70.801 l 437.934 88.398 l 431.312 88.398 l +431.312 70.801 l h +431.312 70.801 m S Q +0.301961 0.654902 0.301961 rg +431.664 148.096 m 436.934 148.096 l 436.934 130.495 l 431.664 130.495 l +431.664 148.096 l h +431.664 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +431.664 18 m 436.934 18 l 436.934 35.602 l 431.664 35.602 l 431.664 18 +l h +431.664 18 m S Q +0.301961 0.654902 0.301961 rg +432.48 130.495 m 438.719 130.495 l 438.719 112.897 l 432.48 112.897 l +432.48 130.495 l h +432.48 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +432.48 35.602 m 438.719 35.602 l 438.719 53.199 l 432.48 53.199 l +432.48 35.602 l h +432.48 35.602 m S Q +0.301961 0.654902 0.301961 rg +432.754 165.698 m 437.305 165.698 l 437.305 148.096 l 432.754 148.096 l +432.754 165.698 l h +432.754 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +432.754 0.398 m 437.305 0.398 l 437.305 18 l 432.754 18 l 432.754 0.398 +l h +432.754 0.398 m S Q +0.301961 0.654902 0.301961 rg +433.625 112.897 m 438.348 112.897 l 438.348 95.295 l 433.625 95.295 l +433.625 112.897 l h +433.625 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +433.625 53.199 m 438.348 53.199 l 438.348 70.801 l 433.625 70.801 l +433.625 53.199 l h +433.625 53.199 m S Q +0.301961 0.654902 0.301961 rg +436.938 148.096 m 440.961 148.096 l 440.961 130.495 l 436.938 130.495 l +436.938 148.096 l h +436.938 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +436.938 18 m 440.961 18 l 440.961 35.602 l 436.938 35.602 l 436.938 18 +l h +436.938 18 m S Q +0.301961 0.654902 0.301961 rg +437.312 165.698 m 441.641 165.698 l 441.641 148.096 l 437.312 148.096 l +437.312 165.698 l h +437.312 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +437.312 0.398 m 441.641 0.398 l 441.641 18 l 437.312 18 l 437.312 0.398 +l h +437.312 0.398 m S Q +0.301961 0.654902 0.301961 rg +437.938 95.295 m 443.746 95.295 l 443.746 77.698 l 437.938 77.698 l +437.938 95.295 l h +437.938 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +437.938 70.801 m 443.746 70.801 l 443.746 88.398 l 437.938 88.398 l +437.938 70.801 l h +437.938 70.801 m S Q +0.301961 0.654902 0.301961 rg +438.355 112.897 m 443.367 112.897 l 443.367 95.295 l 438.355 95.295 l +438.355 112.897 l h +438.355 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +438.355 53.199 m 443.367 53.199 l 443.367 70.801 l 438.355 70.801 l +438.355 53.199 l h +438.355 53.199 m S Q +0.301961 0.654902 0.301961 rg +438.723 130.495 m 444.152 130.495 l 444.152 112.897 l 438.723 112.897 l +438.723 130.495 l h +438.723 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +438.723 35.602 m 444.152 35.602 l 444.152 53.199 l 438.723 53.199 l +438.723 35.602 l h +438.723 35.602 m S Q +0.301961 0.654902 0.301961 rg +440.965 148.096 m 446.418 148.096 l 446.418 130.495 l 440.965 130.495 l +440.965 148.096 l h +440.965 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +440.965 18 m 446.418 18 l 446.418 35.602 l 440.965 35.602 l 440.965 18 +l h +440.965 18 m S Q +0.301961 0.654902 0.301961 rg +441.648 165.698 m 446.695 165.698 l 446.695 148.096 l 441.648 148.096 l +441.648 165.698 l h +441.648 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +441.648 0.398 m 446.695 0.398 l 446.695 18 l 441.648 18 l 441.648 0.398 +l h +441.648 0.398 m S Q +0.301961 0.654902 0.301961 rg +443.375 112.897 m 449.516 112.897 l 449.516 95.295 l 443.375 95.295 l +443.375 112.897 l h +443.375 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +443.375 53.199 m 449.516 53.199 l 449.516 70.801 l 443.375 70.801 l +443.375 53.199 l h +443.375 53.199 m S Q +0.301961 0.654902 0.301961 rg +443.77 95.295 m 447.605 95.295 l 447.605 77.698 l 443.77 77.698 l +443.77 95.295 l h +443.77 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +443.77 70.801 m 447.605 70.801 l 447.605 88.398 l 443.77 88.398 l +443.77 70.801 l h +443.77 70.801 m S Q +0.301961 0.654902 0.301961 rg +444.195 130.495 m 450.383 130.495 l 450.383 112.897 l 444.195 112.897 l +444.195 130.495 l h +444.195 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +444.195 35.602 m 450.383 35.602 l 450.383 53.199 l 444.195 53.199 l +444.195 35.602 l h +444.195 35.602 m S Q +0.301961 0.654902 0.301961 rg +446.422 148.096 m 450.418 148.096 l 450.418 130.495 l 446.422 130.495 l +446.422 148.096 l h +446.422 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +446.422 18 m 450.418 18 l 450.418 35.602 l 446.422 35.602 l 446.422 18 +l h +446.422 18 m S Q +0.301961 0.654902 0.301961 rg +446.703 165.698 m 451.699 165.698 l 451.699 148.096 l 446.703 148.096 l +446.703 165.698 l h +446.703 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +446.703 0.398 m 451.699 0.398 l 451.699 18 l 446.703 18 l 446.703 0.398 +l h +446.703 0.398 m S Q +0.301961 0.654902 0.301961 rg +447.609 95.295 m 453.434 95.295 l 453.434 77.698 l 447.609 77.698 l +447.609 95.295 l h +447.609 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +447.609 70.801 m 453.434 70.801 l 453.434 88.398 l 447.609 88.398 l +447.609 70.801 l h +447.609 70.801 m S Q +0.301961 0.654902 0.301961 rg +449.52 112.897 m 454.148 112.897 l 454.148 95.295 l 449.52 95.295 l +449.52 112.897 l h +449.52 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +449.52 53.199 m 454.148 53.199 l 454.148 70.801 l 449.52 70.801 l +449.52 53.199 l h +449.52 53.199 m S Q +0.301961 0.654902 0.301961 rg +450.391 130.495 m 456.773 130.495 l 456.773 112.897 l 450.391 112.897 l +450.391 130.495 l h +450.391 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +450.391 35.602 m 456.773 35.602 l 456.773 53.199 l 450.391 53.199 l +450.391 35.602 l h +450.391 35.602 m S Q +0.301961 0.654902 0.301961 rg +450.652 148.096 m 455.562 148.096 l 455.562 130.495 l 450.652 130.495 l +450.652 148.096 l h +450.652 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +450.652 18 m 455.562 18 l 455.562 35.602 l 450.652 35.602 l 450.652 18 +l h +450.652 18 m S Q +0.301961 0.654902 0.301961 rg +451.703 165.698 m 457.469 165.698 l 457.469 148.096 l 451.703 148.096 l +451.703 165.698 l h +451.703 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +451.703 0.398 m 457.469 0.398 l 457.469 18 l 451.703 18 l 451.703 0.398 +l h +451.703 0.398 m S Q +0.301961 0.654902 0.301961 rg +453.438 95.295 m 458.297 95.295 l 458.297 77.698 l 453.438 77.698 l +453.438 95.295 l h +453.438 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +453.438 70.801 m 458.297 70.801 l 458.297 88.398 l 453.438 88.398 l +453.438 70.801 l h +453.438 70.801 m S Q +0.301961 0.654902 0.301961 rg +454.152 112.897 m 459.422 112.897 l 459.422 95.295 l 454.152 95.295 l +454.152 112.897 l h +454.152 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +454.152 53.199 m 459.422 53.199 l 459.422 70.801 l 454.152 70.801 l +454.152 53.199 l h +454.152 53.199 m S Q +0.301961 0.654902 0.301961 rg +455.566 148.096 m 461.734 148.096 l 461.734 130.495 l 455.566 130.495 l +455.566 148.096 l h +455.566 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +455.566 18 m 461.734 18 l 461.734 35.602 l 455.566 35.602 l 455.566 18 +l h +455.566 18 m S Q +0.301961 0.654902 0.301961 rg +456.781 130.495 m 461.309 130.495 l 461.309 112.897 l 456.781 112.897 l +456.781 130.495 l h +456.781 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +456.781 35.602 m 461.309 35.602 l 461.309 53.199 l 456.781 53.199 l +456.781 35.602 l h +456.781 35.602 m S Q +0.301961 0.654902 0.301961 rg +457.473 165.698 m 462.91 165.698 l 462.91 148.096 l 457.473 148.096 l +457.473 165.698 l h +457.473 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +457.473 0.398 m 462.91 0.398 l 462.91 18 l 457.473 18 l 457.473 0.398 l +h +457.473 0.398 m S Q +0.301961 0.654902 0.301961 rg +458.301 95.295 m 464.242 95.295 l 464.242 77.698 l 458.301 77.698 l +458.301 95.295 l h +458.301 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +458.301 70.801 m 464.242 70.801 l 464.242 88.398 l 458.301 88.398 l +458.301 70.801 l h +458.301 70.801 m S Q +0.301961 0.654902 0.301961 rg +459.426 112.897 m 463.914 112.897 l 463.914 95.295 l 459.426 95.295 l +459.426 112.897 l h +459.426 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +459.426 53.199 m 463.914 53.199 l 463.914 70.801 l 459.426 70.801 l +459.426 53.199 l h +459.426 53.199 m S Q +0.301961 0.654902 0.301961 rg +461.316 130.495 m 466.895 130.495 l 466.895 112.897 l 461.316 112.897 l +461.316 130.495 l h +461.316 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +461.316 35.602 m 466.895 35.602 l 466.895 53.199 l 461.316 53.199 l +461.316 35.602 l h +461.316 35.602 m S Q +0.301961 0.654902 0.301961 rg +461.742 148.096 m 466 148.096 l 466 130.495 l 461.742 130.495 l 461.742 +148.096 l h +461.742 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +461.742 18 m 466 18 l 466 35.602 l 461.742 35.602 l 461.742 18 l h +461.742 18 m S Q +0.301961 0.654902 0.301961 rg +462.918 165.698 m 470.66 165.698 l 470.66 148.096 l 462.918 148.096 l +462.918 165.698 l h +462.918 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +462.918 0.398 m 470.66 0.398 l 470.66 18 l 462.918 18 l 462.918 0.398 l +h +462.918 0.398 m S Q +0.301961 0.654902 0.301961 rg +463.918 112.897 m 468.359 112.897 l 468.359 95.295 l 463.918 95.295 l +463.918 112.897 l h +463.918 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +463.918 53.199 m 468.359 53.199 l 468.359 70.801 l 463.918 70.801 l +463.918 53.199 l h +463.918 53.199 m S Q +0.301961 0.654902 0.301961 rg +464.25 95.295 m 469.75 95.295 l 469.75 77.698 l 464.25 77.698 l 464.25 +95.295 l h +464.25 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +464.25 70.801 m 469.75 70.801 l 469.75 88.398 l 464.25 88.398 l 464.25 +70.801 l h +464.25 70.801 m S Q +0.301961 0.654902 0.301961 rg +466.004 148.096 m 470.199 148.096 l 470.199 130.495 l 466.004 130.495 l +466.004 148.096 l h +466.004 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +466.004 18 m 470.199 18 l 470.199 35.602 l 466.004 35.602 l 466.004 18 +l h +466.004 18 m S Q +0.301961 0.654902 0.301961 rg +466.898 130.495 m 471.715 130.495 l 471.715 112.897 l 466.898 112.897 l +466.898 130.495 l h +466.898 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +466.898 35.602 m 471.715 35.602 l 471.715 53.199 l 466.898 53.199 l +466.898 35.602 l h +466.898 35.602 m S Q +0.301961 0.654902 0.301961 rg +468.367 112.897 m 474.105 112.897 l 474.105 95.295 l 468.367 95.295 l +468.367 112.897 l h +468.367 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +468.367 53.199 m 474.105 53.199 l 474.105 70.801 l 468.367 70.801 l +468.367 53.199 l h +468.367 53.199 m S Q +0.301961 0.654902 0.301961 rg +469.758 95.295 m 474.609 95.295 l 474.609 77.698 l 469.758 77.698 l +469.758 95.295 l h +469.758 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +469.758 70.801 m 474.609 70.801 l 474.609 88.398 l 469.758 88.398 l +469.758 70.801 l h +469.758 70.801 m S Q +0.301961 0.654902 0.301961 rg +470.207 148.096 m 475.824 148.096 l 475.824 130.495 l 470.207 130.495 l +470.207 148.096 l h +470.207 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +470.207 18 m 475.824 18 l 475.824 35.602 l 470.207 35.602 l 470.207 18 +l h +470.207 18 m S Q +0.301961 0.654902 0.301961 rg +470.664 165.698 m 475.707 165.698 l 475.707 148.096 l 470.664 148.096 l +470.664 165.698 l h +470.664 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +470.664 0.398 m 475.707 0.398 l 475.707 18 l 470.664 18 l 470.664 0.398 +l h +470.664 0.398 m S Q +0.301961 0.654902 0.301961 rg +471.719 130.495 m 477.945 130.495 l 477.945 112.897 l 471.719 112.897 l +471.719 130.495 l h +471.719 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +471.719 35.602 m 477.945 35.602 l 477.945 53.199 l 471.719 53.199 l +471.719 35.602 l h +471.719 35.602 m S Q +0.301961 0.654902 0.301961 rg +474.113 112.897 m 478.902 112.897 l 478.902 95.295 l 474.113 95.295 l +474.113 112.897 l h +474.113 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +474.113 53.199 m 478.902 53.199 l 478.902 70.801 l 474.113 70.801 l +474.113 53.199 l h +474.113 53.199 m S Q +0.301961 0.654902 0.301961 rg +474.617 95.295 m 479.512 95.295 l 479.512 77.698 l 474.617 77.698 l +474.617 95.295 l h +474.617 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +474.617 70.801 m 479.512 70.801 l 479.512 88.398 l 474.617 88.398 l +474.617 70.801 l h +474.617 70.801 m S Q +0.301961 0.654902 0.301961 rg +475.711 165.698 m 482.023 165.698 l 482.023 148.096 l 475.711 148.096 l +475.711 165.698 l h +475.711 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +475.711 0.398 m 482.023 0.398 l 482.023 18 l 475.711 18 l 475.711 0.398 +l h +475.711 0.398 m S Q +0.301961 0.654902 0.301961 rg +475.828 148.096 m 480.551 148.096 l 480.551 130.495 l 475.828 130.495 l +475.828 148.096 l h +475.828 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +475.828 18 m 480.551 18 l 480.551 35.602 l 475.828 35.602 l 475.828 18 +l h +475.828 18 m S Q +0.301961 0.654902 0.301961 rg +477.949 130.495 m 482.918 130.495 l 482.918 112.897 l 477.949 112.897 l +477.949 130.495 l h +477.949 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +477.949 35.602 m 482.918 35.602 l 482.918 53.199 l 477.949 53.199 l +477.949 35.602 l h +477.949 35.602 m S Q +0.301961 0.654902 0.301961 rg +478.906 112.897 m 484.98 112.897 l 484.98 95.295 l 478.906 95.295 l +478.906 112.897 l h +478.906 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +478.906 53.199 m 484.98 53.199 l 484.98 70.801 l 478.906 70.801 l +478.906 53.199 l h +478.906 53.199 m S Q +0.301961 0.654902 0.301961 rg +479.52 95.295 m 484.66 95.295 l 484.66 77.698 l 479.52 77.698 l 479.52 +95.295 l h +479.52 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +479.52 70.801 m 484.66 70.801 l 484.66 88.398 l 479.52 88.398 l 479.52 +70.801 l h +479.52 70.801 m S Q +0.301961 0.654902 0.301961 rg +480.555 148.096 m 485.441 148.096 l 485.441 130.495 l 480.555 130.495 l +480.555 148.096 l h +480.555 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +480.555 18 m 485.441 18 l 485.441 35.602 l 480.555 35.602 l 480.555 18 +l h +480.555 18 m S Q +0.301961 0.654902 0.301961 rg +482.027 165.698 m 487.152 165.698 l 487.152 148.096 l 482.027 148.096 l +482.027 165.698 l h +482.027 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +482.027 0.398 m 487.152 0.398 l 487.152 18 l 482.027 18 l 482.027 0.398 +l h +482.027 0.398 m S Q +0.301961 0.654902 0.301961 rg +482.926 130.495 m 487.977 130.495 l 487.977 112.897 l 482.926 112.897 l +482.926 130.495 l h +482.926 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +482.926 35.602 m 487.977 35.602 l 487.977 53.199 l 482.926 53.199 l +482.926 35.602 l h +482.926 35.602 m S Q +0.301961 0.654902 0.301961 rg +484.664 95.295 m 490.867 95.295 l 490.867 77.698 l 484.664 77.698 l +484.664 95.295 l h +484.664 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +484.664 70.801 m 490.867 70.801 l 490.867 88.398 l 484.664 88.398 l +484.664 70.801 l h +484.664 70.801 m S Q +0.301961 0.654902 0.301961 rg +484.984 112.897 m 489.719 112.897 l 489.719 95.295 l 484.984 95.295 l +484.984 112.897 l h +484.984 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +484.984 53.199 m 489.719 53.199 l 489.719 70.801 l 484.984 70.801 l +484.984 53.199 l h +484.984 53.199 m S Q +0.301961 0.654902 0.301961 rg +485.445 148.096 m 490.32 148.096 l 490.32 130.495 l 485.445 130.495 l +485.445 148.096 l h +485.445 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +485.445 18 m 490.32 18 l 490.32 35.602 l 485.445 35.602 l 485.445 18 l +h +485.445 18 m S Q +0.301961 0.654902 0.301961 rg +487.156 165.698 m 492.801 165.698 l 492.801 148.096 l 487.156 148.096 l +487.156 165.698 l h +487.156 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +487.156 0.398 m 492.801 0.398 l 492.801 18 l 487.156 18 l 487.156 0.398 +l h +487.156 0.398 m S Q +0.301961 0.654902 0.301961 rg +487.98 130.495 m 493.637 130.495 l 493.637 112.897 l 487.98 112.897 l +487.98 130.495 l h +487.98 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +487.98 35.602 m 493.637 35.602 l 493.637 53.199 l 487.98 53.199 l +487.98 35.602 l h +487.98 35.602 m S Q +0.301961 0.654902 0.301961 rg +489.727 112.897 m 496.098 112.897 l 496.098 95.295 l 489.727 95.295 l +489.727 112.897 l h +489.727 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +489.727 53.199 m 496.098 53.199 l 496.098 70.801 l 489.727 70.801 l +489.727 53.199 l h +489.727 53.199 m S Q +0.301961 0.654902 0.301961 rg +490.324 148.096 m 494.141 148.096 l 494.141 130.495 l 490.324 130.495 l +490.324 148.096 l h +490.324 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +490.324 18 m 494.141 18 l 494.141 35.602 l 490.324 35.602 l 490.324 18 +l h +490.324 18 m S Q +0.301961 0.654902 0.301961 rg +490.871 95.295 m 495.691 95.295 l 495.691 77.698 l 490.871 77.698 l +490.871 95.295 l h +490.871 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +490.871 70.801 m 495.691 70.801 l 495.691 88.398 l 490.871 88.398 l +490.871 70.801 l h +490.871 70.801 m S Q +0.301961 0.654902 0.301961 rg +492.809 165.698 m 500.035 165.698 l 500.035 148.096 l 492.809 148.096 l +492.809 165.698 l h +492.809 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +492.809 0.398 m 500.035 0.398 l 500.035 18 l 492.809 18 l 492.809 0.398 +l h +492.809 0.398 m S Q +0.301961 0.654902 0.301961 rg +493.645 130.495 m 497.426 130.495 l 497.426 112.897 l 493.645 112.897 l +493.645 130.495 l h +493.645 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +493.645 35.602 m 497.426 35.602 l 497.426 53.199 l 493.645 53.199 l +493.645 35.602 l h +493.645 35.602 m S Q +0.301961 0.654902 0.301961 rg +494.145 148.096 m 500.473 148.096 l 500.473 130.495 l 494.145 130.495 l +494.145 148.096 l h +494.145 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +494.145 18 m 500.473 18 l 500.473 35.602 l 494.145 35.602 l 494.145 18 +l h +494.145 18 m S Q +0.301961 0.654902 0.301961 rg +495.695 95.295 m 500.773 95.295 l 500.773 77.698 l 495.695 77.698 l +495.695 95.295 l h +495.695 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +495.695 70.801 m 500.773 70.801 l 500.773 88.398 l 495.695 88.398 l +495.695 70.801 l h +495.695 70.801 m S Q +0.301961 0.654902 0.301961 rg +496.102 112.897 m 499.254 112.897 l 499.254 95.295 l 496.102 95.295 l +496.102 112.897 l h +496.102 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +496.102 53.199 m 499.254 53.199 l 499.254 70.801 l 496.102 70.801 l +496.102 53.199 l h +496.102 53.199 m S Q +0.301961 0.654902 0.301961 rg +497.43 130.495 m 503.324 130.495 l 503.324 112.897 l 497.43 112.897 l +497.43 130.495 l h +497.43 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +497.43 35.602 m 503.324 35.602 l 503.324 53.199 l 497.43 53.199 l +497.43 35.602 l h +497.43 35.602 m S Q +0.301961 0.654902 0.301961 rg +499.258 112.897 m 504.543 112.897 l 504.543 95.295 l 499.258 95.295 l +499.258 112.897 l h +499.258 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +499.258 53.199 m 504.543 53.199 l 504.543 70.801 l 499.258 70.801 l +499.258 53.199 l h +499.258 53.199 m S Q +0.301961 0.654902 0.301961 rg +500.043 165.698 m 505.098 165.698 l 505.098 148.096 l 500.043 148.096 l +500.043 165.698 l h +500.043 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +500.043 0.398 m 505.098 0.398 l 505.098 18 l 500.043 18 l 500.043 0.398 +l h +500.043 0.398 m S Q +0.301961 0.654902 0.301961 rg +500.48 148.096 m 505.41 148.096 l 505.41 130.495 l 500.48 130.495 l +500.48 148.096 l h +500.48 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +500.48 18 m 505.41 18 l 505.41 35.602 l 500.48 35.602 l 500.48 18 l h +500.48 18 m S Q +0.301961 0.654902 0.301961 rg +500.785 95.295 m 506.02 95.295 l 506.02 77.698 l 500.785 77.698 l +500.785 95.295 l h +500.785 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +500.785 70.801 m 506.02 70.801 l 506.02 88.398 l 500.785 88.398 l +500.785 70.801 l h +500.785 70.801 m S Q +0.301961 0.654902 0.301961 rg +503.328 130.495 m 508.676 130.495 l 508.676 112.897 l 503.328 112.897 l +503.328 130.495 l h +503.328 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +503.328 35.602 m 508.676 35.602 l 508.676 53.199 l 503.328 53.199 l +503.328 35.602 l h +503.328 35.602 m S Q +0.301961 0.654902 0.301961 rg +504.547 112.897 m 508.461 112.897 l 508.461 95.295 l 504.547 95.295 l +504.547 112.897 l h +504.547 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +504.547 53.199 m 508.461 53.199 l 508.461 70.801 l 504.547 70.801 l +504.547 53.199 l h +504.547 53.199 m S Q +0.301961 0.654902 0.301961 rg +505.102 165.698 m 511.742 165.698 l 511.742 148.096 l 505.102 148.096 l +505.102 165.698 l h +505.102 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +505.102 0.398 m 511.742 0.398 l 511.742 18 l 505.102 18 l 505.102 0.398 +l h +505.102 0.398 m S Q +0.301961 0.654902 0.301961 rg +505.418 148.096 m 510.5 148.096 l 510.5 130.495 l 505.418 130.495 l +505.418 148.096 l h +505.418 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +505.418 18 m 510.5 18 l 510.5 35.602 l 505.418 35.602 l 505.418 18 l h +505.418 18 m S Q +0.301961 0.654902 0.301961 rg +506.023 95.295 m 510.848 95.295 l 510.848 77.698 l 506.023 77.698 l +506.023 95.295 l h +506.023 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +506.023 70.801 m 510.848 70.801 l 510.848 88.398 l 506.023 88.398 l +506.023 70.801 l h +506.023 70.801 m S Q +0.301961 0.654902 0.301961 rg +508.469 112.897 m 513.285 112.897 l 513.285 95.295 l 508.469 95.295 l +508.469 112.897 l h +508.469 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +508.469 53.199 m 513.285 53.199 l 513.285 70.801 l 508.469 70.801 l +508.469 53.199 l h +508.469 53.199 m S Q +0.301961 0.654902 0.301961 rg +508.684 130.495 m 514.465 130.495 l 514.465 112.897 l 508.684 112.897 l +508.684 130.495 l h +508.684 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +508.684 35.602 m 514.465 35.602 l 514.465 53.199 l 508.684 53.199 l +508.684 35.602 l h +508.684 35.602 m S Q +0.301961 0.654902 0.301961 rg +510.508 148.096 m 516.656 148.096 l 516.656 130.495 l 510.508 130.495 l +510.508 148.096 l h +510.508 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +510.508 18 m 516.656 18 l 516.656 35.602 l 510.508 35.602 l 510.508 18 +l h +510.508 18 m S Q +0.301961 0.654902 0.301961 rg +510.855 95.295 m 515.691 95.295 l 515.691 77.698 l 510.855 77.698 l +510.855 95.295 l h +510.855 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +510.855 70.801 m 515.691 70.801 l 515.691 88.398 l 510.855 88.398 l +510.855 70.801 l h +510.855 70.801 m S Q +0.301961 0.654902 0.301961 rg +511.746 165.698 m 516.98 165.698 l 516.98 148.096 l 511.746 148.096 l +511.746 165.698 l h +511.746 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +511.746 0.398 m 516.98 0.398 l 516.98 18 l 511.746 18 l 511.746 0.398 l +h +511.746 0.398 m S Q +0.301961 0.654902 0.301961 rg +513.289 112.897 m 518.645 112.897 l 518.645 95.295 l 513.289 95.295 l +513.289 112.897 l h +513.289 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +513.289 53.199 m 518.645 53.199 l 518.645 70.801 l 513.289 70.801 l +513.289 53.199 l h +513.289 53.199 m S Q +0.301961 0.654902 0.301961 rg +514.484 130.495 m 521.547 130.495 l 521.547 112.897 l 514.484 112.897 l +514.484 130.495 l h +514.484 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +514.484 35.602 m 521.547 35.602 l 521.547 53.199 l 514.484 53.199 l +514.484 35.602 l h +514.484 35.602 m S Q +0.301961 0.654902 0.301961 rg +515.695 95.295 m 520.164 95.295 l 520.164 77.698 l 515.695 77.698 l +515.695 95.295 l h +515.695 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +515.695 70.801 m 520.164 70.801 l 520.164 88.398 l 515.695 88.398 l +515.695 70.801 l h +515.695 70.801 m S Q +0.301961 0.654902 0.301961 rg +516.66 148.096 m 521.219 148.096 l 521.219 130.495 l 516.66 130.495 l +516.66 148.096 l h +516.66 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +516.66 18 m 521.219 18 l 521.219 35.602 l 516.66 35.602 l 516.66 18 l h +516.66 18 m S Q +0.301961 0.654902 0.301961 rg +516.988 165.698 m 522.645 165.698 l 522.645 148.096 l 516.988 148.096 l +516.988 165.698 l h +516.988 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +516.988 0.398 m 522.645 0.398 l 522.645 18 l 516.988 18 l 516.988 0.398 +l h +516.988 0.398 m S Q +0.301961 0.654902 0.301961 rg +518.648 112.897 m 523.953 112.897 l 523.953 95.295 l 518.648 95.295 l +518.648 112.897 l h +518.648 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +518.648 53.199 m 523.953 53.199 l 523.953 70.801 l 518.648 70.801 l +518.648 53.199 l h +518.648 53.199 m S Q +0.301961 0.654902 0.301961 rg +520.305 95.295 m 525.133 95.295 l 525.133 77.698 l 520.305 77.698 l +520.305 95.295 l h +520.305 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +520.305 70.801 m 525.133 70.801 l 525.133 88.398 l 520.305 88.398 l +520.305 70.801 l h +520.305 70.801 m S Q +0.301961 0.654902 0.301961 rg +521.227 148.096 m 526.605 148.096 l 526.605 130.495 l 521.227 130.495 l +521.227 148.096 l h +521.227 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +521.227 18 m 526.605 18 l 526.605 35.602 l 521.227 35.602 l 521.227 18 +l h +521.227 18 m S Q +0.301961 0.654902 0.301961 rg +521.551 130.495 m 528.246 130.495 l 528.246 112.897 l 521.551 112.897 l +521.551 130.495 l h +521.551 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +521.551 35.602 m 528.246 35.602 l 528.246 53.199 l 521.551 53.199 l +521.551 35.602 l h +521.551 35.602 m S Q +0.301961 0.654902 0.301961 rg +522.648 165.698 m 529.059 165.698 l 529.059 148.096 l 522.648 148.096 l +522.648 165.698 l h +522.648 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +522.648 0.398 m 529.059 0.398 l 529.059 18 l 522.648 18 l 522.648 0.398 +l h +522.648 0.398 m S Q +0.301961 0.654902 0.301961 rg +523.957 112.897 m 528.703 112.897 l 528.703 95.295 l 523.957 95.295 l +523.957 112.897 l h +523.957 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +523.957 53.199 m 528.703 53.199 l 528.703 70.801 l 523.957 70.801 l +523.957 53.199 l h +523.957 53.199 m S Q +0.301961 0.654902 0.301961 rg +525.137 95.295 m 530.641 95.295 l 530.641 77.698 l 525.137 77.698 l +525.137 95.295 l h +525.137 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +525.137 70.801 m 530.641 70.801 l 530.641 88.398 l 525.137 88.398 l +525.137 70.801 l h +525.137 70.801 m S Q +0.301961 0.654902 0.301961 rg +526.609 148.096 m 531.863 148.096 l 531.863 130.495 l 526.609 130.495 l +526.609 148.096 l h +526.609 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +526.609 18 m 531.863 18 l 531.863 35.602 l 526.609 35.602 l 526.609 18 +l h +526.609 18 m S Q +0.301961 0.654902 0.301961 rg +528.254 130.495 m 533.836 130.495 l 533.836 112.897 l 528.254 112.897 l +528.254 130.495 l h +528.254 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +528.254 35.602 m 533.836 35.602 l 533.836 53.199 l 528.254 53.199 l +528.254 35.602 l h +528.254 35.602 m S Q +0.301961 0.654902 0.301961 rg +528.711 112.897 m 533.062 112.897 l 533.062 95.295 l 528.711 95.295 l +528.711 112.897 l h +528.711 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +528.711 53.199 m 533.062 53.199 l 533.062 70.801 l 528.711 70.801 l +528.711 53.199 l h +528.711 53.199 m S Q +0.301961 0.654902 0.301961 rg +529.062 165.698 m 534.145 165.698 l 534.145 148.096 l 529.062 148.096 l +529.062 165.698 l h +529.062 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +529.062 0.398 m 534.145 0.398 l 534.145 18 l 529.062 18 l 529.062 0.398 +l h +529.062 0.398 m S Q +0.301961 0.654902 0.301961 rg +530.648 95.295 m 536.102 95.295 l 536.102 77.698 l 530.648 77.698 l +530.648 95.295 l h +530.648 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +530.648 70.801 m 536.102 70.801 l 536.102 88.398 l 530.648 88.398 l +530.648 70.801 l h +530.648 70.801 m S Q +0.301961 0.654902 0.301961 rg +531.875 148.096 m 536.926 148.096 l 536.926 130.495 l 531.875 130.495 l +531.875 148.096 l h +531.875 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +531.875 18 m 536.926 18 l 536.926 35.602 l 531.875 35.602 l 531.875 18 +l h +531.875 18 m S Q +0.301961 0.654902 0.301961 rg +533.066 112.897 m 538.453 112.897 l 538.453 95.295 l 533.066 95.295 l +533.066 112.897 l h +533.066 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +533.066 53.199 m 538.453 53.199 l 538.453 70.801 l 533.066 70.801 l +533.066 53.199 l h +533.066 53.199 m S Q +0.301961 0.654902 0.301961 rg +533.84 130.495 m 539.25 130.495 l 539.25 112.897 l 533.84 112.897 l +533.84 130.495 l h +533.84 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +533.84 35.602 m 539.25 35.602 l 539.25 53.199 l 533.84 53.199 l 533.84 +35.602 l h +533.84 35.602 m S Q +0.301961 0.654902 0.301961 rg +534.152 165.698 m 539.512 165.698 l 539.512 148.096 l 534.152 148.096 l +534.152 165.698 l h +534.152 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +534.152 0.398 m 539.512 0.398 l 539.512 18 l 534.152 18 l 534.152 0.398 +l h +534.152 0.398 m S Q +0.301961 0.654902 0.301961 rg +536.105 95.295 m 541.676 95.295 l 541.676 77.698 l 536.105 77.698 l +536.105 95.295 l h +536.105 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +536.105 70.801 m 541.676 70.801 l 541.676 88.398 l 536.105 88.398 l +536.105 70.801 l h +536.105 70.801 m S Q +0.301961 0.654902 0.301961 rg +536.934 148.096 m 543.855 148.096 l 543.855 130.495 l 536.934 130.495 l +536.934 148.096 l h +536.934 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +536.934 18 m 543.855 18 l 543.855 35.602 l 536.934 35.602 l 536.934 18 +l h +536.934 18 m S Q +0.301961 0.654902 0.301961 rg +538.461 112.897 m 543.121 112.897 l 543.121 95.295 l 538.461 95.295 l +538.461 112.897 l h +538.461 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +538.461 53.199 m 543.121 53.199 l 543.121 70.801 l 538.461 70.801 l +538.461 53.199 l h +538.461 53.199 m S Q +0.301961 0.654902 0.301961 rg +539.254 130.495 m 543.82 130.495 l 543.82 112.897 l 539.254 112.897 l +539.254 130.495 l h +539.254 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +539.254 35.602 m 543.82 35.602 l 543.82 53.199 l 539.254 53.199 l +539.254 35.602 l h +539.254 35.602 m S Q +0.301961 0.654902 0.301961 rg +539.523 165.698 m 543.848 165.698 l 543.848 148.096 l 539.523 148.096 l +539.523 165.698 l h +539.523 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +539.523 0.398 m 543.848 0.398 l 543.848 18 l 539.523 18 l 539.523 0.398 +l h +539.523 0.398 m S Q +0.301961 0.654902 0.301961 rg +541.68 95.295 m 547.676 95.295 l 547.676 77.698 l 541.68 77.698 l +541.68 95.295 l h +541.68 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +541.68 70.801 m 547.676 70.801 l 547.676 88.398 l 541.68 88.398 l +541.68 70.801 l h +541.68 70.801 m S Q +0.301961 0.654902 0.301961 rg +543.125 112.897 m 547 112.897 l 547 95.295 l 543.125 95.295 l 543.125 +112.897 l h +543.125 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +543.125 53.199 m 547 53.199 l 547 70.801 l 543.125 70.801 l 543.125 +53.199 l h +543.125 53.199 m S Q +0.301961 0.654902 0.301961 rg +543.824 130.495 m 550.629 130.495 l 550.629 112.897 l 543.824 112.897 l +543.824 130.495 l h +543.824 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +543.824 35.602 m 550.629 35.602 l 550.629 53.199 l 543.824 53.199 l +543.824 35.602 l h +543.824 35.602 m S Q +0.301961 0.654902 0.301961 rg +543.859 165.698 m 549.789 165.698 l 549.789 148.096 l 543.859 148.096 l +543.859 165.698 l h +543.859 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +543.859 0.398 m 549.789 0.398 l 549.789 18 l 543.859 18 l 543.859 0.398 +l h +543.859 0.398 m S Q +0.301961 0.654902 0.301961 rg +543.984 148.096 m 548.141 148.096 l 548.141 130.495 l 543.984 130.495 l +543.984 148.096 l h +543.984 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +543.984 18 m 548.141 18 l 548.141 35.602 l 543.984 35.602 l 543.984 18 +l h +543.984 18 m S Q +0.301961 0.654902 0.301961 rg +547.008 112.897 m 553.598 112.897 l 553.598 95.295 l 547.008 95.295 l +547.008 112.897 l h +547.008 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +547.008 53.199 m 553.598 53.199 l 553.598 70.801 l 547.008 70.801 l +547.008 53.199 l h +547.008 53.199 m S Q +0.301961 0.654902 0.301961 rg +547.68 95.295 m 552.188 95.295 l 552.188 77.698 l 547.68 77.698 l +547.68 95.295 l h +547.68 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +547.68 70.801 m 552.188 70.801 l 552.188 88.398 l 547.68 88.398 l +547.68 70.801 l h +547.68 70.801 m S Q +0.301961 0.654902 0.301961 rg +548.148 148.096 m 552.793 148.096 l 552.793 130.495 l 548.148 130.495 l +548.148 148.096 l h +548.148 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +548.148 18 m 552.793 18 l 552.793 35.602 l 548.148 35.602 l 548.148 18 +l h +548.148 18 m S Q +0.301961 0.654902 0.301961 rg +549.797 165.698 m 554.57 165.698 l 554.57 148.096 l 549.797 148.096 l +549.797 165.698 l h +549.797 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +549.797 0.398 m 554.57 0.398 l 554.57 18 l 549.797 18 l 549.797 0.398 l +h +549.797 0.398 m S Q +0.301961 0.654902 0.301961 rg +550.637 130.495 m 555.27 130.495 l 555.27 112.897 l 550.637 112.897 l +550.637 130.495 l h +550.637 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +550.637 35.602 m 555.27 35.602 l 555.27 53.199 l 550.637 53.199 l +550.637 35.602 l h +550.637 35.602 m S Q +0.301961 0.654902 0.301961 rg +552.191 95.295 m 558.184 95.295 l 558.184 77.698 l 552.191 77.698 l +552.191 95.295 l h +552.191 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +552.191 70.801 m 558.184 70.801 l 558.184 88.398 l 552.191 88.398 l +552.191 70.801 l h +552.191 70.801 m S Q +0.301961 0.654902 0.301961 rg +552.797 148.096 m 557.555 148.096 l 557.555 130.495 l 552.797 130.495 l +552.797 148.096 l h +552.797 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +552.797 18 m 557.555 18 l 557.555 35.602 l 552.797 35.602 l 552.797 18 +l h +552.797 18 m S Q +0.301961 0.654902 0.301961 rg +553.602 112.897 m 558.805 112.897 l 558.805 95.295 l 553.602 95.295 l +553.602 112.897 l h +553.602 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +553.602 53.199 m 558.805 53.199 l 558.805 70.801 l 553.602 70.801 l +553.602 53.199 l h +553.602 53.199 m S Q +0.301961 0.654902 0.301961 rg +554.578 165.698 m 560.062 165.698 l 560.062 148.096 l 554.578 148.096 l +554.578 165.698 l h +554.578 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +554.578 0.398 m 560.062 0.398 l 560.062 18 l 554.578 18 l 554.578 0.398 +l h +554.578 0.398 m S Q +0.301961 0.654902 0.301961 rg +555.273 130.495 m 560.605 130.495 l 560.605 112.897 l 555.273 112.897 l +555.273 130.495 l h +555.273 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +555.273 35.602 m 560.605 35.602 l 560.605 53.199 l 555.273 53.199 l +555.273 35.602 l h +555.273 35.602 m S Q +0.301961 0.654902 0.301961 rg +557.559 148.096 m 562.52 148.096 l 562.52 130.495 l 557.559 130.495 l +557.559 148.096 l h +557.559 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +557.559 18 m 562.52 18 l 562.52 35.602 l 557.559 35.602 l 557.559 18 l +h +557.559 18 m S Q +0.301961 0.654902 0.301961 rg +558.191 95.295 m 563.609 95.295 l 563.609 77.698 l 558.191 77.698 l +558.191 95.295 l h +558.191 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +558.191 70.801 m 563.609 70.801 l 563.609 88.398 l 558.191 88.398 l +558.191 70.801 l h +558.191 70.801 m S Q +0.301961 0.654902 0.301961 rg +558.809 112.897 m 563.426 112.897 l 563.426 95.295 l 558.809 95.295 l +558.809 112.897 l h +558.809 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +558.809 53.199 m 563.426 53.199 l 563.426 70.801 l 558.809 70.801 l +558.809 53.199 l h +558.809 53.199 m S Q +0.301961 0.654902 0.301961 rg +560.066 165.698 m 565.965 165.698 l 565.965 148.096 l 560.066 148.096 l +560.066 165.698 l h +560.066 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +560.066 0.398 m 565.965 0.398 l 565.965 18 l 560.066 18 l 560.066 0.398 +l h +560.066 0.398 m S Q +0.301961 0.654902 0.301961 rg +560.609 130.495 m 565.496 130.495 l 565.496 112.897 l 560.609 112.897 l +560.609 130.495 l h +560.609 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +560.609 35.602 m 565.496 35.602 l 565.496 53.199 l 560.609 53.199 l +560.609 35.602 l h +560.609 35.602 m S Q +0.301961 0.654902 0.301961 rg +562.527 148.096 m 568.516 148.096 l 568.516 130.495 l 562.527 130.495 l +562.527 148.096 l h +562.527 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +562.527 18 m 568.516 18 l 568.516 35.602 l 562.527 35.602 l 562.527 18 +l h +562.527 18 m S Q +0.301961 0.654902 0.301961 rg +563.43 112.897 m 568.852 112.897 l 568.852 95.295 l 563.43 95.295 l +563.43 112.897 l h +563.43 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +563.43 53.199 m 568.852 53.199 l 568.852 70.801 l 563.43 70.801 l +563.43 53.199 l h +563.43 53.199 m S Q +0.301961 0.654902 0.301961 rg +563.629 95.295 m 567.715 95.295 l 567.715 77.698 l 563.629 77.698 l +563.629 95.295 l h +563.629 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +563.629 70.801 m 567.715 70.801 l 567.715 88.398 l 563.629 88.398 l +563.629 70.801 l h +563.629 70.801 m S Q +0.301961 0.654902 0.301961 rg +565.5 130.495 m 573.277 130.495 l 573.277 112.897 l 565.5 112.897 l +565.5 130.495 l h +565.5 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +565.5 35.602 m 573.277 35.602 l 573.277 53.199 l 565.5 53.199 l 565.5 +35.602 l h +565.5 35.602 m S Q +0.301961 0.654902 0.301961 rg +565.973 165.698 m 570.039 165.698 l 570.039 148.096 l 565.973 148.096 l +565.973 165.698 l h +565.973 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +565.973 0.398 m 570.039 0.398 l 570.039 18 l 565.973 18 l 565.973 0.398 +l h +565.973 0.398 m S Q +0.301961 0.654902 0.301961 rg +567.719 95.295 m 571.504 95.295 l 571.504 77.698 l 567.719 77.698 l +567.719 95.295 l h +567.719 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +567.719 70.801 m 571.504 70.801 l 571.504 88.398 l 567.719 88.398 l +567.719 70.801 l h +567.719 70.801 m S Q +0.301961 0.654902 0.301961 rg +568.523 148.096 m 574.816 148.096 l 574.816 130.495 l 568.523 130.495 l +568.523 148.096 l h +568.523 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +568.523 18 m 574.816 18 l 574.816 35.602 l 568.523 35.602 l 568.523 18 +l h +568.523 18 m S Q +0.301961 0.654902 0.301961 rg +568.859 112.897 m 573.672 112.897 l 573.672 95.295 l 568.859 95.295 l +568.859 112.897 l h +568.859 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +568.859 53.199 m 573.672 53.199 l 573.672 70.801 l 568.859 70.801 l +568.859 53.199 l h +568.859 53.199 m S Q +0.301961 0.654902 0.301961 rg +570.043 165.698 m 575.941 165.698 l 575.941 148.096 l 570.043 148.096 l +570.043 165.698 l h +570.043 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +570.043 0.398 m 575.941 0.398 l 575.941 18 l 570.043 18 l 570.043 0.398 +l h +570.043 0.398 m S Q +0.301961 0.654902 0.301961 rg +571.508 95.295 m 577.152 95.295 l 577.152 77.698 l 571.508 77.698 l +571.508 95.295 l h +571.508 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +571.508 70.801 m 577.152 70.801 l 577.152 88.398 l 571.508 88.398 l +571.508 70.801 l h +571.508 70.801 m S Q +0.301961 0.654902 0.301961 rg +573.285 130.495 m 577.379 130.495 l 577.379 112.897 l 573.285 112.897 l +573.285 130.495 l h +573.285 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +573.285 35.602 m 577.379 35.602 l 577.379 53.199 l 573.285 53.199 l +573.285 35.602 l h +573.285 35.602 m S Q +0.301961 0.654902 0.301961 rg +573.676 112.897 m 579.859 112.897 l 579.859 95.295 l 573.676 95.295 l +573.676 112.897 l h +573.676 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +573.676 53.199 m 579.859 53.199 l 579.859 70.801 l 573.676 70.801 l +573.676 53.199 l h +573.676 53.199 m S Q +0.301961 0.654902 0.301961 rg +574.82 148.096 m 579.551 148.096 l 579.551 130.495 l 574.82 130.495 l +574.82 148.096 l h +574.82 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +574.82 18 m 579.551 18 l 579.551 35.602 l 574.82 35.602 l 574.82 18 l h +574.82 18 m S Q +0.301961 0.654902 0.301961 rg +575.949 165.698 m 581.934 165.698 l 581.934 148.096 l 575.949 148.096 l +575.949 165.698 l h +575.949 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +575.949 0.398 m 581.934 0.398 l 581.934 18 l 575.949 18 l 575.949 0.398 +l h +575.949 0.398 m S Q +0.301961 0.654902 0.301961 rg +577.156 95.295 m 582.293 95.295 l 582.293 77.698 l 577.156 77.698 l +577.156 95.295 l h +577.156 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +577.156 70.801 m 582.293 70.801 l 582.293 88.398 l 577.156 88.398 l +577.156 70.801 l h +577.156 70.801 m S Q +0.301961 0.654902 0.301961 rg +577.387 130.495 m 582.508 130.495 l 582.508 112.897 l 577.387 112.897 l +577.387 130.495 l h +577.387 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +577.387 35.602 m 582.508 35.602 l 582.508 53.199 l 577.387 53.199 l +577.387 35.602 l h +577.387 35.602 m S Q +0.301961 0.654902 0.301961 rg +579.555 148.096 m 585.789 148.096 l 585.789 130.495 l 579.555 130.495 l +579.555 148.096 l h +579.555 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +579.555 18 m 585.789 18 l 585.789 35.602 l 579.555 35.602 l 579.555 18 +l h +579.555 18 m S Q +0.301961 0.654902 0.301961 rg +579.863 112.897 m 585.34 112.897 l 585.34 95.295 l 579.863 95.295 l +579.863 112.897 l h +579.863 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +579.863 53.199 m 585.34 53.199 l 585.34 70.801 l 579.863 70.801 l +579.863 53.199 l h +579.863 53.199 m S Q +0.301961 0.654902 0.301961 rg +581.938 165.698 m 586.992 165.698 l 586.992 148.096 l 581.938 148.096 l +581.938 165.698 l h +581.938 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +581.938 0.398 m 586.992 0.398 l 586.992 18 l 581.938 18 l 581.938 0.398 +l h +581.938 0.398 m S Q +0.301961 0.654902 0.301961 rg +582.301 95.295 m 587.441 95.295 l 587.441 77.698 l 582.301 77.698 l +582.301 95.295 l h +582.301 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +582.301 70.801 m 587.441 70.801 l 587.441 88.398 l 582.301 88.398 l +582.301 70.801 l h +582.301 70.801 m S Q +0.301961 0.654902 0.301961 rg +582.516 130.495 m 587.07 130.495 l 587.07 112.897 l 582.516 112.897 l +582.516 130.495 l h +582.516 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +582.516 35.602 m 587.07 35.602 l 587.07 53.199 l 582.516 53.199 l +582.516 35.602 l h +582.516 35.602 m S Q +0.301961 0.654902 0.301961 rg +585.344 112.897 m 590.75 112.897 l 590.75 95.295 l 585.344 95.295 l +585.344 112.897 l h +585.344 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +585.344 53.199 m 590.75 53.199 l 590.75 70.801 l 585.344 70.801 l +585.344 53.199 l h +585.344 53.199 m S Q +0.301961 0.654902 0.301961 rg +585.797 148.096 m 590.59 148.096 l 590.59 130.495 l 585.797 130.495 l +585.797 148.096 l h +585.797 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +585.797 18 m 590.59 18 l 590.59 35.602 l 585.797 35.602 l 585.797 18 l +h +585.797 18 m S Q +0.301961 0.654902 0.301961 rg +587.082 165.698 m 591.707 165.698 l 591.707 148.096 l 587.082 148.096 l +587.082 165.698 l h +587.082 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +587.082 0.398 m 591.707 0.398 l 591.707 18 l 587.082 18 l 587.082 0.398 +l h +587.082 0.398 m S Q +0.301961 0.654902 0.301961 rg +587.109 130.495 m 592.734 130.495 l 592.734 112.897 l 587.109 112.897 l +587.109 130.495 l h +587.109 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +587.109 35.602 m 592.734 35.602 l 592.734 53.199 l 587.109 53.199 l +587.109 35.602 l h +587.109 35.602 m S Q +0.301961 0.654902 0.301961 rg +587.469 95.295 m 593.238 95.295 l 593.238 77.698 l 587.469 77.698 l +587.469 95.295 l h +587.469 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +587.469 70.801 m 593.238 70.801 l 593.238 88.398 l 587.469 88.398 l +587.469 70.801 l h +587.469 70.801 m S Q +0.301961 0.654902 0.301961 rg +590.594 148.096 m 596.855 148.096 l 596.855 130.495 l 590.594 130.495 l +590.594 148.096 l h +590.594 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +590.594 18 m 596.855 18 l 596.855 35.602 l 590.594 35.602 l 590.594 18 +l h +590.594 18 m S Q +0.301961 0.654902 0.301961 rg +590.77 112.897 m 596.668 112.897 l 596.668 95.295 l 590.77 95.295 l +590.77 112.897 l h +590.77 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +590.77 53.199 m 596.668 53.199 l 596.668 70.801 l 590.77 70.801 l +590.77 53.199 l h +590.77 53.199 m S Q +0.301961 0.654902 0.301961 rg +591.715 165.698 m 596.301 165.698 l 596.301 148.096 l 591.715 148.096 l +591.715 165.698 l h +591.715 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +591.715 0.398 m 596.301 0.398 l 596.301 18 l 591.715 18 l 591.715 0.398 +l h +591.715 0.398 m S Q +0.301961 0.654902 0.301961 rg +592.738 130.495 m 597.207 130.495 l 597.207 112.897 l 592.738 112.897 l +592.738 130.495 l h +592.738 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +592.738 35.602 m 597.207 35.602 l 597.207 53.199 l 592.738 53.199 l +592.738 35.602 l h +592.738 35.602 m S Q +0.301961 0.654902 0.301961 rg +593.246 95.295 m 597.895 95.295 l 597.895 77.698 l 593.246 77.698 l +593.246 95.295 l h +593.246 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +593.246 70.801 m 597.895 70.801 l 597.895 88.398 l 593.246 88.398 l +593.246 70.801 l h +593.246 70.801 m S Q +0.301961 0.654902 0.301961 rg +596.305 165.698 m 601.773 165.698 l 601.773 148.096 l 596.305 148.096 l +596.305 165.698 l h +596.305 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +596.305 0.398 m 601.773 0.398 l 601.773 18 l 596.305 18 l 596.305 0.398 +l h +596.305 0.398 m S Q +0.301961 0.654902 0.301961 rg +596.699 112.897 m 602.727 112.897 l 602.727 95.295 l 596.699 95.295 l +596.699 112.897 l h +596.699 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +596.699 53.199 m 602.727 53.199 l 602.727 70.801 l 596.699 70.801 l +596.699 53.199 l h +596.699 53.199 m S Q +0.301961 0.654902 0.301961 rg +596.875 148.096 m 600.816 148.096 l 600.816 130.495 l 596.875 130.495 l +596.875 148.096 l h +596.875 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +596.875 18 m 600.816 18 l 600.816 35.602 l 596.875 35.602 l 596.875 18 +l h +596.875 18 m S Q +0.301961 0.654902 0.301961 rg +597.215 130.495 m 603.629 130.495 l 603.629 112.897 l 597.215 112.897 l +597.215 130.495 l h +597.215 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +597.215 35.602 m 603.629 35.602 l 603.629 53.199 l 597.215 53.199 l +597.215 35.602 l h +597.215 35.602 m S Q +0.301961 0.654902 0.301961 rg +597.898 95.295 m 602.773 95.295 l 602.773 77.698 l 597.898 77.698 l +597.898 95.295 l h +597.898 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +597.898 70.801 m 602.773 70.801 l 602.773 88.398 l 597.898 88.398 l +597.898 70.801 l h +597.898 70.801 m S Q +0.301961 0.654902 0.301961 rg +600.824 148.096 m 606.219 148.096 l 606.219 130.495 l 600.824 130.495 l +600.824 148.096 l h +600.824 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +600.824 18 m 606.219 18 l 606.219 35.602 l 600.824 35.602 l 600.824 18 +l h +600.824 18 m S Q +0.301961 0.654902 0.301961 rg +601.781 165.698 m 607.414 165.698 l 607.414 148.096 l 601.781 148.096 l +601.781 165.698 l h +601.781 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +601.781 0.398 m 607.414 0.398 l 607.414 18 l 601.781 18 l 601.781 0.398 +l h +601.781 0.398 m S Q +0.301961 0.654902 0.301961 rg +602.73 112.897 m 606.754 112.897 l 606.754 95.295 l 602.73 95.295 l +602.73 112.897 l h +602.73 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +602.73 53.199 m 606.754 53.199 l 606.754 70.801 l 602.73 70.801 l +602.73 53.199 l h +602.73 53.199 m S Q +0.301961 0.654902 0.301961 rg +602.828 95.295 m 608.969 95.295 l 608.969 77.698 l 602.828 77.698 l +602.828 95.295 l h +602.828 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +602.828 70.801 m 608.969 70.801 l 608.969 88.398 l 602.828 88.398 l +602.828 70.801 l h +602.828 70.801 m S Q +0.301961 0.654902 0.301961 rg +603.633 130.495 m 609.633 130.495 l 609.633 112.897 l 603.633 112.897 l +603.633 130.495 l h +603.633 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +603.633 35.602 m 609.633 35.602 l 609.633 53.199 l 603.633 53.199 l +603.633 35.602 l h +603.633 35.602 m S Q +0.301961 0.654902 0.301961 rg +606.227 148.096 m 611.27 148.096 l 611.27 130.495 l 606.227 130.495 l +606.227 148.096 l h +606.227 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +606.227 18 m 611.27 18 l 611.27 35.602 l 606.227 35.602 l 606.227 18 l +h +606.227 18 m S Q +0.301961 0.654902 0.301961 rg +606.762 112.897 m 611.902 112.897 l 611.902 95.295 l 606.762 95.295 l +606.762 112.897 l h +606.762 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +606.762 53.199 m 611.902 53.199 l 611.902 70.801 l 606.762 70.801 l +606.762 53.199 l h +606.762 53.199 m S Q +0.301961 0.654902 0.301961 rg +607.418 165.698 m 613.035 165.698 l 613.035 148.096 l 607.418 148.096 l +607.418 165.698 l h +607.418 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +607.418 0.398 m 613.035 0.398 l 613.035 18 l 607.418 18 l 607.418 0.398 +l h +607.418 0.398 m S Q +0.301961 0.654902 0.301961 rg +608.977 95.295 m 614.129 95.295 l 614.129 77.698 l 608.977 77.698 l +608.977 95.295 l h +608.977 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +608.977 70.801 m 614.129 70.801 l 614.129 88.398 l 608.977 88.398 l +608.977 70.801 l h +608.977 70.801 m S Q +0.301961 0.654902 0.301961 rg +609.641 130.495 m 616.34 130.495 l 616.34 112.897 l 609.641 112.897 l +609.641 130.495 l h +609.641 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +609.641 35.602 m 616.34 35.602 l 616.34 53.199 l 609.641 53.199 l +609.641 35.602 l h +609.641 35.602 m S Q +0.301961 0.654902 0.301961 rg +611.273 148.096 m 616.508 148.096 l 616.508 130.495 l 611.273 130.495 l +611.273 148.096 l h +611.273 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +611.273 18 m 616.508 18 l 616.508 35.602 l 611.273 35.602 l 611.273 18 +l h +611.273 18 m S Q +0.301961 0.654902 0.301961 rg +611.91 112.897 m 616.719 112.897 l 616.719 95.295 l 611.91 95.295 l +611.91 112.897 l h +611.91 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +611.91 53.199 m 616.719 53.199 l 616.719 70.801 l 611.91 70.801 l +611.91 53.199 l h +611.91 53.199 m S Q +0.301961 0.654902 0.301961 rg +613.039 165.698 m 618.566 165.698 l 618.566 148.096 l 613.039 148.096 l +613.039 165.698 l h +613.039 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +613.039 0.398 m 618.566 0.398 l 618.566 18 l 613.039 18 l 613.039 0.398 +l h +613.039 0.398 m S Q +0.301961 0.654902 0.301961 rg +614.242 95.295 m 618.062 95.295 l 618.062 77.698 l 614.242 77.698 l +614.242 95.295 l h +614.242 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +614.242 70.801 m 618.062 70.801 l 618.062 88.398 l 614.242 88.398 l +614.242 70.801 l h +614.242 70.801 m S Q +0.301961 0.654902 0.301961 rg +616.355 130.495 m 621.363 130.495 l 621.363 112.897 l 616.355 112.897 l +616.355 130.495 l h +616.355 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +616.355 35.602 m 621.363 35.602 l 621.363 53.199 l 616.355 53.199 l +616.355 35.602 l h +616.355 35.602 m S Q +0.301961 0.654902 0.301961 rg +616.527 148.096 m 622.074 148.096 l 622.074 130.495 l 616.527 130.495 l +616.527 148.096 l h +616.527 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +616.527 18 m 622.074 18 l 622.074 35.602 l 616.527 35.602 l 616.527 18 +l h +616.527 18 m S Q +0.301961 0.654902 0.301961 rg +616.777 112.897 m 621.738 112.897 l 621.738 95.295 l 616.777 95.295 l +616.777 112.897 l h +616.777 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +616.777 53.199 m 621.738 53.199 l 621.738 70.801 l 616.777 70.801 l +616.777 53.199 l h +616.777 53.199 m S Q +0.301961 0.654902 0.301961 rg +618.066 95.295 m 624.359 95.295 l 624.359 77.698 l 618.066 77.698 l +618.066 95.295 l h +618.066 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +618.066 70.801 m 624.359 70.801 l 624.359 88.398 l 618.066 88.398 l +618.066 70.801 l h +618.066 70.801 m S Q +0.301961 0.654902 0.301961 rg +618.582 165.698 m 624.078 165.698 l 624.078 148.096 l 618.582 148.096 l +618.582 165.698 l h +618.582 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +618.582 0.398 m 624.078 0.398 l 624.078 18 l 618.582 18 l 618.582 0.398 +l h +618.582 0.398 m S Q +0.301961 0.654902 0.301961 rg +621.371 130.495 m 625.562 130.495 l 625.562 112.897 l 621.371 112.897 l +621.371 130.495 l h +621.371 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +621.371 35.602 m 625.562 35.602 l 625.562 53.199 l 621.371 53.199 l +621.371 35.602 l h +621.371 35.602 m S Q +0.301961 0.654902 0.301961 rg +621.762 112.897 m 626.449 112.897 l 626.449 95.295 l 621.762 95.295 l +621.762 112.897 l h +621.762 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +621.762 53.199 m 626.449 53.199 l 626.449 70.801 l 621.762 70.801 l +621.762 53.199 l h +621.762 53.199 m S Q +0.301961 0.654902 0.301961 rg +622.078 148.096 m 626.758 148.096 l 626.758 130.495 l 622.078 130.495 l +622.078 148.096 l h +622.078 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +622.078 18 m 626.758 18 l 626.758 35.602 l 622.078 35.602 l 622.078 18 +l h +622.078 18 m S Q +0.301961 0.654902 0.301961 rg +624.086 165.698 m 629.086 165.698 l 629.086 148.096 l 624.086 148.096 l +624.086 165.698 l h +624.086 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +624.086 0.398 m 629.086 0.398 l 629.086 18 l 624.086 18 l 624.086 0.398 +l h +624.086 0.398 m S Q +0.301961 0.654902 0.301961 rg +624.367 95.295 m 629.461 95.295 l 629.461 77.698 l 624.367 77.698 l +624.367 95.295 l h +624.367 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +624.367 70.801 m 629.461 70.801 l 629.461 88.398 l 624.367 88.398 l +624.367 70.801 l h +624.367 70.801 m S Q +0.301961 0.654902 0.301961 rg +625.574 130.495 m 629.887 130.495 l 629.887 112.897 l 625.574 112.897 l +625.574 130.495 l h +625.574 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +625.574 35.602 m 629.887 35.602 l 629.887 53.199 l 625.574 53.199 l +625.574 35.602 l h +625.574 35.602 m S Q +0.301961 0.654902 0.301961 rg +626.461 112.897 m 630.906 112.897 l 630.906 95.295 l 626.461 95.295 l +626.461 112.897 l h +626.461 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +626.461 53.199 m 630.906 53.199 l 630.906 70.801 l 626.461 70.801 l +626.461 53.199 l h +626.461 53.199 m S Q +0.301961 0.654902 0.301961 rg +626.777 148.096 m 632.895 148.096 l 632.895 130.495 l 626.777 130.495 l +626.777 148.096 l h +626.777 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +626.777 18 m 632.895 18 l 632.895 35.602 l 626.777 35.602 l 626.777 18 +l h +626.777 18 m S Q +0.301961 0.654902 0.301961 rg +629.098 165.698 m 633.445 165.698 l 633.445 148.096 l 629.098 148.096 l +629.098 165.698 l h +629.098 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +629.098 0.398 m 633.445 0.398 l 633.445 18 l 629.098 18 l 629.098 0.398 +l h +629.098 0.398 m S Q +0.301961 0.654902 0.301961 rg +629.516 95.295 m 634.094 95.295 l 634.094 77.698 l 629.516 77.698 l +629.516 95.295 l h +629.516 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +629.516 70.801 m 634.094 70.801 l 634.094 88.398 l 629.516 88.398 l +629.516 70.801 l h +629.516 70.801 m S Q +0.301961 0.654902 0.301961 rg +629.93 130.495 m 636.984 130.495 l 636.984 112.897 l 629.93 112.897 l +629.93 130.495 l h +629.93 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +629.93 35.602 m 636.984 35.602 l 636.984 53.199 l 629.93 53.199 l +629.93 35.602 l h +629.93 35.602 m S Q +0.301961 0.654902 0.301961 rg +630.914 112.897 m 638.766 112.897 l 638.766 95.295 l 630.914 95.295 l +630.914 112.897 l h +630.914 112.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +630.914 53.199 m 638.766 53.199 l 638.766 70.801 l 630.914 70.801 l +630.914 53.199 l h +630.914 53.199 m S Q +0.301961 0.654902 0.301961 rg +632.902 148.096 m 638.59 148.096 l 638.59 130.495 l 632.902 130.495 l +632.902 148.096 l h +632.902 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +632.902 18 m 638.59 18 l 638.59 35.602 l 632.902 35.602 l 632.902 18 l +h +632.902 18 m S Q +0.654902 0.317647 1 rg +633.465 165.698 m 638.27 165.698 l 638.27 148.096 l 633.465 148.096 l +633.465 165.698 l h +633.465 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +633.465 0.398 m 638.27 0.398 l 638.27 18 l 633.465 18 l 633.465 0.398 l +h +633.465 0.398 m S Q +0.654902 0.317647 1 rg +634.113 95.295 m 636.031 95.295 l 636.031 77.698 l 634.113 77.698 l +634.113 95.295 l h +634.113 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +634.113 70.801 m 636.031 70.801 l 636.031 88.398 l 634.113 88.398 l +634.113 70.801 l h +634.113 70.801 m S Q +0.654902 0.317647 1 rg +636.039 95.295 m 639.387 95.295 l 639.387 77.698 l 636.039 77.698 l +636.039 95.295 l h +636.039 95.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +636.039 70.801 m 639.387 70.801 l 639.387 88.398 l 636.039 88.398 l +636.039 70.801 l h +636.039 70.801 m S Q +0.654902 0.317647 1 rg +636.992 130.495 m 639.539 130.495 l 639.539 112.897 l 636.992 112.897 l +636.992 130.495 l h +636.992 130.495 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +636.992 35.602 m 639.539 35.602 l 639.539 53.199 l 636.992 53.199 l +636.992 35.602 l h +636.992 35.602 m S Q +0.654902 0.317647 1 rg +638.277 165.698 m 640.156 165.698 l 640.156 148.096 l 638.277 148.096 l +638.277 165.698 l h +638.277 165.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +638.277 0.398 m 640.156 0.398 l 640.156 18 l 638.277 18 l 638.277 0.398 +l h +638.277 0.398 m S Q +0.654902 0.317647 1 rg +638.848 148.096 m 640.398 148.096 l 640.398 130.495 l 638.848 130.495 l +638.848 148.096 l h +638.848 148.096 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +638.848 18 m 640.398 18 l 640.398 35.602 l 638.848 35.602 l 638.848 18 +l h +638.848 18 m S Q +0.301961 0.654902 0.301961 rg +18 68.897 m 26.801 68.897 l 26.801 60.096 l 18 60.096 l 18 68.897 l h +18 68.897 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +18 97.199 m 26.801 97.199 l 26.801 106 l 18 106 l 18 97.199 l h +18 97.199 m S Q +BT +9.6 0 0 9.6 35.6 60.0961 Tm +/f-0-0 1 Tf +[<16>-1<0c>1<17>-1<070b>1<04020a>-1<0b>1<0c09>-1<060a>-1<0208>-1<18>]TJ +ET +0.8 0.113725 0.113725 rg +18 51.295 m 26.801 51.295 l 26.801 42.495 l 18 42.495 l 18 51.295 l h +18 51.295 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +18 114.801 m 26.801 114.801 l 26.801 123.602 l 18 123.602 l 18 114.801 +l h +18 114.801 m S Q +BT +9.6 0 0 9.6 35.6 42.4961 Tm +/f-0-0 1 Tf +[<190a1a>-1<0503>]TJ +ET +0.654902 0.317647 1 rg +18 33.698 m 26.801 33.698 l 26.801 24.897 l 18 24.897 l 18 33.698 l h +18 33.698 m f* +0 g +q 1 0 0 -1 0 166.0961 cm +18 132.398 m 26.801 132.398 l 26.801 141.199 l 18 141.199 l 18 132.398 +l h +18 132.398 m S Q +BT +9.6 0 0 9.6 35.6 24.8961 Tm +/f-0-0 1 Tf +[<1b0b>1<09>-1<18>-1<060a>-1<0208>-1<18>]TJ +ET +Q +showpage +%%Trailer +count op_count sub {pop} repeat +countdictstack dict_count sub {end} repeat +cairo_eps_state restore +%%EOF diff --git a/docs/book/pics/note.png b/docs/book/pics/note.png new file mode 100644 index 00000000..7c1f3e2f Binary files /dev/null and b/docs/book/pics/note.png differ diff --git a/docs/book/pics/tip.png b/docs/book/pics/tip.png new file mode 100644 index 00000000..f087c73b Binary files /dev/null and b/docs/book/pics/tip.png differ diff --git a/docs/book/pics/waf-64x64.png b/docs/book/pics/waf-64x64.png new file mode 100644 index 00000000..cbe55f63 Binary files /dev/null and b/docs/book/pics/waf-64x64.png differ diff --git a/docs/book/pics/warning.png b/docs/book/pics/warning.png new file mode 100644 index 00000000..d41edb9a Binary files /dev/null and b/docs/book/pics/warning.png differ diff --git a/docs/book/posting.dia b/docs/book/posting.dia new file mode 100644 index 00000000..2ffc3513 --- /dev/null +++ b/docs/book/posting.dia @@ -0,0 +1,1052 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Read the list of features +from self.features and add '*'# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Perform a topological sort +on self.meths using the +precedence constraints # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #pop one method name +from self.meths# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Take a task generator instance +which has not generated its tasks # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Execute the method# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Add all the methods corresponding +to the list of features to self.meths # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #More task +generators to +process? # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no: self.meths +is empty# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #More methods +to execute?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #End# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Start# + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/book/prodcons.dia b/docs/book/prodcons.dia new file mode 100644 index 00000000..1aa9fe8a --- /dev/null +++ b/docs/book/prodcons.dia @@ -0,0 +1,2875 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Add the next task +to the queue 'ready'# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Execute the task# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #1 Producer +(Runner.Parallel)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #N consumers +(Runner.TaskConsumer)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Build complete# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Get one task +from the queue 'ready' +(block)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Put the task into +the queue 'out'# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #error# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Notify the producer +to stop the build# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #No more tasks +to process# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #ok# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Task obtained# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Initialize the task +consumer pool# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #ask +later# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #execute# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Get one task from the +queue 'out' (block)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Enough tasks +in the queue +'ready'?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Add the task to +the a list 'frozen'# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Get a task from +the queue 'out'# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Waiting tasks? +(list 'outstanding')# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Are there tasks in +the list 'frozen'?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Add all the tasks +from the list 'frozen' +to the list 'outstanding'# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Get a task from +'outstanding'# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Are there tasks +being processed?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Waiting tasks +(list outstanding) +or tasks being +processed?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #skip# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Get group tasks from the +build context (list 'outstanding')# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #task +status?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Start# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #End# + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/book/scenarios.txt b/docs/book/scenarios.txt new file mode 100644 index 00000000..8a47698d --- /dev/null +++ b/docs/book/scenarios.txt @@ -0,0 +1,699 @@ +== Advanced scenarios + +This chapter demonstrates a few examples of the waf library for more complicated and less common scenarios. + +=== Project organization + +==== Building the compiler first[[build_compiler_first]] + +The example below demonstrates how to build a compiler which is used for building the remaining targets. The requirements are the following: + +. Create the compiler and all its intermediate tasks +. Re-use the compiler in a second build step +. The compiler will transform '.src' files into '.cpp' files, which will be processed too +. Call the compiler again if it was rebuilt (add the dependency on the compiler) + +The first thing to do is to write the expected user script: + +// scenarios_compiler +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(ctx): + ctx.load('g++') + ctx.load('src2cpp', tooldir='.') + +def build(ctx): + ctx.program( <1> + source = 'comp.cpp', + target = 'comp') + + ctx.add_group() <2> + + ctx.program( + source = 'main.cpp a.src', <3> + target = 'foo') +--------------- + +<1> Build the compiler first, it will result in a binary named _comp_ +<2> Add a new build group to make certain the compiler is complete before processing the next tasks +<3> The file 'a.src' is to be transformed by 'comp' into 'a.cpp' + +The code for the _src → cpp_ conversion will be the following: + +[source,python] +--------------- +from waflib.Task import Task +class src2cpp(Task): <1> + run_str = '${SRC[0].abspath()} ${SRC[1].abspath()} ${TGT}' + color = 'PINK' + +from waflib.TaskGen import extension + +@extension('.src') +def process_src(self, node): <2> + tg = self.bld.get_tgen_by_name('comp') <3> + comp = tg.link_task.outputs[0] + tsk = self.create_task('src2cpp', [comp, node], node.change_ext('.cpp')) <4> + self.source.extend(tsk.outputs) <5> +--------------- + +<1> Declare a new task class for processing the source file by our compiler +<2> Files of extension '.src' are to be processed by this method +<3> Obtain a reference on the task generator producing the compiler +<4> Create the task 'src → cpp', the compiler being as used as the first source file +<5> Add the generated 'cpp' file to be processed too + +The compilation results will be the following: + +[source,shishell] +--------------- +$ waf distclean configure build -v +'distclean' finished successfully (0.006s) +Setting top to : /tmp/scenarios_compiler +Setting out to : /tmp/scenarios_compiler/build +Checking for program g++,c++ : /usr/bin/g++ +Checking for program ar : /usr/bin/ar +'configure' finished successfully (0.118s) +Waf: Entering directory `/tmp/scenarios_compiler/build' +[1/5] cxx: comp.cpp -> build/comp.cpp.0.o +10:21:14 runner ['/usr/bin/g++', '../comp.cpp', '-c', '-o', 'comp.cpp.0.o'] +[2/5] cxxprogram: build/comp.cpp.0.o -> build/comp <1> +10:21:14 runner ['/usr/bin/g++', 'comp.cpp.0.o', '-o', '/tmp/scenarios_compiler/build/comp'] +[3/5] cxx: main.cpp -> build/main.cpp.1.o +10:21:15 runner ['/usr/bin/g++', '../main.cpp', '-c', '-o', 'main.cpp.1.o'] +[4/5] src2cpp: a.src -> build/a.cpp +10:21:15 runner ['/tmp/scenarios_compiler/build/comp', '../a.src', 'a.cpp'] <2> +[5/5] cxxprogram: build/main.cpp.1.o -> build/foo <3> +10:21:15 runner ['/usr/bin/g++', 'main.cpp.1.o', '-o', 'foo'] +Waf: Leaving directory `/tmp/scenarios_compiler/build' +'build' finished successfully (1.009s) +--------------- + +<1> Creation of the 'comp' program +<2> Use the compiler to generate 'a.cpp' +<3> Compile and link 'a.cpp' and 'main.cpp' into the program 'foo' + +NOTE: When `waf --targets=foo' is called, the task generator `comp' will create its tasks too (task generators from previous groups are processed). + +==== Providing arbitrary configuration files + +A file is copied into the build directory before the build starts. The build may use this file for building other targets. + +// scenarios_impfile +[source,python] +--------------- +cfg_file = 'somedir/foo.txt' + +def configure(conf): + + orig = conf.root.find_node('/etc/fstab') + txt = orig.read() <1> + + dest = conf.bldnode.make_node(cfg_file) + dest.parent.mkdir() <2> + dest.write(txt) <3> + + conf.env.append_value('cfg_files', dest.abspath()) <4> + +def build(ctx): + ctx(rule='cp ${SRC} ${TGT}', source=cfg_file, target='bar.txt') +--------------- + +<1> Read the file '/etc/fstab' +<2> Create the destination directory in case it does not already exist +<3> Create a new file in the build directory +<4> Mark the output as a configuration file so it can be used during the build + +The execution output will be the following: + +[source,shishell] +--------------- +$ waf configure build +Setting top to : /tmp/scenarios_impfile +Setting out to : /tmp/scenarios_impfile/build +'configure' finished successfully (0.003s) +Waf: Entering directory `/tmp/scenarios_impfile/build' +[1/1] bar.txt: build/somedir/foo.txt -> build/bar.txt +Waf: Leaving directory `/tmp/scenarios_impfile/build' +'build' finished successfully (0.008s) + +$ tree +. +|-- build +| |-- bar.txt +| |-- c4che +| | |-- build.config.py +| | `-- _cache.py +| |-- config.log +| `-- somedir +| `-- foo.txt +`-- wscript +--------------- + + + +=== Mixing extensions and C/C++ features + +==== Files processed by a single task generator + +Now let's illustrate the @extension decorator on idl file processing. Files with .idl extension are processed to produce .c and .h files (`foo.idl` → `foo.c` + `foo.h`). The .c files must be compiled after being generated. + +First, here is the declaration expected in user scripts: + +// scenarios_idl +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + conf.load('g++') + +def build(bld): + bld.program( + source = 'foo.idl main.cpp', + target = 'myapp' + ) +--------------- + +The file +foo.idl+ is listed as a source. It will be processed to +foo.cpp+ and compiled and linked with +main.cpp+ + +Here is the code to support this scenario: + +[source,python] +--------------- +from waflib.Task import Task +from waflib.TaskGen import extension + +class idl(Task): + run_str = 'cp ${SRC} ${TGT[0].abspath()} && touch ${TGT[1].abspath()}' <1> + color = 'BLUE' + ext_out = ['.h'] <2> + +@extension('.idl') +def process_idl(self, node): + cpp_node = node.change_ext('.cpp') + hpp_node = node.change_ext('.hpp') + self.create_task('idl', node, [cpp_node, hpp_node]) <3> + self.source.append(cpp_node) <4> +--------------- + +<1> Dummy command for demonstration purposes. In practice the rule to use would be like _omniidl -bcxx $\{SRC} -C$\{TGT}_ +<2> Because the idl task produces headers, it must be executed before any other +cpp+ file is compiled +<3> Create the task from the '.idl' extension. +<4> Reinject the file to compile by the C++ compiler + +The execution results will be the following: + +[source,shishell] +--------------- +$ waf distclean configure build -v +'distclean' finished successfully (0.002s) +Setting top to : /tmp/scenarios_idl +Setting out to : /tmp/scenarios_idl/build +Checking for program g++,c++ : /usr/bin/g++ +Checking for program ar : /usr/bin/ar +'configure' finished successfully (0.072s) +Waf: Entering directory `/tmp/scenarios_idl/build' +[1/4] idl: foo.idl -> build/foo.cpp build/foo.hpp +19:47:11 runner 'cp ../foo.idl foo.cpp && touch foo.hpp' +[2/4] cxx: main.cpp -> build/main.cpp.0.o +19:47:11 runner ['/usr/bin/g++', '-I.', '-I..', '../main.cpp', '-c', '-o', 'main.cpp.0.o'] +[3/4] cxx: build/foo.cpp -> build/foo.cpp.0.o +19:47:11 runner ['/usr/bin/g++', '-I.', '-I..', 'foo.cpp', '-c', '-o', 'foo.cpp.0.o'] +[4/4] cxxprogram: build/main.cpp.0.o build/foo.cpp.0.o -> build/myapp +19:47:11 runner ['/usr/bin/g++', 'main.cpp.0.o', 'foo.cpp.0.o', '-o', 'myapp'] +Waf: Leaving directory `/tmp/scenarios_idl/build' +'build' finished successfully (0.149s) +--------------- + +NOTE: The drawback of this declaration is that the source files produced by the idl transformation can be used by only one task generator. + +==== Resources shared by several task generators + +Let's suppose now that the idl outputs will be shared by several task generators. We will first start by writing the expected user script: + +// scenarios_idl2 +[source,python] +--------------- +top = '.' +out = 'out' + +def configure(ctx): + ctx.load('g++') + +def build(ctx): + ctx( <1> + source = 'notify.idl', + name = 'idl_gen') + + ctx.program( <2> + source = ['main.cpp'], + target = 'testprog', + includes = '.', + add_idl = 'idl_gen') <3> +--------------- + +<1> Process an idl file in a first task generator. Name this task generator 'idl_gen' +<2> Somewhere else (maybe in another script), another task generator will use the source generated by the idl processing +<3> Reference the idl processing task generator by the name 'idl_gen'. + +The code to support this scenario will be the following: + +[source,python] +--------------- +from waflib.Task import Task +from waflib.TaskGen import feature, before_method, extension + +class idl(Task): + run_str = 'cp ${SRC} ${TGT[0].abspath()} && touch ${TGT[1].abspath()}' + color = 'BLUE' + ext_out = ['.h'] <1> + +@extension('.idl') +def process_idl(self, node): + cpp_node = node.change_ext('.cpp') + hpp_node = node.change_ext('.hpp') + self.create_task('idl', node, [cpp_node, hpp_node]) + self.more_source = [cpp_node] <2> + +@feature('*') +@before_method('process_source') <3> +def process_add_source(self): + for x in self.to_list(getattr(self, 'add_idl', [])): <4> + y = self.bld.get_tgen_by_name(x) + y.post() <5> + if getattr(y, 'more_source', None): + self.source.extend(y.more_source) <6> +--------------- + +<1> The idl processing must be performed before any C++ task is executed +<2> Bind the output file to a new attribute +<3> Add the source from another task generator object +<4> Process _add_idl_, finding the other task generator +<5> Ensure that the other task generator has created its tasks +<6> Update the source list + +The task execution output will be very similar to the output from the first example: + +[source,shishell] +--------------- +$ waf distclean configure build -v +'distclean' finished successfully (0.007s) +Setting top to : /tmp/scenarios_idl2 +Setting out to : /tmp/scenarios_idl2/build +Checking for program g++,c++ : /usr/bin/g++ +Checking for program ar : /usr/bin/ar +'configure' finished successfully (0.080s) +Waf: Entering directory `/tmp/scenarios_idl2/build' +[1/4] idl: foo.idl -> build/foo.cpp build/foo.hpp +20:20:24 runner 'cp ../foo.idl foo.cpp && touch foo.hpp' +[2/4] cxx: main.cpp -> build/main.cpp.1.o +20:20:24 runner ['/usr/bin/g++', '-I.', '-I..', '../main.cpp', '-c', '-o', 'main.cpp.1.o'] +[3/4] cxx: build/foo.cpp -> build/foo.cpp.1.o +20:20:24 runner ['/usr/bin/g++', '-I.', '-I..', 'foo.cpp', '-c', '-o', 'foo.cpp.1.o'] +[4/4] cxxprogram: build/main.cpp.1.o build/foo.cpp.1.o -> build/testprog +20:20:24 runner ['/usr/bin/g++', 'main.cpp.1.o', 'foo.cpp.1.o', '-o', 'testprog'] +Waf: Leaving directory `/tmp/scenarios_idl2/build' +'build' finished successfully (0.130s) +--------------- + +=== Task generator methods + +==== Replacing particular attributes + +In general, task generator attributes are not replaced, so the following is not going to be compile +main.c+: + +[source,python] +--------------- +bld.env.FOO = '/usr/includes' +bld.env.MAIN = 'main.c' +bld( + features = 'c cprogram', + source = '${MAIN}', + target = 'app', + includes = '. ${FOO}') +--------------- + +This design decision is motivated by two main reasons: + +. Processing the attributes has a negative performance impact +. For consistency all attributes would have to be processed + +Nevertheless, it is we will demonstrate how to provide Waf with a method to process some attributes. To add a new task generator method, it is necessary to think about its integration with other methods: is there a particular order? The answer is yes, for example, the source attribute is used to create the compilation tasks. To display what methods are in use, execute Waf with the following logging key: + +[source,shishell] +--------------- +$ waf --zones=task_gen +... +19:20:51 task_gen posting task_gen 'app' declared in 'scenarios_expansion' <1> +19:20:51 task_gen -> process_rule (9232720) <2> +19:20:51 task_gen -> process_source (9232720) +19:20:51 task_gen -> apply_link (9232720) +19:20:51 task_gen -> apply_objdeps (9232720) +19:20:51 task_gen -> process_use (9232720) +19:20:51 task_gen -> propagate_uselib_vars (9232720) +19:20:51 task_gen -> apply_incpaths (9232720) +19:20:51 task_gen posted app +--------------- + +<1> Task generator execution +<2> Method name and task generator id in parentheses + +From the method list, we find that *process_rule* and *process_source* are processing the _source_ attribute. The _includes_ attribute is processed by *apply_incpaths*. + +// scenarios_expansion +[source,python] +--------------- +from waflib import Utils, TaskGen +@TaskGen.feature('*') <1> +@TaskGen.before('process_source', 'process_rule', 'apply_incpaths') <2> +def transform_strings(self): + for x in 'includes source'.split(): <3> + val = getattr(self, x, None) + if val: + if isinstance(val, str): + setattr(self, x, Utils.subst_vars(val, self.env)) <4> + elif isinstance(val, list): + for i in xrange(len(val)): + if isinstance(val[i], str): + val[i] = Utils.subst_vars(val[i], self.env) +--------------- + +<1> Execute this method in all task generators +<2> Methods to take into account +<3> Iterate over all interesting attributes +<4> Substitute the attributes + +==== Inserting special include flags + +A scenario that appears from times to times in C/C++ projects is the need to insert specific flags before others, regardless of how flags are usually processed. We will now consider the following case: execute all C++ compilations with the flag `-I.' in first position (before any other include). + +First, a look at the definition of the C++ compilation rule shows that the variable 'INCPATHS' contains the include flags: + +[source,python] +--------------- +class cxx(Task.Task): + color = 'GREEN' + run_str = '${CXX} ${CXXFLAGS} ${CPPPATH_ST:INCPATHS} ${CXX_SRC_F}${SRC} ${CXX_TGT_F}${TGT}' + vars = ['CXXDEPS'] + ext_in = ['.h'] + scan = c_preproc.scan +--------------- + +Those include flags are set by the method 'apply_incpaths'. The trick is then to modify 'INCPATHS' after that method has been executed: + +// scenarios_incflags +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + conf.load('g++') + +def build(bld): + bld.program(features='cxx cxxprogram', source='main.cpp', target='test') + +from waflib.TaskGen import after, feature + +@feature('cxx') +@after_method('apply_incpaths') +def insert_blddir(self): + self.env.prepend_value('INCPATHS', '.') +--------------- + +A related case is how to add the top-level directory containing a configuration header: + +[source,python] +--------------- +@feature('cxx') +@after_method('apply_incpaths', 'insert_blddir') +def insert_srcdir(self): + path = self.bld.srcnode.abspath() + self.env.prepend_value('INCPATHS', path) +--------------- + + +=== Custom tasks + +==== Force the compilation of a particular task + +In some applications, it may be interesting to keep track of the date and time of the last build. In C this may be done by using the macros `__DATE__' and `__TIME__', for example, the following +about.c+ file will contain: + +[source,c] +--------------- +void ping() { + printf("Project compiled: %s %s\n", __DATE__, __TIME__); +} +--------------- + +The files are only compiled when they change though, so it is necessary to find a way to force the +about.c+ recompilation. To sum up, the compilation should be performed whenever: + +. One of the c files of the project is compiled +. The link flags for any task change +. The link task including the object for our macro is removed + +To illustrate this behaviour, we will now set up a project will use various c files: + +// scenarios_end +[source,python] +--------------- +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + bld.program( + source = 'main.c about.c', + target = 'app', + includes = '.', + use = 'my_static_lib') + + bld.stlib( + source = 'test_staticlib.c', + target = 'my_static_lib') +--------------- + +The main file will just call the function _ping_ defined +about.c+ to display the date and time: + +[source,c] +--------------- +#include "a.h" + +int main() { + ping(); + return 0; +} +--------------- + +The task method _runnable_status_ must be overridden to take into account the dependencies: + +[source,python] +--------------- +import os +from waflib import Task +def runnable_status(self): + if self.inputs[0].name == 'about.c': <1> + h = 0 <2> + for g in self.generator.bld.groups: + for tg in g: + if isinstance(tg, TaskBase): + continue <3> + + h = hash((self.generator.bld.hash_env_vars(self.generator.env, ['LINKFLAGS']), h)) + for tsk in getattr(tg, 'compiled_tasks', []): # all .c or .cpp compilations + if id(tsk) == id(self): + continue + if not tsk.hasrun: + return Task.ASK_LATER + h = hash((tsk.signature(), h)) <4> + self.env.CCDEPS = h + + try: + os.stat(self.generator.link_task.outputs[0].abspath()) <5> + except: + return Task.RUN_ME + + return Task.Task.runnable_status(self) <6> + +from waflib.Tools.c import c <7> +c.runnable_status = runnable_status +--------------- + +<1> If the task processes +about.c+ +<2> Define a hash value that the task will depend on (CCDEPS) +<3> Iterate over all task generators of the project +<4> Hash the link flags and the signatures of all other compilation tasks +<5> Make sure to execute the task if it was never executed before +<6> Normal behaviour +<7> Modify the 'c' task class + +The execution will produce the following output: + +[source,shishell] +--------------- +$ waf +Waf: Entering directory `/tmp/scenarios_end/build' +[2/5] c: test_staticlib.c -> build/test_staticlib.c.1.o +[3/5] cstlib: build/test_staticlib.c.1.o -> build/libmy_static_lib.a +[4/5] c: about.c -> build/about.c.0.o +[5/5] cprogram: build/main.c.0.o build/about.c.0.o -> build/app +Waf: Leaving directory `/tmp/scenarios_end/build' <1> +'build' finished successfully (0.088s) + +$ ./build/app +Project compiled: Jul 25 2010 14:05:30 + +$ echo " " >> main.c <2> + +$ waf +Waf: Entering directory `/tmp/scenarios_end/build' +[1/5] c: main.c -> build/main.c.0.o +[4/5] c: about.c -> build/about.c.0.o <3> +[5/5] cprogram: build/main.c.0.o build/about.c.0.o -> build/app +Waf: Leaving directory `/tmp/scenarios_end/build' +'build' finished successfully (0.101s) + +$ ./build/app +Project compiled: Jul 25 2010 14:05:49 +--------------- + +<1> All files are compiled on the first build +<2> The file +main.c+ is modified +<3> The build generates +about.c+ again to update the build time string + +==== A compiler producing source files with names unknown in advance + +The requirements for this problem are the following: + +. A compiler *creates source files* (one .src file -> several .c files) +. The source file names to create are *known only when the compiler is executed* +. The compiler is slow so it should run *only when absolutely necessary* +. Other tasks will *depend on the generated files* (compile and link the .c files into a program) + +To do this, the information on the source files must be shared between the build executions. + +// scenarios_unknown +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + conf.load('gcc') + conf.load('mytool', tooldir='.') + +def build(bld): + bld.env.COMP = bld.path.find_resource('evil_comp.py').abspath() <1> + bld.stlib(source='x.c foo.src', target='astaticlib') <2> +--------------- + +<1> Compiler path +<2> An example, having a _.src_ file + +The contents of _mytool_ will be the following: + +[source,python] +--------------- +import os +from waflib import Task, Utils, Context +from waflib.Utils import subprocess +from waflib.TaskGen import extension + +@extension('.src') +def process_shpip(self, node): <1> + self.create_task('src2c', node) + +class src2c(Task.Task): + color = 'PINK' + quiet = True <2> + ext_out = ['.h'] <3> + + def run(self): + cmd = '%s %s' % (self.env.COMP, self.inputs[0].abspath()) + n = self.inputs[0].parent.get_bld() + n.mkdir() + cwd = n.abspath() + out = self.generator.bld.cmd_and_log(cmd, cwd=cwd, quiet=Context.STDOUT) <4> + + out = Utils.to_list(out) + self.outputs = [self.generator.path.find_or_declare(x) for x in out] + self.generator.bld.raw_deps[self.uid()] = [self.signature()] + self.outputs <5> + self.add_c_tasks(self.outputs) <6> + + def add_c_tasks(self, lst): + self.more_tasks = [] + for node in lst: + if node.name.endswith('.h'): + continue + tsk = self.generator.create_compiled_task('c', node) + self.more_tasks.append(tsk) <7> + + tsk.env.append_value('INCPATHS', [node.parent.abspath()]) + + if getattr(self.generator, 'link_task', None): <8> + self.generator.link_task.set_run_after(tsk) + self.generator.link_task.inputs.append(tsk.outputs[0]) + + def runnable_status(self): + ret = super(src2c, self).runnable_status() + if ret == Task.SKIP_ME: + + lst = self.generator.bld.raw_deps[self.uid()] + if lst[0] != self.signature(): + return Task.RUN_ME + + nodes = lst[1:] + for x in nodes: + try: + os.stat(x.abspath()) + except: + return Task.RUN_ME + + nodes = lst[1:] + self.set_outputs(nodes) + self.add_c_tasks(nodes) <9> + + return ret +--------------- + +<1> The processing will be delegated to the task +<2> Disable the warnings raised when a task has no outputs +<3> Make certain the processing will be executed before any task using _.h_ files +<4> When the task is executed, collect the process stdout which contains the generated file names +<5> Store the output file nodes in a persistent cache +<6> Create the tasks to compile the outputs +<7> The c tasks will be processed after the current task is done. This does not mean that the c tasks will always be executed. +<8> If the task generator of the _src_ file has a link task, set the build order +<9> When this task can be skipped, force the dynamic c task creation + +The output will be the following: + +[source,shishell] +--------------- +$ waf distclean configure build build +'distclean' finished successfully (0.006s) +Setting top to : /tmp/scenarios_unknown +Setting out to : /tmp/scenarios_unknown/build +Checking for program gcc,cc : /usr/bin/gcc +Checking for program ar : /usr/bin/ar +'configure' finished successfully (0.115s) +Waf: Entering directory `/tmp/scenarios_unknown/build' +[1/3] src2c: foo.src +[2/5] c: build/shpip/a12.c -> build/shpip/a12.c.0.o +[3/5] c: build/shpop/a13.c -> build/shpop/a13.c.0.o +[4/5] c: x.c -> build/x.c.0.o +[5/5] cstlib: build/x.c.0.o build/shpip/a12.c.0.o build/shpop/a13.c.0.o -> build/libastaticlib.a +Waf: Leaving directory `/tmp/scenarios_unknown/build' +'build' finished successfully (0.188s) +Waf: Entering directory `/tmp/scenarios_unknown/build' +Waf: Leaving directory `/tmp/scenarios_unknown/build' +'build' finished successfully (0.013s) +--------------- + diff --git a/docs/book/shishell.lang b/docs/book/shishell.lang new file mode 100644 index 00000000..b9d24ce9 --- /dev/null +++ b/docs/book/shishell.lang @@ -0,0 +1,3 @@ + +optionalargument = '^(\$|#)(.*?)$' + diff --git a/docs/book/source.dia b/docs/book/source.dia new file mode 100644 index 00000000..5f43145f --- /dev/null +++ b/docs/book/source.dia @@ -0,0 +1,837 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Transform the file into +a Node object# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Execute the mapping +with the node +as parameter# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #No mapping +raise an error# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Is there +a mapping?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #End# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Start# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #More entries +in self.source?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/book/symbols.lang b/docs/book/symbols.lang new file mode 100644 index 00000000..354fb047 --- /dev/null +++ b/docs/book/symbols.lang @@ -0,0 +1,4 @@ +co_symbol = "<1>","<2>","<3>","<4>","<5>","<6>","<7>","<8>", + "<9>","<10>","<11>","<12>","<13>","<14>","<15>" +symbol = "~","!","%","^","*","(",")","-","+","=","[", + "]","\\",":",";",",",".","/","?","&","<",">","\|" diff --git a/docs/book/task_generators.txt b/docs/book/task_generators.txt new file mode 100644 index 00000000..583f9f70 --- /dev/null +++ b/docs/book/task_generators.txt @@ -0,0 +1,339 @@ +=== General purpose task generators + +So far, various task generators uses have been demonstrated. This chapter provides a detailed description of task generator structure and usage. + +==== Task generator definition + +The chapter on make-like rules illustrated how the attribute 'rule' is processed. Then the chapter on name and extension-based file processing illustrated how the attribute 'source' is processed (in the absence of the rule attribute). To process 'any attribute', the following properties should hold: + +. Attributes should be processed only when the task generator is set to generate the tasks (lazy processing) +. There is no list of authorized attributes (task generators may be extended by user scripts) +. Attribute processing should be controlable on a task generator instance basis (special rules for particular task generators) +. The extensions should be split into independent files (low coupling between the Waf tools) + +Implementing such a system is a difficult problem which lead to the creation of very different designs: + +. _A hierarchy of task generator subclasses_ It was abandoned due to the high coupling between the Waf tools: the C tools required knowledge from the D tool for building hybrid applications +. _Method decoration (creating linked lists of method calls)_ Replacing or disabling a method safely was no longer possible (addition-only), so this system disappeared quickly +. _Flat method and execution constraint declaration_ The concept is close to aspect-oriented programming and might scare programmers. + +So far, the third design proved to be the most flexible and was kept. Here is how to define a task generator method: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + v = bld(myattr='Hello, world!') + v.myattr = 'Hello, world!' <1> + v.myMethod() <2> + +from waflib import TaskGen + +@TaskGen.taskgen_method <3> +def myMethod(tgen): <4> + print(getattr(self, 'myattr', None)) <5> +--------------- + +<1> Attributes may be set by arguments or by accessing the object. It is set two times in this example. +<2> Call the task generator method explicitly +<3> Use a python decorator +<4> Task generator methods have a unique argument representing the current instance +<5> Process the attribute 'myattr' when present (the case in the example) + +The output from the build will be the following: + +[source,shishell] +--------------- +$ waf distclean configure build +'distclean' finished successfully (0.001s) +'configure' finished successfully (0.001s) +Waf: Entering directory `/tmp/simpleproject/build' +hello world +Waf: Leaving directory `/tmp/simpleproject/build' +'build' finished successfully (0.003s) +--------------- + +NOTE: The method could be bound by using 'setattr' directly, like for binding any new method on a python class. + +==== Executing the method during the build + +So far, the task generator methods defined are only executed through explicit calls. Another decorator is necessary to have a task generator executed during the build phase automatically. Here is the updated example: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + bld(myattr='Hello, world!') + +from waflib import TaskGen + +@TaskGen.taskgen_method <1> +@TaskGen.feature('*') <2> +def methodName(self): + print(getattr(self, 'myattr', None)) +--------------- + +<1> Bind a method to the task generator class (redundant when other methods such as 'TaskGen.feature' are used) +<2> Bind the method to the symbol 'myfeature' + +The execution results will be the following: + +[source,shishell] +--------------- +$ waf distclean configure build --zones=task_gen <1> +'distclean' finished successfully (0.004s) +'configure' finished successfully (0.001s) +Waf: Entering directory `/tmp/simpleproject/build' +23:03:44 task_gen posting objects (normal) +23:03:44 task_gen posting >task_gen '' of type task_gen defined in dir:///tmp/simpleproject> 139657958706768 <2> +23:03:44 task_gen -> exec_rule (139657958706768) <3> +23:03:44 task_gen -> process_source (139657958706768) <4> +23:03:44 task_gen -> methodName (139657958706768) <5> +Hello, world! +23:03:44 task_gen posted <6> +Waf: Leaving directory `/tmp/simpleproject/build' +23:03:44 task_gen posting objects (normal) +'build' finished successfully (0.004s) +--------------- + +<1> The debugging zone 'task_gen' is used to display the task generator methods being executed +<2> Display which task generator is being executed +<3> The method 'exec_rule' is used to process the 'rule'. It is always executed. +<4> The method 'process_source' is used to process the 'source' attribute. It is always executed exept if the method 'exec_rule' processes a 'rule' attribute +<5> Our task generator method is executed, and prints 'Hello, world!' +<6> The task generator methods have been executed, the task generator is marked as done (posted) + +==== Task generator features + +So far, the task generator methods we added were declared to be executed by all task generator instances. Limiting the execution to specific task generators requires the use of the 'feature' decorator: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + bld(features='ping') + bld(features='ping pong') + +from waflib import TaskGen + +@TaskGen.feature('ping') +def ping(self): + print('ping') + +@TaskGen.feature('pong') +def pong(self): + print('pong') +--------------- + +The execution output will be the following: + +[source,shishell] +--------------- +$ waf distclean configure build --zones=task_gen +'distclean' finished successfully (0.003s) +'configure' finished successfully (0.001s) +Waf: Entering directory `/tmp/simpleproject/build' +16:22:07 task_gen posting objects (normal) +16:22:07 task_gen posting 140631018237584 +16:22:07 task_gen -> exec_rule (140631018237584) +16:22:07 task_gen -> process_source (140631018237584) +16:22:07 task_gen -> ping (140631018237584) +ping +16:22:07 task_gen posted +16:22:07 task_gen posting 140631018237776 +16:22:07 task_gen -> exec_rule (140631018237776) +16:22:07 task_gen -> process_source (140631018237776) +16:22:07 task_gen -> pong (140631018237776) +pong +16:22:07 task_gen -> ping (140631018237776) +ping +16:22:07 task_gen posted +Waf: Leaving directory `/tmp/simpleproject/build' +16:22:07 task_gen posting objects (normal) +'build' finished successfully (0.005s) +--------------- + +WARNING: Although the task generator instances are processed in order, the task generator method execution requires a specific declaration for the order of execution. Here, the method 'pong' is executed before the method 'ping' + +==== Task generator method execution order + +To control the execution order, two new decorators need to be added. We will now show a new example with two custom task generator methods 'method1' and 'method2', executed in that order: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + bld(myattr='Hello, world!') + +from waflib import TaskGen + +@TaskGen.feature('*') +@TaskGen.before('process_source', 'exec_rule') +def method1(self): + print('method 1 %r' % getattr(self, 'myattr', None)) + +@TaskGen.feature('*') +@TaskGen.before('process_source') +@TaskGen.after('method1') +def method2(self): + print('method 2 %r' % getattr(self, 'myattr', None)) +--------------- + +The execution output will be the following: + +[source,shishell] +--------------- +$ waf distclean configure build --zones=task_gen +'distclean' finished successfully (0.003s) +'configure' finished successfully (0.001s) +Waf: Entering directory `/tmp/simpleproject/build' +15:54:02 task_gen posting objects (normal) +15:54:02 task_gen posting 139808568487632 +15:54:02 task_gen -> method1 (139808568487632) +method 1 'Hello, world!' +15:54:02 task_gen -> exec_rule (139808568487632) +15:54:02 task_gen -> method2 (139808568487632) +method 2 'Hello, world!' +15:54:02 task_gen -> process_source (139808568487632) +15:54:02 task_gen posted +Waf: Leaving directory `/tmp/simpleproject/build' +15:54:02 task_gen posting objects (normal) +'build' finished successfully (0.005s) +--------------- + +==== Adding or removing a method for execution + +The order constraints on the methods (after/before), are used to sort the list of methods in the attribute 'meths'. The sorting is performed once, and the list is consumed as methods are executed. Though no new feature may be added once the first method is executed, new methods may be added dynamically in self.meths. Here is how to create an infinite loop by adding the same method at the end: + +[source,python] +--------------- +from waflib.TaskGen import feature + +@feature('*') +def infinite_loop(self): + self.meths.append('infinite_loop') +--------------- + +Likewise, methods may be removed from the list of methods to execute: + +[source,python] +--------------- +from waflib.TaskGen import feature + +@feature('*') +@before_method('process_source') +def remove_process_source(self): + self.meths.remove('process_source') +--------------- + +The task generator method workflow is represented in the following illustration: + +image::posting{PIC}["Task generator workflow"{backend@docbook:,width=320:},align="center"] + +==== Expressing abstract dependencies between task generators + +We will now illustrate how task generator methods can be used to express abstract dependencies between task generator objects. Here is a new project file located under '/tmp/targets/': + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + bld(rule='echo A', always=True, name='A') + bld(rule='echo B', always=True, name='B') +--------------- + +By executing 'waf --targets=B', only the task generator 'B' will create its tasks, and the output will be the following: + +[source,shishell] +--------------- +$ waf distclean configure build --targets=B +'distclean' finished successfully (0.000s) +'configure' finished successfully (0.042s) +Waf: Entering directory `/tmp/targets/build' +[1/1] B: +B +Waf: Leaving directory `/tmp/targets/build' +'build' finished successfully (0.032s) +--------------- + +Here is a way to ensure that the task generator 'A' has created its tasks when 'B' does: + +[source,python] +--------------- +top = '.' +out = 'build' + +def configure(conf): + pass + +def build(bld): + bld(rule='echo A', always=True, name='A') + bld(rule='echo B', always=True, name='B', depends_on='A') + +from waflib.TaskGen import feature, before_method +@feature('*') <1> +@before_method('process_rule') +def post_the_other(self): + deps = getattr(self, 'depends_on', []) <2> + for name in self.to_list(deps): + other = self.bld.get_tgen_by_name(name) <3> + print('other task generator tasks (before) %s' % other.tasks) + other.post() <4> + print('other task generator tasks (after) %s' % other.tasks) +--------------- + +<1> This method will be executed for all task generators, before the attribute `rule` is processed +<2> Try to process the attribute `depends_on`, if present +<3> Obtain the task generator by name, and for the same variant +<4> Force the other task generator to create its tasks + +The output will be: + +[source,shishell] +--------------- +$ waf distclean configure build --targets=B +'distclean' finished successfully (0.001s) +'configure' finished successfully (0.001s) +Waf: Entering directory `/tmp/targets/build' +other task generator tasks (before) [] <1> +other task generator tasks (after) [ <2> + {task: A -> }] +[1/2] B: +B +[2/2] A: <3> +A +Waf: Leaving directory `/tmp/targets/build' +'build' finished successfully (0.014s) +--------------- + +<1> The other task generator has not created any task yet +<2> A task generator creates all its tasks by calling its method `post()` +<3> Although `--targets=B` was requested, the task from target 'A' was created and executed too + +In practice, the dependencies will often re-use the task objects created by the other task generator: node, configuration set, etc. This is used by the uselib system (see the next chapter on c/c++ builds). + diff --git a/docs/book/task_run.dia b/docs/book/task_run.dia new file mode 100644 index 00000000..98fd6761 --- /dev/null +++ b/docs/book/task_run.dia @@ -0,0 +1,1132 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Execute the task +(method run)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Perform additional checks +(method post_run)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #EXCEPTION# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #SUCCESS# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Start# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #End# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Compute the status +(runnable_status)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #ASK_LATER# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #RUN_ME# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #SKIP_ME# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #CRASHED# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #MISSING +# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #SKIPPED# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #NOT_RUN# + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/book/task_signature.dia b/docs/book/task_signature.dia new file mode 100644 index 00000000..1f569633 --- /dev/null +++ b/docs/book/task_signature.dia @@ -0,0 +1,1327 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Execute the task# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Do the current and +previous signatures +match?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Associate the task uid +to the signature# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Compute the task +identifier (method uid)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Task was executed +successfully +and not skipped# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Compute the task signature +(method signature)# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Is a previous signature +associated to the uid +present?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Are the output nodes +associated to the +same signature?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Store the task signature +on the output nodes# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #ok# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Start# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #End# + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/book/tasks.txt b/docs/book/tasks.txt new file mode 100644 index 00000000..78d379f2 --- /dev/null +++ b/docs/book/tasks.txt @@ -0,0 +1,570 @@ + +== Task processing + +This chapter provides a description of the task classes which are used during the build phase. + +=== Task execution + +==== Main actors + +The build context is only used to create the tasks and to return lists of tasks that may be executed in parallel. The scheduling is delegated to a task producer which lets task consumers to execute the tasks. The task producer keeps a record of the build state such as the amount of tasks processed or the errors. + +image::tasks_actors{PIC}["Actors processing the tasks"{backend@docbook:,width=150:},align="center"] + +// To reduce the build time, it is interesting to take advantage of the hardware (multiple cpu cores) or of the environment (distributed builds). +The amount of consumers is determined from the number of processors, or may be set manually by using the '-j' option: + +[source,shishell] +------------------ +$ waf -j3 +------------------ + +==== Build groups + +The task producer iterates over lists of tasks returned by the build context. Although the tasks from a list may be executed in parallel by the consumer threads, all the tasks from one list must be consumed before processing another list of tasks. The build ends when there are no more tasks to process. + +These lists of tasks are called _build groups_ and may be accessed from the build scripts. Let's demonstrate this behaviour on an example: + +// tasks_groups +[source,python] +--------------- +def build(ctx): + for i in range(8): + ctx(rule='cp ${SRC} ${TGT}', source='wscript', target='wscript_a_%d' % i, + color='YELLOW', name='tasks a') + ctx(rule='cp ${SRC} ${TGT}', source='wscript_a_%d' % i, target='wscript_b_%d' % i, + color='GREEN', name='tasks b') + for i in range(8) + ctx(rule='cp ${SRC} ${TGT}', source='wscript', target='wscript_c_%d' % i, + color='BLUE', name='tasks c') + ctx(rule='cp ${SRC} ${TGT}', source='wscript_c_%d' % i, target='wscript_d_%d' % i, + color='PINK', name='tasks d') +--------------- + +Each green task must be executed after one yellow task and each pink task must be executed after one blue task. Because there is only one group by default, the parallel execution will be similar to the following: + +image::tasks_nogroup{PIC}["One build group"{backend@docbook:,width=440:},align="center"] + +We will now modify the example to add one more build group. + +[source,python] +--------------- +def build(ctx): + for i in range(8): + ctx(rule='cp ${SRC} ${TGT}', source='wscript', target='wscript_a_%d' % i, + color='YELLOW', name='tasks a') + ctx(rule='cp ${SRC} ${TGT}', source='wscript_a_%d' % i, target='wscript_b_%d' % i, + color='GREEN', name='tasks b') + ctx.add_group() + for i in range(8): + ctx(rule='cp ${SRC} ${TGT}', source='wscript', target='wscript_c_%d' % i, + color='BLUE', name='tasks c') + ctx(rule='cp ${SRC} ${TGT}', source='wscript_c_%d' % i, target='wscript_d_%d' % i, + color='PINK', name='tasks d') +--------------- + +Now a separator will appear between the group of yellow and green tasks and the group of blue and violet taks: + +image::tasks_twogroups{PIC}["Two build groups"{backend@docbook:,width=440:},align="center"] + +The tasks and tasks generator are added implicitely to the current group. By giving a name to the groups, it is easy to control what goes where: + +// tasks_groups2 +[source,python] +--------------- +def build(ctx): + + ctx.add_group('group1') + ctx.add_group('group2') + + for i in range(8): + ctx.set_group('group1') + ctx(rule='cp ${SRC} ${TGT}', source='wscript', target='wscript_a_%d' % i, + color='YELLOW', name='tasks a') + ctx(rule='cp ${SRC} ${TGT}', source='wscript_a_%d' % i, target='wscript_b_%d' % i, + color='GREEN', name='tasks b') + + ctx.set_group('group2') + ctx(rule='cp ${SRC} ${TGT}', source='wscript', target='wscript_c_%d' % i, + color='BLUE', name='tasks c') + ctx(rule='cp ${SRC} ${TGT}', source='wscript_c_%d' % i, target='wscript_d_%d' % i, + color='PINK', name='tasks d') +--------------- + +In the previous examples, all task generators from all build groups are processed before the build +actually starts. This default is provided to ensure that the task count is as accurate as possible. +Here is how to tune the build groups: + +[source,python] +--------------- +def build(ctx): + from waflib.Build import POST_LAZY, POST_BOTH, POST_AT_ONCE + ctx.post_mode = POST_AT_ONCE <1> + #ctx.post_mode = POST_LAZY <2> + #ctx.post_mode = POST_BOTH <3> +--------------- + +<1> All task generators create their tasks before the build starts (default behaviour) +<2> Groups are processed sequentially: all tasks from previous groups are executed before the task generators from the next group are processed +<3> Combination of the two previous behaviours: task generators created by tasks in the next groups may create tasks + +Build groups can be used for <> to process. + +==== The Producer-consumer system + +In most python interpreters, a global interpreter lock prevents parallelization by more than one cpu core at a time. Therefore, it makes sense to restrict the task scheduling on a single task producer, and to let the threads access only the task execution. + +The communication between producer and consumers is based on two queues _ready_ and _out_. The producer adds the tasks to _ready_ and reads them back from _out_. The consumers obtain the tasks from _ready_ and give them back to the producer into _out_ after executing 'task.run'. + +The producer uses the an internal list named _outstanding_ to iterate over the tasks and to decide which ones to put in the queue _ready_. The tasks that cannot be processed are temporarily output in the list _frozen_ to avoid looping endlessly over the tasks waiting for others. + +The following illustrates the relationship between the task producers and consumers as performed during the build: + +image::prodcons{PIC}["Parallel execution"{backend@docbook:,width=470:},align="center"] + +==== Task states and status + +A state is assigned to each task (_task.hasrun = state_) to keep track of the execution. The possible values are the following: + +[options="header", cols="1,1,6"] +|================= +|State | Numeric value | Description +|NOT_RUN | 0 | The task has not been processed yet +|MISSING | 1 | The task outputs are missing +|CRASHED | 2 | The task method 'run' returned a non-0 value +|EXCEPTION| 3 | An exception occured in the Task method 'run' +|SKIPPED | 8 | The task was skipped (it was up-to-date) +|SUCCESS | 9 | The execution was successful +|================= + +To decide to execute a task or not, the producer uses the value returned by the task method 'runnable_status'. The possible return values are the following: + +[options="header", cols="1,6"] +|================= +|Code | Description +| ASK_LATER | The task may depend on other tasks which have not finished to run (not ready) +| SKIP_ME | The task does not have to be executed, it is up-to-date +| RUN_ME | The task is ready to be executed +|================= + +The following diagram represents the interaction between the main task methods and the states and status: + +image::task_run{PIC}["Task states"{backend@docbook:,width=310:},align="center"] + +=== Build order constraints + +==== The method set_run_after + +The method _set_run_after_ is used to declare ordering constraints between tasks: + +[source,python] +--------------- +task1.set_run_after(task2) +--------------- + +The tasks to wait for are stored in the attribute _run_after_. They are used by the method _runnable_status_ to yield the status 'ASK_LATER' when a task has not run yet. This is merely for the build order and not for forcing a rebuild if one of the previous tasks is executed. + +==== Computed constraints + +===== Attribute after/before + +The attributes _before_ and _after_ are used to declare ordering constraints between tasks: + +[source,python] +--------------- +from waflib.Task import TaskBase +class task_test_a(TaskBase): + before = ['task_test_b'] +class task_test_b(TaskBase): + after = ['task_test_a'] +--------------- + +===== ext_in/ext_out + +Another way to force the order is by declaring lists of abstract symbols on the class attributes. This way the classes are not named explicitly, for example: + +[source,python] +--------------- +from waflib.Task import TaskBase +class task_test_a(TaskBase): + ext_in = ['.h'] +class task_test_b(TaskBase): + ext_out = ['.h'] +--------------- + +The 'extensions' ext_in and ext_out do not mean that the tasks have to produce files with such extensions, but are mere symbols for use as precedence constraints. + +===== Order extraction + +Before feeding the tasks to the producer-consumer system, a constraint extraction is performed on the tasks having input and output files. The attributes _run_after_ are initialized with the tasks to wait for. + +The two functions called on lists of tasks are: + +. _waflib.Task.set_precedence_constraints_: extract the build order from the task classes attributes ext_in/ext_out/before/after +. _waflib.Task.set_file_constraints_: extract the constraints from the tasks having input and output files + +==== Weak order constraints + +Tasks that are known to take a lot of time may be launched first to improve the build times. The general problem of finding an optimal order for launching tasks in parallel and with constraints is called http://en.wikipedia.org/wiki/Job-shop_problem[Job Shop]. In practice this problem can often be reduced to a critical path problem (approximation). + +The following pictures illustrate the difference in scheduling a build with different independent tasks, in which a slow task is clearly identified, and launched first: + +[source,python] +--------------- +def build(ctx): + for x in range(5): + ctx(rule='sleep 1', color='GREEN', name='short task') + ctx(rule='sleep 5', color='RED', name='long task') +--------------- + +image::tasks_nosort{PIC}["No particular order"{backend@docbook:,width=440:},align="center"] + +A function is used to reorder the tasks from a group before they are passed to the producer. We will replace it to reorder the long task in first position: + +// tasks_weak +[source,python] +--------------- +from waflib import Task +old = Task.set_file_constraints +def meth(lst): + lst.sort(cmp=lambda x, y: cmp(x.__class__.__name__, y.__class__.__name__)) <1> + old(lst) <2> +Task.set_file_constraints = meth <3> +--------------- + +<1> Set the long task in first position +<2> Execute the original code +<3> Replace the method + +Here is a representation of the effect: + +image::tasks_sort{PIC}["Slowest task first"{backend@docbook:,width=440:},align="center"] + +=== Dependencies + +==== Task signatures + +The direct instances of 'waflib.Task.TaskBase' are very limited and cannot be used to track file changes. The subclass 'waflib.Task.Task' provides the necessary features for the most common builds in which source files are used to produce target files. + +The dependency tracking is based on the use of hashes of the dependencies called *task signatures*. The signature is computed from various dependencies source, such as input files and configuration set values. + +The following diagram describes how 'waflib.Task.Task' instances are processed: + +image::task_signature{PIC}["Signatures"{backend@docbook:,height=350:},align="center"] + +The following data is used in the signature computation: + +. Explicit dependencies: _input nodes_ and dependencies set explicitly by using _bld.depends_on_ +. Implicit dependencies: dependencies searched by a scanner method (the method _scan_) +. Values: configuration set values such as compilation flags + +==== Explicit dependencies + +===== Input and output nodes + +The task objects do not directly depend on other tasks. Other tasks may exist or not, and be executed or nodes. Rather, the input and output nodes hold themselves signatures values, which come from different sources: + +. Nodes for build files usually inherit the signature of the task that generated the file +. Nodes from elsewhere have a signature computed automatically from the file contents (hash) + +===== Global dependencies on other nodes + +The tasks may be informed that some files may depend on other files transitively without listing them in the inputs. This is achieved by the method _add_manual_dependency_ from the build context: + +// tasks_manual_deps +[source,python] +--------------- +def configure(ctx): + pass + +def build(ctx): + ctx(rule='cp ${SRC} ${TGT}', source='wscript', target='somecopy') + ctx.add_manual_dependency( + ctx.path.find_node('wscript'), + ctx.path.find_node('testfile')) +--------------- + +The file _somecopy_ will be rebuilt whenever _wscript_ or _testfile_ change, even by one character: + +[source,shishell] +--------------- +$ waf build +Waf: Entering directory `/tmp/tasks_manual_deps/build' +[1/1] somecopy: wscript -> build/somecopy +Waf: Leaving directory `/tmp/tasks_manual_deps/build' +'build' finished successfully (0.034s) + +$ waf +Waf: Entering directory `/tmp/tasks_manual_deps/build' +Waf: Leaving directory `/tmp/tasks_manual_deps/build' +'build' finished successfully (0.006s) + +$ echo " " >> testfile + +$ waf +Waf: Entering directory `/tmp/tasks_manual_deps/build' +[1/1] somecopy: wscript -> build/somecopy +Waf: Leaving directory `/tmp/tasks_manual_deps/build' +'build' finished successfully (0.022s) +--------------- + +==== Implicit dependencies (scanner methods) + +Some tasks can be created dynamically after the build has started, so the dependencies cannot be known in advance. Task subclasses can provide a method named _scan_ to obtain additional nodes implicitly. In the following example, the _copy_ task provides a scanner method to depend on the wscript file found next to the input file. + +// tasks_scan +[source,python] +--------------- +import time +from waflib.Task import Task +class copy(Task): + + def run(self): + return self.exec_command('cp %s %s' % (self.inputs[0].abspath(), self.outputs[0].abspath())) + + def scan(self): <1> + print('→ calling the scanner method') + node = self.inputs[0].parent.find_resource('wscript') + return ([node], time.time()) <2> + + def runnable_status(self): + ret = super(copy, self).runnable_status() <3> + bld = self.generator.bld <4> + print('nodes: %r' % bld.node_deps[self.uid()]) <5> + print('custom data: %r' % bld.raw_deps[self.uid()]) <6> + return ret + +def configure(ctx): + pass + +def build(ctx): + tsk = copy(env=ctx.env) <7> + tsk.set_inputs(ctx.path.find_resource('a.in')) + tsk.set_outputs(ctx.path.find_or_declare('b.out')) + ctx.add_to_group(tsk) +--------------- + +<1> A scanner method +<2> The return value is a tuple containing a list of nodes to depend on and serializable data for custom uses +<3> Override the method runnable_status to add some logging +<4> Obtain a reference to the build context associated to this task +<5> The nodes returned by the scanner method are stored in the map *bld.node_deps* +<6> The custom data returned by the scanner method is stored in the map *bld.raw_deps* +<7> Create a task manually (encapsulation by task generators will be described in the next chapters) + +[source,shishell] +--------------- +$ waf +→ calling the scanner method <1> +nodes: [/tmp/tasks_scan/wscript] +custom data: 55.51 +[1/1] copy: a.in -> build/b.out +'build' finished successfully (0.021s) + +$ waf <2> +nodes: [/tmp/tasks_scan/wscript] +custom data: 1280561555.512006 +'build' finished successfully (0.005s) + +$ echo " " >> wscript <3> + +$ waf +→ calling the scanner method +nodes: [/tmp/tasks_scan/wscript] +custom data: 64.31 +[1/1] copy: a.in -> build/b.out +'build' finished successfully (0.022s) +--------------- + +<1> The scanner method is always called on a clean build +<2> The scanner method is not called when nothing has changed, although the data returned is retrieved +<3> When a dependency changes, the scanner method is executed once again (the custom data has changed) + +WARNING: If the build order is incorrect, the method _scan_ may fail to find dependent nodes (missing nodes) or the signature calculation may throw an exception (missing signature for dependent nodes). + +==== Values + +The habitual use of command-line parameters such as compilation flags lead to the creation of _dependencies on values_, and more specifically the configuration set values. The Task class attribute 'vars' is used to control what values can enter in the signature calculation. In the following example, the task created has no inputs and no outputs nodes, and only depends on the values. + +// tasks_values +[source,python] +--------------- +from waflib.Task import Task +class foo(Task): <1> + vars = ['FLAGS'] <2> + def run(self): + print('the flags are %r' % self.env.FLAGS) <3> + +def options(ctx): + ctx.add_option('--flags', default='-f', dest='flags', type='string') + +def build(ctx): + ctx.env.FLAGS = ctx.options.flags <4> + tsk = foo(env=ctx.env) + ctx.add_to_group(tsk) + +def configure(ctx): + pass +--------------- + +<1> Create a task class named _foo_ +<2> The task instances will be executed whenever 'self.env.FLAGS' changes +<3> Print the value for debugging purposes +<4> Read the value from the command-line + +The execution will produce the following output: + +[source,shishell] +--------------- +$ waf --flags abcdef +[1/1] foo: +the flags are 'abcdef' <1> +'build' finished successfully (0.006s) + +$ waf --flags abcdef <2> +'build' finished successfully (0.004s) + +$ waf --flags abc +[1/1] foo: <3> +the flags are 'abc' +'build' finished successfully (0.006s) +--------------- + +<1> The task is executed on the first run +<2> The dependencies have not changed, so the task is not executed +<3> The flags have changed so the task is executed + +=== Task tuning + +==== Class access + +When a task provides an attribute named _run_str_ as in the following example: + +// tasks_values2 +[source,python] +--------------- +def configure(ctx): + ctx.env.COPY = '/bin/cp' + ctx.env.COPYFLAGS = ['-f'] + +def build(ctx): + from waflib.Task import Task + class copy(Task): + run_str = '${COPY} ${COPYFLAGS} ${SRC} ${TGT}' + print(copy.vars) + + tsk = copy(env=ctx.env) + tsk.set_inputs(ctx.path.find_resource('wscript')) + tsk.set_outputs(ctx.path.find_or_declare('b.out')) + ctx.add_to_group(tsk) +--------------- + +It is assumed that 'run_str' represents a command-line, and that the variables in _$\{}_ such as 'COPYFLAGS' represent variables to add to the dependencies. A metaclass processes 'run_str' to obtain the method 'run' (called to execute the task) and the variables in the attribute 'vars' (merged with existing variables). The function created is displayed in the following output: + +[source,shishell] +--------------- +$ waf --zones=action +13:36:49 action def f(tsk): + env = tsk.env + gen = tsk.generator + bld = gen.bld + wd = getattr(tsk, 'cwd', None) + def to_list(xx): + if isinstance(xx, str): return [xx] + return xx + lst = [] + lst.extend(to_list(env['COPY'])) + lst.extend(to_list(env['COPYFLAGS'])) + lst.extend([a.path_from(bld.bldnode) for a in tsk.inputs]) + lst.extend([a.path_from(bld.bldnode) for a in tsk.outputs]) + lst = [x for x in lst if x] + return tsk.exec_command(lst, cwd=wd, env=env.env or None) +[1/1] copy: wscript -> build/b.out +['COPY', 'COPYFLAGS'] +'build' finished successfully (0.007s) +--------------- + +All subclasses of 'waflib.Task.TaskBase' are stored on the module attribute 'waflib.Task.classes'. Therefore, the 'copy' task can be accessed by using: + +[source,python] +--------------- +from waflib import Task +cls = Task.classes['copy'] +--------------- + +==== Scriptlet expressions + +Although the 'run_str' is aimed at configuration set variables, a few special cases are provided for convenience: + +. If the value starts by *env*, *gen*, *bld* or *tsk*, a method call will be made +. If the value starts by SRC[n] or TGT[n], a method call to the input/output node _n_ will be made +. SRC represents the list of task inputs seen from the root of the build directory +. TGT represents the list of task outputs seen from the root of the build directory + +Here are a few examples: + +[source,python] +--------------- +${SRC[0].parent.abspath()} <1> +${bld.root.abspath()} <2> +${tsk.uid()} <3> +${CPPPATH_ST:INCPATHS} <4> +--------------- + +<1> Absolute path of the parent folder of the task first source file +<2> File system root +<3> Print the task unique identifier +<4> Perform a map replacement equivalent to _[env.CPPPATH_ST % x for x in env.INCPATHS]_ + +==== Direct class modifications + +===== Always execute + +The function 'waflib.Task.always_run' is used to force a task to be executed whenever a build is performed. It sets a method 'runnable_status' that always return _RUN_ME_. + +// task_always +[source,python] +--------------- +def configure(ctx): + pass + +def build(ctx): + from waflib import Task + class copy(Task.Task): + run_str = 'cp ${SRC} ${TGT}' + copy = waflib.Task.always_run(copy) + + tsk = copy(env=ctx.env) + tsk.set_inputs(ctx.path.find_resource('wscript')) + tsk.set_outputs(ctx.path.find_or_declare('b.out')) + ctx.add_to_group(tsk) +--------------- + +For convenience, rule-based task generators can declare the *always* attribute to achieve the same results: + +[source,python] +--------------- +def build(ctx): + ctx( + rule = 'echo hello', + always = True + ) +--------------- + +===== File hashes + +To detect if the outputs have really been produced by a task, the task signature is used as the signature of the task nodes outputs. As a consequence, files created in the source directory are going to be rebuilt each time. To avoid this, the node signature should match the actual file contents. This is enforced by using the function 'waflib.Task.update_outputs' on task classes. It replaces the methods _post_run_ and _runnable_status_ of a task class to set the hash file contents to the output nodes. + +For convenience, rule-based task generators can declare the *update_outputs* attribute to achieve the same results: + +[source,python] +--------------- +def build(ctx): + ctx( + rule = 'cp ${SRC} ${TGT}', + source = 'wcript', + target = ctx.path.make_node('wscript2'), + update_outputs = True + ) +--------------- + diff --git a/docs/book/tasks_actors.dia b/docs/book/tasks_actors.dia new file mode 100644 index 00000000..57979885 --- /dev/null +++ b/docs/book/tasks_actors.dia @@ -0,0 +1,533 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #BuildContext# + + + + + + + + + + + + + + + + + + + + ## + + + ## + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Task Producer# + + + + + + + + + + + + + + + + + + + + ## + + + ## + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Task consumer# + + + + + + + + + + + + + + + + + + + + ## + + + ## + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Process a task# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Get tasks to process# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Task consumer n# + + + + + + + + + + + + + + + + + + + + ## + + + ## + + + + + ## + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Process a task# + + + + + + + + + + + + + + diff --git a/docs/book/tasks_nogroup.eps b/docs/book/tasks_nogroup.eps new file mode 100644 index 00000000..c034854e --- /dev/null +++ b/docs/book/tasks_nogroup.eps @@ -0,0 +1,700 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.8.10 (http://cairographics.org) +%%CreationDate: Thu Jul 29 16:47:07 2010 +%%Pages: 1 +%%BoundingBox: 0 0 637 164 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%EndComments +%%BeginProlog +/cairo_eps_state save def +/dict_count countdictstack def +/op_count count 1 sub def +userdict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/B { fill stroke } bind def +/B* { eofill stroke } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +%%EndProlog +11 dict begin +/FontType 42 def +/FontName /f-0-0 def +/PaintType 0 def +/FontMatrix [ 1 0 0 1 0 0 ] def +/FontBBox [ 0 0 0 0 ] def +/Encoding 256 array def +0 1 255 { Encoding exch /.notdef put } for +Encoding 1 /uni0050 put +Encoding 2 /uni0061 put +Encoding 3 /uni0072 put +Encoding 4 /uni006C put +Encoding 5 /uni0065 put +Encoding 6 /uni0020 put +Encoding 7 /uni0062 put +Encoding 8 /uni0075 put +Encoding 9 /uni0069 put +Encoding 10 /uni0064 put +Encoding 11 /uni0070 put +Encoding 12 /uni0073 put +Encoding 13 /uni006E put +Encoding 14 /uni0074 put +Encoding 15 /uni006F put +Encoding 16 /uni0066 put +Encoding 17 /uni0022 put +Encoding 18 /uni0077 put +Encoding 19 /uni002D put +Encoding 20 /uni006A put +Encoding 21 /uni0034 put +Encoding 22 /uni0028 put +Encoding 23 /uni0067 put +Encoding 24 /uni0079 put +Encoding 25 /uni0029 put +Encoding 26 /uni006B put +Encoding 27 /uni0063 put +/CharStrings 28 dict dup begin +/.notdef 0 def +/uni0050 1 def +/uni0061 2 def +/uni0072 3 def +/uni006C 4 def +/uni0065 5 def +/uni0020 6 def +/uni0062 7 def +/uni0075 8 def +/uni0069 9 def +/uni0064 10 def +/uni0070 11 def +/uni0073 12 def +/uni006E 13 def +/uni0074 14 def +/uni006F 15 def +/uni0066 16 def +/uni0022 17 def +/uni0077 18 def +/uni002D 19 def +/uni006A 20 def +/uni0034 21 def +/uni0028 22 def +/uni0067 23 def +/uni0079 24 def +/uni0029 25 def +/uni006B 26 def +/uni0063 27 def +end readonly def +/sfnts [ +<00010000000a008000030020636d617000f2f18200001328000000766376742000691d390000 +13a0000001fe6670676d7134766a000015a0000000ab676c79662855b3c7000000ac0000127c +68656164f1f329920000164c00000036686865610cb8066d0000168400000024686d747873a1 +0dd5000016a8000000706c6f63610000f22400001718000000746d617870048906710000178c +00000020707265703b07f100000017ac0000056800020066fe96046605a400030007001a400c +04fb0006fb0108057f0204002fc4d4ec310010d4ecd4ec301311211125211121660400fc7303 +1bfce5fe96070ef8f2720629000200c90000048d05d500080013003a40180195100095098112 +100a0802040005190d3f11001c09041410fcec32fcec11173931002ff4ecd4ec30400b0f151f +153f155f15af1505015d011133323635342623252132041514042b0111230193fe8d9a9a8dfe +3801c8fb0101fefffbfeca052ffdcf92878692a6e3dbdde2fda80002007bffe3042d047b000a +002500bc4027191f0b17090e00a91706b90e1120861fba1cb923b8118c170c001703180d0908 +0b1f030814452610fcecccd4ec323211393931002fc4e4f4fcf4ec10c6ee10ee113911391239 +30406e301d301e301f3020302130223f27401d401e401f402040214022501d501e501f502050 +21502250277027851d871e871f8720872185229027a027f0271e301e301f30203021401e401f +40204021501e501f50205021601e601f60206021701e701f70207021801e801f80208021185d +015d0122061514163332363d01371123350e01232226353436332135342623220607353e0133 +321602bedfac816f99b9b8b83fbc88accbfdfb0102a79760b65465be5af3f00233667b6273d9 +b4294cfd81aa6661c1a2bdc0127f8b2e2eaa2727fc00000100ba0000034a047b001100304014 +060b0700110b03870eb809bc070a06080008461210fcc4ec3231002fe4f4ecc4d4cc11123930 +b450139f1302015d012e012322061511231133153e0133321617034a1f492c9ca7b9b93aba85 +132e1c03b41211cbbefdb20460ae666305050000000100c100000179061400030022b7009702 +010800460410fcec31002fec30400d10054005500560057005f00506015d13331123c1b8b806 +14f9ec0000020071ffe3047f047b0014001b00704024001501098608880515a90105b90c01bb +18b912b80c8c1c1b1502081508004b02120f451c10fcecf4ecc4111239310010e4f4ece410ee +10ee10f4ee1112393040293f1d701da01dd01df01d053f003f013f023f153f1b052c072f082f +092c0a6f006f016f026f156f1b095d71015d0115211e0133323637150e012320001110003332 +00072e0123220607047ffcb20ccdb76ac76263d06bfef4fec70129fce20107b802a5889ab90e +025e5abec73434ae2a2c0138010a01130143feddc497b4ae9e00000200baffe304a40614000b +001c0038401903b90c0f09b918158c0fb81b971900121247180c06081a461d10fcec3232f4ec +31002fece4f4c4ec10c6ee30b6601e801ea01e03015d013426232206151416333236013e0133 +3200111002232226271523113303e5a79292a7a79292a7fd8e3ab17bcc00ffffcc7bb13ab9b9 +022fcbe7e7cbcbe7e702526461febcfef8fef8febc6164a80614000200aeffe30458047b0013 +0014003b401c030900030e0106870e118c0a01bc14b80c0d0908140b4e020800461510fcecf4 +39ec3231002fe4e432f4c4ec1112173930b46f15c01502015d13113311141633323635113311 +23350e0123222601aeb87c7c95adb8b843b175c1c801cf01ba02a6fd619f9fbea4027bfba0ac +6663f003a800000200c100000179061400030007002b400e06be04b100bc0205010804004608 +10fc3cec3231002fe4fcec30400b1009400950096009700905015d1333112311331523c1b8b8 +b8b80460fba00614e90000020071ffe3045a06140010001c003840191ab9000e14b905088c0e +b801970317040008024711120b451d10fcecf4ec323231002fece4f4c4ec10c4ee30b6601e80 +1ea01e03015d0111331123350e0123220211100033321601141633323635342623220603a2b8 +b83ab17ccbff00ffcb7cb1fdc7a79292a8a89292a703b6025ef9eca864610144010801080144 +61fe15cbe7e7cbcbe7e7000200bafe5604a4047b0010001c003e401b1ab9000e14b90508b80e +8c01bd03bc1d11120b471704000802461d10fcec3232f4ec310010e4e4e4f4c4ec10c4ee3040 +09601e801ea01ee01e04015d2511231133153e01333200111002232226013426232206151416 +3332360173b9b93ab17bcc00ffffcc7bb10238a79292a7a79292a7a8fdae060aaa6461febcfe +f8fef8febc6101ebcbe7e7cbcbe7e70000000001006fffe303c7047b002700e7403c0d0c020e +0b531f1e080902070a531f1f1e420a0b1e1f041500860189041486158918b91104b925b8118c +281e0a0b1f1b0700521b080e07081422452810fcc4ecd4ece4111239393939310010e4f4ec10 +fef5ee10f5ee121739304b535807100eed111739070eed1117395922b2002701015d406d1c0a +1c0b1c0c2e092c0a2c0b2c0c3b093b0a3b0b3b0c0b200020012402280a280b2a132f142f152a +16281e281f292029212427860a860b860c860d12000000010202060a060b030c030d030e030f +03100319031a031b031c041d09272f293f295f297f2980299029a029f029185d005d7101152e +012322061514161f011e0115140623222627351e013332363534262f012e0135343633321603 +8b4ea85a898962943fc4a5f7d85ac36c66c661828c65ab40ab98e0ce66b4043fae2828545440 +49210e2a99899cb62323be353559514b50250f2495829eac1e000000000100ba00000464047b +001300364019030900030e0106870e11b80cbc0a010208004e0d09080b461410fcec32f4ec31 +002f3ce4f4c4ec1112173930b46015cf1502015d0111231134262322061511231133153e0133 +32160464b87c7c95acb9b942b375c1c602a4fd5c029e9f9ebea4fd870460ae6564ef00010037 +000002f2059e0013003840190e05080f03a9001101bc08870a0b08090204000810120e461410 +fc3cc4fc3cc432393931002fecf43cc4ec3211393930b2af1501015d01112115211114163b01 +152322263511233533110177017bfe854b73bdbdd5a28787059efec28ffda0894e9a9fd20260 +8f013e00000000020071ffe30475047b000b0017004a401306b91200b90cb8128c1809120f51 +031215451810fcecf4ec310010e4f4ec10ee3040233f197b007b067f077f087f097f0a7f0b7b +0c7f0d7f0e7f0f7f107f117b12a019f01911015d012206151416333236353426273200111000 +232200111000027394acab9593acac93f00112feeef0f1feef011103dfe7c9c9e7e8c8c7e99c +fec8feecfeedfec7013901130114013800000001002f000002f8061400130059401c0510010c +08a906018700970e06bc0a02130700070905080d0f0b4c1410fc4bb00a5458b9000b00403859 +4bb00e5458b9000bffc038593cc4fc3cc4c412393931002fe432fcec10ee321239393001b640 +155015a015035d01152322061d012115211123112335333534363302f8b0634d012ffed1b9b0 +b0aebd0614995068638ffc2f03d18f4ebbab000200c503aa02e905d5000300070042400f0501 +8404008108040506000502040810fc4bb012544bb013545b58b90002ffc03859fcdcec310010 +f43cec323001400f30094009500960097009a009bf09075d0111231121112311016faa0224aa +05d5fdd5022bfdd5022b000000010056000006350460000c01eb404905550605090a0904550a +0903550a0b0a025501020b0b0a061107080705110405080807021103020c000c011100000c42 +0a050203060300bf0b080c0b0a09080605040302010b07000d10d44bb00a544bb011545b4bb0 +12545b4bb013545b4bb00b545b58b9000000403859014bb00c544bb00d545b4bb010545b58b9 +0000ffc03859cc173931002f3cec32321739304b5358071005ed071008ed071008ed071005ed +071008ed071005ed0705ed071008ed59220140ff050216021605220a350a49024905460a400a +5b025b05550a500a6e026e05660a79027f0279057f05870299029805940abc02bc05ce02c703 +cf051d0502090306040b050a080b09040b050c1502190316041a051b081b09140b150c250025 +0123022703210425052206220725082709240a210b230c390336043608390c300e4602480346 +04400442054006400740084409440a440b400e400e5600560156025004510552065207500853 +09540a550b6300640165026a0365046a056a066a076e09610b670c6f0e7500750179027d0378 +047d057a067f067a077f07780879097f097b0a760b7d0c870288058f0e97009701940293039c +049b05980698079908402f960c9f0ea600a601a402a403ab04ab05a906a907ab08a40caf0eb5 +02b103bd04bb05b809bf0ec402c303cc04ca05795d005d13331b01331b013301230b012356b8 +e6e5d9e6e5b8fedbd9f1f2d90460fc96036afc96036afba00396fc6a0001006401df027f0283 +00030011b6009c020401000410dccc310010d4ec301321152164021bfde50283a4000002ffdb +fe5601790614000b000f0044401c0b0207000ebe0c078705bd00bc0cb110081005064f0d0108 +0c00461010fc3cec32e4391239310010ece4f4ec10ee1112393930400b101140115011601170 +1105015d13331114062b01353332363511331523c1b8a3b54631694cb8b80460fb8cd6c09c61 +990628e9000000020064000004a405d50002000d0081401d010d030d0003030d4200030b07a0 +0501038109010c0a001c0608040c0e10dc4bb00b544bb00d545b58b9000cffc03859d43cc4ec +32113931002fe4d43cec321239304b5358071004c9071005c9592201402a0b002a0048005900 +690077008a000716012b0026012b0336014e014f0c4f0d5601660175017a0385010d5d005d09 +012103331133152311231121350306fe0201fe35fed5d5c9fd5e0525fce303cdfc33a8fea001 +60c30000000100b0fef2027b0612000d0037400f069800970e0d070003120600130a0e10dc4b +b0135458b9000affc038594bb00f5458b9000a00403859e432ec113939310010fcec30010602 +1514121723260235341237027b86828385a0969594970612e6fe3ee7e7fe3be5eb01c6e0df01 +c4ec00020071fe56045a047b000b0028004a4023190c1d0912861316b90f03b92623b827bc09 +b90fbd1a1d261900080c4706121220452910fcc4ecf4ec323231002fc4e4ece4f4c4ec10fed5 +ee1112393930b6602a802aa02a03015d01342623220615141633323617100221222627351e01 +3332363d010e0123220211101233321617353303a2a59594a5a59495a5b8fefefa61ac51519e +52b5b439b27ccefcfcce7cb239b8023dc8dcdcc8c7dcdcebfee2fee91d1eb32c2abdbf5b6362 +013a01030104013a6263aa000001003dfe56047f0460000f018b40430708020911000f0a110b +0a00000f0e110f000f0d110c0d00000f0d110e0d0a0b0a0c110b0b0a420d0b0910000b058703 +bd0e0bbc100e0d0c0a09060300080f040f0b1010d44bb00a544bb008545b58b9000b00403859 +4bb0145458b9000bffc03859c4c4111739310010e432f4ec113911391239304b5358071005ed +071008ed071008ed071005ed071008ed0705ed173259220140f0060005080609030d160a170d +100d230d350d490a4f0a4e0d5a095a0a6a0a870d800d930d120a000a09060b050c0b0e0b0f17 +01150210041005170a140b140c1a0e1a0f2700240124022004200529082809250a240b240c27 +0d2a0e2a0f201137003501350230043005380a360b360c380d390e390f301141004001400240 +03400440054006400740084209450a470d490e490f4011540051015102550350045005560655 +0756085709570a550b550c590e590f501166016602680a690e690f60117b08780e780f89008a +09850b850c890d890e890f9909950b950c9a0e9a0fa40ba40cab0eab0fb011cf11df11ff1165 +5d005d050e012b01353332363f01013309013302934e947c936c4c543321fe3bc3015e015ec3 +68c87a9a488654044efc94036c000000000100a4fef2026f0612000d001f400f079800970e07 +01000b12041308000e10dc3cf4ec113939310010fcec301333161215140207233612353402a4 +a096959596a08583830612ecfe3cdfe0fe3aebe501c5e7e701c20000000100ba0000049c0614 +000a00bc40290811050605071106060503110405040211050504420805020303bc0097090605 +01040608010800460b10fcec32d4c4113931002f3cece41739304b5358071004ed071005ed07 +1005ed071004ed5922b2100c01015d405f04020a081602270229052b08560266026708730277 +05820289058e08930296059708a3021209050906020b030a072803270428052b062b07400c68 +03600c8903850489058d068f079a039707aa03a705b607c507d607f703f003f704f0041a5d71 +005d1333110133090123011123bab90225ebfdae026bf0fdc7b90614fc6901e3fdf4fdac0223 +fddd00010071ffe303e7047b0019003f401b00860188040e860d880ab91104b917b8118c1a07 +120d004814451a10fce432ec310010e4f4ec10fef4ee10f5ee30400b0f1b101b801b901ba01b +05015d01152e0123220615141633323637150e0123220011100021321603e74e9d50b3c6c6b3 +509d4e4da55dfdfed6012d010655a20435ac2b2be3cdcde32b2baa2424013e010e0112013a23 +000000000002000300000000001400010000000000340004002000000004000400010000f01b +ffff0000f000ffff10000001000000000006004200000000001c000000010002000300040005 +0006000700080009000a000b000c000d000e000f001000110012001300140015001600170018 +0019001a001b0000013500b800cb00cb00c100aa009c01a600b800660000007100cb00a002b2 +0085007500b800c301cb0189022d00cb00a600f000d300aa008700cb03aa0400014a003300cb +000000d9050200f4015400b4009c01390114013907060400044e04b4045204b804e704cd0037 +047304cd04600473013303a2055605a60556053903c5021200c9001f00b801df007300ba03e9 +033303bc0444040e00df03cd03aa00e503aa0404000000cb008f00a4007b00b80014016f007f +027b0252008f00c705cd009a009a006f00cb00cd019e01d300f000ba018300d5009803040248 +009e01d500c100cb00f600830354027f00000333026600d300c700a400cd008f009a00730400 +05d5010a00fe022b00a400b4009c00000062009c0000001d032d05d505d505d505f0007f007b +005400a406b80614072301d300b800cb00a601c301ec069300a000d3035c037103db01850423 +04a80448008f0139011401390360008f05d5019a0614072306660179046004600460047b009c +00000277046001aa00e904600762007b00c5007f027b000000b4025205cd006600bc00660077 +061000cd013b01850389008f007b0000001d00cd074a042f009c009c0000077d006f0000006f +0335006a006f007b00ae00b2002d0396008f027b00f600830354063705f6008f009c04e10266 +008f018d02f600cd03440029006604ee00730000140000960000b707060504030201002c2010 +b002254964b040515820c859212d2cb002254964b040515820c859212d2c20100720b00050b0 +0d7920b8ffff5058041b0559b0051cb0032508b0042523e120b00050b00d7920b8ffff505804 +1b0559b0051cb0032508e12d2c4b505820b0fd454459212d2cb002254560442d2c4b5358b002 +25b0022545445921212d2c45442d2cb00225b0022549b00525b005254960b0206368208a108a +233a8a10653a2d000001000000024cccffd9d8525f0f3cf5001f080000000000c6bc48a00000 +0000c6bc48a0f7d6fd330d72095500000008000000010000000000010000076dfe1d00000de2 +f7d6fa510d7200010000000000000000000000000000001c04cd006604d300c904e7007b034a +00ba023900c104ec0071028b0000051400ba051200ae023900c105140071051400ba042b006f +051200ba0323003704e5007102d1002f03ae00c5068b005602e300640239ffdb05170064031f +00b00514007104bc003d031f00a404a200ba046600710000000000000044000000c4000001f0 +000002600000029c0000037000000370000004080000048c000004dc00000574000006140000 +0774000007ec000008680000090c000009a400000a1000000c3400000c6000000cdc00000d98 +00000e0800000ed00000109c000010f4000011e40000127c00010000001c0354002b0068000c +000200100099000800000415021600080004b8028040fffbfe03fa1403f92503f83203f79603 +f60e03f5fe03f4fe03f32503f20e03f19603f02503ef8a4105effe03ee9603ed9603ecfa03eb +fa03eafe03e93a03e84203e7fe03e63203e5e45305e59603e48a4105e45303e3e22f05e3fa03 +e22f03e1fe03e0fe03df3203de1403dd9603dcfe03db1203da7d03d9bb03d8fe03d68a4105d6 +7d03d5d44705d57d03d44703d3d21b05d3fe03d21b03d1fe03d0fe03cffe03cefe03cd9603cc +cb1e05ccfe03cb1e03ca3203c9fe03c6851105c61c03c51603c4fe03c3fe03c2fe03c1fe03c0 +fe03bffe03befe03bdfe03bcfe03bbfe03ba1103b9862505b9fe03b8b7bb05b8fe03b7b65d05 +b7bb03b78004b6b52505b65d40ff03b64004b52503b4fe03b39603b2fe03b1fe03b0fe03affe +03ae6403ad0e03acab2505ac6403abaa1205ab2503aa1203a98a4105a9fa03a8fe03a7fe03a6 +fe03a51203a4fe03a3a20e05a33203a20e03a16403a08a4105a096039ffe039e9d0c059efe03 +9d0c039c9b19059c64039b9a10059b19039a1003990a0398fe0397960d0597fe03960d03958a +410595960394930e05942803930e0392fa039190bb0591fe03908f5d0590bb039080048f8e25 +058f5d038f40048e25038dfe038c8b2e058cfe038b2e038a8625058a410389880b0589140388 +0b03878625058764038685110586250385110384fe038382110583fe0382110381fe0380fe03 +7ffe0340ff7e7d7d057efe037d7d037c64037b5415057b25037afe0379fe03780e03770c0376 +0a0375fe0374fa0373fa0372fa0371fa0370fe036ffe036efe036c21036bfe036a1142056a53 +0369fe03687d036711420566fe0365fe0364fe0363fe0362fe03613a0360fa035e0c035dfe03 +5bfe035afe0359580a0559fa03580a035716190557320356fe03555415055542035415035301 +1005531803521403514a130551fe03500b034ffe034e4d10054efe034d10034cfe034b4a1305 +4bfe034a4910054a1303491d0d05491003480d0347fe0346960345960344fe0343022d0543fa +0342bb03414b0340fe033ffe033e3d12053e14033d3c0f053d12033c3b0d053c40ff0f033b0d +033afe0339fe033837140538fa033736100537140336350b05361003350b03341e03330d0332 +310b0532fe03310b03302f0b05300d032f0b032e2d09052e10032d09032c32032b2a25052b64 +032a2912052a25032912032827250528410327250326250b05260f03250b0324fe0323fe0322 +0f03210110052112032064031ffa031e1d0d051e64031d0d031c1142051cfe031bfa031a4203 +1911420519fe031864031716190517fe031601100516190315fe0314fe0313fe031211420512 +fe0311022d05114203107d030f64030efe030d0c16050dfe030c0110050c16030bfe030a1003 +09fe0308022d0508fe030714030664030401100504fe03401503022d0503fe0302011005022d +0301100300fe0301b80164858d012b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b002b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b1d00> +] def +FontName currentdict end definefont pop +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 637 164 +%%EndPageSetup +q +0 g +BT +12 0 0 12 126.246094 2.496091 Tm +/f-0-0 1 Tf +[<01>44<0203>-1<02>-1<04>1<040504>1<06>-1<0708>-1<09>1<040a0603>20<050b +03>21<050c>-1<05>]TJ +10.523438 0 Td +[<0d>-1<0e02>-1<0e090f>1<0d>-1<06>-1<10>1<0f03>-1<0611>-1<12>-1<021006 +13>-1<14>1<15>-1<11>-1<06>]TJ +10.185547 0 Td +[<160f0d>-1<05061703>20<0f>1<08>-1<0b06>-1<0718060a05100208>]TJ +10.189453 0 Td +<040e19>Tj +ET +0.996078 0.996078 0.266667 rg +0.398 163.298 m 37.223 163.298 l 37.223 145.696 l 0.398 145.696 l 0.398 +163.298 l h +0.398 163.298 m f* +0 g +0.8 w +0 J +0 j +[] 0.0 d +4 M q 1 0 0 -1 0 163.696091 cm +0.398 0.398 m 37.223 0.398 l 37.223 18 l 0.398 18 l 0.398 0.398 l h +0.398 0.398 m S Q +0.996078 0.996078 0.266667 rg +22.645 145.696 m 80.012 145.696 l 80.012 128.095 l 22.645 128.095 l +22.645 145.696 l h +22.645 145.696 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +22.645 18 m 80.012 18 l 80.012 35.602 l 22.645 35.602 l 22.645 18 l h +22.645 18 m S Q +0.4 0.529412 0.733333 rg +23.207 128.095 m 79.758 128.095 l 79.758 110.497 l 23.207 110.497 l +23.207 128.095 l h +23.207 128.095 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +23.207 35.602 m 79.758 35.602 l 79.758 53.199 l 23.207 53.199 l 23.207 +35.602 l h +23.207 35.602 m S Q +0.996078 0.996078 0.266667 rg +41.914 163.298 m 100.129 163.298 l 100.129 145.696 l 41.914 145.696 l +41.914 163.298 l h +41.914 163.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +41.914 0.398 m 100.129 0.398 l 100.129 18 l 41.914 18 l 41.914 0.398 l +h +41.914 0.398 m S Q +0.996078 0.996078 0.266667 rg +41.793 110.497 m 129.777 110.497 l 129.777 92.895 l 41.793 92.895 l +41.793 110.497 l h +41.793 110.497 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +41.793 53.199 m 129.777 53.199 l 129.777 70.801 l 41.793 70.801 l +41.793 53.199 l h +41.793 53.199 m S Q +0.996078 0.996078 0.266667 rg +84.816 128.095 m 141.984 128.095 l 141.984 110.497 l 84.816 110.497 l +84.816 128.095 l h +84.816 128.095 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +84.816 35.602 m 141.984 35.602 l 141.984 53.199 l 84.816 53.199 l +84.816 35.602 l h +84.816 35.602 m S Q +0.4 0.529412 0.733333 rg +85.754 145.696 m 171.168 145.696 l 171.168 128.095 l 85.754 128.095 l +85.754 145.696 l h +85.754 145.696 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +85.754 18 m 171.168 18 l 171.168 35.602 l 85.754 35.602 l 85.754 18 l h +85.754 18 m S Q +0.4 0.529412 0.733333 rg +112.055 163.298 m 172.578 163.298 l 172.578 145.696 l 112.055 145.696 l +112.055 163.298 l h +112.055 163.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +112.055 0.398 m 172.578 0.398 l 172.578 18 l 112.055 18 l 112.055 0.398 +l h +112.055 0.398 m S Q +0.996078 0.996078 0.266667 rg +131.078 110.497 m 271.371 110.497 l 271.371 92.895 l 131.078 92.895 l +131.078 110.497 l h +131.078 110.497 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +131.078 53.199 m 271.371 53.199 l 271.371 70.801 l 131.078 70.801 l +131.078 53.199 l h +131.078 53.199 m S Q +0.996078 0.996078 0.266667 rg +149.387 128.095 m 272.578 128.095 l 272.578 110.497 l 149.387 110.497 l +149.387 128.095 l h +149.387 128.095 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +149.387 35.602 m 272.578 35.602 l 272.578 53.199 l 149.387 53.199 l +149.387 35.602 l h +149.387 35.602 m S Q +0.4 0.529412 0.733333 rg +172.984 145.696 m 256.016 145.696 l 256.016 128.095 l 172.984 128.095 l +172.984 145.696 l h +172.984 145.696 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +172.984 18 m 256.016 18 l 256.016 35.602 l 172.984 35.602 l 172.984 18 +l h +172.984 18 m S Q +0.4 0.529412 0.733333 rg +176.34 163.298 m 230.262 163.298 l 230.262 145.696 l 176.34 145.696 l +176.34 163.298 l h +176.34 163.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +176.34 0.398 m 230.262 0.398 l 230.262 18 l 176.34 18 l 176.34 0.398 l +h +176.34 0.398 m S Q +0.4 0.529412 0.733333 rg +230.66 163.298 m 291.715 163.298 l 291.715 145.696 l 230.66 145.696 l +230.66 163.298 l h +230.66 163.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +230.66 0.398 m 291.715 0.398 l 291.715 18 l 230.66 18 l 230.66 0.398 l +h +230.66 0.398 m S Q +0.4 0.529412 0.733333 rg +261.297 145.696 m 330.078 145.696 l 330.078 128.095 l 261.297 128.095 l +261.297 145.696 l h +261.297 145.696 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +261.297 18 m 330.078 18 l 330.078 35.602 l 261.297 35.602 l 261.297 18 +l h +261.297 18 m S Q +0.4 0.529412 0.733333 rg +273.594 110.497 m 328.016 110.497 l 328.016 92.895 l 273.594 92.895 l +273.594 110.497 l h +273.594 110.497 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +273.594 53.199 m 328.016 53.199 l 328.016 70.801 l 273.594 70.801 l +273.594 53.199 l h +273.594 53.199 m S Q +0.996078 0.996078 0.266667 rg +289.801 128.095 m 383.754 128.095 l 383.754 110.497 l 289.801 110.497 l +289.801 128.095 l h +289.801 128.095 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +289.801 35.602 m 383.754 35.602 l 383.754 53.199 l 289.801 53.199 l +289.801 35.602 l h +289.801 35.602 m S Q +0.301961 0.654902 0.301961 rg +291.879 163.298 m 359.137 163.298 l 359.137 145.696 l 291.879 145.696 l +291.879 163.298 l h +291.879 163.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +291.879 0.398 m 359.137 0.398 l 359.137 18 l 291.879 18 l 291.879 0.398 +l h +291.879 0.398 m S Q +0.301961 0.654902 0.301961 rg +328.812 110.497 m 410.258 110.497 l 410.258 92.895 l 328.812 92.895 l +328.812 110.497 l h +328.812 110.497 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +328.812 53.199 m 410.258 53.199 l 410.258 70.801 l 328.812 70.801 l +328.812 53.199 l h +328.812 53.199 m S Q +0.654902 0.317647 1 rg +330.262 145.696 m 383.422 145.696 l 383.422 128.095 l 330.262 128.095 l +330.262 145.696 l h +330.262 145.696 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +330.262 18 m 383.422 18 l 383.422 35.602 l 330.262 35.602 l 330.262 18 +l h +330.262 18 m S Q +0.301961 0.654902 0.301961 rg +360.406 163.298 m 441.703 163.298 l 441.703 145.696 l 360.406 145.696 l +360.406 163.298 l h +360.406 163.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +360.406 0.398 m 441.703 0.398 l 441.703 18 l 360.406 18 l 360.406 0.398 +l h +360.406 0.398 m S Q +0.301961 0.654902 0.301961 rg +385.086 145.696 m 478.281 145.696 l 478.281 128.095 l 385.086 128.095 l +385.086 145.696 l h +385.086 145.696 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +385.086 18 m 478.281 18 l 478.281 35.602 l 385.086 35.602 l 385.086 18 +l h +385.086 18 m S Q +0.301961 0.654902 0.301961 rg +389.156 128.095 m 453.434 128.095 l 453.434 110.497 l 389.156 110.497 l +389.156 128.095 l h +389.156 128.095 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +389.156 35.602 m 453.434 35.602 l 453.434 53.199 l 389.156 53.199 l +389.156 35.602 l h +389.156 35.602 m S Q +0.654902 0.317647 1 rg +412.273 110.497 m 555.293 110.497 l 555.293 92.895 l 412.273 92.895 l +412.273 110.497 l h +412.273 110.497 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +412.273 53.199 m 555.293 53.199 l 555.293 70.801 l 412.273 70.801 l +412.273 53.199 l h +412.273 53.199 m S Q +0.654902 0.317647 1 rg +442.27 163.298 m 481.043 163.298 l 481.043 145.696 l 442.27 145.696 l +442.27 163.298 l h +442.27 163.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +442.27 0.398 m 481.043 0.398 l 481.043 18 l 442.27 18 l 442.27 0.398 l +h +442.27 0.398 m S Q +0.654902 0.317647 1 rg +454.785 128.095 m 529.438 128.095 l 529.438 110.497 l 454.785 110.497 l +454.785 128.095 l h +454.785 128.095 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +454.785 35.602 m 529.438 35.602 l 529.438 53.199 l 454.785 53.199 l +454.785 35.602 l h +454.785 35.602 m S Q +0.654902 0.317647 1 rg +479.332 145.696 m 543.594 145.696 l 543.594 128.095 l 479.332 128.095 l +479.332 145.696 l h +479.332 145.696 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +479.332 18 m 543.594 18 l 543.594 35.602 l 479.332 35.602 l 479.332 18 +l h +479.332 18 m S Q +0.301961 0.654902 0.301961 rg +481.242 163.298 m 556.941 163.298 l 556.941 145.696 l 481.242 145.696 l +481.242 163.298 l h +481.242 163.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +481.242 0.398 m 556.941 0.398 l 556.941 18 l 481.242 18 l 481.242 0.398 +l h +481.242 0.398 m S Q +0.301961 0.654902 0.301961 rg +529.836 128.095 m 592.621 128.095 l 592.621 110.497 l 529.836 110.497 l +529.836 128.095 l h +529.836 128.095 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +529.836 35.602 m 592.621 35.602 l 592.621 53.199 l 529.836 53.199 l +529.836 35.602 l h +529.836 35.602 m S Q +0.654902 0.317647 1 rg +544.656 145.696 m 622.719 145.696 l 622.719 128.095 l 544.656 128.095 l +544.656 145.696 l h +544.656 145.696 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +544.656 18 m 622.719 18 l 622.719 35.602 l 544.656 35.602 l 544.656 18 +l h +544.656 18 m S Q +0.654902 0.317647 1 rg +557.746 110.497 m 615.496 110.497 l 615.496 92.895 l 557.746 92.895 l +557.746 110.497 l h +557.746 110.497 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +557.746 53.199 m 615.496 53.199 l 615.496 70.801 l 557.746 70.801 l +557.746 53.199 l h +557.746 53.199 m S Q +0.654902 0.317647 1 rg +566.812 163.298 m 614.324 163.298 l 614.324 145.696 l 566.812 145.696 l +566.812 163.298 l h +566.812 163.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +566.812 0.398 m 614.324 0.398 l 614.324 18 l 566.812 18 l 566.812 0.398 +l h +566.812 0.398 m S Q +0.301961 0.654902 0.301961 rg +593.141 128.11 m 636.805 128.11 l 636.805 110.481 l 593.141 110.481 l +593.141 128.11 l h +593.141 128.11 m f* +0 g +0.769764 w +q 1 0 0 -1 0 163.696091 cm +593.141 35.586 m 636.805 35.586 l 636.805 53.215 l 593.141 53.215 l +593.141 35.586 l h +593.141 35.586 m S Q +0.996078 0.996078 0.266667 rg +17.199 82.497 m 26 82.497 l 26 73.696 l 17.199 73.696 l 17.199 82.497 l +h +17.199 82.497 m f* +0 g +0.8 w +q 1 0 0 -1 0 163.696091 cm +17.199 81.199 m 26 81.199 l 26 90 l 17.199 90 l 17.199 81.199 l h +17.199 81.199 m S Q +BT +9.6 0 0 9.6 34.8 74.496091 Tm +/f-0-0 1 Tf +[<0e02>-1<0c1a>-1<0c>-1<0602>]TJ +ET +0.301961 0.654902 0.301961 rg +17.199 64.895 m 26 64.895 l 26 56.095 l 17.199 56.095 l 17.199 64.895 l +h +17.199 64.895 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +17.199 98.801 m 26 98.801 l 26 107.602 l 17.199 107.602 l 17.199 98.801 +l h +17.199 98.801 m S Q +BT +9.6 0 0 9.6 34.8 56.896091 Tm +/f-0-0 1 Tf +[<0e02>-1<0c1a>-1<0c>-1<0607>]TJ +ET +0.4 0.529412 0.733333 rg +17.199 47.298 m 26 47.298 l 26 38.497 l 17.199 38.497 l 17.199 47.298 l +h +17.199 47.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +17.199 116.398 m 26 116.398 l 26 125.199 l 17.199 125.199 l 17.199 +116.398 l h +17.199 116.398 m S Q +BT +9.6 0 0 9.6 34.8 39.296091 Tm +/f-0-0 1 Tf +[<0e02>-1<0c1a>-1<0c>-1<061b>]TJ +ET +0.654902 0.317647 1 rg +17.199 29.696 m 26 29.696 l 26 20.895 l 17.199 20.895 l 17.199 29.696 l +h +17.199 29.696 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +17.199 134 m 26 134 l 26 142.801 l 17.199 142.801 l 17.199 134 l h +17.199 134 m S Q +BT +9.6 0 0 9.6 34.8 21.696091 Tm +/f-0-0 1 Tf +[<0e02>-1<0c1a>-1<0c>-1<060a>]TJ +ET +Q +showpage +%%Trailer +count op_count sub {pop} repeat +countdictstack dict_count sub {end} repeat +cairo_eps_state restore +%%EOF diff --git a/docs/book/tasks_overview.dia b/docs/book/tasks_overview.dia new file mode 100644 index 00000000..2ca3111d --- /dev/null +++ b/docs/book/tasks_overview.dia @@ -0,0 +1,690 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Execute the build functions +from user scripts# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Have the task generators +generate the tasks# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Set the build order +on the tasks# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Execute +the tasks# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Save the BuildContext +data to the cache# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Start# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #End# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Read the BuildContext +data from the cache# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/book/tasks_twogroups.eps b/docs/book/tasks_twogroups.eps new file mode 100644 index 00000000..95085bfb --- /dev/null +++ b/docs/book/tasks_twogroups.eps @@ -0,0 +1,684 @@ +%!PS-Adobe-3.0 EPSF-3.0 +%%Creator: cairo 1.8.10 (http://cairographics.org) +%%CreationDate: Thu Jul 29 16:55:39 2010 +%%Pages: 1 +%%BoundingBox: 0 0 637 164 +%%DocumentData: Clean7Bit +%%LanguageLevel: 2 +%%EndComments +%%BeginProlog +/cairo_eps_state save def +/dict_count countdictstack def +/op_count count 1 sub def +userdict begin +/q { gsave } bind def +/Q { grestore } bind def +/cm { 6 array astore concat } bind def +/w { setlinewidth } bind def +/J { setlinecap } bind def +/j { setlinejoin } bind def +/M { setmiterlimit } bind def +/d { setdash } bind def +/m { moveto } bind def +/l { lineto } bind def +/c { curveto } bind def +/h { closepath } bind def +/re { exch dup neg 3 1 roll 5 3 roll moveto 0 rlineto + 0 exch rlineto 0 rlineto closepath } bind def +/S { stroke } bind def +/f { fill } bind def +/f* { eofill } bind def +/B { fill stroke } bind def +/B* { eofill stroke } bind def +/n { newpath } bind def +/W { clip } bind def +/W* { eoclip } bind def +/BT { } bind def +/ET { } bind def +/pdfmark where { pop globaldict /?pdfmark /exec load put } + { globaldict begin /?pdfmark /pop load def /pdfmark + /cleartomark load def end } ifelse +/BDC { mark 3 1 roll /BDC pdfmark } bind def +/EMC { mark /EMC pdfmark } bind def +/cairo_store_point { /cairo_point_y exch def /cairo_point_x exch def } def +/Tj { show currentpoint cairo_store_point } bind def +/TJ { + { + dup + type /stringtype eq + { show } { -0.001 mul 0 cairo_font_matrix dtransform rmoveto } ifelse + } forall + currentpoint cairo_store_point +} bind def +/cairo_selectfont { cairo_font_matrix aload pop pop pop 0 0 6 array astore + cairo_font exch selectfont cairo_point_x cairo_point_y moveto } bind def +/Tf { pop /cairo_font exch def /cairo_font_matrix where + { pop cairo_selectfont } if } bind def +/Td { matrix translate cairo_font_matrix matrix concatmatrix dup + /cairo_font_matrix exch def dup 4 get exch 5 get cairo_store_point + /cairo_font where { pop cairo_selectfont } if } bind def +/Tm { 2 copy 8 2 roll 6 array astore /cairo_font_matrix exch def + cairo_store_point /cairo_font where { pop cairo_selectfont } if } bind def +/g { setgray } bind def +/rg { setrgbcolor } bind def +/d1 { setcachedevice } bind def +%%EndProlog +11 dict begin +/FontType 42 def +/FontName /f-0-0 def +/PaintType 0 def +/FontMatrix [ 1 0 0 1 0 0 ] def +/FontBBox [ 0 0 0 0 ] def +/Encoding 256 array def +0 1 255 { Encoding exch /.notdef put } for +Encoding 1 /uni0050 put +Encoding 2 /uni0061 put +Encoding 3 /uni0072 put +Encoding 4 /uni006C put +Encoding 5 /uni0065 put +Encoding 6 /uni0020 put +Encoding 7 /uni0062 put +Encoding 8 /uni0075 put +Encoding 9 /uni0069 put +Encoding 10 /uni0064 put +Encoding 11 /uni0070 put +Encoding 12 /uni0073 put +Encoding 13 /uni006E put +Encoding 14 /uni0074 put +Encoding 15 /uni006F put +Encoding 16 /uni0066 put +Encoding 17 /uni0022 put +Encoding 18 /uni0077 put +Encoding 19 /uni002D put +Encoding 20 /uni006A put +Encoding 21 /uni0034 put +Encoding 22 /uni0028 put +Encoding 23 /uni0067 put +Encoding 24 /uni0029 put +Encoding 25 /uni006B put +Encoding 26 /uni0063 put +/CharStrings 27 dict dup begin +/.notdef 0 def +/uni0050 1 def +/uni0061 2 def +/uni0072 3 def +/uni006C 4 def +/uni0065 5 def +/uni0020 6 def +/uni0062 7 def +/uni0075 8 def +/uni0069 9 def +/uni0064 10 def +/uni0070 11 def +/uni0073 12 def +/uni006E 13 def +/uni0074 14 def +/uni006F 15 def +/uni0066 16 def +/uni0022 17 def +/uni0077 18 def +/uni002D 19 def +/uni006A 20 def +/uni0034 21 def +/uni0028 22 def +/uni0067 23 def +/uni0029 24 def +/uni006B 25 def +/uni0063 26 def +end readonly def +/sfnts [ +<00010000000a008000030020636d617000d6f17f0000115c000000746376742000691d390000 +11d0000001fe6670676d7134766a000013d0000000ab676c7966fd4948e5000000ac000010b0 +68656164f1f329920000147c00000036686865610cb8066c000014b400000024686d74786ee5 +0d98000014d80000006c6c6f63610000dc2400001544000000706d61787004880671000015b4 +00000020707265703b07f100000015d40000056800020066fe96046605a400030007001a400c +04fb0006fb0108057f0204002fc4d4ec310010d4ecd4ec301311211125211121660400fc7303 +1bfce5fe96070ef8f2720629000200c90000048d05d500080013003a40180195100095098112 +100a0802040005190d3f11001c09041410fcec32fcec11173931002ff4ecd4ec30400b0f151f +153f155f15af1505015d011133323635342623252132041514042b0111230193fe8d9a9a8dfe +3801c8fb0101fefffbfeca052ffdcf92878692a6e3dbdde2fda80002007bffe3042d047b000a +002500bc4027191f0b17090e00a91706b90e1120861fba1cb923b8118c170c001703180d0908 +0b1f030814452610fcecccd4ec323211393931002fc4e4f4fcf4ec10c6ee10ee113911391239 +30406e301d301e301f3020302130223f27401d401e401f402040214022501d501e501f502050 +21502250277027851d871e871f8720872185229027a027f0271e301e301f30203021401e401f +40204021501e501f50205021601e601f60206021701e701f70207021801e801f80208021185d +015d0122061514163332363d01371123350e01232226353436332135342623220607353e0133 +321602bedfac816f99b9b8b83fbc88accbfdfb0102a79760b65465be5af3f00233667b6273d9 +b4294cfd81aa6661c1a2bdc0127f8b2e2eaa2727fc00000100ba0000034a047b001100304014 +060b0700110b03870eb809bc070a06080008461210fcc4ec3231002fe4f4ecc4d4cc11123930 +b450139f1302015d012e012322061511231133153e0133321617034a1f492c9ca7b9b93aba85 +132e1c03b41211cbbefdb20460ae666305050000000100c100000179061400030022b7009702 +010800460410fcec31002fec30400d10054005500560057005f00506015d13331123c1b8b806 +14f9ec0000020071ffe3047f047b0014001b00704024001501098608880515a90105b90c01bb +18b912b80c8c1c1b1502081508004b02120f451c10fcecf4ecc4111239310010e4f4ece410ee +10ee10f4ee1112393040293f1d701da01dd01df01d053f003f013f023f153f1b052c072f082f +092c0a6f006f016f026f156f1b095d71015d0115211e0133323637150e012320001110003332 +00072e0123220607047ffcb20ccdb76ac76263d06bfef4fec70129fce20107b802a5889ab90e +025e5abec73434ae2a2c0138010a01130143feddc497b4ae9e00000200baffe304a40614000b +001c0038401903b90c0f09b918158c0fb81b971900121247180c06081a461d10fcec3232f4ec +31002fece4f4c4ec10c6ee30b6601e801ea01e03015d013426232206151416333236013e0133 +3200111002232226271523113303e5a79292a7a79292a7fd8e3ab17bcc00ffffcc7bb13ab9b9 +022fcbe7e7cbcbe7e702526461febcfef8fef8febc6164a80614000200aeffe30458047b0013 +0014003b401c030900030e0106870e118c0a01bc14b80c0d0908140b4e020800461510fcecf4 +39ec3231002fe4e432f4c4ec1112173930b46f15c01502015d13113311141633323635113311 +23350e0123222601aeb87c7c95adb8b843b175c1c801cf01ba02a6fd619f9fbea4027bfba0ac +6663f003a800000200c100000179061400030007002b400e06be04b100bc0205010804004608 +10fc3cec3231002fe4fcec30400b1009400950096009700905015d1333112311331523c1b8b8 +b8b80460fba00614e90000020071ffe3045a06140010001c003840191ab9000e14b905088c0e +b801970317040008024711120b451d10fcecf4ec323231002fece4f4c4ec10c4ee30b6601e80 +1ea01e03015d0111331123350e0123220211100033321601141633323635342623220603a2b8 +b83ab17ccbff00ffcb7cb1fdc7a79292a8a89292a703b6025ef9eca864610144010801080144 +61fe15cbe7e7cbcbe7e7000200bafe5604a4047b0010001c003e401b1ab9000e14b90508b80e +8c01bd03bc1d11120b471704000802461d10fcec3232f4ec310010e4e4e4f4c4ec10c4ee3040 +09601e801ea01ee01e04015d2511231133153e01333200111002232226013426232206151416 +3332360173b9b93ab17bcc00ffffcc7bb10238a79292a7a79292a7a8fdae060aaa6461febcfe +f8fef8febc6101ebcbe7e7cbcbe7e70000000001006fffe303c7047b002700e7403c0d0c020e +0b531f1e080902070a531f1f1e420a0b1e1f041500860189041486158918b91104b925b8118c +281e0a0b1f1b0700521b080e07081422452810fcc4ecd4ece4111239393939310010e4f4ec10 +fef5ee10f5ee121739304b535807100eed111739070eed1117395922b2002701015d406d1c0a +1c0b1c0c2e092c0a2c0b2c0c3b093b0a3b0b3b0c0b200020012402280a280b2a132f142f152a +16281e281f292029212427860a860b860c860d12000000010202060a060b030c030d030e030f +03100319031a031b031c041d09272f293f295f297f2980299029a029f029185d005d7101152e +012322061514161f011e0115140623222627351e013332363534262f012e0135343633321603 +8b4ea85a898962943fc4a5f7d85ac36c66c661828c65ab40ab98e0ce66b4043fae2828545440 +49210e2a99899cb62323be353559514b50250f2495829eac1e000000000100ba00000464047b +001300364019030900030e0106870e11b80cbc0a010208004e0d09080b461410fcec32f4ec31 +002f3ce4f4c4ec1112173930b46015cf1502015d0111231134262322061511231133153e0133 +32160464b87c7c95acb9b942b375c1c602a4fd5c029e9f9ebea4fd870460ae6564ef00010037 +000002f2059e0013003840190e05080f03a9001101bc08870a0b08090204000810120e461410 +fc3cc4fc3cc432393931002fecf43cc4ec3211393930b2af1501015d01112115211114163b01 +152322263511233533110177017bfe854b73bdbdd5a28787059efec28ffda0894e9a9fd20260 +8f013e00000000020071ffe30475047b000b0017004a401306b91200b90cb8128c1809120f51 +031215451810fcecf4ec310010e4f4ec10ee3040233f197b007b067f077f087f097f0a7f0b7b +0c7f0d7f0e7f0f7f107f117b12a019f01911015d012206151416333236353426273200111000 +232200111000027394acab9593acac93f00112feeef0f1feef011103dfe7c9c9e7e8c8c7e99c +fec8feecfeedfec7013901130114013800000001002f000002f8061400130059401c0510010c +08a906018700970e06bc0a02130700070905080d0f0b4c1410fc4bb00a5458b9000b00403859 +4bb00e5458b9000bffc038593cc4fc3cc4c412393931002fe432fcec10ee321239393001b640 +155015a015035d01152322061d012115211123112335333534363302f8b0634d012ffed1b9b0 +b0aebd0614995068638ffc2f03d18f4ebbab000200c503aa02e905d5000300070042400f0501 +8404008108040506000502040810fc4bb012544bb013545b58b90002ffc03859fcdcec310010 +f43cec323001400f30094009500960097009a009bf09075d0111231121112311016faa0224aa +05d5fdd5022bfdd5022b000000010056000006350460000c01eb404905550605090a0904550a +0903550a0b0a025501020b0b0a061107080705110405080807021103020c000c011100000c42 +0a050203060300bf0b080c0b0a09080605040302010b07000d10d44bb00a544bb011545b4bb0 +12545b4bb013545b4bb00b545b58b9000000403859014bb00c544bb00d545b4bb010545b58b9 +0000ffc03859cc173931002f3cec32321739304b5358071005ed071008ed071008ed071005ed +071008ed071005ed0705ed071008ed59220140ff050216021605220a350a49024905460a400a +5b025b05550a500a6e026e05660a79027f0279057f05870299029805940abc02bc05ce02c703 +cf051d0502090306040b050a080b09040b050c1502190316041a051b081b09140b150c250025 +0123022703210425052206220725082709240a210b230c390336043608390c300e4602480346 +04400442054006400740084409440a440b400e400e5600560156025004510552065207500853 +09540a550b6300640165026a0365046a056a066a076e09610b670c6f0e7500750179027d0378 +047d057a067f067a077f07780879097f097b0a760b7d0c870288058f0e97009701940293039c +049b05980698079908402f960c9f0ea600a601a402a403ab04ab05a906a907ab08a40caf0eb5 +02b103bd04bb05b809bf0ec402c303cc04ca05795d005d13331b01331b013301230b012356b8 +e6e5d9e6e5b8fedbd9f1f2d90460fc96036afc96036afba00396fc6a0001006401df027f0283 +00030011b6009c020401000410dccc310010d4ec301321152164021bfde50283a4000002ffdb +fe5601790614000b000f0044401c0b0207000ebe0c078705bd00bc0cb110081005064f0d0108 +0c00461010fc3cec32e4391239310010ece4f4ec10ee1112393930400b101140115011601170 +1105015d13331114062b01353332363511331523c1b8a3b54631694cb8b80460fb8cd6c09c61 +990628e9000000020064000004a405d50002000d0081401d010d030d0003030d4200030b07a0 +0501038109010c0a001c0608040c0e10dc4bb00b544bb00d545b58b9000cffc03859d43cc4ec +32113931002fe4d43cec321239304b5358071004c9071005c9592201402a0b002a0048005900 +690077008a000716012b0026012b0336014e014f0c4f0d5601660175017a0385010d5d005d09 +012103331133152311231121350306fe0201fe35fed5d5c9fd5e0525fce303cdfc33a8fea001 +60c30000000100b0fef2027b0612000d0037400f069800970e0d070003120600130a0e10dc4b +b0135458b9000affc038594bb00f5458b9000a00403859e432ec113939310010fcec30010602 +1514121723260235341237027b86828385a0969594970612e6fe3ee7e7fe3be5eb01c6e0df01 +c4ec00020071fe56045a047b000b0028004a4023190c1d0912861316b90f03b92623b827bc09 +b90fbd1a1d261900080c4706121220452910fcc4ecf4ec323231002fc4e4ece4f4c4ec10fed5 +ee1112393930b6602a802aa02a03015d01342623220615141633323617100221222627351e01 +3332363d010e0123220211101233321617353303a2a59594a5a59495a5b8fefefa61ac51519e +52b5b439b27ccefcfcce7cb239b8023dc8dcdcc8c7dcdcebfee2fee91d1eb32c2abdbf5b6362 +013a01030104013a6263aa00000100a4fef2026f0612000d001f400f079800970e0701000b12 +041308000e10dc3cf4ec113939310010fcec301333161215140207233612353402a4a0969595 +96a08583830612ecfe3cdfe0fe3aebe501c5e7e701c20000000100ba0000049c0614000a00bc +40290811050605071106060503110405040211050504420805020303bc009709060501040608 +010800460b10fcec32d4c4113931002f3cece41739304b5358071004ed071005ed071005ed07 +1004ed5922b2100c01015d405f04020a081602270229052b0856026602670873027705820289 +058e08930296059708a3021209050906020b030a072803270428052b062b07400c6803600c89 +03850489058d068f079a039707aa03a705b607c507d607f703f003f704f0041a5d71005d1333 +110133090123011123bab90225ebfdae026bf0fdc7b90614fc6901e3fdf4fdac0223fddd0001 +0071ffe303e7047b0019003f401b00860188040e860d880ab91104b917b8118c1a07120d0048 +14451a10fce432ec310010e4f4ec10fef4ee10f5ee30400b0f1b101b801b901ba01b05015d01 +152e0123220615141633323637150e0123220011100021321603e74e9d50b3c6c6b3509d4e4d +a55dfdfed6012d010655a20435ac2b2be3cdcde32b2baa2424013e010e0112013a2300000000 +0002000300000000001400010000000000340004002000000004000400010000f01affff0000 +f000ffff10000001000000000006004000000000001b00000001000200030004000500060007 +00080009000a000b000c000d000e000f0010001100120013001400150016001700180019001a +013500b800cb00cb00c100aa009c01a600b800660000007100cb00a002b20085007500b800c3 +01cb0189022d00cb00a600f000d300aa008700cb03aa0400014a003300cb000000d9050200f4 +015400b4009c01390114013907060400044e04b4045204b804e704cd0037047304cd04600473 +013303a2055605a60556053903c5021200c9001f00b801df007300ba03e9033303bc0444040e +00df03cd03aa00e503aa0404000000cb008f00a4007b00b80014016f007f027b0252008f00c7 +05cd009a009a006f00cb00cd019e01d300f000ba018300d5009803040248009e01d500c100cb +00f600830354027f00000333026600d300c700a400cd008f009a0073040005d5010a00fe022b +00a400b4009c00000062009c0000001d032d05d505d505d505f0007f007b005400a406b80614 +072301d300b800cb00a601c301ec069300a000d3035c037103db0185042304a80448008f0139 +011401390360008f05d5019a0614072306660179046004600460047b009c00000277046001aa +00e904600762007b00c5007f027b000000b4025205cd006600bc00660077061000cd013b0185 +0389008f007b0000001d00cd074a042f009c009c0000077d006f0000006f0335006a006f007b +00ae00b2002d0396008f027b00f600830354063705f6008f009c04e10266008f018d02f600cd +03440029006604ee00730000140000960000b707060504030201002c2010b002254964b04051 +5820c859212d2cb002254964b040515820c859212d2c20100720b00050b00d7920b8ffff5058 +041b0559b0051cb0032508b0042523e120b00050b00d7920b8ffff5058041b0559b0051cb003 +2508e12d2c4b505820b0fd454459212d2cb002254560442d2c4b5358b00225b0022545445921 +212d2c45442d2cb00225b0022549b00525b005254960b0206368208a108a233a8a10653a2d00 +0001000000024ccc5fa4ecce5f0f3cf5001f080000000000c6bc48a000000000c6bc48a0f7d6 +fd330d72095500000008000000010000000000010000076dfe1d00000de2f7d6fa510d720001 +0000000000000000000000000000001b04cd006604d300c904e7007b034a00ba023900c104ec +0071028b0000051400ba051200ae023900c105140071051400ba042b006f051200ba03230037 +04e5007102d1002f03ae00c5068b005602e300640239ffdb05170064031f00b005140071031f +00a404a200ba046600710000000000000044000000c4000001f0000002600000029c00000370 +00000370000004080000048c000004dc000005740000061400000774000007ec000008680000 +090c000009a400000a1000000c3400000c6000000cdc00000d9800000e0800000ed000000f28 +00001018000010b000010000001b0354002b0068000c00020010009900080000041502160008 +0004b8028040fffbfe03fa1403f92503f83203f79603f60e03f5fe03f4fe03f32503f20e03f1 +9603f02503ef8a4105effe03ee9603ed9603ecfa03ebfa03eafe03e93a03e84203e7fe03e632 +03e5e45305e59603e48a4105e45303e3e22f05e3fa03e22f03e1fe03e0fe03df3203de1403dd +9603dcfe03db1203da7d03d9bb03d8fe03d68a4105d67d03d5d44705d57d03d44703d3d21b05 +d3fe03d21b03d1fe03d0fe03cffe03cefe03cd9603cccb1e05ccfe03cb1e03ca3203c9fe03c6 +851105c61c03c51603c4fe03c3fe03c2fe03c1fe03c0fe03bffe03befe03bdfe03bcfe03bbfe +03ba1103b9862505b9fe03b8b7bb05b8fe03b7b65d05b7bb03b78004b6b52505b65d40ff03b6 +4004b52503b4fe03b39603b2fe03b1fe03b0fe03affe03ae6403ad0e03acab2505ac6403abaa +1205ab2503aa1203a98a4105a9fa03a8fe03a7fe03a6fe03a51203a4fe03a3a20e05a33203a2 +0e03a16403a08a4105a096039ffe039e9d0c059efe039d0c039c9b19059c64039b9a10059b19 +039a1003990a0398fe0397960d0597fe03960d03958a410595960394930e05942803930e0392 +fa039190bb0591fe03908f5d0590bb039080048f8e25058f5d038f40048e25038dfe038c8b2e +058cfe038b2e038a8625058a410389880b05891403880b038786250587640386851105862503 +85110384fe038382110583fe0382110381fe0380fe037ffe0340ff7e7d7d057efe037d7d037c +64037b5415057b25037afe0379fe03780e03770c03760a0375fe0374fa0373fa0372fa0371fa +0370fe036ffe036efe036c21036bfe036a1142056a530369fe03687d036711420566fe0365fe +0364fe0363fe0362fe03613a0360fa035e0c035dfe035bfe035afe0359580a0559fa03580a03 +5716190557320356fe035554150555420354150353011005531803521403514a130551fe0350 +0b034ffe034e4d10054efe034d10034cfe034b4a13054bfe034a4910054a1303491d0d054910 +03480d0347fe0346960345960344fe0343022d0543fa0342bb03414b0340fe033ffe033e3d12 +053e14033d3c0f053d12033c3b0d053c40ff0f033b0d033afe0339fe033837140538fa033736 +100537140336350b05361003350b03341e03330d0332310b0532fe03310b03302f0b05300d03 +2f0b032e2d09052e10032d09032c32032b2a25052b64032a2912052a25032912032827250528 +410327250326250b05260f03250b0324fe0323fe03220f03210110052112032064031ffa031e +1d0d051e64031d0d031c1142051cfe031bfa031a42031911420519fe031864031716190517fe +031601100516190315fe0314fe0313fe031211420512fe0311022d05114203107d030f64030e +fe030d0c16050dfe030c0110050c16030bfe030a100309fe0308022d0508fe03071403066403 +0401100504fe03401503022d0503fe0302011005022d0301100300fe0301b80164858d012b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b002b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b +2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b2b1d00> +] def +FontName currentdict end definefont pop +%%Page: 1 1 +%%BeginPageSetup +%%PageBoundingBox: 0 0 637 164 +%%EndPageSetup +q +0 g +BT +12 0 0 12 155.648438 2.496091 Tm +/f-0-0 1 Tf +[<01>44<0203>-1<02>-1<04>1<040504>1<06>-1<0708>-1<09>1<040a0603>20<050b +03>21<050c>-1<05>]TJ +10.523438 0 Td +[<0d>-1<0e02>-1<0e090f>1<0d>-1<06>-1<10>1<0f03>-1<0611>-1<12>-1<021006 +13>-1<14>1<15>-1<11>-1<06>]TJ +10.185547 0 Td +[<160e>-1<120f061703>20<0f>1<08>-1<0b0c>-1<18>]TJ +ET +0.996078 0.996078 0.266667 rg +0.398 163.298 m 32.543 163.298 l 32.543 145.696 l 0.398 145.696 l 0.398 +163.298 l h +0.398 163.298 m f* +0 g +0.8 w +0 J +0 j +[] 0.0 d +4 M q 1 0 0 -1 0 163.696091 cm +0.398 0.398 m 32.543 0.398 l 32.543 18 l 0.398 18 l 0.398 0.398 l h +0.398 0.398 m S Q +0.996078 0.996078 0.266667 rg +16.613 145.696 m 57.781 145.696 l 57.781 128.095 l 16.613 128.095 l +16.613 145.696 l h +16.613 145.696 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +16.613 18 m 57.781 18 l 57.781 35.602 l 16.613 35.602 l 16.613 18 l h +16.613 18 m S Q +0.996078 0.996078 0.266667 rg +30.258 128.095 m 90.859 128.095 l 90.859 110.497 l 30.258 110.497 l +30.258 128.095 l h +30.258 128.095 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +30.258 35.602 m 90.859 35.602 l 90.859 53.199 l 30.258 53.199 l 30.258 +35.602 l h +30.258 35.602 m S Q +0.996078 0.996078 0.266667 rg +32.984 110.497 m 135.012 110.497 l 135.012 92.895 l 32.984 92.895 l +32.984 110.497 l h +32.984 110.497 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +32.984 53.199 m 135.012 53.199 l 135.012 70.801 l 32.984 70.801 l +32.984 53.199 l h +32.984 53.199 m S Q +0.996078 0.996078 0.266667 rg +33.492 163.298 m 106.977 163.298 l 106.977 145.696 l 33.492 145.696 l +33.492 163.298 l h +33.492 163.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +33.492 0.398 m 106.977 0.398 l 106.977 18 l 33.492 18 l 33.492 0.398 l +h +33.492 0.398 m S Q +0.996078 0.996078 0.266667 rg +59.219 145.696 m 149.867 145.696 l 149.867 128.095 l 59.219 128.095 l +59.219 145.696 l h +59.219 145.696 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +59.219 18 m 149.867 18 l 149.867 35.602 l 59.219 35.602 l 59.219 18 l h +59.219 18 m S Q +0.996078 0.996078 0.266667 rg +91.305 128.095 m 151.242 128.095 l 151.242 110.497 l 91.305 110.497 l +91.305 128.095 l h +91.305 128.095 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +91.305 35.602 m 151.242 35.602 l 151.242 53.199 l 91.305 53.199 l +91.305 35.602 l h +91.305 35.602 m S Q +0.996078 0.996078 0.266667 rg +107.328 163.298 m 172.461 163.298 l 172.461 145.696 l 107.328 145.696 l +107.328 163.298 l h +107.328 163.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +107.328 0.398 m 172.461 0.398 l 172.461 18 l 107.328 18 l 107.328 0.398 +l h +107.328 0.398 m S Q +0.301961 0.654902 0.301961 rg +135.324 110.497 m 188.598 110.497 l 188.598 92.895 l 135.324 92.895 l +135.324 110.497 l h +135.324 110.497 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +135.324 53.199 m 188.598 53.199 l 188.598 70.801 l 135.324 70.801 l +135.324 53.199 l h +135.324 53.199 m S Q +0.301961 0.654902 0.301961 rg +150.242 145.696 m 277.414 145.696 l 277.414 128.095 l 150.242 128.095 l +150.242 145.696 l h +150.242 145.696 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +150.242 18 m 277.414 18 l 277.414 35.602 l 150.242 35.602 l 150.242 18 +l h +150.242 18 m S Q +0.301961 0.654902 0.301961 rg +152.797 128.095 m 285.664 128.095 l 285.664 110.497 l 152.797 110.497 l +152.797 128.095 l h +152.797 128.095 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +152.797 35.602 m 285.664 35.602 l 285.664 53.199 l 152.797 53.199 l +152.797 35.602 l h +152.797 35.602 m S Q +0.301961 0.654902 0.301961 rg +173.746 163.298 m 234.887 163.298 l 234.887 145.696 l 173.746 145.696 l +173.746 163.298 l h +173.746 163.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +173.746 0.398 m 234.887 0.398 l 234.887 18 l 173.746 18 l 173.746 0.398 +l h +173.746 0.398 m S Q +0.301961 0.654902 0.301961 rg +188.934 110.497 m 266.977 110.497 l 266.977 92.895 l 188.934 92.895 l +188.934 110.497 l h +188.934 110.497 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +188.934 53.199 m 266.977 53.199 l 266.977 70.801 l 188.934 70.801 l +188.934 53.199 l h +188.934 53.199 m S Q +0.301961 0.654902 0.301961 rg +235.305 163.298 m 288.809 163.298 l 288.809 145.696 l 235.305 145.696 l +235.305 163.298 l h +235.305 163.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +235.305 0.398 m 288.809 0.398 l 288.809 18 l 235.305 18 l 235.305 0.398 +l h +235.305 0.398 m S Q +0.301961 0.654902 0.301961 rg +267.375 110.497 m 302.695 110.497 l 302.695 92.895 l 267.375 92.895 l +267.375 110.497 l h +267.375 110.497 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +267.375 53.199 m 302.695 53.199 l 302.695 70.801 l 267.375 70.801 l +267.375 53.199 l h +267.375 53.199 m S Q +0.301961 0.654902 0.301961 rg +277.965 145.696 m 316.703 145.696 l 316.703 128.095 l 277.965 128.095 l +277.965 145.696 l h +277.965 145.696 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +277.965 18 m 316.703 18 l 316.703 35.602 l 277.965 35.602 l 277.965 18 +l h +277.965 18 m S Q +0.4 0.529412 0.733333 rg +323.336 128.095 m 355.992 128.095 l 355.992 110.497 l 323.336 110.497 l +323.336 128.095 l h +323.336 128.095 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +323.336 35.602 m 355.992 35.602 l 355.992 53.199 l 323.336 53.199 l +323.336 35.602 l h +323.336 35.602 m S Q +0.4 0.529412 0.733333 rg +329.84 163.298 m 388.895 163.298 l 388.895 145.696 l 329.84 145.696 l +329.84 163.298 l h +329.84 163.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +329.84 0.398 m 388.895 0.398 l 388.895 18 l 329.84 18 l 329.84 0.398 l +h +329.84 0.398 m S Q +0.4 0.529412 0.733333 rg +339.926 110.497 m 389.945 110.497 l 389.945 92.895 l 339.926 92.895 l +339.926 110.497 l h +339.926 110.497 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +339.926 53.199 m 389.945 53.199 l 389.945 70.801 l 339.926 70.801 l +339.926 53.199 l h +339.926 53.199 m S Q +0.4 0.529412 0.733333 rg +350.719 145.696 m 444.461 145.696 l 444.461 128.095 l 350.719 128.095 l +350.719 145.696 l h +350.719 145.696 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +350.719 18 m 444.461 18 l 444.461 35.602 l 350.719 35.602 l 350.719 18 +l h +350.719 18 m S Q +0.4 0.529412 0.733333 rg +356.457 128.095 m 430.148 128.095 l 430.148 110.497 l 356.457 110.497 l +356.457 128.095 l h +356.457 128.095 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +356.457 35.602 m 430.148 35.602 l 430.148 53.199 l 356.457 53.199 l +356.457 35.602 l h +356.457 35.602 m S Q +0.4 0.529412 0.733333 rg +390.84 163.298 m 460.586 163.298 l 460.586 145.696 l 390.84 145.696 l +390.84 163.298 l h +390.84 163.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +390.84 0.398 m 460.586 0.398 l 460.586 18 l 390.84 18 l 390.84 0.398 l +h +390.84 0.398 m S Q +0.4 0.529412 0.733333 rg +391.004 110.497 m 480.523 110.497 l 480.523 92.895 l 391.004 92.895 l +391.004 110.497 l h +391.004 110.497 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +391.004 53.199 m 480.523 53.199 l 480.523 70.801 l 391.004 70.801 l +391.004 53.199 l h +391.004 53.199 m S Q +0.4 0.529412 0.733333 rg +430.762 128.095 m 555.664 128.095 l 555.664 110.497 l 430.762 110.497 l +430.762 128.095 l h +430.762 128.095 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +430.762 35.602 m 555.664 35.602 l 555.664 53.199 l 430.762 53.199 l +430.762 35.602 l h +430.762 35.602 m S Q +0.654902 0.317647 1 rg +444.758 145.696 m 567.516 145.696 l 567.516 128.095 l 444.758 128.095 l +444.758 145.696 l h +444.758 145.696 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +444.758 18 m 567.516 18 l 567.516 35.602 l 444.758 35.602 l 444.758 18 +l h +444.758 18 m S Q +0.654902 0.317647 1 rg +460.945 163.298 m 523.148 163.298 l 523.148 145.696 l 460.945 145.696 l +460.945 163.298 l h +460.945 163.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +460.945 0.398 m 523.148 0.398 l 523.148 18 l 460.945 18 l 460.945 0.398 +l h +460.945 0.398 m S Q +0.654902 0.317647 1 rg +480.906 110.497 m 542.262 110.497 l 542.262 92.895 l 480.906 92.895 l +480.906 110.497 l h +480.906 110.497 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +480.906 53.199 m 542.262 53.199 l 542.262 70.801 l 480.906 70.801 l +480.906 53.199 l h +480.906 53.199 m S Q +0.654902 0.317647 1 rg +523.539 163.298 m 583.68 163.298 l 583.68 145.696 l 523.539 145.696 l +523.539 163.298 l h +523.539 163.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +523.539 0.398 m 583.68 0.398 l 583.68 18 l 523.539 18 l 523.539 0.398 l +h +523.539 0.398 m S Q +0.654902 0.317647 1 rg +543.719 110.497 m 635.758 110.497 l 635.758 92.895 l 543.719 92.895 l +543.719 110.497 l h +543.719 110.497 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +543.719 53.199 m 635.758 53.199 l 635.758 70.801 l 543.719 70.801 l +543.719 53.199 l h +543.719 53.199 m S Q +0.654902 0.317647 1 rg +556.992 128.095 m 620.215 128.095 l 620.215 110.497 l 556.992 110.497 l +556.992 128.095 l h +556.992 128.095 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +556.992 35.602 m 620.215 35.602 l 620.215 53.199 l 556.992 53.199 l +556.992 35.602 l h +556.992 35.602 m S Q +0.654902 0.317647 1 rg +568.746 145.696 m 612.66 145.696 l 612.66 128.095 l 568.746 128.095 l +568.746 145.696 l h +568.746 145.696 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +568.746 18 m 612.66 18 l 612.66 35.602 l 568.746 35.602 l 568.746 18 l +h +568.746 18 m S Q +0.654902 0.317647 1 rg +584.07 163.309 m 636.793 163.309 l 636.793 145.684 l 584.07 145.684 l +584.07 163.309 l h +584.07 163.309 m f* +0 g +0.774621 w +q 1 0 0 -1 0 163.696091 cm +584.07 0.387 m 636.793 0.387 l 636.793 18.012 l 584.07 18.012 l 584.07 +0.387 l h +584.07 0.387 m S Q +0.996078 0.996078 0.266667 rg +17.199 82.497 m 26 82.497 l 26 73.696 l 17.199 73.696 l 17.199 82.497 l +h +17.199 82.497 m f* +0 g +0.8 w +q 1 0 0 -1 0 163.696091 cm +17.199 81.199 m 26 81.199 l 26 90 l 17.199 90 l 17.199 81.199 l h +17.199 81.199 m S Q +BT +9.6 0 0 9.6 34.8 74.496091 Tm +/f-0-0 1 Tf +[<0e02>-1<0c19>-1<0c>-1<0602>]TJ +ET +0.301961 0.654902 0.301961 rg +17.199 64.895 m 26 64.895 l 26 56.095 l 17.199 56.095 l 17.199 64.895 l +h +17.199 64.895 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +17.199 98.801 m 26 98.801 l 26 107.602 l 17.199 107.602 l 17.199 98.801 +l h +17.199 98.801 m S Q +BT +9.6 0 0 9.6 34.8 56.896091 Tm +/f-0-0 1 Tf +[<0e02>-1<0c19>-1<0c>-1<0607>]TJ +ET +0.4 0.529412 0.733333 rg +17.199 47.298 m 26 47.298 l 26 38.497 l 17.199 38.497 l 17.199 47.298 l +h +17.199 47.298 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +17.199 116.398 m 26 116.398 l 26 125.199 l 17.199 125.199 l 17.199 +116.398 l h +17.199 116.398 m S Q +BT +9.6 0 0 9.6 34.8 39.296091 Tm +/f-0-0 1 Tf +[<0e02>-1<0c19>-1<0c>-1<061a>]TJ +ET +0.654902 0.317647 1 rg +17.199 29.696 m 26 29.696 l 26 20.895 l 17.199 20.895 l 17.199 29.696 l +h +17.199 29.696 m f* +0 g +q 1 0 0 -1 0 163.696091 cm +17.199 134 m 26 134 l 26 142.801 l 17.199 142.801 l 17.199 134 l h +17.199 134 m S Q +BT +9.6 0 0 9.6 34.8 21.696091 Tm +/f-0-0 1 Tf +[<0e02>-1<0c19>-1<0c>-1<060a>]TJ +ET +Q +showpage +%%Trailer +count op_count sub {pop} repeat +countdictstack dict_count sub {end} repeat +cairo_eps_state restore +%%EOF diff --git a/docs/book/waf-activity.dia b/docs/book/waf-activity.dia new file mode 100644 index 00000000..a4228d32 --- /dev/null +++ b/docs/book/waf-activity.dia @@ -0,0 +1,1300 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Call options# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Load user scripts# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Parse the +command-line options# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Failure?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Unpack the waf library# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #The waf library +is present?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Execute the command# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #More commands +to process?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Command is +'configure', +or the project +is configured# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Error: configure +the project# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/book/waf.css b/docs/book/waf.css new file mode 100644 index 00000000..0e365379 --- /dev/null +++ b/docs/book/waf.css @@ -0,0 +1,28 @@ +div.tableblock > table { + border: 1px solid gray; +} + +div#header h1 { + background: url('waf-64x64.png') no-repeat left center; + padding-left: 80px; + line-height: 80px; + height: 80px; +} + +div.title, caption.title { + text-align: center; + margin-bottom: 0.2em; +} + +div.tableblock > table th { + background-color: #F4F4F4; +} + +h1, h2, h3, h4, h5, h6, span#author, div.title, caption.title, div.admonitionblock .icon, div#toctitle, div.sidebar-title, div.image-title { + color: #333; +} + +body, div.sectionbody, div#toctitle { + font-family: 'Lucida Grande', Verdana, Arial, sans-serif; +} + diff --git a/docs/book/waf.txt b/docs/book/waf.txt new file mode 100644 index 00000000..2084a3f1 --- /dev/null +++ b/docs/book/waf.txt @@ -0,0 +1,54 @@ +The Waf Book +============ +:author: Thomas Nagy +:quotes.++: +:numbered!: + +{set:PIC:{backend@docbook:.eps:.png}} + +[preface] +== Introduction + +Copyright (C) 2010-2011 Thomas Nagy + +Copies of this book may be redistributed, verbatim, and for non-commercial +purposes. The license for this book is + http://creativecommons.org/licenses/by-nc-nd/3.0/[by-nc-nd license]. + +=== A word on build systems + +As software is becoming increasingly complex, the process of creating software is becoming more complex too. Today's software uses various languages, requires various compilers, and the input data is spread into many files. + +Software is now used to express the process of building software, it can take the form of simple scripts (shell scripts, makefiles), or compilers (CMake, Qmake), or complete applications (SCons, Maven, Waf). The term `build system' is used to design the tools used to build applications. + +=== The Waf framework + +Build systems make assumptions on software it is trying to build, and are typically limited where it comes to processing other languages or different projects. For example, Ant is better suited than Make for managing Java projects, but is more limited than Make for managing simple c projects. The programming tools are evolving constantly, making the creation of a complete build system for end-users impossible. + +The Waf framework is somewhat different from traditional build systems in the sense that it does not provide support for a specific language. Rather, the focus is to support the major usecases encountered when working on a software project. As such, it is essentially a library of components that are suitable for use in a build system, with an emphasis on extensibility. Although the default distribution contains various plugins for several programming languages and different tools (c, d, ocaml, java, etc), it is by no means a frozen product. Creating new extensions is both a standard and a recommended practice. + +=== Objectives of this book + +The objective of this book is to expose the use of the Waf build system though the use of Waf in practice, the description of the Waf extension system, and an overview of the Waf internals. We hope that this book will serve as a reference for both new and advanced users. Although this book does not deal with build systems in general, a secondary objective is to illustrate quite a few new techniques and patterns through numerous examples. + +The chapters are ordered by difficulty, starting from the basic use of Waf and Python, and diving gradually into the most difficult topics. It is therefore recommended to read the chapters in order. It is also possible to start by looking at the http://code.google.com/p/waf/source/browse/trunk/demos/[examples] from the Waf distribution before starting the reading. + +:numbered: + +include::download.txt[] +include::execution.txt[] +include::configuration.txt[] +include::build.txt[] +include::nodes.txt[] +include::advbuild.txt[] +include::tasks.txt[] +include::make_like_rules.txt[] +include::chains.txt[] +include::task_generators.txt[] +include::cprog.txt[] +include::scenarios.txt[] +include::development.txt[] +include::architecture.txt[] +include::conclusion.txt[] +include::glossary.txt[] + diff --git a/docs/book/waflib.dia b/docs/book/waflib.dia new file mode 100644 index 00000000..160dd4fe --- /dev/null +++ b/docs/book/waflib.dia @@ -0,0 +1,1140 @@ + + + + + + + + + + + + + #A4# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #No waflib directory +raise an error# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #End# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Start# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Does the environment +variable 'WAFDIR' point +at a folder containing +'waflib' ?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Does the folder of 'waf' +contain a directory +named 'waflib' ?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Unpack the binary +stream contained in 'waf'# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #yes # + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #no# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Was the local library +.waf-1.6-version +extracted from the +'waf' script?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + #Is the local library +usable?# + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/book/wscript b/docs/book/wscript new file mode 100644 index 00000000..c241fd8a --- /dev/null +++ b/docs/book/wscript @@ -0,0 +1,165 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +""" +call 'waf --targets=waf.pdf' or use 'waf list' to see the targets available +""" + +VERSION='0.0.1' +APPNAME='wafdocs' + +import os, re, shutil + +top = '.' +out = 'build' + +re_xi = re.compile('''^(include|image)::([^.]*.(txt|\\{PIC\\}))\[''', re.M) +def ascii_doc_scan(self): + p = self.inputs[0].parent + node_lst = [self.inputs[0]] + seen = [] + depnodes = [] + while node_lst: + nd = node_lst.pop(0) + if nd in seen: continue + seen.append(nd) + + code = nd.read() + for m in re_xi.finditer(code): + name = m.group(2) + if m.group(3) == '{PIC}': + + ext = '.eps' + if self.generator.rule.rfind('A2X') > 0: + ext = '.png' + + k = p.find_resource(name.replace('{PIC}', ext)) + if k: + depnodes.append(k) + else: + k = p.find_resource(name) + if k: + depnodes.append(k) + node_lst.append(k) + return [depnodes, ()] + +import re +def scansize(self): + name = 'image::%s\\{PIC\\}\\[.*,(width|height)=(\\d+)' % self.inputs[0].name[:-4] + re_src = re.compile(name) + lst = self.inputs[0].parent.get_src().ant_glob('*.txt') + for x in lst: + m = re_src.search(x.read()) + if m: + val = str(int(1.6 * int(m.group(2)))) + if m.group(1) == 'width': + w = val + h = "800" + else: + w = "800" + h = val + + ext = self.inputs[0].name[-3:] + if ext == 'eps': + code = '-geometry %sx%s' % (w, h) + elif ext == 'dia': + if m.group(1) == 'width': + h = '' + else: + w = '' + code = '--size %sx%s' % (w, h) + else: + code = '-Gsize="%s,%s"' % (w, h) + break + else: + return ([], '') + + return ([], code) + +def options(opt): + opt.add_option('--exe', action='store_true', default=False, help='Execute the program after it is compiled') + +def configure(conf): + conf.find_program('a2x', var='A2X') + conf.find_program('asciidoc', var='ADOC') + conf.find_program('dia', var='DIA') + conf.find_program('convert', var='CONVERT') + conf.find_program('source-highlight', var='SOURCE_HIGHLIGHT') + +def build(bld): + + + for x in bld.path.ant_glob('*.eps'): + nd = bld.path.get_bld().make_node(x.name) + bld(rule='cp ${SRC} ${TGT}', source=x, target=nd) + bld(rule='${CONVERT} ${bld.raw_deps[tsk.uid()]} -density 600 ${SRC} ${TGT}', source=x, target=x.change_ext('.png'), scan=scansize) + + for x in bld.path.ant_glob('*.dot'): + tg = bld(rule='dot -Teps -o${TGT} ${SRC}', source=x, target=x.change_ext('.eps')) + tg = bld(rule='dot -Tpng -o${TGT} ${SRC}', source=x, target=x.change_ext('.png'), scan=scansize) + #tg = bld(rule='${CONVERT} ${bld.raw_deps[tsk.uid()]} ${SRC} ${TGT}', source=x.change_ext('.eps'), target=x.change_ext('.png'), scan=scansize) + + for x in bld.path.ant_glob('*.dia'): + tg = bld(rule='${DIA} -t eps ${SRC} -e ${TGT}', source=x, target=x.change_ext('.eps')) + tg = bld(rule='${DIA} -t png ${SRC} -e ${TGT}', source=x, target=x.change_ext('.png'), scan=scansize) + #bld(rule='${CONVERT} ${bld.raw_deps[tsk.uid()]} ${SRC} ${TGT}', source=tg.target, target=tg.target.change_ext('.png'), scan=scansize) + + for x in bld.path.ant_glob('pics/*.png'): + bld(rule='cp ${SRC} ${TGT}', source=x, target=x.name) + + bld(rule='mkdir -p ${SRC[0].parent.get_bld().abspath()} && cp ${SRC} ${SRC[0].parent.get_bld().abspath()}', + source=bld.path.ant_glob('callouts/*.png')) + + bld(rule='cp ${SRC} ${bld.bldnode.abspath()}', source='shishell.lang symbols.lang default.style lang.map waf.css') + + bld.add_group() # separator, the documents may require any of the pictures from above + + bld(rule='${ADOC} -a icons=true -a stylesheet=${SRC[1].abspath()} -a iconsdir=. -a toc -d book -o ${TGT} ${SRC[0].abspath()}', + source='waf.txt waf.css', target='single.html', scan=ascii_doc_scan) + + bld(rule='${A2X} -L -a toc --icons-dir=. --icons -D ${gen.path.get_bld().abspath()} -d book -f pdf ${SRC}', + source='waf.txt', target='waf.pdf', scan=ascii_doc_scan) + + bld(rule='${A2X} -L -a toc --icons-dir=. --icons -D ${gen.path.get_bld().abspath()} -d article -f pdf ${SRC}', + source='intro_waf_1.6.txt', target='intro_waf_1.6.pdf', scan=ascii_doc_scan) + + bld(rule='ln -sf single.html index.html', shell=True) + + if bld.options.exe: + def exe(ctx): + bld.exec_command('firefox build/single.html') + bld.add_post_fun(exe) + +""" +Add the following to asciidoc/dblatex/asciidoc-dblatex.sty + +\\usepackage{color} +\\usepackage{listings} +\definecolor{gray}{gray}{0.5} +\definecolor{plum}{rgb}{0.55078125,0.09765625,0.55859375} +\lstset{commentstyle=\color{plum}} +\lstdefinelanguage{shishell} { + morekeywords={}, + sensitive=false, + morecomment=[l]{\$} +} + +Set the following values in asciidoc-dblatex.xsl: + 0 + ... + 0 + +And for vim highlighting: +cp vim/syntax/asciidoc.vim /usr/share/vim/site/syntax/ +cp vim/ftdetect/asciidoc_filetype.vim /usr/share/vim/site/ftdetect/ + +When adding an eps from a svg file, convert it with inscape first +convert (imagemagick) does not process svg files too well + +colors: + yellow fffea6 + green aef9a5 + blue d2d5ff +""" + diff --git a/docs/gfx/slides-executable_deps.dia b/docs/gfx/slides-executable_deps.dia new file mode 100644 index 00000000..345f2f22 Binary files /dev/null and b/docs/gfx/slides-executable_deps.dia differ diff --git a/docs/gfx/waflogo.svg b/docs/gfx/waflogo.svg new file mode 100644 index 00000000..0bc262ee --- /dev/null +++ b/docs/gfx/waflogo.svg @@ -0,0 +1,371 @@ + + + + + waf logo + + + + + + image/svg+xml + + waf logo + License: + cc by-nc + http://creativecommons.org/about/licenses/ + + + + Thomas Nagy + + + + + Thomas Nagy + + + + + waf + + + + + + + + W + A + F + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/docs/gfx/wscript b/docs/gfx/wscript new file mode 100644 index 00000000..958ba0c9 --- /dev/null +++ b/docs/gfx/wscript @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +top = '.' +out = '.' + +def configure(conf): + conf.find_program('convert', var='CONVERT') + conf.find_program('dia', var='DIA') + +def build(bld): + + for x in bld.path.ant_glob('*.svg'): + bld( + rule='${CONVERT} -density 600 ${SRC} ${TGT}', + source=x, + target=x.change_ext('.png'), + ) + + for x in bld.path.ant_glob('*.dia'): + bld( + rule='${DIA} -t png ${SRC} -e ${TGT}', + source=x, + target=x.change_ext('.png'), + ) + +def options(opt): + pass diff --git a/docs/slides/presentation/.gitignore b/docs/slides/presentation/.gitignore new file mode 100644 index 00000000..227924ab --- /dev/null +++ b/docs/slides/presentation/.gitignore @@ -0,0 +1,7 @@ +build +.lock* +*~ +*.o +*.a +*.so + diff --git a/docs/slides/presentation/beamerthemewaf.sty b/docs/slides/presentation/beamerthemewaf.sty new file mode 100644 index 00000000..d9115422 --- /dev/null +++ b/docs/slides/presentation/beamerthemewaf.sty @@ -0,0 +1,28 @@ +\ProvidesPackageRCS $Header: /cvsroot/latex-beamer/latex-beamer/themes/theme/beamerthemewaf.sty,v 1.0 2010/11/05 20:48:30 zougloub Exp $ + + +\DeclareOptionBeamer{hideothersubsections}{\PassOptionsToPackage{hideothersubsections}{beamerouterthemesidebar}} +\DeclareOptionBeamer{hideallsubsections}{\PassOptionsToPackage{hideallsubsections}{beamerouterthemesidebar}} + +\PassOptionsToPackage{right}{beamerouterthemesidebar} +\PassOptionsToPackage{width=2cm}{beamerouterthemesidebar} + +\DeclareOptionBeamer{width}{\PassOptionsToPackage{width=#1}{beamerouterthemesidebar}} +\DeclareOptionBeamer{left}{\PassOptionsToPackage{left}{beamerouterthemesidebar}} +\DeclareOptionBeamer{right}{\PassOptionsToPackage{right}{beamerouterthemesidebar}} + +\ProcessOptionsBeamer + +\mode + +\useoutertheme[height=0pt]{sidebar} + +\definecolor{wafblue}{rgb}{0,0.6,1} +{\usebeamercolor{structure}} +%\setbeamercolor{normal text}{fg=black,bg=mylightgrey} +%\setbeamertemplate{sidebar canvas \beamer@sidebarside}[vertical shading][top=structure.fg!45,bottom=structure.fg!30] +\setbeamertemplate{sidebar canvas \beamer@sidebarside}[vertical shading][top=wafblue!25,bottom=wafblue!15] + +\mode + + diff --git a/docs/slides/presentation/gfx b/docs/slides/presentation/gfx new file mode 120000 index 00000000..af141a40 --- /dev/null +++ b/docs/slides/presentation/gfx @@ -0,0 +1 @@ +../../gfx \ No newline at end of file diff --git a/docs/slides/presentation/slides.tex b/docs/slides/presentation/slides.tex new file mode 100644 index 00000000..cb65372f --- /dev/null +++ b/docs/slides/presentation/slides.tex @@ -0,0 +1,392 @@ +% © Jérôme Carretero 2010 (Zougloub) +% See license at end of file + +\documentclass[xetex]{beamer} +\RequirePackage{fontspec} +\RequirePackage{xunicode} %Unicode extras! +\RequirePackage{xltxtra} %Fixes +\RequirePackage{verbatim} +\RequirePackage{listings} +\RequirePackage{multicol} +\RequirePackage{moreverb} + +\usetheme[hideothersubsections, width=.15\textwidth]{waf} +\usecolortheme{orchid} +\setbeamertemplate{navigation symbols}{} % hides navigation symbols + +\usepackage{fancybox} +\usepackage{multimedia} + +\usepackage{eso-pic} + +\title[Build Tools]{ + Build Automation Tools +} + +\subtitle{ + Comparison of make and waf for non-technical users +} + +\author[]{Jérôme Carretero} + +\institute[] {} + +\date{2010-11-05} + +\lstdefinelanguage{waf} + {morekeywords={def, ctx, bld, opt, configure, options, build, features, source, target, load, use}, + sensitive=true, + morecomment=[l]{\#}, + %morecomment=[s]{/*}{*/}, + morestring=[b]", +} +\begin{document} + +\lstset{ + language=make, + basicstyle=\tiny,%\footnotesize, + %numbers=left,% -> where to put the line-numbers + %numberstyle=\footnotesize,% -> size of the fonts used for the line-numbers + %stepnumber=5,% -> the step between two line-numbers. + %numbersep=5pt,% -> how far the line-numbers are from the code + %backgroundcolor=\color{white},% -> sets background color (needs package) + showspaces=false,% -> show spaces adding particular underscores + showstringspaces=false,% -> underline spaces within strings + showtabs=false,% -> show tabs within strings through particular underscores + frame=single,% -> adds a frame around the code + tabsize=4,% -> sets default tab-size to 2 spaces + captionpos=b,% -> sets the caption-position to bottom + breaklines=true,% -> sets automatic line breaking + breakatwhitespace=false,% -> automatic breaks happen at whitespace + %morecomment=[l]{//}, +} + +\setbeamertemplate{background}{ + \begin{picture}(320, 270) + \put(300, 250){\includegraphics[width=.05\textwidth]{gfx/waflogo.png}} + \end{picture} +} + + +\frame{ + \begin{picture}(0,0) + %\put(0,-5){\includegraphics[width=.2\textwidth]{RT09_Logo.jpg}} + \end{picture} + \vspace{1cm} + \titlepage +} + + +\frame{ +\frametitle{Outline} + \tableofcontents[subsectionstyle=hide/show/hide] +} + +\section{Introduction} + +\begin{frame}{Definition and Context} + \begin{itemize} + \item Build automation is the act of scripting or automating a wide variety of tasks that software developers do in their day-to-day activities \cite{wp_build_automation}. + \item I have seen a lot of code and used many build automation tools + \item For technical reasons, I consider waf to be the best tool out there + \end{itemize} +\end{frame} + +\section{Make} + + +\frame{ +\frametitle{Make: outline} + \tableofcontents[sectionstyle=hide/hide,subsectionstyle=hide/show/hide] +} + +\begin{frame}[fragile] + \scriptsize + \frametitle{Simplest Makefile} + \lstinputlisting{snippets/make-1/Makefile} + \lstinputlisting{snippets/make-1/output} +\end{frame} + +\subsection{Structure of a Makefile} + +\begin{frame}[fragile]{Makefile properties} + Makefiles are like basic kitchen recipes: + \begin{itemize} + \item basic syntax:\\ + \begin{lstlisting} + variable = value + + target: dependencies + commands to run + \end{lstlisting} + \item plus include ability and a few commands + \end{itemize} + + Features: + \begin{itemize} + \item the return codes of the commands are checked for errors + \item the variables are expanded into commands, dependencies, or command strings + \item pretty simple, but relatively efficient + \item nothing beats make for tiny jobs + \end{itemize} +\end{frame} + + + +\subsection{Limitations} + +\begin{frame}{It can become complicated} + The following is a little caricature of what becomes necessary with big projects.\\ + It only works with gcc-style compilers on UNIX: + \lstinputlisting{snippets/make-2/Makefile} + \lstinputlisting{snippets/make-2/output} +\end{frame} + +\subsection{Conclusion} + +\begin{frame}{So what} + \begin{itemize} + \item make was born in 1973\\ + It's mature, but not very high-tech + \item very efficient for small 80's projects + \item since then, other tools have appeared + \item domain-specific language + \item string-based, command-line based (based on the usage of shell scripts) + \end{itemize} +\end{frame} + +\section{Waf Intro} + +\frame{ +\frametitle{Waf: outline} + \tableofcontents[sectionstyle=hide/hide,subsectionstyle=hide/show/hide] +} + +\subsection{Waf is to Make as C++ is to assembly} + + +\begin{frame}{Intro} + \begin{itemize} + \item waf is a newest-generation build tool\\ + History: sh $\to$ make $\to$ scons $\to$ waf + \item built on the Python language, not a custom, restricted special-purpose one\\ + $\neq$ CMake, ant, maven\\ + a waf script can do \textbf{anything} + \item task-based, not file-based\\ + can build abstract stuff + \item includes build configuration + \item custom commands can be added + \item tool abstraction, more portability, cross-compilation friendly + \end{itemize} +\end{frame} + + +\begin{frame}[fragile]{Simplest waf script} + \scriptsize + \lstinputlisting[language=waf]{snippets/waf-1/wscript} + \lstinputlisting{snippets/waf-1/output} +\end{frame} + +\subsection{New Concepts} + +\begin{frame}{New Concepts} + \begin{itemize} + \item \texttt{configure()} configures the build machine to enable proper compilation (library detection, config.h writing, etc.).\\ + Similar to autoconf.\\ + Useful to provide better portability of code. + + \item \texttt{build()} is where you define your build tasks + + \item \texttt{options()} allows to provide compilation options to the build\\ + Mostly unused in simple cases + \end{itemize} +\end{frame} + +\begin{frame}[fragile]{Dependencies Graph} + If you wanted to export modules A and/or B: + \lstinputlisting[language=waf]{snippets/waf-2/wscript} +\end{frame} + + +\begin{frame}{Dependencies Specifications} + \begin{center} + \includegraphics[height=.3\textheight]{gfx/slides-executable_deps.png} + \end{center} + \begin{itemize} + \item You don't need to specify the whole dependency tree of your target\\ + just the closest nodes ! $\neq$ make + \item every \textit{task generator} defines what it does and what it uses + \item[+] less redundancy + \item[+] build scripts quickly become much smaller and powerful than equivalent makefiles + \end{itemize} +\end{frame} + + +\begin{frame}{Tools} + \begin{itemize} + \item You can use waf just like make (specify everything) but there are \textit{tools} + \item ex: compiler\_c, compiler\_cxx, java, cs, winres, d, bison, qt4, ... + \item[+] The tools avoid you to type and build scripts are more readable + \item[+] compiler\_c automatically detects MSVC, GCC, ICC, SUNCC, ... + \item[+] Less lines of code, less maintenance + \end{itemize} +\end{frame} + +\begin{frame}{Waf “Recursivity”} + \begin{itemize} + \item Variables do not get shared with recursive make + \item They do with recursive waf scripts + \end{itemize} + + You define a component in a module, and use it in another module “as is”. + \\ + Ex: NGPF build scripts, the targets are shared between folders + +\end{frame} + + +\begin{frame}{Dependencies Scanning} + \begin{itemize} + \item waf tools automatically scan include dependencies (C, C++, LaTeX, Java, C\#), ... + \item waf keeps a cache of the task yields checksums + \item[$\to$] waf will only rebuild modified files + \item[$\to$] faster online compilation (a lot less \texttt{make clean} performed) + \end{itemize} +\end{frame} + + +\begin{frame}{More} + \begin{itemize} + \item built-in support for build folders\\ + \textbf{very important} + \item built-in support for unit testing + \item built-in support for build machines (auto-build) + \item built-in support for build variants and cross-compilation + \end{itemize} +\end{frame} + +\subsection{Myth Busting} + +\begin{frame}{“waf is a big pile of dark crap”} + Wrong: + \begin{itemize} + \item waf is very small!\\ + core files are <6500 lines of Python code including comments (25\%)\\ + GNU make: 32000 (C) + \item logical and extensible architecture + \begin{itemize} + \scriptsize + \item Core: Context, Build, Node, Configure, Task, TaskGen, Runner, misc %(Errors, Utils, ConfigSet) + \item Plugins: Tools/ + \end{itemize} + \item lots and lots of documentation and examples, great support + \item used by lots of high-tech people: + \begin{itemize} + \scriptsize + \item big enterprise projects (Cisco® IOS, ...) + \item big OpenSource projects (Samba \cite{samba_waf}, XMMS2, Ardour \cite{ardour_waf}, Jack, ...) + \item big research projects (NS-3, ...) + \end{itemize} + \end{itemize} +\end{frame} + + +\begin{frame}{“waf is too complex for me”} + waf scripts are easy to read and write. + A wscript is typically composed of: + \begin{itemize} + \item \texttt{configure()} + \begin{itemize} + \scriptsize + \item external libraries definitions for multiple build platforms (eg. Linux 32+64, QNX, Win32 cross, Win32 native) + \item targets configuration + \item environment checks + \item config.h writing + \end{itemize} + \item \texttt{build()}: build rules (the most touched and simplest part) + \item misc (optional): multi-variant build functions, command-line options parsing, etc. + \end{itemize} + + It's easy to create a simple wscript (lots of examples, very simple syntax). + \\ + ~\\ + Contributing to a big wscript is easier than contributing to a big Makefile. + \\ + The only important part when contributing is the definition of the build task generators. +\end{frame} + + +\begin{frame}{Summary} + \begin{itemize} + \item a recent tool (2005)\\ + still, a lot of examples, and support for complex usages are available + \item a little more complex than make for tiny projects\\ + but it's worth it for everyday development + \item more performant + \item a lot more powerful and flexible + \item me likes it + \end{itemize} +\end{frame} + +\section{Conclusion} + +\begin{frame}{Conclusion} + \begin{itemize} + \item I am convinced that waf brings long-term advantages\\ + I definitely recommend it for complex builds + \item very appropriate for modular project integration + \item I would not maintain huge unreadable makefiles + \end{itemize} +\end{frame} + +\begin{frame}{Links} + +\begin{thebibliography}{9} + +\bibitem{samba_waf} Samba team waf page\\ +\url{http://wiki.samba.org/index.php/Waf} + +\bibitem{wp_build_automation} Wikipedia article on Build Automation\\ +\url{http://en.wikipedia.org/wiki/Build_automation} + +\bibitem{ardour_waf} Ardour build page\\ +\url{http://www.ardour.org/building_ardour3} + +\bibitem{xmms2_waf} XMMS2 installation instructions\\ +\url{http://xmms2.org/wiki/Install_instructions} + +\end{thebibliography} + +\end{frame} + +\end{document} + +License: + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + + diff --git a/docs/slides/presentation/snippets/a.c b/docs/slides/presentation/snippets/a.c new file mode 100644 index 00000000..e69de29b diff --git a/docs/slides/presentation/snippets/a.h b/docs/slides/presentation/snippets/a.h new file mode 100644 index 00000000..79a48975 --- /dev/null +++ b/docs/slides/presentation/snippets/a.h @@ -0,0 +1,2 @@ +#include "b.h" + diff --git a/docs/slides/presentation/snippets/b.cpp b/docs/slides/presentation/snippets/b.cpp new file mode 100644 index 00000000..217b2550 --- /dev/null +++ b/docs/slides/presentation/snippets/b.cpp @@ -0,0 +1,3 @@ +void b() { +} + diff --git a/docs/slides/presentation/snippets/b.h b/docs/slides/presentation/snippets/b.h new file mode 100644 index 00000000..e69de29b diff --git a/docs/slides/presentation/snippets/main.c b/docs/slides/presentation/snippets/main.c new file mode 100644 index 00000000..034a59e2 --- /dev/null +++ b/docs/slides/presentation/snippets/main.c @@ -0,0 +1,5 @@ +#include "a.h" + +int main() { +} + diff --git a/docs/slides/presentation/snippets/make-1/Makefile b/docs/slides/presentation/snippets/make-1/Makefile new file mode 100644 index 00000000..51c4e785 --- /dev/null +++ b/docs/slides/presentation/snippets/make-1/Makefile @@ -0,0 +1,3 @@ + +program: main.o a.o b.o + $(CXX) -o $@ $(LDFLAGS) $+ diff --git a/docs/slides/presentation/snippets/make-1/a.c b/docs/slides/presentation/snippets/make-1/a.c new file mode 120000 index 00000000..e8ec0205 --- /dev/null +++ b/docs/slides/presentation/snippets/make-1/a.c @@ -0,0 +1 @@ +../a.c \ No newline at end of file diff --git a/docs/slides/presentation/snippets/make-1/a.h b/docs/slides/presentation/snippets/make-1/a.h new file mode 120000 index 00000000..f343e76f --- /dev/null +++ b/docs/slides/presentation/snippets/make-1/a.h @@ -0,0 +1 @@ +../a.h \ No newline at end of file diff --git a/docs/slides/presentation/snippets/make-1/b.cpp b/docs/slides/presentation/snippets/make-1/b.cpp new file mode 120000 index 00000000..9a0c5ab5 --- /dev/null +++ b/docs/slides/presentation/snippets/make-1/b.cpp @@ -0,0 +1 @@ +../b.cpp \ No newline at end of file diff --git a/docs/slides/presentation/snippets/make-1/b.h b/docs/slides/presentation/snippets/make-1/b.h new file mode 120000 index 00000000..ab4e2dbc --- /dev/null +++ b/docs/slides/presentation/snippets/make-1/b.h @@ -0,0 +1 @@ +../b.h \ No newline at end of file diff --git a/docs/slides/presentation/snippets/make-1/main.c b/docs/slides/presentation/snippets/make-1/main.c new file mode 120000 index 00000000..60166b0c --- /dev/null +++ b/docs/slides/presentation/snippets/make-1/main.c @@ -0,0 +1 @@ +../main.c \ No newline at end of file diff --git a/docs/slides/presentation/snippets/make-2/Makefile b/docs/slides/presentation/snippets/make-2/Makefile new file mode 100644 index 00000000..5d5bcdba --- /dev/null +++ b/docs/slides/presentation/snippets/make-2/Makefile @@ -0,0 +1,37 @@ +LIBZ_INCLUDES = /usr/include + +LIBA_DIR = liba +LIBA_INCLUDES = $(LIBA_DIR) +LIBA_LIBS = -lz +LIBA_OBJS = $(LIBA_DIR)/a.o + + +LIBB_DIR = libb +LIBB_OBJS = $(LIBB_DIR)/b.o +LIBB_INCLUDES = $(LIBB_DIR) $(LIBA_INCLUDES) +LIBB_DEPOBJS = $(LIBA_OBJS) +LIBB_LIBS = $(LIBA_LIBS) $(LIBA_OBJS) + +PROGRAM_OBJS = prog.o +PROGRAM_INCLUDES = $(LIBB_INCLUDES) +PROGRAM_DEPOBJS = $(LIBB_DEPOBJS) $(LIBB_OBJS) +PROGRAM_LIBS = $(LIBA_LIBS) $(LIBB_LIBS) +PROGRAM = program + +all: $(PROGRAM) + +$(LIBA_OBJS): $(LIBA_DIR)/a.c $(LIBA_DIR)/a.h + $(CC) -o $@ -c $< $(CFLAGS) + +$(LIBB_OBJS): $(LIBB_DIR)/b.cpp $(LIBB_DIR)/b.h $(LIBB_DEPOBJS) + $(CXX) -o $@ -c $< $(CFLAGS) + +$(PROGRAM_OBJS): program.c + $(CC) -o $@ -c $< $(CFLAGS) $(foreach incdir, $(LIBB_INCLUDES), -I $(incdir)) + +$(PROGRAM): $(PROGRAM_OBJS) $(PROGRAM_DEPOBJS) + $(CXX) -o $@ $+ $(LDFLAGS) $(LIBB_LIBS) + +clean: + echo TODO + diff --git a/docs/slides/presentation/snippets/make-2/liba/a.c b/docs/slides/presentation/snippets/make-2/liba/a.c new file mode 120000 index 00000000..91f998bc --- /dev/null +++ b/docs/slides/presentation/snippets/make-2/liba/a.c @@ -0,0 +1 @@ +../../a.c \ No newline at end of file diff --git a/docs/slides/presentation/snippets/make-2/liba/a.h b/docs/slides/presentation/snippets/make-2/liba/a.h new file mode 120000 index 00000000..339337ad --- /dev/null +++ b/docs/slides/presentation/snippets/make-2/liba/a.h @@ -0,0 +1 @@ +../../a.h \ No newline at end of file diff --git a/docs/slides/presentation/snippets/make-2/libb/b.cpp b/docs/slides/presentation/snippets/make-2/libb/b.cpp new file mode 120000 index 00000000..60f99a02 --- /dev/null +++ b/docs/slides/presentation/snippets/make-2/libb/b.cpp @@ -0,0 +1 @@ +../../b.cpp \ No newline at end of file diff --git a/docs/slides/presentation/snippets/make-2/libb/b.h b/docs/slides/presentation/snippets/make-2/libb/b.h new file mode 120000 index 00000000..614f8db9 --- /dev/null +++ b/docs/slides/presentation/snippets/make-2/libb/b.h @@ -0,0 +1 @@ +../../b.h \ No newline at end of file diff --git a/docs/slides/presentation/snippets/make-2/main.c b/docs/slides/presentation/snippets/make-2/main.c new file mode 120000 index 00000000..60166b0c --- /dev/null +++ b/docs/slides/presentation/snippets/make-2/main.c @@ -0,0 +1 @@ +../main.c \ No newline at end of file diff --git a/docs/slides/presentation/snippets/make-2/program.c b/docs/slides/presentation/snippets/make-2/program.c new file mode 100644 index 00000000..034a59e2 --- /dev/null +++ b/docs/slides/presentation/snippets/make-2/program.c @@ -0,0 +1,5 @@ +#include "a.h" + +int main() { +} + diff --git a/docs/slides/presentation/snippets/waf-1/a.c b/docs/slides/presentation/snippets/waf-1/a.c new file mode 120000 index 00000000..e8ec0205 --- /dev/null +++ b/docs/slides/presentation/snippets/waf-1/a.c @@ -0,0 +1 @@ +../a.c \ No newline at end of file diff --git a/docs/slides/presentation/snippets/waf-1/a.h b/docs/slides/presentation/snippets/waf-1/a.h new file mode 120000 index 00000000..f343e76f --- /dev/null +++ b/docs/slides/presentation/snippets/waf-1/a.h @@ -0,0 +1 @@ +../a.h \ No newline at end of file diff --git a/docs/slides/presentation/snippets/waf-1/b.cpp b/docs/slides/presentation/snippets/waf-1/b.cpp new file mode 120000 index 00000000..9a0c5ab5 --- /dev/null +++ b/docs/slides/presentation/snippets/waf-1/b.cpp @@ -0,0 +1 @@ +../b.cpp \ No newline at end of file diff --git a/docs/slides/presentation/snippets/waf-1/b.h b/docs/slides/presentation/snippets/waf-1/b.h new file mode 120000 index 00000000..ab4e2dbc --- /dev/null +++ b/docs/slides/presentation/snippets/waf-1/b.h @@ -0,0 +1 @@ +../b.h \ No newline at end of file diff --git a/docs/slides/presentation/snippets/waf-1/main.c b/docs/slides/presentation/snippets/waf-1/main.c new file mode 120000 index 00000000..60166b0c --- /dev/null +++ b/docs/slides/presentation/snippets/waf-1/main.c @@ -0,0 +1 @@ +../main.c \ No newline at end of file diff --git a/docs/slides/presentation/snippets/waf-1/wscript b/docs/slides/presentation/snippets/waf-1/wscript new file mode 100644 index 00000000..a94cbbb1 --- /dev/null +++ b/docs/slides/presentation/snippets/waf-1/wscript @@ -0,0 +1,11 @@ +def configure(ctx): + ctx.load("compiler_c compiler_cxx") + +def build(bld): + bld.program( + source="b.cpp a.c main.c", + target="program", + ) + +def options(opt): + opt.load("compiler_c compiler_cxx") diff --git a/docs/slides/presentation/snippets/waf-2/liba/a.c b/docs/slides/presentation/snippets/waf-2/liba/a.c new file mode 120000 index 00000000..91f998bc --- /dev/null +++ b/docs/slides/presentation/snippets/waf-2/liba/a.c @@ -0,0 +1 @@ +../../a.c \ No newline at end of file diff --git a/docs/slides/presentation/snippets/waf-2/liba/a.h b/docs/slides/presentation/snippets/waf-2/liba/a.h new file mode 120000 index 00000000..339337ad --- /dev/null +++ b/docs/slides/presentation/snippets/waf-2/liba/a.h @@ -0,0 +1 @@ +../../a.h \ No newline at end of file diff --git a/docs/slides/presentation/snippets/waf-2/libb/b.cpp b/docs/slides/presentation/snippets/waf-2/libb/b.cpp new file mode 120000 index 00000000..60f99a02 --- /dev/null +++ b/docs/slides/presentation/snippets/waf-2/libb/b.cpp @@ -0,0 +1 @@ +../../b.cpp \ No newline at end of file diff --git a/docs/slides/presentation/snippets/waf-2/libb/b.h b/docs/slides/presentation/snippets/waf-2/libb/b.h new file mode 120000 index 00000000..614f8db9 --- /dev/null +++ b/docs/slides/presentation/snippets/waf-2/libb/b.h @@ -0,0 +1 @@ +../../b.h \ No newline at end of file diff --git a/docs/slides/presentation/snippets/waf-2/main.c b/docs/slides/presentation/snippets/waf-2/main.c new file mode 120000 index 00000000..60166b0c --- /dev/null +++ b/docs/slides/presentation/snippets/waf-2/main.c @@ -0,0 +1 @@ +../main.c \ No newline at end of file diff --git a/docs/slides/presentation/snippets/waf-2/wscript b/docs/slides/presentation/snippets/waf-2/wscript new file mode 100644 index 00000000..09fc94ef --- /dev/null +++ b/docs/slides/presentation/snippets/waf-2/wscript @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# encoding: utf-8 + +def configure(ctx): + ctx.load("compiler_c compiler_cxx") + +def build(bld): + bld( + features="cxx", + source="liba/a.c", + export_includes="liba", + target="a", + ) + + bld( + features="cxx", + source="libb/b.cpp", + export_includes="libb", + target="b", + use="a" + ) + + bld.program( + features="cxx", + source="main.c", + target="program", + use="b", + ) + +def options(opt): + opt.load("compiler_c compiler_cxx") + +def hello(ctx): + print("Hello World!") diff --git a/docs/slides/presentation/wscript b/docs/slides/presentation/wscript new file mode 100644 index 00000000..cc0e8a7b --- /dev/null +++ b/docs/slides/presentation/wscript @@ -0,0 +1,53 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Jérôme Carretero, 2010 (zougloub) + +import sys, os +from Utils import subprocess + +def configure(cfg): + cfg.load("tex") + +def build(bld): + def waf_cmd(task): + outfile = os.path.join(task.generator.cwd, "output") + with open(outfile, "w") as f: + cmd = [ + sys.executable, + sys.argv[0], + "configure", + "build", + ] + proc = subprocess.Popen(cmd, cwd=task.generator.cwd, stdout=f, stderr=f) + ret = proc.wait() + if ret != 0: + raise Exception("command failed in %s: %s" % (task.generator.cwd, cmd)) + + waf_dirs = [ os.path.join(bld.path.abspath(), "snippets", d) for d in ["waf-1", "waf-2"] ] + for d in waf_dirs: + bld( + rule=waf_cmd, + cwd=d, + always=True, + name=d, + ) + + make_dirs = [ os.path.join(bld.path.abspath(), "snippets", d) for d in ["make-1", "make-2"] ] + for d in make_dirs: + bld( + rule="make -B > output", + cmd="", + cwd=d, + always=True, + name=d, + ) + + bld.add_group() + + bld( + features="tex", + type="xelatex", + source="slides.tex", + prompt=0, + ) + diff --git a/docs/sphinx/.gitignore b/docs/sphinx/.gitignore new file mode 100644 index 00000000..5b7b53a2 --- /dev/null +++ b/docs/sphinx/.gitignore @@ -0,0 +1,3 @@ +.lock* +build +*~ diff --git a/docs/sphinx/Build.rst b/docs/sphinx/Build.rst new file mode 100644 index 00000000..10de1541 --- /dev/null +++ b/docs/sphinx/Build.rst @@ -0,0 +1,5 @@ +Build +----- + +.. automodule:: waflib.Build + diff --git a/docs/sphinx/ConfigSet.rst b/docs/sphinx/ConfigSet.rst new file mode 100644 index 00000000..36a2f278 --- /dev/null +++ b/docs/sphinx/ConfigSet.rst @@ -0,0 +1,5 @@ +ConfigSet +--------- + +.. automodule:: waflib.ConfigSet + diff --git a/docs/sphinx/Configure.rst b/docs/sphinx/Configure.rst new file mode 100644 index 00000000..46b423ba --- /dev/null +++ b/docs/sphinx/Configure.rst @@ -0,0 +1,5 @@ +Configure +--------- + +.. automodule:: waflib.Configure + diff --git a/docs/sphinx/Context.rst b/docs/sphinx/Context.rst new file mode 100644 index 00000000..b1f90747 --- /dev/null +++ b/docs/sphinx/Context.rst @@ -0,0 +1,5 @@ +Context +--------- + +.. automodule:: waflib.Context + diff --git a/docs/sphinx/Errors.rst b/docs/sphinx/Errors.rst new file mode 100644 index 00000000..03d49dfd --- /dev/null +++ b/docs/sphinx/Errors.rst @@ -0,0 +1,5 @@ +Errors +------ + +.. automodule:: waflib.Errors + diff --git a/docs/sphinx/Logs.rst b/docs/sphinx/Logs.rst new file mode 100644 index 00000000..37ce2b8b --- /dev/null +++ b/docs/sphinx/Logs.rst @@ -0,0 +1,5 @@ +Logs +---- + +.. automodule:: waflib.Logs + diff --git a/docs/sphinx/Makefile b/docs/sphinx/Makefile new file mode 100644 index 00000000..a4749f3b --- /dev/null +++ b/docs/sphinx/Makefile @@ -0,0 +1,130 @@ +# Makefile for Sphinx documentation +# + +# You can set these variables from the command line. +SPHINXOPTS = +SPHINXBUILD = sphinx-build +PAPER = +BUILDDIR = build + +# Internal variables. +PAPEROPT_a4 = -D latex_paper_size=a4 +PAPEROPT_letter = -D latex_paper_size=letter +ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . + +.PHONY: help clean html dirhtml singlehtml pickle json htmlhelp qthelp devhelp epub latex latexpdf text man changes linkcheck doctest + +help: + @echo "Please use \`make ' where is one of" + @echo " html to make standalone HTML files" + @echo " dirhtml to make HTML files named index.html in directories" + @echo " singlehtml to make a single large HTML file" + @echo " pickle to make pickle files" + @echo " json to make JSON files" + @echo " htmlhelp to make HTML files and a HTML help project" + @echo " qthelp to make HTML files and a qthelp project" + @echo " devhelp to make HTML files and a Devhelp project" + @echo " epub to make an epub" + @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" + @echo " latexpdf to make LaTeX files and run them through pdflatex" + @echo " text to make text files" + @echo " man to make manual pages" + @echo " changes to make an overview of all changed/added/deprecated items" + @echo " linkcheck to check all external links for integrity" + @echo " doctest to run all doctests embedded in the documentation (if enabled)" + +clean: + -rm -rf $(BUILDDIR)/* + +html: + $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." + +dirhtml: + $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml + @echo + @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." + +singlehtml: + $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml + @echo + @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." + +pickle: + $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle + @echo + @echo "Build finished; now you can process the pickle files." + +json: + $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json + @echo + @echo "Build finished; now you can process the JSON files." + +htmlhelp: + $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp + @echo + @echo "Build finished; now you can run HTML Help Workshop with the" \ + ".hhp project file in $(BUILDDIR)/htmlhelp." + +qthelp: + $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp + @echo + @echo "Build finished; now you can run "qcollectiongenerator" with the" \ + ".qhcp project file in $(BUILDDIR)/qthelp, like this:" + @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/waf.qhcp" + @echo "To view the help file:" + @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/waf.qhc" + +devhelp: + $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp + @echo + @echo "Build finished." + @echo "To view the help file:" + @echo "# mkdir -p $$HOME/.local/share/devhelp/waf" + @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/waf" + @echo "# devhelp" + +epub: + $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub + @echo + @echo "Build finished. The epub file is in $(BUILDDIR)/epub." + +latex: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo + @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." + @echo "Run \`make' in that directory to run these through (pdf)latex" \ + "(use \`make latexpdf' here to do that automatically)." + +latexpdf: + $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex + @echo "Running LaTeX files through pdflatex..." + make -C $(BUILDDIR)/latex all-pdf + @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." + +text: + $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text + @echo + @echo "Build finished. The text files are in $(BUILDDIR)/text." + +man: + $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man + @echo + @echo "Build finished. The manual pages are in $(BUILDDIR)/man." + +changes: + $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes + @echo + @echo "The overview file is in $(BUILDDIR)/changes." + +linkcheck: + $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck + @echo + @echo "Link check complete; look for any errors in the above output " \ + "or in $(BUILDDIR)/linkcheck/output.txt." + +doctest: + $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest + @echo "Testing of doctests in the sources finished, look at the " \ + "results in $(BUILDDIR)/doctest/output.txt." diff --git a/docs/sphinx/Node.rst b/docs/sphinx/Node.rst new file mode 100644 index 00000000..6fd9bc85 --- /dev/null +++ b/docs/sphinx/Node.rst @@ -0,0 +1,5 @@ +Node +---- + +.. automodule:: waflib.Node + diff --git a/docs/sphinx/Options.rst b/docs/sphinx/Options.rst new file mode 100644 index 00000000..b7a2e917 --- /dev/null +++ b/docs/sphinx/Options.rst @@ -0,0 +1,5 @@ +Options +------- + +.. automodule:: waflib.Options + diff --git a/docs/sphinx/Runner.rst b/docs/sphinx/Runner.rst new file mode 100644 index 00000000..fb255429 --- /dev/null +++ b/docs/sphinx/Runner.rst @@ -0,0 +1,5 @@ +Runner +------ + +.. automodule:: waflib.Runner + diff --git a/docs/sphinx/Scripting.rst b/docs/sphinx/Scripting.rst new file mode 100644 index 00000000..d0f134cf --- /dev/null +++ b/docs/sphinx/Scripting.rst @@ -0,0 +1,5 @@ +Scripting +--------- + +.. automodule:: waflib.Scripting + diff --git a/docs/sphinx/Task.rst b/docs/sphinx/Task.rst new file mode 100644 index 00000000..222a9a39 --- /dev/null +++ b/docs/sphinx/Task.rst @@ -0,0 +1,5 @@ +Task +---- + +.. automodule:: waflib.Task + diff --git a/docs/sphinx/TaskGen.rst b/docs/sphinx/TaskGen.rst new file mode 100644 index 00000000..55e43406 --- /dev/null +++ b/docs/sphinx/TaskGen.rst @@ -0,0 +1,5 @@ +TaskGen +------- + +.. automodule:: waflib.TaskGen + diff --git a/docs/sphinx/Utils.rst b/docs/sphinx/Utils.rst new file mode 100644 index 00000000..d3ec33d4 --- /dev/null +++ b/docs/sphinx/Utils.rst @@ -0,0 +1,5 @@ +Utils +----- + +.. automodule:: waflib.Utils + diff --git a/docs/sphinx/_images/waf-64x64.png b/docs/sphinx/_images/waf-64x64.png new file mode 100644 index 00000000..cbe55f63 Binary files /dev/null and b/docs/sphinx/_images/waf-64x64.png differ diff --git a/docs/sphinx/_templates/indexcontent.html b/docs/sphinx/_templates/indexcontent.html new file mode 100644 index 00000000..ef916e46 --- /dev/null +++ b/docs/sphinx/_templates/indexcontent.html @@ -0,0 +1,59 @@ +{% extends "defindex.html" %} +{% block tables %} + +

Parts of the documentation:

+ + + +
+ + + + + + + + + + + +
+ +

Indices and tables:

+ + +
+ + + + + +
+ +

Meta information:

+ + + +
+ + + + + + + +
+ +{% endblock %} diff --git a/docs/sphinx/_templates/layout.html b/docs/sphinx/_templates/layout.html new file mode 100644 index 00000000..a1771191 --- /dev/null +++ b/docs/sphinx/_templates/layout.html @@ -0,0 +1,20 @@ +{% extends "!layout.html" %} + +{% block footer %} +{{ super() }} + +{% endblock %} diff --git a/docs/sphinx/about.rst b/docs/sphinx/about.rst new file mode 100644 index 00000000..4ebbbbbc --- /dev/null +++ b/docs/sphinx/about.rst @@ -0,0 +1,15 @@ +.. _about: + +About this documentation +------------------------ + +These documents are generated from `reStructuredText`_ sources by `Sphinx`_, a +document processor specifically written for the Python documentation. + +.. _reStructuredText: http://docutils.sf.net/rst.html +.. _Sphinx: http://sphinx.pocoo.org/ + +The development of the documentation takes place on the mailing-list +http://groups.google.com/group/waf-users. We are always looking for volunteers wanting +to help with the docs, so feel free to send a mail there! + diff --git a/docs/sphinx/conf.py b/docs/sphinx/conf.py new file mode 100644 index 00000000..6f8ec285 --- /dev/null +++ b/docs/sphinx/conf.py @@ -0,0 +1,543 @@ +# -*- coding: utf-8 -*- +# +# waf documentation build configuration file, created by +# sphinx-quickstart on Sat Nov 6 20:46:09 2010. +# +# This file is execfile()d with the current directory set to its containing dir. +# +# Note that not all possible configuration values are present in this +# autogenerated file. +# +# All configuration values have a default; values that are commented out +# serve to show the default. + +import sys, os + +# If extensions (or modules to document with autodoc) are in another directory, +# add these directories to sys.path here. If the directory is relative to the +# documentation root, use os.path.abspath to make it absolute, like shown here. +sys.path.insert(0, os.path.abspath(os.path.join('..', ".."))) +sys.path.append(os.path.abspath('.')) + +# monkey patch a few waf classes for documentation purposes! +#----------------------------------------------------------- + +from waflib import TaskGen +from waflib.TaskGen import task_gen, feats + +exclude_taskgen = [] +def taskgen_method(func): + exclude_taskgen.append(func.__name__) + setattr(task_gen, func.__name__, func) + fix_fun_doc(func) + return func +taskgen_method.__doc__ = TaskGen.taskgen_method.__doc__ +TaskGen.taskgen_method = taskgen_method + +def extension(*k): + def deco(func): + exclude_taskgen.append(func.__name__) + setattr(task_gen, func.__name__, func) + for x in k: + task_gen.mappings[x] = func + return func + return deco +extension.__doc__ = TaskGen.extension.__doc__ +TaskGen.extension = extension + +def fix_fun_doc(fun): + try: + if not fun.__doc__.startswith('\tTask generator method'): + fun.__doc__ = '\tTask generator method\n\t\n' + (fun.__doc__ or '') + except Exception as e: + print("Undocumented function %r (%r)" % (fun.__name__, e)) + fun.__doc__ = "" + +def fixmeth(x): + if x == 'process_source': + return ":py:func:`waflib.TaskGen.process_source`" + if x == 'process_rule': + return ":py:func:`waflib.TaskGen.process_rule`" + return ":py:func:`%s`" % x + +def fixfeat(x): + app = '../' + if x in ('*', 'subst'): + app = '' + return "`%s <%sfeaturemap.html#feature%s>`_" % (x=='*' and 'all' or x, app, x!='*' and '-'+x or '') + +def append_doc(fun, keyword, meths): + + if keyword == "feature": + meths = [fixfeat(x) for x in meths] + else: + meths = [fixmeth(x) for x in meths] + + dc = ", ".join(meths) + fun.__doc__ += '\n\t:%s: %s' % (keyword, dc) + +def feature(*k): + def deco(func): + exclude_taskgen.append(func.__name__) + setattr(task_gen, func.__name__, func) + for name in k: + feats[name].update([func.__name__]) + fix_fun_doc(func) + append_doc(func, 'feature', k) + #print "feature", name, k + return func + return deco +feature.__doc__ = TaskGen.feature.__doc__ +TaskGen.feature = feature + + +def before(*k): + def deco(func): + exclude_taskgen.append(func.__name__) + setattr(task_gen, func.__name__, func) + for fun_name in k: + if not func.__name__ in task_gen.prec[fun_name]: + task_gen.prec[fun_name].append(func.__name__) + fix_fun_doc(func) + append_doc(func, 'before', k) + return func + return deco +before.__doc__ = TaskGen.before.__doc__ +TaskGen.before = before + +def after(*k): + def deco(func): + exclude_taskgen.append(func.__name__) + setattr(task_gen, func.__name__, func) + for fun_name in k: + if not fun_name in task_gen.prec[func.__name__]: + task_gen.prec[func.__name__].append(fun_name) + fix_fun_doc(func) + append_doc(func, 'after', k) + return func + return deco +after.__doc__ = TaskGen.after.__doc__ +TaskGen.after = after + +# replay existing methods +TaskGen.taskgen_method(TaskGen.to_nodes) +TaskGen.feature('*')(TaskGen.process_source) +TaskGen.feature('*')(TaskGen.process_rule) +TaskGen.before('process_source')(TaskGen.process_rule) +TaskGen.feature('seq')(TaskGen.sequence_order) +TaskGen.extension('.pc.in')(TaskGen.add_pcfile) +TaskGen.feature('subst')(TaskGen.process_subst) +TaskGen.before('process_source','process_rule')(TaskGen.process_subst) + +from waflib.Task import Task + +Task.__dict__['run'].__doc__ = """ + Execute the task (executed by threads). Override in subclasses. + + :rtype: int + """ +Task.__dict__['post_run'].__doc__ = "Update the cache files (executed by threads). Override in subclasses." + + +from waflib import Configure, Build +confmeths = [] +def conf(f): + def fun(*k, **kw): + mandatory = True + if 'mandatory' in kw: + mandatory = kw['mandatory'] + del kw['mandatory'] + + try: + return f(*k, **kw) + except Errors.ConfigurationError as e: + if mandatory: + raise e + confmeths.append(f.__name__) + f.__doc__ = "\tConfiguration Method bound to :py:class:`waflib.Configure.ConfigurationContext`\n" + (f.__doc__ or '') + setattr(Configure.ConfigurationContext, f.__name__, fun) + setattr(Build.BuildContext, f.__name__, fun) + return f +conf.__doc__ = Configure.conf.__doc__ +Configure.conf = conf + +Configure.ConfigurationContext.__doc__ = """ + Configure the project. + + Waf tools may bind new methods to this class:: + + from waflib.Configure import conf + @conf + def myhelper(self): + print("myhelper") + + def configure(ctx): + ctx.myhelper() +""" + + + + +# Import all tools and build tool->feature map +tool_to_features = {} +import os +lst = [x.replace('.py', '') for x in os.listdir('../../waflib/Tools/') if x.endswith('.py')] +for x in lst: + if x == '__init__': + continue + tool = __import__('waflib.Tools.%s' % x) + + mod = tool.__dict__['Tools'].__dict__[x] + dc = mod.__all__ = list(mod.__dict__.keys()) + + excl = ['before', 'after', 'feature', 'taskgen_method', 'extension'] + if x != 'ccroot': + excl += ['link_task', 'stlink_task'] + for k in excl: + try: + dc.remove(k) + except: + pass + + thetool = getattr(tool.Tools, x) + funcs = dir(thetool) + for func_name in funcs: + thefunc = getattr(TaskGen.task_gen, func_name, None) + if getattr(thefunc, "__name__", None) is None: continue + for feat in TaskGen.feats.keys(): + funcs = list(TaskGen.feats[feat]) + if func_name in funcs: + if x not in tool_to_features: + tool_to_features[x] = [] + tool_to_features[x].append(feat) + + txt = "" + txt += "%s\n%s\n\n.. automodule:: waflib.Tools.%s\n\n" % (x, "="*len(x), x) + if x in tool_to_features: + txt += "Features defined in this module:" + for feat in sorted(list(set(tool_to_features[x]))): + link = "../featuremap.html#feature-%s" % feat + txt += "\n\n* `%s <%s>`_" % (feat, link) + + try: old = open("tools/%s.rst" % x, "r").read() + except: old = None + if old != txt: + with open("tools/%s.rst" % x, "w") as f: + f.write(txt) + +lst = list(TaskGen.feats.keys()) +lst.sort() + +accu = [] +for z in lst: + meths = TaskGen.feats[z] + links = [] + + allmeths = set(TaskGen.feats[z]) + for x in meths: + for y in TaskGen.task_gen.prec.get(x, []): + links.append((x, y)) + allmeths.add(x) + allmeths.add(y) + + color = ',fillcolor="#fffea6",style=filled' + ms = [] + for x in allmeths: + try: + m = TaskGen.task_gen.__dict__[x] + except: + raise ValueError("undefined method %r" % x) + + k = "%s.html#%s.%s" % (m.__module__.split('.')[-1], m.__module__, m.__name__) + if str(m.__module__).find('.Tools') > 0: + k = 'tools/' + k + + ms.append('\t"%s" [style="setlinewidth(0.5)",URL="%s",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10%s];' % (x, k, x in TaskGen.feats[z] and color or '')) + + for x, y in links: + ms.append('\t"%s" -> "%s" [arrowsize=0.5,style="setlinewidth(0.5)"];' % (x, y)) + + rs = '\tdigraph feature_%s {\n\tsize="8.0, 12.0";\n\t%s\n\t}\n' % (z == '*' and 'all' or z, '\n'.join(ms)) + title = "Feature %s" % (z == '*' and '\\*' or z) + title += "\n" + len(title) * '=' + + accu.append("%s\n\n.. graphviz::\n\n%s\n\n" % (title, rs)) + +f = open('tmpmap', 'w') +f.write(""".. _featuremap: + +Feature reference +================= + +.. include:: featuremap_example.txt +""") +f.write("\n".join(accu)) +f.close() + +# now for the configuration methods +confmeths.extend('find_program find_file find_perl_program cmd_to_list add_os_flags check_waf_version'.split()) +confmeths.sort() +confmeths_dict = {} +accu = [] +lst = [x.replace('.py', '') for x in os.listdir('../../waflib/Tools/') if x.endswith('.py')] +for x in lst: + if x == '__init__': + continue + tool = __import__('waflib.Tools.%s' % x) + + mod = tool.__dict__['Tools'].__dict__[x] + dc = mod.__all__ = list(mod.__dict__.keys()) + + thetool = getattr(tool.Tools, x) + funcs = dir(thetool) + for func_name in funcs: + thefunc = getattr(Configure.ConfigurationContext, func_name, None) + if getattr(thefunc, "__name__", None) is None: continue + if thefunc: + confmeths_dict[func_name] = x + +for x in confmeths: + modname = confmeths_dict.get(x, '') + if modname: + d = 'tools/%s.html' % modname + modname = 'Tools.' + modname + else: + modname = 'Configure' + d = '%s.html' % modname + + accu.append('.. _%s: %s#waflib.%s.%s\n' % (x, d, modname, x)) + accu.append('* %s_\n' % x) + +f = open('tmpconf', 'w') +f.write(""".. _confmap: + +Configuration methods +===================== + +.. include:: confmap_example.txt + +""") +f.write("\n".join(accu)) +f.close() + + +#print("Path: %s" % sys.path) + +# -- General configuration ----------------------------------------------------- + +# If your documentation needs a minimal Sphinx version, state it here. +#needs_sphinx = '1.0' + +# Add any Sphinx extension module names here, as strings. They can be extensions +# coming with Sphinx (named 'sphinx.ext.*') or your custom ones. +extensions = ['sphinx.ext.autodoc', 'sphinx.ext.todo', 'sphinx.ext.pngmath', 'sphinx.ext.inheritance_diagram', 'sphinx.ext.graphviz', 'sphinx.ext.viewcode'] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ['_templates'] + +# The suffix of source filenames. +source_suffix = '.rst' + +# The encoding of source files. +#source_encoding = 'utf-8-sig' + +# The master toctree document. +master_doc = 'index' + +# General information about the project. +project = u'Waf' +copyright = u'2010, Thomas Nagy' + +# The version info for the project you're documenting, acts as replacement for +# |version| and |release|, also used in various other places throughout the +# built documents. +# +# The short X.Y version. +version = '1.6.4' +# The full version, including alpha/beta/rc tags. +release = '1.6.4' + +# The language for content autogenerated by Sphinx. Refer to documentation +# for a list of supported languages. +#language = None + +# There are two options for replacing |today|: either, you set today to some +# non-false value, then it is used: +#today = '' +# Else, today_fmt is used as the format for a strftime call. +#today_fmt = '%B %d, %Y' + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +exclude_patterns = [] + +# The reST default role (used for this markup: `text`) to use for all documents. +#default_role = None + +# If true, '()' will be appended to :func: etc. cross-reference text. +#add_function_parentheses = True + +# If true, the current module name will be prepended to all description +# unit titles (such as .. function::). +#add_module_names = True + +# If true, sectionauthor and moduleauthor directives will be shown in the +# output. They are ignored by default. +#show_authors = False + +# The name of the Pygments (syntax highlighting) style to use. +pygments_style = 'sphinx' + +# A list of ignored prefixes for module index sorting. +#modindex_common_prefix = [] + + +# -- Options for HTML output --------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +html_theme = 'default' + +# Theme options are theme-specific and customize the look and feel of a theme +# further. For a list of options available for each theme, see the +# documentation. +#html_theme_options = {} + +# Add any paths that contain custom themes here, relative to this directory. +#html_theme_path = [] + +# The name for this set of Sphinx documents. If None, it defaults to +# " v documentation". +#html_title = None + +# A shorter title for the navigation bar. Default is the same as html_title. +#html_short_title = None + +# The name of an image file (relative to this directory) to place at the top +# of the sidebar. +html_logo = '_images/waf-64x64.png' + +# The name of an image file (within the static path) to use as favicon of the +# docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 +# pixels large. +#html_favicon = None + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +html_static_path = ['_static'] + +# If not '', a 'Last updated on:' timestamp is inserted at every page bottom, +# using the given strftime format. +#html_last_updated_fmt = '%b %d, %Y' + +# If true, SmartyPants will be used to convert quotes and dashes to +# typographically correct entities. +#html_use_smartypants = True + +# Custom sidebar templates, maps document names to template names. +#html_sidebars = {} + +# Additional templates that should be rendered to pages, maps page names to +# template names. +html_additional_pages = {'index':'indexcontent.html'} + +# If false, no module index is generated. +#html_domain_indices = True + +# If false, no index is generated. +#html_use_index = True + +# If true, the index is split into individual pages for each letter. +#html_split_index = False + +# If true, links to the reST sources are added to the pages. +#html_show_sourcelink = True + +# If true, "Created using Sphinx" is shown in the HTML footer. Default is True. +html_show_sphinx = False + +# If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. +#html_show_copyright = True + +# If true, an OpenSearch description file will be output, and all pages will +# contain a tag referring to it. The value of this option must be the +# base URL from which the finished HTML is served. +#html_use_opensearch = '' + +# This is the file name suffix for HTML files (e.g. ".xhtml"). +#html_file_suffix = None + +# Output file base name for HTML help builder. +htmlhelp_basename = 'wafdoc' + + +# -- Options for LaTeX output -------------------------------------------------- + +# The paper size ('letter' or 'a4'). +#latex_paper_size = 'letter' + +# The font size ('10pt', '11pt' or '12pt'). +#latex_font_size = '10pt' + +# Grouping the document tree into LaTeX files. List of tuples +# (source start file, target name, title, author, documentclass [howto/manual]). +latex_documents = [ + ('index', 'waf.tex', u'waf Documentation', + u'Thomas Nagy', 'manual'), +] + +# The name of an image file (relative to this directory) to place at the top of +# the title page. +#latex_logo = None + +# For "manual" documents, if this is true, then toplevel headings are parts, +# not chapters. +#latex_use_parts = False + +# If true, show page references after internal links. +#latex_show_pagerefs = False + +# If true, show URL addresses after external links. +#latex_show_urls = False + +# Additional stuff for the LaTeX preamble. +#latex_preamble = '' + +# Documents to append as an appendix to all manuals. +#latex_appendices = [] + +# If false, no module index is generated. +#latex_domain_indices = True + + +# -- Options for manual page output -------------------------------------------- + +# One entry per manual page. List of tuples +# (source start file, name, description, authors, manual section). +man_pages = [ + ('index', 'waf', u'waf Documentation', + [u'Thomas Nagy'], 1) +] + +#autodoc_default_flags = ['members', 'no-undoc-members', 'show-inheritance'] +autodoc_default_flags = ['members', 'show-inheritance'] +autodoc_member_order = 'bysource' + +def maybe_skip_member(app, what, name, obj, skip, options): + + # from http://sphinx.pocoo.org/ext/autodoc.html#event-autodoc-skip-member + # param name: the fully qualified name of the object <- it is not, the name does not contain the module path + if name == 'Nod3': + return True + global exclude_taskgen + if what == 'class' and name in exclude_taskgen: + return True + if name == '__weakref__': + return True + if obj.__doc__: + return False + +def setup(app): + app.connect('autodoc-skip-member', maybe_skip_member) + diff --git a/docs/sphinx/confmap.rst b/docs/sphinx/confmap.rst new file mode 100644 index 00000000..e16f9e12 --- /dev/null +++ b/docs/sphinx/confmap.rst @@ -0,0 +1,538 @@ +.. _confmap: + +Configuration methods +===================== + +.. include:: confmap_example.txt + +.. _add_as_needed: tools/c_config.html#waflib.Tools.c_config.add_as_needed + +* add_as_needed_ + +.. _add_os_flags: Configure.html#waflib.Configure.add_os_flags + +* add_os_flags_ + +.. _autodetect: tools/msvc.html#waflib.Tools.msvc.autodetect + +* autodetect_ + +.. _cc_add_flags: tools/c_config.html#waflib.Tools.c_config.cc_add_flags + +* cc_add_flags_ + +.. _cc_load_tools: tools/c_config.html#waflib.Tools.c_config.cc_load_tools + +* cc_load_tools_ + +.. _check: tools/c_config.html#waflib.Tools.c_config.check + +* check_ + +.. _check_cc: tools/c_config.html#waflib.Tools.c_config.check_cc + +* check_cc_ + +.. _check_cfg: tools/c_config.html#waflib.Tools.c_config.check_cfg + +* check_cfg_ + +.. _check_cxx: tools/c_config.html#waflib.Tools.c_config.check_cxx + +* check_cxx_ + +.. _check_dlibrary: tools/d_config.html#waflib.Tools.d_config.check_dlibrary + +* check_dlibrary_ + +.. _check_fortran: tools/fc_config.html#waflib.Tools.fc_config.check_fortran + +* check_fortran_ + +.. _check_fortran_clib: tools/fc_config.html#waflib.Tools.fc_config.check_fortran_clib + +* check_fortran_clib_ + +.. _check_fortran_dummy_main: tools/fc_config.html#waflib.Tools.fc_config.check_fortran_dummy_main + +* check_fortran_dummy_main_ + +.. _check_fortran_mangling: tools/fc_config.html#waflib.Tools.fc_config.check_fortran_mangling + +* check_fortran_mangling_ + +.. _check_fortran_verbose_flag: tools/fc_config.html#waflib.Tools.fc_config.check_fortran_verbose_flag + +* check_fortran_verbose_flag_ + +.. _check_inline: tools/c_tests.html#waflib.Tools.c_tests.check_inline + +* check_inline_ + +.. _check_java_class: tools/javaw.html#waflib.Tools.javaw.check_java_class + +* check_java_class_ + +.. _check_jni_headers: tools/javaw.html#waflib.Tools.javaw.check_jni_headers + +* check_jni_headers_ + +.. _check_large_file: tools/c_tests.html#waflib.Tools.c_tests.check_large_file + +* check_large_file_ + +.. _check_lib_msvc: tools/msvc.html#waflib.Tools.msvc.check_lib_msvc + +* check_lib_msvc_ + +.. _check_library: tools/c_tests.html#waflib.Tools.c_tests.check_library + +* check_library_ + +.. _check_libs_msvc: tools/msvc.html#waflib.Tools.msvc.check_libs_msvc + +* check_libs_msvc_ + +.. _check_perl_ext_devel: tools/perl.html#waflib.Tools.perl.check_perl_ext_devel + +* check_perl_ext_devel_ + +.. _check_perl_module: tools/perl.html#waflib.Tools.perl.check_perl_module + +* check_perl_module_ + +.. _check_perl_version: tools/perl.html#waflib.Tools.perl.check_perl_version + +* check_perl_version_ + +.. _check_python_headers: tools/python.html#waflib.Tools.python.check_python_headers + +* check_python_headers_ + +.. _check_python_module: tools/python.html#waflib.Tools.python.check_python_module + +* check_python_module_ + +.. _check_python_version: tools/python.html#waflib.Tools.python.check_python_version + +* check_python_version_ + +.. _check_ruby_ext_devel: tools/ruby.html#waflib.Tools.ruby.check_ruby_ext_devel + +* check_ruby_ext_devel_ + +.. _check_ruby_version: tools/ruby.html#waflib.Tools.ruby.check_ruby_version + +* check_ruby_version_ + +.. _check_vala: tools/vala.html#waflib.Tools.vala.check_vala + +* check_vala_ + +.. _check_vala_deps: tools/vala.html#waflib.Tools.vala.check_vala_deps + +* check_vala_deps_ + +.. _check_waf_version: Configure.html#waflib.Configure.check_waf_version + +* check_waf_version_ + +.. _cmd_to_list: Configure.html#waflib.Configure.cmd_to_list + +* cmd_to_list_ + +.. _common_flags_dmd: tools/dmd.html#waflib.Tools.dmd.common_flags_dmd + +* common_flags_dmd_ + +.. _common_flags_gdc: tools/gdc.html#waflib.Tools.gdc.common_flags_gdc + +* common_flags_gdc_ + +.. _common_flags_ldc: tools/dmd.html#waflib.Tools.dmd.common_flags_ldc + +* common_flags_ldc_ + +.. _cxx_add_flags: tools/c_config.html#waflib.Tools.c_config.cxx_add_flags + +* cxx_add_flags_ + +.. _cxx_load_tools: tools/c_config.html#waflib.Tools.c_config.cxx_load_tools + +* cxx_load_tools_ + +.. _d_platform_flags: tools/d_config.html#waflib.Tools.d_config.d_platform_flags + +* d_platform_flags_ + +.. _define: tools/c_config.html#waflib.Tools.c_config.define + +* define_ + +.. _define_cond: tools/c_config.html#waflib.Tools.c_config.define_cond + +* define_cond_ + +.. _exec_cfg: tools/c_config.html#waflib.Tools.c_config.exec_cfg + +* exec_cfg_ + +.. _fc_flags: tools/fc_config.html#waflib.Tools.fc_config.fc_flags + +* fc_flags_ + +.. _find_ar: tools/ar.html#waflib.Tools.ar.find_ar + +* find_ar_ + +.. _find_dmd: tools/dmd.html#waflib.Tools.dmd.find_dmd + +* find_dmd_ + +.. _find_file: Configure.html#waflib.Configure.find_file + +* find_file_ + +.. _find_g95: tools/g95.html#waflib.Tools.g95.find_g95 + +* find_g95_ + +.. _find_gcc: tools/gcc.html#waflib.Tools.gcc.find_gcc + +* find_gcc_ + +.. _find_gdc: tools/gdc.html#waflib.Tools.gdc.find_gdc + +* find_gdc_ + +.. _find_gfortran: tools/gfortran.html#waflib.Tools.gfortran.find_gfortran + +* find_gfortran_ + +.. _find_gxx: tools/gxx.html#waflib.Tools.gxx.find_gxx + +* find_gxx_ + +.. _find_icc: tools/icc.html#waflib.Tools.icc.find_icc + +* find_icc_ + +.. _find_icpc: tools/icpc.html#waflib.Tools.icpc.find_icpc + +* find_icpc_ + +.. _find_ifort: tools/ifort.html#waflib.Tools.ifort.find_ifort + +* find_ifort_ + +.. _find_lt_names_msvc: tools/msvc.html#waflib.Tools.msvc.find_lt_names_msvc + +* find_lt_names_msvc_ + +.. _find_msvc: tools/msvc.html#waflib.Tools.msvc.find_msvc + +* find_msvc_ + +.. _find_perl_program: Configure.html#waflib.Configure.find_perl_program + +* find_perl_program_ + +.. _find_program: Configure.html#waflib.Configure.find_program + +* find_program_ + +.. _find_scc: tools/suncc.html#waflib.Tools.suncc.find_scc + +* find_scc_ + +.. _find_sxx: tools/suncxx.html#waflib.Tools.suncxx.find_sxx + +* find_sxx_ + +.. _find_valac: tools/vala.html#waflib.Tools.vala.find_valac + +* find_valac_ + +.. _find_xlc: tools/xlc.html#waflib.Tools.xlc.find_xlc + +* find_xlc_ + +.. _find_xlcxx: tools/xlcxx.html#waflib.Tools.xlcxx.find_xlcxx + +* find_xlcxx_ + +.. _fortran_modifier_cygwin: tools/fc_config.html#waflib.Tools.fc_config.fortran_modifier_cygwin + +* fortran_modifier_cygwin_ + +.. _fortran_modifier_darwin: tools/fc_config.html#waflib.Tools.fc_config.fortran_modifier_darwin + +* fortran_modifier_darwin_ + +.. _fortran_modifier_win32: tools/fc_config.html#waflib.Tools.fc_config.fortran_modifier_win32 + +* fortran_modifier_win32_ + +.. _g95_flags: tools/g95.html#waflib.Tools.g95.g95_flags + +* g95_flags_ + +.. _g95_modifier_cygwin: tools/g95.html#waflib.Tools.g95.g95_modifier_cygwin + +* g95_modifier_cygwin_ + +.. _g95_modifier_darwin: tools/g95.html#waflib.Tools.g95.g95_modifier_darwin + +* g95_modifier_darwin_ + +.. _g95_modifier_platform: tools/g95.html#waflib.Tools.g95.g95_modifier_platform + +* g95_modifier_platform_ + +.. _g95_modifier_win32: tools/g95.html#waflib.Tools.g95.g95_modifier_win32 + +* g95_modifier_win32_ + +.. _gather_icl_versions: tools/msvc.html#waflib.Tools.msvc.gather_icl_versions + +* gather_icl_versions_ + +.. _gather_msvc_versions: tools/msvc.html#waflib.Tools.msvc.gather_msvc_versions + +* gather_msvc_versions_ + +.. _gather_wsdk_versions: tools/msvc.html#waflib.Tools.msvc.gather_wsdk_versions + +* gather_wsdk_versions_ + +.. _gcc_common_flags: tools/gcc.html#waflib.Tools.gcc.gcc_common_flags + +* gcc_common_flags_ + +.. _gcc_modifier_aix: tools/gcc.html#waflib.Tools.gcc.gcc_modifier_aix + +* gcc_modifier_aix_ + +.. _gcc_modifier_cygwin: tools/gcc.html#waflib.Tools.gcc.gcc_modifier_cygwin + +* gcc_modifier_cygwin_ + +.. _gcc_modifier_darwin: tools/gcc.html#waflib.Tools.gcc.gcc_modifier_darwin + +* gcc_modifier_darwin_ + +.. _gcc_modifier_hpux: tools/gcc.html#waflib.Tools.gcc.gcc_modifier_hpux + +* gcc_modifier_hpux_ + +.. _gcc_modifier_platform: tools/gcc.html#waflib.Tools.gcc.gcc_modifier_platform + +* gcc_modifier_platform_ + +.. _gcc_modifier_win32: tools/gcc.html#waflib.Tools.gcc.gcc_modifier_win32 + +* gcc_modifier_win32_ + +.. _get_cc_version: tools/c_config.html#waflib.Tools.c_config.get_cc_version + +* get_cc_version_ + +.. _get_config_header: tools/c_config.html#waflib.Tools.c_config.get_config_header + +* get_config_header_ + +.. _get_define: tools/c_config.html#waflib.Tools.c_config.get_define + +* get_define_ + +.. _get_g95_version: tools/g95.html#waflib.Tools.g95.get_g95_version + +* get_g95_version_ + +.. _get_gfortran_version: tools/gfortran.html#waflib.Tools.gfortran.get_gfortran_version + +* get_gfortran_version_ + +.. _get_ifort_version: tools/ifort.html#waflib.Tools.ifort.get_ifort_version + +* get_ifort_version_ + +.. _get_msvc_version: tools/msvc.html#waflib.Tools.msvc.get_msvc_version + +* get_msvc_version_ + +.. _get_msvc_versions: tools/msvc.html#waflib.Tools.msvc.get_msvc_versions + +* get_msvc_versions_ + +.. _get_python_variables: tools/python.html#waflib.Tools.python.get_python_variables + +* get_python_variables_ + +.. _gfortran_flags: tools/gfortran.html#waflib.Tools.gfortran.gfortran_flags + +* gfortran_flags_ + +.. _gfortran_modifier_cygwin: tools/gfortran.html#waflib.Tools.gfortran.gfortran_modifier_cygwin + +* gfortran_modifier_cygwin_ + +.. _gfortran_modifier_darwin: tools/gfortran.html#waflib.Tools.gfortran.gfortran_modifier_darwin + +* gfortran_modifier_darwin_ + +.. _gfortran_modifier_platform: tools/gfortran.html#waflib.Tools.gfortran.gfortran_modifier_platform + +* gfortran_modifier_platform_ + +.. _gfortran_modifier_win32: tools/gfortran.html#waflib.Tools.gfortran.gfortran_modifier_win32 + +* gfortran_modifier_win32_ + +.. _gxx_common_flags: tools/gxx.html#waflib.Tools.gxx.gxx_common_flags + +* gxx_common_flags_ + +.. _gxx_modifier_aix: tools/gxx.html#waflib.Tools.gxx.gxx_modifier_aix + +* gxx_modifier_aix_ + +.. _gxx_modifier_cygwin: tools/gxx.html#waflib.Tools.gxx.gxx_modifier_cygwin + +* gxx_modifier_cygwin_ + +.. _gxx_modifier_darwin: tools/gxx.html#waflib.Tools.gxx.gxx_modifier_darwin + +* gxx_modifier_darwin_ + +.. _gxx_modifier_hpux: tools/gxx.html#waflib.Tools.gxx.gxx_modifier_hpux + +* gxx_modifier_hpux_ + +.. _gxx_modifier_platform: tools/gxx.html#waflib.Tools.gxx.gxx_modifier_platform + +* gxx_modifier_platform_ + +.. _gxx_modifier_win32: tools/gxx.html#waflib.Tools.gxx.gxx_modifier_win32 + +* gxx_modifier_win32_ + +.. _have_define: tools/c_config.html#waflib.Tools.c_config.have_define + +* have_define_ + +.. _ifort_modifier_cygwin: tools/ifort.html#waflib.Tools.ifort.ifort_modifier_cygwin + +* ifort_modifier_cygwin_ + +.. _ifort_modifier_platform: tools/ifort.html#waflib.Tools.ifort.ifort_modifier_platform + +* ifort_modifier_platform_ + +.. _is_defined: tools/c_config.html#waflib.Tools.c_config.is_defined + +* is_defined_ + +.. _is_link_verbose: tools/fc_config.html#waflib.Tools.fc_config.is_link_verbose + +* is_link_verbose_ + +.. _libname_msvc: tools/msvc.html#waflib.Tools.msvc.libname_msvc + +* libname_msvc_ + +.. _link_add_flags: tools/c_config.html#waflib.Tools.c_config.link_add_flags + +* link_add_flags_ + +.. _msvc_common_flags: tools/msvc.html#waflib.Tools.msvc.msvc_common_flags + +* msvc_common_flags_ + +.. _multicheck: tools/c_config.html#waflib.Tools.c_config.multicheck + +* multicheck_ + +.. _no_autodetect: tools/msvc.html#waflib.Tools.msvc.no_autodetect + +* no_autodetect_ + +.. _objects: tools/c_aliases.html#waflib.Tools.c_aliases.objects + +* objects_ + +.. _parse_flags: tools/c_config.html#waflib.Tools.c_config.parse_flags + +* parse_flags_ + +.. _post_check: tools/c_config.html#waflib.Tools.c_config.post_check + +* post_check_ + +.. _print_all_msvc_detected: tools/msvc.html#waflib.Tools.msvc.print_all_msvc_detected + +* print_all_msvc_detected_ + +.. _program: tools/c_aliases.html#waflib.Tools.c_aliases.program + +* program_ + +.. _read_csshlib: tools/cs.html#waflib.Tools.cs.read_csshlib + +* read_csshlib_ + +.. _read_shlib: tools/ccroot.html#waflib.Tools.ccroot.read_shlib + +* read_shlib_ + +.. _read_stlib: tools/ccroot.html#waflib.Tools.ccroot.read_stlib + +* read_stlib_ + +.. _ret_msg: tools/c_config.html#waflib.Tools.c_config.ret_msg + +* ret_msg_ + +.. _run_c_code: tools/c_config.html#waflib.Tools.c_config.run_c_code + +* run_c_code_ + +.. _scc_common_flags: tools/suncc.html#waflib.Tools.suncc.scc_common_flags + +* scc_common_flags_ + +.. _shlib: tools/c_aliases.html#waflib.Tools.c_aliases.shlib + +* shlib_ + +.. _stlib: tools/c_aliases.html#waflib.Tools.c_aliases.stlib + +* stlib_ + +.. _sxx_common_flags: tools/suncxx.html#waflib.Tools.suncxx.sxx_common_flags + +* sxx_common_flags_ + +.. _undefine: tools/c_config.html#waflib.Tools.c_config.undefine + +* undefine_ + +.. _validate_c: tools/c_config.html#waflib.Tools.c_config.validate_c + +* validate_c_ + +.. _validate_cfg: tools/c_config.html#waflib.Tools.c_config.validate_cfg + +* validate_cfg_ + +.. _write_config_header: tools/c_config.html#waflib.Tools.c_config.write_config_header + +* write_config_header_ + +.. _xlc_common_flags: tools/xlc.html#waflib.Tools.xlc.xlc_common_flags + +* xlc_common_flags_ + +.. _xlcxx_common_flags: tools/xlcxx.html#waflib.Tools.xlcxx.xlcxx_common_flags + +* xlcxx_common_flags_ diff --git a/docs/sphinx/confmap_example.txt b/docs/sphinx/confmap_example.txt new file mode 100644 index 00000000..f25640da --- /dev/null +++ b/docs/sphinx/confmap_example.txt @@ -0,0 +1,22 @@ +The configuration methods `@conf` are bound to the configuration context, and to the build context:: + + @waflib.Configure.conf + def find_program(ctx): + pass + + def configure(conf): + conf.find_program(...) + +The object `conf.env` is usually modified during the execution. If several methods have to be called, then +a transaction should be used, for example:: + + def configure(conf): + conf.env.stash() + try: + conf.find_program('strip') + conf.env.append_value('CFLAGS', '-O3') + finally: + conf.env.revert() + + + diff --git a/docs/sphinx/copyright.rst b/docs/sphinx/copyright.rst new file mode 100644 index 00000000..5766a89f --- /dev/null +++ b/docs/sphinx/copyright.rst @@ -0,0 +1,33 @@ +.. _license: + +Waf copyright +------------- + +The waf file, and this documentation are subject to the following notice:: + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + 1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + 2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + 3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + + THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR + IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED + WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, + INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR + SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, + STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING + IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + diff --git a/docs/sphinx/coremodules.rst b/docs/sphinx/coremodules.rst new file mode 100644 index 00000000..5c539b23 --- /dev/null +++ b/docs/sphinx/coremodules.rst @@ -0,0 +1,55 @@ +.. _coremodules: + + +The core modules +================ + +Waf is based on 13 core modules which provide the main functionality of the framework. +They may be used alone, although the support for programming languages or compilers is provided by extensions called Waf tools. + +.. graphviz:: + + digraph module_deps { + size="8.0, 12.0"; + "Build" [style="setlinewidth(0.5)",URL="Build.html",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "ConfigSet" [style="setlinewidth(0.5)",URL="ConfigSet.html",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "Configure" [style="setlinewidth(0.5)",URL="Configure.html",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "Context" [style="setlinewidth(0.5)",URL="Context.html",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "Logs" [style="setlinewidth(0.5)",URL="Logs.html",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "Node" [style="setlinewidth(0.5)",URL="Node.html",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "Options" [style="setlinewidth(0.5)",URL="Options.html",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "Runner" [style="setlinewidth(0.5)",URL="Runner.html",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "Scripting" [style="setlinewidth(0.5)",URL="Scripting.html",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "TaskGen" [style="setlinewidth(0.5)",URL="TaskGen.html",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "Task" [style="setlinewidth(0.5)",URL="Task.html",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "Utils" [style="setlinewidth(0.5)",URL="Utils.html",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "Errors" [style="setlinewidth(0.5)",URL="Errors.html",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + + "Build" -> "Runner" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "Build" -> "TaskGen" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "Build" -> "ConfigSet" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "Build" -> "Options" [arrowsize=0.5,style="setlinewidth(0.5)"]; + + "ConfigSet" -> "Utils" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "ConfigSet" -> "Logs" [arrowsize=0.5,style="setlinewidth(0.5)"]; + + "Configure" -> "Build" [arrowsize=0.5,style="setlinewidth(0.5)"]; + + "Context" -> "Logs" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "Context" -> "Node" [arrowsize=0.5,style="setlinewidth(0.5)"]; + + "Node" -> "Utils" [arrowsize=0.5,style="setlinewidth(0.5)"]; + + "Options" -> "Context" [arrowsize=0.5,style="setlinewidth(0.5)"]; + + "Runner" -> "Task" [arrowsize=0.5,style="setlinewidth(0.5)"]; + + "Scripting" -> "Configure" [arrowsize=0.5,style="setlinewidth(0.5)"]; + + "TaskGen" -> "Task" [arrowsize=0.5,style="setlinewidth(0.5)"]; + + "Task" -> "Logs" [arrowsize=0.5,style="setlinewidth(0.5)"]; + + "Utils" -> "Errors" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + diff --git a/docs/sphinx/featuremap.rst b/docs/sphinx/featuremap.rst new file mode 100644 index 00000000..4e940e27 --- /dev/null +++ b/docs/sphinx/featuremap.rst @@ -0,0 +1,810 @@ +.. _featuremap: + +Feature reference +================= + +.. include:: featuremap_example.txt +Feature \* +========== + +.. graphviz:: + + digraph feature_all { + size="8.0, 12.0"; + "apply_intltool_in_f" [style="setlinewidth(0.5)",URL="tools/intltool.html#waflib.Tools.intltool.apply_intltool_in_f",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "process_source" [style="setlinewidth(0.5)",URL="TaskGen.html#waflib.TaskGen.process_source",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "process_rule" [style="setlinewidth(0.5)",URL="TaskGen.html#waflib.TaskGen.process_rule",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "jar_files" [style="setlinewidth(0.5)",URL="tools/javaw.html#waflib.Tools.javaw.jar_files",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "process_marshal" [style="setlinewidth(0.5)",URL="tools/glib2.html#waflib.Tools.glib2.process_marshal",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "link_lib_test_fun" [style="setlinewidth(0.5)",URL="tools/c_tests.html#waflib.Tools.c_tests.link_lib_test_fun",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "process_enums" [style="setlinewidth(0.5)",URL="tools/glib2.html#waflib.Tools.glib2.process_enums",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_java" [style="setlinewidth(0.5)",URL="tools/javaw.html#waflib.Tools.javaw.apply_java",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "link_main_routines_tg_method" [style="setlinewidth(0.5)",URL="tools/fc_config.html#waflib.Tools.fc_config.link_main_routines_tg_method",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "process_subst" [style="setlinewidth(0.5)",URL="TaskGen.html#waflib.TaskGen.process_subst",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_cs" [style="setlinewidth(0.5)",URL="tools/cs.html#waflib.Tools.cs.apply_cs",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_tex" [style="setlinewidth(0.5)",URL="tools/tex.html#waflib.Tools.tex.apply_tex",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "process_rule" -> "process_subst" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "process_source" -> "process_rule" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "process_source" -> "process_subst" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "process_source" -> "link_lib_test_fun" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "process_source" -> "link_main_routines_tg_method" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "process_source" -> "apply_tex" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "process_source" -> "apply_java" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "process_source" -> "jar_files" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "process_source" -> "process_marshal" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "process_source" -> "process_enums" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "process_source" -> "apply_cs" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "process_source" -> "apply_intltool_in_f" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature asm +=========== + +.. graphviz:: + + digraph feature_asm { + size="8.0, 12.0"; + "process_use" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.process_use",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_nasm_vars" [style="setlinewidth(0.5)",URL="tools/nasm.html#waflib.Tools.nasm.apply_nasm_vars",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_perlext" [style="setlinewidth(0.5)",URL="tools/perl.html#waflib.Tools.perl.init_perlext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_ruby_so_name" [style="setlinewidth(0.5)",URL="tools/ruby.html#waflib.Tools.ruby.apply_ruby_so_name",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "process_source" [style="setlinewidth(0.5)",URL="TaskGen.html#waflib.TaskGen.process_source",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_incpaths" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_incpaths",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_link" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_link",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_pyext" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.init_pyext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "init_rubyext" [style="setlinewidth(0.5)",URL="tools/ruby.html#waflib.Tools.ruby.init_rubyext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "set_lib_pat" [style="setlinewidth(0.5)",URL="tools/fc_config.html#waflib.Tools.fc_config.set_lib_pat",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "propagate_uselib_vars" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.propagate_uselib_vars",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_bundle" [style="setlinewidth(0.5)",URL="tools/c_osx.html#waflib.Tools.c_osx.apply_bundle",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_incpaths" -> "propagate_uselib_vars" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "process_source" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "process_use" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "init_rubyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "apply_bundle" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "process_source" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "set_lib_pat" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "init_rubyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "apply_ruby_so_name" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "init_pyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature c +========= + +.. graphviz:: + + digraph feature_c { + size="8.0, 12.0"; + "process_use" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.process_use",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "process_source" [style="setlinewidth(0.5)",URL="TaskGen.html#waflib.TaskGen.process_source",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_incpaths" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_incpaths",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "set_lib_pat" [style="setlinewidth(0.5)",URL="tools/fc_config.html#waflib.Tools.fc_config.set_lib_pat",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_bundle" [style="setlinewidth(0.5)",URL="tools/c_osx.html#waflib.Tools.c_osx.apply_bundle",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "set_macosx_deployment_target" [style="setlinewidth(0.5)",URL="tools/c_osx.html#waflib.Tools.c_osx.set_macosx_deployment_target",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_perlext" [style="setlinewidth(0.5)",URL="tools/perl.html#waflib.Tools.perl.init_perlext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_ruby_so_name" [style="setlinewidth(0.5)",URL="tools/ruby.html#waflib.Tools.ruby.apply_ruby_so_name",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_link" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_link",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_pyext" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.init_pyext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "init_rubyext" [style="setlinewidth(0.5)",URL="tools/ruby.html#waflib.Tools.ruby.init_rubyext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_flags_msvc" [style="setlinewidth(0.5)",URL="tools/msvc.html#waflib.Tools.msvc.apply_flags_msvc",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "propagate_uselib_vars" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.propagate_uselib_vars",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_pyembed" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.init_pyembed",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "process_use" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "process_use" -> "process_source" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "propagate_uselib_vars" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "process_source" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "process_use" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "init_rubyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "apply_bundle" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "process_source" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "set_lib_pat" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "init_rubyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "apply_ruby_so_name" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "init_pyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_flags_msvc" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "apply_bundle" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "process_use" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "set_lib_pat" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_pyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_pyembed" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_bundle" -> "init_rubyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature cprogram +================ + +.. graphviz:: + + digraph feature_cprogram { + size="8.0, 12.0"; + "create_task_macapp" [style="setlinewidth(0.5)",URL="tools/c_osx.html#waflib.Tools.c_osx.create_task_macapp",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "create_task_macplist" [style="setlinewidth(0.5)",URL="tools/c_osx.html#waflib.Tools.c_osx.create_task_macplist",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_link" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_link",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_manifest" [style="setlinewidth(0.5)",URL="tools/msvc.html#waflib.Tools.msvc.apply_manifest",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "create_task_macapp" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "create_task_macplist" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_manifest" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature cs +========== + +.. graphviz:: + + digraph feature_cs { + size="8.0, 12.0"; + "process_use" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.process_use",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "init_perlext" [style="setlinewidth(0.5)",URL="tools/perl.html#waflib.Tools.perl.init_perlext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "debug_cs" [style="setlinewidth(0.5)",URL="tools/cs.html#waflib.Tools.cs.debug_cs",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_pyext" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.init_pyext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "use_cs" [style="setlinewidth(0.5)",URL="tools/cs.html#waflib.Tools.cs.use_cs",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "set_lib_pat" [style="setlinewidth(0.5)",URL="tools/fc_config.html#waflib.Tools.fc_config.set_lib_pat",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "propagate_uselib_vars" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.propagate_uselib_vars",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_pyembed" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.init_pyembed",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_cs" [style="setlinewidth(0.5)",URL="tools/cs.html#waflib.Tools.cs.apply_cs",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_bundle" [style="setlinewidth(0.5)",URL="tools/c_osx.html#waflib.Tools.c_osx.apply_bundle",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "use_cs" -> "apply_cs" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "apply_bundle" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "process_use" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "set_lib_pat" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_pyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_pyembed" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "debug_cs" -> "apply_cs" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "debug_cs" -> "use_cs" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature cshlib +============== + +.. graphviz:: + + digraph feature_cshlib { + size="8.0, 12.0"; + "apply_implib" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_implib",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_bundle_remove_dynamiclib" [style="setlinewidth(0.5)",URL="tools/c_osx.html#waflib.Tools.c_osx.apply_bundle_remove_dynamiclib",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_vnum" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_vnum",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_manifest" [style="setlinewidth(0.5)",URL="tools/msvc.html#waflib.Tools.msvc.apply_manifest",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_link" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_link",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_implib" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_bundle_remove_dynamiclib" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_vnum" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_manifest" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature cxx +=========== + +.. graphviz:: + + digraph feature_cxx { + size="8.0, 12.0"; + "process_use" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.process_use",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "process_source" [style="setlinewidth(0.5)",URL="TaskGen.html#waflib.TaskGen.process_source",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_incpaths" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_incpaths",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "set_lib_pat" [style="setlinewidth(0.5)",URL="tools/fc_config.html#waflib.Tools.fc_config.set_lib_pat",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_bundle" [style="setlinewidth(0.5)",URL="tools/c_osx.html#waflib.Tools.c_osx.apply_bundle",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "set_macosx_deployment_target" [style="setlinewidth(0.5)",URL="tools/c_osx.html#waflib.Tools.c_osx.set_macosx_deployment_target",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_perlext" [style="setlinewidth(0.5)",URL="tools/perl.html#waflib.Tools.perl.init_perlext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_ruby_so_name" [style="setlinewidth(0.5)",URL="tools/ruby.html#waflib.Tools.ruby.apply_ruby_so_name",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_link" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_link",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_pyext" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.init_pyext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "init_rubyext" [style="setlinewidth(0.5)",URL="tools/ruby.html#waflib.Tools.ruby.init_rubyext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_flags_msvc" [style="setlinewidth(0.5)",URL="tools/msvc.html#waflib.Tools.msvc.apply_flags_msvc",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "propagate_uselib_vars" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.propagate_uselib_vars",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_pyembed" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.init_pyembed",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "process_use" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "process_use" -> "process_source" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "propagate_uselib_vars" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "process_source" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "process_use" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "init_rubyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "apply_bundle" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "process_source" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "set_lib_pat" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "init_rubyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "apply_ruby_so_name" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "init_pyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_flags_msvc" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "apply_bundle" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "process_use" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "set_lib_pat" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_pyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_pyembed" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_bundle" -> "init_rubyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature cxxprogram +================== + +.. graphviz:: + + digraph feature_cxxprogram { + size="8.0, 12.0"; + "create_task_macapp" [style="setlinewidth(0.5)",URL="tools/c_osx.html#waflib.Tools.c_osx.create_task_macapp",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "create_task_macplist" [style="setlinewidth(0.5)",URL="tools/c_osx.html#waflib.Tools.c_osx.create_task_macplist",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_link" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_link",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_manifest" [style="setlinewidth(0.5)",URL="tools/msvc.html#waflib.Tools.msvc.apply_manifest",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "create_task_macapp" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "create_task_macplist" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_manifest" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature cxxshlib +================ + +.. graphviz:: + + digraph feature_cxxshlib { + size="8.0, 12.0"; + "apply_implib" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_implib",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_bundle_remove_dynamiclib" [style="setlinewidth(0.5)",URL="tools/c_osx.html#waflib.Tools.c_osx.apply_bundle_remove_dynamiclib",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_vnum" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_vnum",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_manifest" [style="setlinewidth(0.5)",URL="tools/msvc.html#waflib.Tools.msvc.apply_manifest",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_link" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_link",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_implib" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_bundle_remove_dynamiclib" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_vnum" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_manifest" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature d +========= + +.. graphviz:: + + digraph feature_d { + size="8.0, 12.0"; + "process_use" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.process_use",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "process_header" [style="setlinewidth(0.5)",URL="tools/d.html#waflib.Tools.d.process_header",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_ruby_so_name" [style="setlinewidth(0.5)",URL="tools/ruby.html#waflib.Tools.ruby.apply_ruby_so_name",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "process_source" [style="setlinewidth(0.5)",URL="TaskGen.html#waflib.TaskGen.process_source",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_incpaths" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_incpaths",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_link" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_link",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_pyext" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.init_pyext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "init_rubyext" [style="setlinewidth(0.5)",URL="tools/ruby.html#waflib.Tools.ruby.init_rubyext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "init_perlext" [style="setlinewidth(0.5)",URL="tools/perl.html#waflib.Tools.perl.init_perlext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "set_lib_pat" [style="setlinewidth(0.5)",URL="tools/fc_config.html#waflib.Tools.fc_config.set_lib_pat",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "propagate_uselib_vars" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.propagate_uselib_vars",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_pyembed" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.init_pyembed",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_bundle" [style="setlinewidth(0.5)",URL="tools/c_osx.html#waflib.Tools.c_osx.apply_bundle",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "process_use" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "process_use" -> "process_source" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "propagate_uselib_vars" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "process_source" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "process_use" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "init_rubyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "apply_bundle" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "process_use" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "set_lib_pat" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_pyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_pyembed" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "apply_bundle" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "process_source" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "set_lib_pat" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "init_rubyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "apply_ruby_so_name" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "init_pyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature dshlib +============== + +.. graphviz:: + + digraph feature_dshlib { + size="8.0, 12.0"; + "apply_vnum" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_vnum",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_link" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_link",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_vnum" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature fake_lib +================ + +.. graphviz:: + + digraph feature_fake_lib { + size="8.0, 12.0"; + "process_lib" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.process_lib",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + } + + + +Feature fc +========== + +.. graphviz:: + + digraph feature_fc { + size="8.0, 12.0"; + "process_use" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.process_use",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_perlext" [style="setlinewidth(0.5)",URL="tools/perl.html#waflib.Tools.perl.init_perlext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_ruby_so_name" [style="setlinewidth(0.5)",URL="tools/ruby.html#waflib.Tools.ruby.apply_ruby_so_name",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "process_source" [style="setlinewidth(0.5)",URL="TaskGen.html#waflib.TaskGen.process_source",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_incpaths" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_incpaths",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_link" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_link",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_pyext" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.init_pyext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "init_rubyext" [style="setlinewidth(0.5)",URL="tools/ruby.html#waflib.Tools.ruby.init_rubyext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "set_lib_pat" [style="setlinewidth(0.5)",URL="tools/fc_config.html#waflib.Tools.fc_config.set_lib_pat",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "propagate_uselib_vars" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.propagate_uselib_vars",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_pyembed" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.init_pyembed",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_bundle" [style="setlinewidth(0.5)",URL="tools/c_osx.html#waflib.Tools.c_osx.apply_bundle",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "process_use" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "process_use" -> "process_source" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "propagate_uselib_vars" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "process_source" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "process_use" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "init_rubyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "apply_bundle" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "process_use" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "set_lib_pat" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_pyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_pyembed" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "apply_bundle" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "process_source" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "set_lib_pat" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "init_rubyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "apply_ruby_so_name" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "init_pyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature fcprogram +================= + +.. graphviz:: + + digraph feature_fcprogram { + size="8.0, 12.0"; + "dummy" [style="setlinewidth(0.5)",URL="tools/fc.html#waflib.Tools.fc.dummy",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + } + + + +Feature fcprogram_test +====================== + +.. graphviz:: + + digraph feature_fcprogram_test { + size="8.0, 12.0"; + "dummy" [style="setlinewidth(0.5)",URL="tools/fc.html#waflib.Tools.fc.dummy",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + } + + + +Feature fcshlib +=============== + +.. graphviz:: + + digraph feature_fcshlib { + size="8.0, 12.0"; + "dummy" [style="setlinewidth(0.5)",URL="tools/fc.html#waflib.Tools.fc.dummy",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_vnum" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_vnum",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_link" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_link",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_vnum" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature fcstlib +=============== + +.. graphviz:: + + digraph feature_fcstlib { + size="8.0, 12.0"; + "dummy" [style="setlinewidth(0.5)",URL="tools/fc.html#waflib.Tools.fc.dummy",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + } + + + +Feature glib2 +============= + +.. graphviz:: + + digraph feature_glib2 { + size="8.0, 12.0"; + "process_settings" [style="setlinewidth(0.5)",URL="tools/glib2.html#waflib.Tools.glib2.process_settings",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + } + + + +Feature go +========== + +.. graphviz:: + + digraph feature_go { + size="8.0, 12.0"; + "process_use" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.process_use",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "init_perlext" [style="setlinewidth(0.5)",URL="tools/perl.html#waflib.Tools.perl.init_perlext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_ruby_so_name" [style="setlinewidth(0.5)",URL="tools/ruby.html#waflib.Tools.ruby.apply_ruby_so_name",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "process_source" [style="setlinewidth(0.5)",URL="TaskGen.html#waflib.TaskGen.process_source",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_incpaths" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_incpaths",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_link" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_link",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_pyext" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.init_pyext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "init_rubyext" [style="setlinewidth(0.5)",URL="tools/ruby.html#waflib.Tools.ruby.init_rubyext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "set_lib_pat" [style="setlinewidth(0.5)",URL="tools/fc_config.html#waflib.Tools.fc_config.set_lib_pat",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "propagate_uselib_vars" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.propagate_uselib_vars",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_bundle" [style="setlinewidth(0.5)",URL="tools/c_osx.html#waflib.Tools.c_osx.apply_bundle",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_incpaths" -> "propagate_uselib_vars" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "process_source" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "process_use" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "init_rubyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "apply_bundle" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "process_source" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "set_lib_pat" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "init_rubyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "apply_ruby_so_name" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "init_pyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_link" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature includes +================ + +.. graphviz:: + + digraph feature_includes { + size="8.0, 12.0"; + "process_use" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.process_use",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "init_perlext" [style="setlinewidth(0.5)",URL="tools/perl.html#waflib.Tools.perl.init_perlext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "process_source" [style="setlinewidth(0.5)",URL="TaskGen.html#waflib.TaskGen.process_source",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_incpaths" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_incpaths",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_rubyext" [style="setlinewidth(0.5)",URL="tools/ruby.html#waflib.Tools.ruby.init_rubyext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "propagate_uselib_vars" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.propagate_uselib_vars",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_incpaths" -> "propagate_uselib_vars" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "process_source" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "process_use" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "init_rubyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "apply_incpaths" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature intltool_in +=================== + +.. graphviz:: + + digraph feature_intltool_in { + size="8.0, 12.0"; + "apply_intltool_in_f" [style="setlinewidth(0.5)",URL="tools/intltool.html#waflib.Tools.intltool.apply_intltool_in_f",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + } + + + +Feature intltool_po +=================== + +.. graphviz:: + + digraph feature_intltool_po { + size="8.0, 12.0"; + "apply_intltool_po" [style="setlinewidth(0.5)",URL="tools/intltool.html#waflib.Tools.intltool.apply_intltool_po",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + } + + + +Feature jar +=========== + +.. graphviz:: + + digraph feature_jar { + size="8.0, 12.0"; + "apply_java" [style="setlinewidth(0.5)",URL="tools/javaw.html#waflib.Tools.javaw.apply_java",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "jar_files" [style="setlinewidth(0.5)",URL="tools/javaw.html#waflib.Tools.javaw.jar_files",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "use_javac_files" [style="setlinewidth(0.5)",URL="tools/javaw.html#waflib.Tools.javaw.use_javac_files",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "use_jar_files" [style="setlinewidth(0.5)",URL="tools/javaw.html#waflib.Tools.javaw.use_jar_files",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "jar_files" -> "apply_java" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "jar_files" -> "use_javac_files" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "use_jar_files" -> "jar_files" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature javac +============= + +.. graphviz:: + + digraph feature_javac { + size="8.0, 12.0"; + "process_use" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.process_use",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "init_perlext" [style="setlinewidth(0.5)",URL="tools/perl.html#waflib.Tools.perl.init_perlext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_java" [style="setlinewidth(0.5)",URL="tools/javaw.html#waflib.Tools.javaw.apply_java",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_pyext" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.init_pyext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "set_lib_pat" [style="setlinewidth(0.5)",URL="tools/fc_config.html#waflib.Tools.fc_config.set_lib_pat",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "propagate_uselib_vars" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.propagate_uselib_vars",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_pyembed" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.init_pyembed",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "use_javac_files" [style="setlinewidth(0.5)",URL="tools/javaw.html#waflib.Tools.javaw.use_javac_files",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "set_classpath" [style="setlinewidth(0.5)",URL="tools/javaw.html#waflib.Tools.javaw.set_classpath",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_bundle" [style="setlinewidth(0.5)",URL="tools/c_osx.html#waflib.Tools.c_osx.apply_bundle",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "propagate_uselib_vars" -> "apply_bundle" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "process_use" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "set_lib_pat" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_pyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_pyembed" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "use_javac_files" -> "apply_java" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "set_classpath" -> "apply_java" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "set_classpath" -> "propagate_uselib_vars" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "set_classpath" -> "use_javac_files" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature link_lib_test +===================== + +.. graphviz:: + + digraph feature_link_lib_test { + size="8.0, 12.0"; + "link_lib_test_fun" [style="setlinewidth(0.5)",URL="tools/c_tests.html#waflib.Tools.c_tests.link_lib_test_fun",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + } + + + +Feature link_main_routines_func +=============================== + +.. graphviz:: + + digraph feature_link_main_routines_func { + size="8.0, 12.0"; + "link_main_routines_tg_method" [style="setlinewidth(0.5)",URL="tools/fc_config.html#waflib.Tools.fc_config.link_main_routines_tg_method",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + } + + + +Feature msgfmt +============== + +.. graphviz:: + + digraph feature_msgfmt { + size="8.0, 12.0"; + "apply_msgfmt" [style="setlinewidth(0.5)",URL="tools/kde4.html#waflib.Tools.kde4.apply_msgfmt",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + } + + + +Feature perlext +=============== + +.. graphviz:: + + digraph feature_perlext { + size="8.0, 12.0"; + "init_perlext" [style="setlinewidth(0.5)",URL="tools/perl.html#waflib.Tools.perl.init_perlext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + } + + + +Feature py +========== + +.. graphviz:: + + digraph feature_py { + size="8.0, 12.0"; + "feature_py" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.feature_py",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + } + + + +Feature pyembed +=============== + +.. graphviz:: + + digraph feature_pyembed { + size="8.0, 12.0"; + "init_pyembed" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.init_pyembed",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + } + + + +Feature pyext +============= + +.. graphviz:: + + digraph feature_pyext { + size="8.0, 12.0"; + "set_lib_pat" [style="setlinewidth(0.5)",URL="tools/fc_config.html#waflib.Tools.fc_config.set_lib_pat",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "init_pyext" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.init_pyext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + } + + + +Feature qt4 +=========== + +.. graphviz:: + + digraph feature_qt4 { + size="8.0, 12.0"; + "apply_qt4" [style="setlinewidth(0.5)",URL="tools/qt4.html#waflib.Tools.qt4.apply_qt4",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_link" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_link",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_qt4" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature rubyext +=============== + +.. graphviz:: + + digraph feature_rubyext { + size="8.0, 12.0"; + "init_rubyext" [style="setlinewidth(0.5)",URL="tools/ruby.html#waflib.Tools.ruby.init_rubyext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_ruby_so_name" [style="setlinewidth(0.5)",URL="tools/ruby.html#waflib.Tools.ruby.apply_ruby_so_name",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + } + + + +Feature seq +=========== + +.. graphviz:: + + digraph feature_seq { + size="8.0, 12.0"; + "sequence_order" [style="setlinewidth(0.5)",URL="TaskGen.html#waflib.TaskGen.sequence_order",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + } + + + +Feature subst +============= + +.. graphviz:: + + digraph feature_subst { + size="8.0, 12.0"; + "process_subst" [style="setlinewidth(0.5)",URL="TaskGen.html#waflib.TaskGen.process_subst",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + } + + + +Feature test +============ + +.. graphviz:: + + digraph feature_test { + size="8.0, 12.0"; + "make_test" [style="setlinewidth(0.5)",URL="tools/waf_unit_test.html#waflib.Tools.waf_unit_test.make_test",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_link" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_link",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "make_test" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature test_exec +================= + +.. graphviz:: + + digraph feature_test_exec { + size="8.0, 12.0"; + "apply_link" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_link",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "test_exec_fun" [style="setlinewidth(0.5)",URL="tools/c_config.html#waflib.Tools.c_config.test_exec_fun",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "test_exec_fun" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature tex +=========== + +.. graphviz:: + + digraph feature_tex { + size="8.0, 12.0"; + "apply_tex" [style="setlinewidth(0.5)",URL="tools/tex.html#waflib.Tools.tex.apply_tex",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + } + + + +Feature use +=========== + +.. graphviz:: + + digraph feature_use { + size="8.0, 12.0"; + "process_use" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.process_use",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_link" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_link",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "process_source" [style="setlinewidth(0.5)",URL="TaskGen.html#waflib.TaskGen.process_source",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "process_use" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "process_use" -> "process_source" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature uselib +============== + +.. graphviz:: + + digraph feature_uselib { + size="8.0, 12.0"; + "process_use" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.process_use",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "init_perlext" [style="setlinewidth(0.5)",URL="tools/perl.html#waflib.Tools.perl.init_perlext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "init_pyext" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.init_pyext",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "set_lib_pat" [style="setlinewidth(0.5)",URL="tools/fc_config.html#waflib.Tools.fc_config.set_lib_pat",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "propagate_uselib_vars" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.propagate_uselib_vars",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_bundle" [style="setlinewidth(0.5)",URL="tools/c_osx.html#waflib.Tools.c_osx.apply_bundle",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "init_pyembed" [style="setlinewidth(0.5)",URL="tools/python.html#waflib.Tools.python.init_pyembed",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "propagate_uselib_vars" -> "apply_bundle" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "process_use" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "set_lib_pat" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_pyext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_pyembed" [arrowsize=0.5,style="setlinewidth(0.5)"]; + "propagate_uselib_vars" -> "init_perlext" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + + +Feature vnum +============ + +.. graphviz:: + + digraph feature_vnum { + size="8.0, 12.0"; + "apply_vnum" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_vnum",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10,fillcolor="#fffea6",style=filled]; + "apply_link" [style="setlinewidth(0.5)",URL="tools/ccroot.html#waflib.Tools.ccroot.apply_link",fontname=Vera Sans, DejaVu Sans, Liberation Sans, Arial, Helvetica, sans,height=0.25,shape=box,fontsize=10]; + "apply_vnum" -> "apply_link" [arrowsize=0.5,style="setlinewidth(0.5)"]; + } + + diff --git a/docs/sphinx/featuremap_example.txt b/docs/sphinx/featuremap_example.txt new file mode 100644 index 00000000..a7a408a8 --- /dev/null +++ b/docs/sphinx/featuremap_example.txt @@ -0,0 +1,54 @@ + +The Waf features are names linked to specific functions by the decorator +:py:func:`waflib.TaskGen.feature`. The functions +are mapped to the class :py:class:`waflib.TaskGen.task_gen` as methods. + +The association between feature names and methods is *many-to-many*, which means +that a method may be involved in several features, and that a feature may be bound +to several methods. + +Here is how to create and use a new feature named **foo**:: + + from waflib.TaskGen import feature + @feature('foo') + def print_hello(self): + print("Hello, World!") + +The function *print_hello* is now associated with the :py:class:`waflib.TaskGen.task_gen` class, which means +that it may be used directly:: + + def build(bld): + tg = bld() + tg.print_hello() + +The method may be called directly, and several times. If a method creates task, the same tasks will be created +more than once, which may cause build errors. The *feature* attribute is used to have the associated +methods called *exactly once* before the build starts:: + + def build(bld): + bld(features='foo') + +Here is a more complete example with two methods:: + + from waflib.TaskGen import feature, after_method + + @feature('foo') + @after_method('print_bar') + def print_hello(self): + print("Hello, Foo!") + + @feature('bar') + def print_bar(self): + print("Hello, Bar!") + + def build(bld): + bld(features='foo bar') + +The order of method execution is unrelated to the order of the features given. For instance, +this example will print "Hello, Bar!" then "Hello, Foo!". The decorators +:py:func:`waflib.TaskGen.after` and :py:func:`waflib.TaskGen.before` are +enforcing partial order constraints on the methods to execute. + +The following maps represent the associations betwen feature methods (represented in yellow) and +methods associated to other feature names. + diff --git a/docs/sphinx/index.rst b/docs/sphinx/index.rst new file mode 100644 index 00000000..91aa36db --- /dev/null +++ b/docs/sphinx/index.rst @@ -0,0 +1,26 @@ +Waf documentation +================= + +.. toctree:: + + coremodules.rst + tools.rst + tutorial.rst + about.rst + copyright.rst + featuremap.rst + + Build.rst + ConfigSet.rst + Configure.rst + Context.rst + Errors.rst + Logs.rst + Node.rst + Options.rst + Runner.rst + Scripting.rst + Task.rst + TaskGen.rst + Utils.rst + tools/errcheck.rst diff --git a/docs/sphinx/tools.rst b/docs/sphinx/tools.rst new file mode 100644 index 00000000..ada9cdf6 --- /dev/null +++ b/docs/sphinx/tools.rst @@ -0,0 +1,146 @@ +Waf Tools +========= + +Special python modules called Waf **tools** provide functions and classes to help +using compilers, libraries or programs. The typical usage from a user script is:: + + def function(ctx): + # ... + ctx.load('toolname') + +Where the function is usually: + +* options: add command-line options used by the tool +* configure: modify ``conf.env``, raise a configuration error if a prerequisite is not met + +The tools will usually enhance the application by adding: + +* new commands deriving from :py:class:`waflib.Context.Context` +* new task classes deriving from :py:class:`waflib.Task.Task` +* new methods to :py:class:`waflib.Configure.ConfigurationContext` and :py:class:`waflib.Build.BuildContext` through :py:func:`waflib.Configure.conf` +* new task generator methods to :py:class:`waflib.TaskGen.task_gen` through :py:func:`waflib.TaskGen.taskgen_method`, :py:func:`waflib.TaskGen.after` + +As a general rule, existing methods or classes are hardly ever replaced. + +C/C++ compiler detection +------------------------ + +The following Waf tools are used for loading specific C or C++ compilers. They may +be used directly, for example:: + + def options(opt): + opt.load('compiler_c') + def configure(conf): + conf.load('compiler_c') + +.. toctree:: + + tools/compiler_c + tools/compiler_cxx + tools/ar + tools/gcc + tools/gxx + tools/icc + tools/icpc + tools/suncc + tools/suncxx + tools/xlc + tools/xlcxx + tools/msvc + tools/winres + tools/irixcc + +C/C++ support +------------- + +The following modules contain the functions and classes required for building C and C++ applications. They +are almost always loaded by other Waf tools. Among these, the most important from a user point of view +is :py:mod:`waflib.Tools.c_config` which provides the :py:func:`waflib.Tools.c_config.check` and +:py:func:`waflib.Tools.c_config.check_cfg` functions. + +.. toctree:: + + tools/ccroot + tools/c + tools/cxx + tools/c_config + tools/c_osx + tools/c_preproc + tools/c_tests + tools/c_aliases + +Assembly +-------- + +The following tools provide support for assembly. The module :py:mod:`waflib.Tools.asm` is loaded automatically by :py:mod:`waflib.Tools.nasm` or :py:mod:`waflib.Tools.gas`. + +.. toctree:: + + tools/gas + tools/nasm + tools/asm + +D language and compilers +------------------------ + +The first three tools in the following list may be used for detecting D compilers. The remaining contain the support functions and classes. + +.. toctree:: + + tools/compiler_d + tools/dmd + tools/gdc + tools/d_config + tools/d + tools/d_scan + +Fortran support +--------------- + +The first four tools in the following list are used for detecting fortran compilers. The three remaining contain the routines for compiling fortran applications. + +.. toctree:: + + tools/compiler_fc + tools/g95 + tools/gfortran + tools/ifort + tools/fc + tools/fc_config + tools/fc_scan + +C/C++-related applications +-------------------------- + +The next tools provide support for code generators used in C and C++ projects. + +.. toctree:: + + tools/bison + tools/flex + tools/dbus + tools/vala + tools/glib2 + tools/qt4 + tools/kde4 + tools/perl + tools/python + tools/ruby + +Other compilers and tools +------------------------- + +.. _extras: http://code.google.com/p/waf/source/browse/trunk/waflib/extras/ + +The following tools provide support for specific compilers or configurations. More tools are present in the extras_ folder, although they are not documented and as stable as the default tools. + +.. toctree:: + + tools/waf_unit_test + tools/tex + tools/javaw + tools/cs + tools/gnu_dirs + tools/intltool + tools/lua + diff --git a/docs/sphinx/tools/.keepme b/docs/sphinx/tools/.keepme new file mode 100644 index 00000000..e69de29b diff --git a/docs/sphinx/tutorial.rst b/docs/sphinx/tutorial.rst new file mode 100644 index 00000000..de2fe960 --- /dev/null +++ b/docs/sphinx/tutorial.rst @@ -0,0 +1,174 @@ +Waf tutorial +============ + +Waf is a piece of software used to help building software projects. +The goal of this tutorial is to provide a quick overview of how to set up +the scripts for a project using Waf. + +Waf scripts and commands +------------------------ + +A software typically has *source files* which are kept in a version control system (git, subversion, etc), +and *build scripts* (Makefiles, ..) which describe what to do with those files. A few *build files* are usually +obtained after transforming the *source files*, but they are optional. The build scripts in Waf are files named 'wscript'. + +In general, a project will consist of several phases: + +* configure: configure the project, find the location of the prerequisites +* build: transform the source files into build files +* install: install the build files +* uninstall: uninstall the build files +* dist: create an archive of the source files +* clean: remove the build files + +Each phase is modelled in the wscript file as a python function which takes as argument an instance of :py:class:`waflib.Context.Context`. +Let's start with a new wscript file in the directory '/tmp/myproject':: + + def configure(conf): + print("configure!") + + def build(bld): + print("build!") + +We will also use a Waf binary file, for example http://waf.googlecode.com/files/waf-1.6.1, which we will copy in the project directory:: + + $ cd /tmp/myproject + $ wget http://waf.googlecode.com/files/waf-1.6.1 + +To execute the project, we will simply call the command as an argument to ``waf``:: + + $ ./waf-1.6.1 configure build + configure! + build! + +Targets +------- + +An important part of the build system is to declare the creation of targets. Here is a very simple example:: + + def build(bld): + tg = bld(rule='cp ${SRC} ${TGT}', source='wscript', target='foo.txt') + bld(rule='cp ${SRC} ${TGT}', source='foo.txt', target='bar.txt') + +The call ``bld(..)`` creates an object called *task generator*, which is used to create *tasks* which will actually +call the command ``cp``. The commands are not executed unless all the scripts have been read, which is important +for computing the build order. + +The expressions *${SRC}* and *${TGT}* are shortcuts to avoid repeating the file names. More shortcuts can be defined +by using the *${}* symbol, which reads the values from the attribute bld.env:: + + def build(bld): + bld.env.MESSAGE = 'Hello, world!' + bld(rule='echo ${MESSAGE}', always=True) + +The bld object is an instance of :py:class:`waflib.Build.BuildContext`, its *env* attribute is an instance :py:class:`waflib.ConfigSet.ConfigSet`. +The values are set in this object to be shared/stored/loaded easily. Here is how to do the same thing by sharing data between the configuration and build:: + + def configure(cnf): + cnf.env.MESSAGE = 'Hello, world!' + + def build(bld): + bld(rule='echo ${MESSAGE}', always=True) + +Scripts and Tools +----------------- + +To let a script use a script from a subdirectory, the method :py:meth:`waflib.Context.Context.recurse` has to be used with +the relative path to the folder containing the wscript file. For example, to call the function *build* in the script ``src/wscript``, +one should write:: + + def build(bld): + bld.recurse('src') + +The support for specific languages and compilers is provided through specific modules called *Waf tools*. The tools are +similar to wscript files and provide functions such as *configure* or *build*. Here is a simple project for the C programming language:: + + def options(opt): + opt.load('compiler_c') + def configure(cnf): + cnf.load('compiler_c') + def build(bld): + bld(features='c cprogram', source='main.c', target='app') + +The function *options* is another predefined command used for setting command-line options. Its argument is an instance of :py:meth:`waflib.Options.OptionsContext`. The tool *compiler_c* is provided for detecting if a C compiler is present and to set various variables such as ``cnf.env.CFLAGS``. + +The task generator declared in *bld* does not have a *rule* keyword, but a list of *features* which is used to reference methods that will call the appropriate rules. In this case, a rule is called for compiling the file, and another is used for linking the object files into the binary *app*. Other tool-dependent features exist such as *javac*, *cs*, or *tex*. + +A C and C++ project +------------------- + +Here is a script for a more complicated project:: + + def options(opt): + opt.load('compiler_c compiler_cxx') + def configure(cnf): + cnf.load('compiler_c compiler_cxx') + conf.check(features='cxx cxxprogram', lib=['m'], cflags=['-Wall'], defines=['var=foo'], uselib_store='M') + def build(bld): + bld(features='c cshlib', source='b.c', target='mylib') + bld(features='c cxx cxxprogram', source='a.c main.cpp', target='app', use=['M','mylib'], lib=['dl']) + +The method :py:func:`waflib.Tools.c_config.check` executes a build internally to check if the library ``libm`` is present on the operating system. +It will then define variables such as: + +* ``conf.env.LIB_M = ['m']`` +* ``conf.env.CFLAGS_M = ['-Wall']`` +* ``conf.env.DEFINES_M = ['var=foo']`` + +By stating ``use=['M', 'mylib']``, the program *app* is going to inherit all the *M* variables defined +during the configuration. The program will also use the library *mylib* and both the build order and the dependencies +will be modified so that *mylib* is linked before *app*. + +The ``use`` attribute is also working for other languages such as Java (dependencies between jar files) or C# (dependencies between assemblies). + +Project-specific extensions +--------------------------- + +The *feature* keyword is a high-level reference to existing Waf methods. +For example, the **c** feature will add the method :py:func:`waflib.Tools.ccroot.apply_incpaths` for execution. +To add a new method that will add the task generator path to the include path for all C targets, +one may use such a declaration:: + + from waflib import Utils + from waflib.TaskGen import feature, before_method + @feature('c') + @before_method('apply_incpaths') + def add_current_dir_to_includes(self): + self.includes = Utils.to_list(self.includes) + self.includes.append(self.path) + + def build(bld): + tg = bld(features='c', source='main.c', target='app') + +The *feature* methods are bound to the :py:class:`waflib.TaskGen.task_gen` class, which is the class of the +object *tg* in the example. New features can be declared in the same manner:: + + from waflib.TaskGen import feature, after_method + @feature('debug_tasks') + @after_method('apply_link') + def print_debug(self): + print('tasks created %r' % self.tasks) + + def build(bld): + tg = bld(features='c cprogram debug_tasks', source='main.c', target='app') + +The declaration can be made more user-friendly by binding new methods to the context classes:: + + from waflib.Build import BuildContext + def enterprise_program(self, *k, **kw): + kw['features'] = 'c cprogram debug_tasks' + return self(*k, **kw) + BuildContext.enterprise_program = enterprise_program + + def build(bld): + # no feature line + bld.enterprise_program(source='main.c', target='app') + +The support code may be turned into a Waf tool by moving it to a separate file. +To ease the deployment, the new Waf tool can even be added to the waf file (see http://code.google.com/p/waf/source/browse/trunk/README). + +Conclusion +---------- + +This concludes the tutorial. For more information consult the apis, the Waf book and the examples. + diff --git a/docs/sphinx/wscript b/docs/sphinx/wscript new file mode 100644 index 00000000..86bc0218 --- /dev/null +++ b/docs/sphinx/wscript @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Jérôme Carretero, 2010 (zougloub) + +import sys, os +from waflib.Utils import subprocess + +""" +""" + +top = '.' +out = 'build' + +def options(opt): + opt.load('daemon', tooldir=['../../playground/daemon/']) + +def configure(cfg): + cfg.find_program('dot', var='DOT') + cfg.find_program('convert', var='CONVERT') + cfg.load('daemon', tooldir=['../../playground/daemon/']) + cfg.find_program("sphinx-build", var="SPHINX_BUILD") + +def build(bld): + + bld( + rule = "${SPHINX_BUILD} -b html -d %s . %s" % (os.path.join(out, "doctrees"), os.path.join(out, "html")), + cwd = bld.path.abspath(), + source = bld.path.parent.parent.find_dir('waflib').ant_glob('**/*.py') + + bld.path.ant_glob('**/*.rst') + + bld.path.ant_glob('_templates/indexcontent.html') + + bld.path.ant_glob('conf.py'), + target = bld.path.find_or_declare('html/index.html') + ) + diff --git a/docs/unsorted.txt b/docs/unsorted.txt new file mode 100644 index 00000000..58cfc4b3 --- /dev/null +++ b/docs/unsorted.txt @@ -0,0 +1,16 @@ + +- to make a fully static app, replace env.SHLIB_MARKER and env.STLIB_MARKER +- string expansion on other attributes +- compile targets at the end +- set CC and LINK to a particular variable (the wine example) +- show a complete example of c compilation with rules vs task generator code (procedural vs object-oriented) + +- get the path of a config test executable created + from waflib.TaskGen import feature, after_method + @feature('getpath') + @after_method('apply_link') + def getpath(self): + self.bld.retval = self.link_task.outputs[0].abspath() + ret = conf.check_cc(fragment="""#include\nint main(){fprintf(stderr, "mu"); printf("%d", 22);return 0;}\n""", features='c cprogram getpath') + print ret + diff --git a/playground/R/src/test.R b/playground/R/src/test.R new file mode 100644 index 00000000..94ae92ef --- /dev/null +++ b/playground/R/src/test.R @@ -0,0 +1,22 @@ +#!/usr/bin/env Rscript + +require(R.utils) +require(tools) + +cmdArgs <- commandArgs(asValues = TRUE, excludeReserved = FALSE, excludeEnvVars = TRUE, os = "current") + +if(!is.null(cmdArgs[["numTrees"]])) nTrees <- (as.integer(cmdArgs[["numTrees"]])) else nTrees <- 500 + +ffff <- cmdArgs[["ffff"]] +print(paste("nTrees =", nTrees, sep = " ")) + +Sweave(ffff) +texi2dvi("testSweave.tex", pdf = TRUE) + +Stangle(ffff) +source("testSweave.R") + +print(paste("Current Date/Time: ", date(), sep="")) + +(sessionInfoObj <- sessionInfo()) + diff --git a/playground/R/src/testSweave.Rnw b/playground/R/src/testSweave.Rnw new file mode 100644 index 00000000..fe07ca69 --- /dev/null +++ b/playground/R/src/testSweave.Rnw @@ -0,0 +1,13 @@ +\documentclass{article} +\SweaveOpts{eps=FALSE} + +<>= +options(width=80) +@ + +\begin{document} +Estimate a random Forest model composed of +\Sexpr{format(nTrees, big.mark = ',')} +trees. + +\end{document} diff --git a/playground/R/wscript b/playground/R/wscript new file mode 100644 index 00000000..80d0ba6a --- /dev/null +++ b/playground/R/wscript @@ -0,0 +1,29 @@ +#! /usr/bin/env python +# encoding: utf-8 + +VERSION = '1.0.0' +APPNAME = 'testR' + +top = '.' +out = 'build' + +def configure(ctx): + ctx.find_program('texi2dvi') + ctx.load('R') + +def build(ctx): + ctx( + rule = '${R} CMD BATCH ${tsk.generator.ropts} ${tsk.generator.fopt % tsk.inputs[1].abspath()} -- ${SRC[0].abspath()} foo.log', + source = 'src/test.R src/testSweave.Rnw', + target = 'src/testSweave.pdf', + cwd = ctx.path.find_node('src').get_bld().abspath(), + install_path = '${PREFIX}/share', + shell = False, + ropts = '--no-restore --no-save --no-readline --numTrees=1000', + fopt = '--ffff=%s' + ) + +def dist(ctx): + ctx.algo = 'zip' + ctx.excl = ' **/waf-* **/.waf-* **/*~ **/*.orig **/*.pyc **/*.pyo **/*.bak **/.lock-w* **/*.log **/*.lot **/*.lof **/*.lst **/*.aux **/*.bbl **/*.blg **/*.out **/*.toc' + diff --git a/playground/abstract_process/wscript b/playground/abstract_process/wscript new file mode 100644 index 00000000..cb8281e5 --- /dev/null +++ b/playground/abstract_process/wscript @@ -0,0 +1,46 @@ +#! /usr/bin/env python + +def configure(conf): + pass + +def build(bld): + bld.process(name='foo', version='1.0') + bld.process(name='bar', use='foo') + +######################################################################## + +# user api +from waflib.Build import BuildContext +def process(ctx, *k, **kw): + kw['features'] = 'proc' + return ctx(*k, **kw) +BuildContext.process = process + +# create the tasks +from waflib.TaskGen import feature +@feature('proc') +def create_a_few_tasks(self): + fetch = self.create_task('process') + fetch.outputs = [self.path.find_or_declare(self.name + '.fetch')] + fetch.env.A = getattr(self, 'version', '') # rebuild if changes + configure = self.create_task('process', fetch.outputs) + configure.outputs = [self.path.find_or_declare(self.name + '.configure')] + build = self.create_task('process', configure.outputs) + build.outputs = [self.path.find_or_declare(self.name + '.build')] + self.install = install = self.create_task('process', build.outputs) + install.outputs = [self.path.find_or_declare(self.name + '.install')] + + if getattr(self, 'use', None): + lst = self.to_list(self.use) + for x in lst: + tg = self.bld.get_tgen_by_name(x) + fetch.inputs.extend(tg.install.outputs) + +# task classes +from waflib.Task import Task +class process(Task): + vars = ['A', 'B'] # change env.A to trigger a rebuild... + def run(self): + # add your operations here + self.outputs[0].write('all ok') + diff --git a/playground/boo/hello.boo b/playground/boo/hello.boo new file mode 100644 index 00000000..777450eb --- /dev/null +++ b/playground/boo/hello.boo @@ -0,0 +1 @@ +World.SayHello() diff --git a/playground/boo/world.boo b/playground/boo/world.boo new file mode 100644 index 00000000..c5fb49d5 --- /dev/null +++ b/playground/boo/world.boo @@ -0,0 +1,3 @@ +class World: + static def SayHello(): + print "hello from boo" diff --git a/playground/boo/world.cs b/playground/boo/world.cs new file mode 100644 index 00000000..40e52413 --- /dev/null +++ b/playground/boo/world.cs @@ -0,0 +1,6 @@ +public class World { + public static void SayHello() + { + System.Console.WriteLine("Hello from C#"); + } +} diff --git a/playground/boo/wscript b/playground/boo/wscript new file mode 100644 index 00000000..bfc69165 --- /dev/null +++ b/playground/boo/wscript @@ -0,0 +1,49 @@ +#! /usr/bin/env python + +## +# This wscript shows the power of the CLI! +# You have an hello.exe using a world.dll, +# the world.dll can be generating using +# world.cs (in C#) or world.boo. + +top = '.' +out = 'build' + +def options(opt): + opt.load('cs') + opt.add_option("--use-cs", dest="use_cs", action="store_true", + help="use world.cs to generate world.dll") + +def configure(conf): + conf.env.USE_CS = conf.options.use_cs + if conf.env.USE_CS: + conf.load('cs') + conf.load('boo') + +def build(bld): + if bld.env.USE_CS: + # C# world library + bld(features = "cs", + source = "world.cs", + type = "library", + gen = "world.dll", + name = "world" + ) + + else: + # boo world library + bld(features = "boo", + source = "world.boo", + type = "library", + gen = "world.dll", + name = "world" + ) + + # executable that uses the world library + bld(features = "boo", + source = "hello.boo", + type = "exe", + gen = "hello.exe", + use = "world" + ) + diff --git a/playground/boost/main.cpp b/playground/boost/main.cpp new file mode 100644 index 00000000..2bb9f6d6 --- /dev/null +++ b/playground/boost/main.cpp @@ -0,0 +1,7 @@ +#include + +int main() +{ + boost::filesystem::path my_path("c:\\"); + return 0; +} diff --git a/playground/boost/waf.bat b/playground/boost/waf.bat new file mode 100644 index 00000000..ec97c98f --- /dev/null +++ b/playground/boost/waf.bat @@ -0,0 +1,3 @@ +@echo off +rem path are automatically detected on Linux +python waf --boost-includes=c:\boost_1_45_0 --boost-libs=c:\boost_1_45_0\stage\lib %1 %2 %3 diff --git a/playground/boost/wscript b/playground/boost/wscript new file mode 100644 index 00000000..e15fa8e8 --- /dev/null +++ b/playground/boost/wscript @@ -0,0 +1,12 @@ +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_cxx boost') + +def configure(conf): + conf.load('compiler_cxx boost') + conf.check_boost('system filesystem', mt=True) + +def build(bld): + bld.program(source='main.cpp', target='app', use='BOOST') diff --git a/playground/c/deps/foo.c b/playground/c/deps/foo.c new file mode 100644 index 00000000..eb7f69a4 --- /dev/null +++ b/playground/c/deps/foo.c @@ -0,0 +1,2 @@ +#include +void foo() {} diff --git a/playground/c/deps/foo.h.tmpl b/playground/c/deps/foo.h.tmpl new file mode 100644 index 00000000..3df1edde --- /dev/null +++ b/playground/c/deps/foo.h.tmpl @@ -0,0 +1 @@ +void foo(); diff --git a/playground/c/deps/main.c b/playground/c/deps/main.c new file mode 100644 index 00000000..c7504178 --- /dev/null +++ b/playground/c/deps/main.c @@ -0,0 +1,7 @@ +#include + +int main() +{ + foo(); + return 0; +} diff --git a/playground/c/deps/wscript b/playground/c/deps/wscript new file mode 100644 index 00000000..7a19dcf1 --- /dev/null +++ b/playground/c/deps/wscript @@ -0,0 +1,27 @@ +#!/usr/bin/env python +import shutil +import time + +top='.' +APPNAME='test' +VERSION='0.0.1' + +def copy(task): + time.sleep(2) + shutil.copyfile(task.inputs[0].abspath(), task.outputs[0].abspath()) + return 0 + +def options(opt): + opt.load('compiler_c') + +def configure(cfg): + cfg.load('compiler_c') + +def build(bld): + dnode = bld.path.find_or_declare('depen.c') + print ('dnode = %r' % dnode) + assert dnode != None + bld(rule=copy, target='foo.h', source='foo.h.tmpl') + bld(rule=copy, target='depen.c', source='foo.c') + bld.program(target='test_foo', source=['main.c', 'foo.c'], includes='.') + bld.add_manual_dependency(bld.path.find_node('main.c'), dnode) diff --git a/playground/c/program/a.h b/playground/c/program/a.h new file mode 100644 index 00000000..1fc12a6a --- /dev/null +++ b/playground/c/program/a.h @@ -0,0 +1 @@ +int k = 123; diff --git a/playground/c/program/main.c b/playground/c/program/main.c new file mode 100644 index 00000000..059a3097 --- /dev/null +++ b/playground/c/program/main.c @@ -0,0 +1,5 @@ +#include "a.h" +#include "config.h" +int main() { + return 0; +} diff --git a/playground/c/program/wscript_build b/playground/c/program/wscript_build new file mode 100644 index 00000000..948c881e --- /dev/null +++ b/playground/c/program/wscript_build @@ -0,0 +1,19 @@ +#! /usr/bin/env python + +bld.program(source='main.c', target=bld.path.get_bld().make_node('foo.exe'), includes=". ..") + +""" +bld.env.CFLAGS_TEST = ['-O3'] +bld.env.CFLAGS_BAR = ['-O2'] + +bld.shlib(source='a.c', target='meh') + +bld( + features = 'c cprogram', + source = 'main.c', + includes = '. ..', + #ccflags = '-O3', + use='TEST meh', + uselib='BAR', + target = 'myprogram') +""" diff --git a/playground/c/shlib/foo.def b/playground/c/shlib/foo.def new file mode 100644 index 00000000..dcc7ccc8 --- /dev/null +++ b/playground/c/shlib/foo.def @@ -0,0 +1,2 @@ +EXPORTS + foo diff --git a/playground/c/shlib/main.c b/playground/c/shlib/main.c new file mode 100644 index 00000000..4f56fe06 --- /dev/null +++ b/playground/c/shlib/main.c @@ -0,0 +1,8 @@ + +extern void foo(); + +int main() +{ + foo(); + return 0; +} diff --git a/playground/c/shlib/test_shlib.c b/playground/c/shlib/test_shlib.c new file mode 100644 index 00000000..becdecb1 --- /dev/null +++ b/playground/c/shlib/test_shlib.c @@ -0,0 +1,5 @@ + +static const int truc=5; + +void foo() { } + diff --git a/playground/c/shlib/wscript_build b/playground/c/shlib/wscript_build new file mode 100644 index 00000000..c74945e1 --- /dev/null +++ b/playground/c/shlib/wscript_build @@ -0,0 +1,15 @@ +#! /usr/bin/env python + +bld( + features = 'c cshlib', + source = 'test_shlib.c', + vnum = '1.2.3', + defs = 'foo.def', + target = 'my_shared_lib') + +bld( + features = 'c cprogram', + source = 'main.c', + target = 'test_shared_link', + use = 'my_shared_lib') + diff --git a/playground/c/stlib/main.c b/playground/c/stlib/main.c new file mode 100644 index 00000000..a46866d9 --- /dev/null +++ b/playground/c/stlib/main.c @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/playground/c/stlib/test_staticlib.c b/playground/c/stlib/test_staticlib.c new file mode 100644 index 00000000..d67f6447 --- /dev/null +++ b/playground/c/stlib/test_staticlib.c @@ -0,0 +1,2 @@ +int k = 3; + diff --git a/playground/c/stlib/wscript_build b/playground/c/stlib/wscript_build new file mode 100644 index 00000000..2cd16e0c --- /dev/null +++ b/playground/c/stlib/wscript_build @@ -0,0 +1,13 @@ +#! /usr/bin/env python + +bld( + features = 'c cstlib', + source = 'test_staticlib.c', + target = 'my_static_lib') + +bld( + features = 'c cprogram', + source = 'main.c', + target = 'test_static_link', + use = 'my_static_lib') + diff --git a/playground/c/stlib2/a/a.c b/playground/c/stlib2/a/a.c new file mode 100644 index 00000000..bf785e2b --- /dev/null +++ b/playground/c/stlib2/a/a.c @@ -0,0 +1,4 @@ +int a() +{ + return 1; +} diff --git a/playground/c/stlib2/a/a.h b/playground/c/stlib2/a/a.h new file mode 100644 index 00000000..14fe6590 --- /dev/null +++ b/playground/c/stlib2/a/a.h @@ -0,0 +1 @@ +int a(); diff --git a/playground/c/stlib2/b/b.c b/playground/c/stlib2/b/b.c new file mode 100644 index 00000000..f594e521 --- /dev/null +++ b/playground/c/stlib2/b/b.c @@ -0,0 +1,6 @@ +#include "a.h" + +int b() +{ + return a() + 1; +} diff --git a/playground/c/stlib2/b/b.h b/playground/c/stlib2/b/b.h new file mode 100644 index 00000000..de454e6b --- /dev/null +++ b/playground/c/stlib2/b/b.h @@ -0,0 +1 @@ +int b(); diff --git a/playground/c/stlib2/b/test.c b/playground/c/stlib2/b/test.c new file mode 100644 index 00000000..645908cf --- /dev/null +++ b/playground/c/stlib2/b/test.c @@ -0,0 +1,9 @@ +#include +#include "b.h" + +int main() +{ + printf("b=%d\n", b()); + + return 0; +} diff --git a/playground/c/stlib2/wscript b/playground/c/stlib2/wscript new file mode 100644 index 00000000..628dc68a --- /dev/null +++ b/playground/c/stlib2/wscript @@ -0,0 +1,30 @@ +#! /usr/bin/env python + +"""static libraries using other static libraries""" + +top = '.' +out = 'build' + +parts = [ 'a', 'b' ] + +def build(bld): + bld.stlib( + target = 'a', + source = 'a/a.c', + includes = 'a', + export_includes = 'a', + ) + + bld.stlib( + target = 'b', + source = 'b/b.c', + includes = 'b', + export_includes = 'b', + use = 'a', + ) + + bld.program( + target = 'test', + source = 'b/test.c', + use = 'b') + diff --git a/playground/c/wscript b/playground/c/wscript new file mode 100644 index 00000000..85f17831 --- /dev/null +++ b/playground/c/wscript @@ -0,0 +1,165 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +# the following two variables are used by the target "waf dist" +VERSION='0.0.1' +APPNAME='cc_test' + +top = '.' + +import waflib.Configure +waflib.Configure.autoconfig = True + +def options(opt): + opt.load('compiler_c') + opt.load('gnu_dirs') + +def build(bld): + bld.recurse('program stlib shlib stlib2') + + +lst = 'debug release foo bar one two'.split() + +def configure(conf): + conf.load('compiler_c') + conf.check_features() + conf.check_cc(fragment="""#include\nint main(){fprintf(stderr, "mu"); printf("%d", 22);return 0;}\n""", execute=True, define_name='HAVE_MU') + conf.write_config_header('config.h') + + + # gotcha - the config.h must be written for each variant + txt = conf.bldnode.search('config.h').read() + for x in lst: + node = conf.bldnode.make_node(x + '/config.h') + node.parent.mkdir() + node.write(txt) + +from waflib import Utils, Build +class buildall_ctx(Build.BuildContext): + cmd = fun = 'buildall' + def compile(self): + pass + +def buildall(ctx): + """call 'waf buildall' to build all the variants in parallel""" + + timer = Utils.Timer() + threads = [] + count = [0] + line_lock = Utils.threading.Lock() + class sub_build(Utils.threading.Thread): + def run(self): + bld = self.bld = self.cls(top_dir=ctx.top_dir, out_dir=ctx.out_dir) + bld.load() + bld.siblings = threads + bld.count = count + bld.line_lock = line_lock + bld.timer = timer + bld.logger = ctx.logger + bld.load_envs() + bld.targets = ctx.targets + bld.recurse([bld.run_dir]) + bld.compile() + + for x in lst: + cls = type(Build.BuildContext)(x, (Build.BuildContext,), {'cmd': x, 'variant': x}) + cls.progress_line = locked_progress_line + f = sub_build() + f.cls = cls + threads.append(f) + f.start() + + for x in threads: + x.join() + +def locked_progress_line(self, state, total, col1, col2): + try: + self.line_lock.acquire() + self.count[0] += 1 + total = 0 + for x in self.siblings: + try: + p = x.bld.producer + except: + pass + else: + total += p.total + + return Build.BuildContext.progress_line(self, self.count[0], total, col1, col2) + finally: + self.line_lock.release() + + + +class cleanall_ctx(Build.CleanContext): + cmd = fun = 'cleanall' + +def cleanall(ctx): + for x in lst: + cls = type(Build.CleanContext)(x, (Build.CleanContext,), {'cmd': x, 'variant': x}) + bld = cls(top_dir=ctx.top_dir, out_dir=ctx.out_dir) + bld.load() + bld.load_envs() + bld.recurse([bld.run_dir]) + try: + bld.clean() + finally: + bld.save() + + + +features_str = ''' +#include +int is_big_endian() +{ + long one = 1; + return !(*((char *)(&one))); +} +int main() +{ + if (is_big_endian()) printf("bigendian=1\\n"); + else printf("bigendian=0\\n"); + printf("int_size=%d\\n", sizeof(int)); + printf("long_int_size=%d\\n", sizeof(long int)); + printf("long_long_int_size=%d\\n", sizeof(long long int)); + printf("double_size=%d\\n", sizeof(double)); + return 0; +} +''' + +def check_features(self): + + mp = self.check(fragment=features_str, define_ret=True, execute=True) + try: + mp = mp.decode('utf-8') + except: + pass + + t = Utils.str_to_dict(mp) + try: + is_big = int(t['bigendian']) + except KeyError: + raise Configure.ConfigurationError('endian test failed %s (see the config.log)' % features_str) + + if is_big: strbig = 'big endian' + else: strbig = 'little endian' + self.msg('endianness', strbig) + + self.msg('int size', t['int_size']) + self.msg('long int size', t['long_int_size']) + self.msg('long long int size', t['long_long_int_size']) + self.msg('double size', t['double_size']) + + self.define_cond('IS_BIGENDIAN', is_big) + self.define_cond('INT_SIZE', int(t['int_size'])) + self.define_cond('LONG_INT_SIZE', int(t['long_int_size'])) + self.define_cond('LONG_LONG_INT_SIZE', int(t['long_long_int_size'])) + self.define_cond('DOUBLE_SIZE', int(t['double_size'])) + + return is_big + +from waflib import Configure +Configure.conf(check_features) # bind the method + + diff --git a/playground/cfg_cache/wscript b/playground/cfg_cache/wscript new file mode 100644 index 00000000..f30ef6a4 --- /dev/null +++ b/playground/cfg_cache/wscript @@ -0,0 +1,20 @@ +#! /usr/bin/env python + +""" +compare the execution time of +waf configure +and +waf configure --confcache +""" + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_c') + opt.add_option('--confcache', dest='confcache', default=0, action='count', help='Use a configuration cache') + +def configure(conf): + conf.load('compiler_c') + conf.check(fragment='int main() { return 0; }') + diff --git a/playground/codegen/generated/test432808665.c b/playground/codegen/generated/test432808665.c new file mode 100644 index 00000000..f3f60843 --- /dev/null +++ b/playground/codegen/generated/test432808665.c @@ -0,0 +1 @@ +int k432808665 = 432808665; \ No newline at end of file diff --git a/playground/codegen/generated/test61797613.c b/playground/codegen/generated/test61797613.c new file mode 100644 index 00000000..6a43bb41 --- /dev/null +++ b/playground/codegen/generated/test61797613.c @@ -0,0 +1 @@ +int k61797613 = 61797613; \ No newline at end of file diff --git a/playground/codegen/generated/test876396158.c b/playground/codegen/generated/test876396158.c new file mode 100644 index 00000000..f6492c72 --- /dev/null +++ b/playground/codegen/generated/test876396158.c @@ -0,0 +1 @@ +int k876396158 = 876396158; \ No newline at end of file diff --git a/playground/codegen/generated/test877273174.c b/playground/codegen/generated/test877273174.c new file mode 100644 index 00000000..5448d088 --- /dev/null +++ b/playground/codegen/generated/test877273174.c @@ -0,0 +1 @@ +int k877273174 = 877273174; \ No newline at end of file diff --git a/playground/codegen/main.c b/playground/codegen/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/playground/codegen/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/playground/codegen/wscript b/playground/codegen/wscript new file mode 100644 index 00000000..2ca93c41 --- /dev/null +++ b/playground/codegen/wscript @@ -0,0 +1,35 @@ +#! /usr/bin/env python +# coding: utf-8 + +def options(opt): + opt.load('compiler_c') +def configure(cnf): + cnf.load('compiler_c') +def build(bld): + from waflib import Build + bld.post_mode = Build.POST_LAZY + + def create_files(tsk): + out = tsk.generator.path.make_node('generated') + from waflib import Utils + for x in out.ant_glob('*.c'): + x.delete() + import random + for x in range(2): + num = random.randint(0, 2**31) + k = out.make_node('test%d.c' % num) + k.write('int k%d = %d;' % (num, num)) + bld(rule=create_files, source='wscript', name='codegen') + + bld.add_group() + + bld.program(features='find_them', source=['main.c'], target='app') + +from waflib.TaskGen import feature, before +from waflib import Utils +@feature('find_them') +@before('process_source') +def list_the_source_files(self): + self.source = Utils.to_list(self.source) + self.path.ant_glob('generated/*.c') + + diff --git a/playground/compress/optim.py b/playground/compress/optim.py new file mode 100755 index 00000000..f6162732 --- /dev/null +++ b/playground/compress/optim.py @@ -0,0 +1,122 @@ +#! /usr/bin/env python + +import os, subprocess, shutil, random, optparse + +comp = { + 'bz2': 'cjf', + 'xz' : 'cJf', + 'gz' : 'czf', +} + +def read_wafdir(): + try: + os.listdir('waflib') + except: + raise ImportError('please provide a waflib directory in the current folder') + + d = 'waflib' + lst = [d + os.sep + x for x in os.listdir(d) if x.endswith('.py')] + e = d + os.sep + 'Tools' + lst.extend([e + os.sep + x for x in os.listdir(e) if x.endswith('.py')]) + f = d + os.sep + 'extras' + lst.extend([f + os.sep + x for x in os.listdir(f) if x.endswith('.py')]) + + random.shuffle(lst) + #lst.sort() + return lst + +def gen(lst, options): + + if options.maxi: + opti_ref = 0 + filename = 'max.tar.%s' % options.kind + def compare(a, b): + return a > b + else: + opti_ref = 1000000000 + filename = 'min.tar.%s' % options.kind + def compare(a, b): + return a < b + cmd = 'tar %s %s ' % (comp[options.kind], filename) + opti = [opti_ref] + + LEN = len(lst) + + POP = 3*LEN + 1 + popul = [range(LEN) for x in xrange(POP)] + fitn = [0 for x in xrange(POP)] + + def rnd(): + return random.randint(0, LEN -1) + + def mutate(): + for x in xrange(LEN): + # rotate the previous element by one + v = popul[x+LEN] = popul[x+LEN - 1] + a = v.pop(0) + v.append(a) + + for x in xrange(LEN): + # swap elements + a = rnd() + b = rnd() + + v = popul[x] + c = v[a] + v[a] = v[b] + v[b] = c + + for x in xrange(LEN): + # get one element out, add at the end + v = popul[x+2*LEN] + + a = rnd() + c = v[a] + del v[a] + v.append(c) + + def evil(): + + best = opti_ref + pos = -1 + for x in xrange(len(popul)): + v = popul[x] + arr = [lst[a] for a in v] + tmp = '%s %s' % (cmd, ' '.join(arr)) + subprocess.Popen(tmp, shell=True).wait() + siz = os.stat(filename).st_size + + fitn[x] = siz + if compare(siz, best): + best = siz + pos = x + + if compare(siz, opti[0]): + opti[0] = siz + shutil.copy2(filename, 'best_' + filename) + + #print popul[x], sum(popul[x]), sum(range(LEN)) + assert (sum(popul[x]) == sum(range(LEN))) + + #print pos + for x in xrange(len(popul)): + if x == pos: + continue + popul[x] = popul[pos][:] + assert(len(popul[x]) == LEN) + return best + + for i in xrange(10000): + mutate() + print(evil()) + +if __name__ == '__main__': + + parser = optparse.OptionParser() + parser.add_option('--max', dest='maxi', default=False, action='store_true', help='maximize the file size (default is minimize)') + parser.add_option('--kind', dest='kind', default='bz2', action='store', help='bz2, xz or gz') + (options, args) = parser.parse_args() + + lst = read_wafdir() + gen(lst, options) + diff --git a/playground/compress/wscript b/playground/compress/wscript new file mode 100644 index 00000000..14352771 --- /dev/null +++ b/playground/compress/wscript @@ -0,0 +1,144 @@ +#! /usr/bin/env python + +""" +You will need either bzip2 or gzip, and a local waf copy +(unset the variable WAFDIR if set) + +Using more than 100000 tasks may eat your memory +""" + +top = '.' +out = 'build' + +TEMPLATE = """ +#! /usr/bin/gnuplot -persist +# output file, compression type, input file +set terminal png +set output "%s" +set ylabel "Amount of files created" +set xlabel "File size in kB" +set title "Compressed tar file distribution (%s)" +plot '%s' using 1:2 with lines lt 3 title "" +""" + +import random, bz2, os, threading +lock = threading.Lock() + +def options(opt): + opt.add_option('--num', action='store', type='int', default=200, help='amount of compressed files to create') + +# values for storing the min and max +gzip = [10000000, 0] +bzip2 = [10000000, 0] +xz = [10000000, 0] + +def try_compress(self): + global mi, ma + + frompath = self.generator.frompath + + uid = id(threading.current_thread()) + filename = frompath.abspath() + os.sep + 'test%d.bin' % uid + + self.files = self.generator.files[:] + random.shuffle(self.files) + + if self.generator.kind == 'bzip2': + store = bzip2 + cmd = 'cjf' + ext = 'bz2' + elif self.generator.kind == 'xz': + store = xz + cmd = 'cJf' + ext = 'xz' + else: + store = gzip + cmd = 'czf' + ext = 'gz' + + self.generator.bld.exec_command('tar %s %s %s' % (cmd, filename, ' '.join(self.files)), cwd=frompath.abspath()) + + siz = os.stat(filename).st_size + if siz == 0: + return -1 + + try: + lock.acquire() + self.outputs[0].write('%d\n' % siz, 'a') + + if siz < store[0]: + store[0] = siz + os.rename(filename, self.generator.bld.bldnode.abspath() + os.sep + 'min%d.tar.%s' % (siz, ext)) + elif siz > store[1]: + store[1] = siz + os.rename(filename, self.generator.bld.bldnode.abspath() + os.sep + 'max%d.tar.%s' % (siz, ext)) + else: + os.unlink(filename) + finally: + lock.release() + +def count_result(self): + txt = self.inputs[0].read().strip() + lst = txt.split() + lst = [int(x) for x in lst if x] + mi = min(lst) + ma = max(lst) + + dc = {} + for x in lst: + try: + dc[x] += 1 + except KeyError: + dc[x] = 1 + + nlst = ['%d %d' % (x, dc.get(x, 0)) for x in range(mi, ma+1)] + self.outputs[0].write('\n'.join(nlst)) + +def write_template(self): + t = self.generator.triplet + self.outputs[0].write(TEMPLATE % (t[0].abspath(), t[1], t[2].abspath())) + +def configure(conf): + conf.find_program('gzip', mandatory=False) + conf.find_program('bzip2', mandatory=False) + if not conf.env.GZIP and not conf.env.BZIP2: + conf.fatal('Either gzip or bzip2 is necessary for this') + + # xz is a gzip-like, lzma-based compression tool + conf.find_program('xz', mandatory=False) + + conf.find_program('gnuplot', var='GNUPLOT') + +def build(bld): + wafdir_lst = bld.srcnode.ant_glob('.waf-1.6*', dir=True) + if not wafdir_lst: + bld.fatal('Missing local Waf directory') + node = wafdir_lst[0] + rels = [x.path_from(node) for x in node.ant_glob('**/*.py')] + + KINDS = [] + if bld.env.BZIP2: + KINDS.append('bzip2') + if bld.env.GZIP: + KINDS.append('gzip') + if bld.env.XZ: + KINDS.append('xz') + + for kind in KINDS: + p = bld.bldnode + + ini = p.make_node('size_%s_1.txt' % kind) # list of file sizes + dist = p.make_node('size_%s_2.txt' % kind) # distribution file (count the results) + plot = p.make_node('size_%s_3.plot' % kind) # script file created for gnuplot + png = p.make_node('size_%s_4.png' % kind) # picture created + + for x in range(bld.options.num): + # the same target cannot have the signature of all the tasks that update it + # so the tasks will be executed each time + bld(rule=try_compress, target=ini, always=True, kind=kind, frompath=node, files=rels) + + # for the same reason, count_result will be executed each time + bld(rule=count_result, target=dist, source=ini, always=True, update_outputs=True) + bld(rule=write_template, target=plot, triplet=[png, kind, dist], always=True) + bld(rule='${GNUPLOT} < ${SRC[1].abspath()}', target=png, source=[dist, plot]) + diff --git a/playground/cpp_gen/a.cpp b/playground/cpp_gen/a.cpp new file mode 100644 index 00000000..139597f9 --- /dev/null +++ b/playground/cpp_gen/a.cpp @@ -0,0 +1,2 @@ + + diff --git a/playground/cpp_gen/main.cpp b/playground/cpp_gen/main.cpp new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/playground/cpp_gen/main.cpp @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/playground/cpp_gen/wscript b/playground/cpp_gen/wscript new file mode 100644 index 00000000..14c557e7 --- /dev/null +++ b/playground/cpp_gen/wscript @@ -0,0 +1,144 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +VERSION='0.0.1' +APPNAME='cpp_gen' + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_cxx') + +def configure(conf): + conf.load('compiler_cxx') + conf.check(header_name='stdio.h', features='cxx cxxprogram', mandatory=False) + +def build(bld): + bld.program(source='main.cpp a.cpp', target='app') + +#-------- + +import os +from waflib import Task, TaskGen, Utils +from waflib.Tools import cxx + +@TaskGen.extension('.cpp') +def more_tasks_at_once(self, node): + task1 = self.create_task('prog1', node, []) + task2 = self.create_compiled_task('cxx', node) + +def cmpnodes(a, b): + return cmp(a.abspath(), b.abspath()) + +class prog1(Task.Task): + before = ['cxxprogram', 'cxxshlib', 'cxxstlib'] + + def uid(self): + """ + the unique id of this task should only depend on the file inputs + """ + m = Utils.md5() + up = m.update + up(self.__class__.__name__.encode()) + for x in self.inputs: + up(x.abspath().encode()) + return m.digest() + + def runnable_status(self): + """ + since it is called after the build has started, + any task added must be passed through 'more_tasks' + """ + for x in self.run_after: + if not x.hasrun: + return Task.ASK_LATER + + # so this is a bit special, the goal here is to set the output nodes + # and to create the c++ tasks before the normal processing is done + sig = self.signature() + for x in self.generator.bld.raw_deps.get(sig, []): + self.outputs.append(self.generator.bld.srcnode.find_node(x)) + + if not self.outputs: + self.read_outputs_from_cache() + + if self.outputs: + self.create_cxx_task() + + ret = Task.Task.runnable_status(self) + return ret + + def create_cxx_task(self): + """ + create a task dynamically during the build + notice the use of 'more_tasks' + """ + tsk = cxx.cxx_hook(self.generator, self.outputs[0]) + tsk.set_run_after(self) # the build has started, so the order must be set manually + self.more_tasks = [tsk] # add tasks dynamically during the build + self.generator.link_task.inputs.append(tsk.outputs[0]) # add another input for the link task + try: + self.generator.link_task.inputs.sort(cmp=cmpnodes) # eliminate the random order (more tasks like this) + except: + self.generator.link_task.inputs.sort(key=lambda x: x.abspath()) # python3 + self.generator.link_task.set_run_after(tsk) # do not forget to set the build order there too + + def run(self): + """ + actual execution + this code should not be executed if the files are retrieved from the cache + """ + if self.inputs[0].name == 'a.cpp': + # simulate the creation of an interface file + out = self.inputs[0].parent.get_bld().make_node('a.ser.cpp') + out.write('\n\n') + + # read the file system + # remember that nodes cannot be created concurrently + # so you might to crate a lock if several tasks want the same nodes + inp = self.inputs[0] + node = inp.parent.get_bld().find_node(inp.name.replace('.cpp', '.ser.cpp')) + if node: + self.outputs = [node] + h_node = inp.parent.find_node(inp.name.replace('.cpp', '.ser.h')) + if h_node: + self.outputs.append(h_node) + + # if there are outputs, create a new c++ task + if self.outputs: + self.create_cxx_task() + + # access the scanner data + self.generator.bld.raw_deps[self.signature()] = [x.path_from(self.generator.bld.srcnode) for x in self.outputs] + + def read_outputs_from_cache(self): + """ + set the outputs from the results found in the cache + we assume that the files are created in the same folder as the inputs + if it is not like this, the nodes should be restored by another system, for example + by storing them in a separate file during run() + """ + env = self.env + sig = self.signature() + ssig = Utils.to_hex(sig) + + # first try to access the cache folder for the task + dname = os.path.join(self.generator.bld.cache_global, ssig) + try: + t1 = os.stat(dname).st_mtime + except OSError: + return None + + try: + lst = os.listdir(dname) + except: + return + + for x in lst: + self.outputs.append(self.inputs[0].parent.find_or_declare(x)) + + # not a fresh build and the cache is removed -> remember the files in the scanner data + self.generator.bld.raw_deps[self.signature()] = [x.path_from(self.generator.bld.srcnode) for x in self.outputs] + diff --git a/playground/cuda/cuda.py b/playground/cuda/cuda.py new file mode 100644 index 00000000..9ec45267 --- /dev/null +++ b/playground/cuda/cuda.py @@ -0,0 +1,55 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 + +"cuda" + +import os +from waflib import Task +from waflib.TaskGen import extension +from waflib.Tools import ccroot, c_preproc +from waflib.Configure import conf + +class cuda(Task.Task): + run_str = '${NVCC} ${CUDAFLAGS} ${CXXFLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${CPPPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} ${CXX_SRC_F}${SRC} ${CXX_TGT_F}${TGT}' + color = 'GREEN' + ext_in = ['.h'] + vars = ['CCDEPS'] + scan = c_preproc.scan + shell = False + +@extension('.cu', '.cuda') +def c_hook(self, node): + return self.create_compiled_task('cuda', node) + +def configure(conf): + conf.find_program('nvcc', var='NVCC') + conf.find_cuda_libs() + +@conf +def find_cuda_libs(self): + """ + find the cuda include and library folders + + use ctx.program(source='main.c', target='app', use='CUDA CUDART') + """ + + if not self.env.NVCC: + self.fatal('check for nvcc first') + + d = self.root.find_node(self.env.NVCC).parent.parent + + node = d.find_node('include') + _includes = node and node.abspath() or '' + + _libpath = [] + for x in ('lib64', 'lib'): + try: + _libpath.append(d.find_node(x).abspath()) + except: + pass + + # this should not raise any error + self.check_cxx(header='cuda.h', lib='cuda', libpath=_libpath, includes=_includes) + self.check_cxx(header='cuda.h', lib='cudart', libpath=_libpath, includes=_includes) + diff --git a/playground/cuda/main.cpp b/playground/cuda/main.cpp new file mode 100644 index 00000000..21f6273e --- /dev/null +++ b/playground/cuda/main.cpp @@ -0,0 +1,9 @@ +#include + +#include "test.h" + +int main() +{ + testcuda(); + return 0; +} diff --git a/playground/cuda/test.cu b/playground/cuda/test.cu new file mode 100644 index 00000000..2ca3c45d --- /dev/null +++ b/playground/cuda/test.cu @@ -0,0 +1,84 @@ +#include +#include +#include + +#include "test.h" + +// these macros are really really helpful +# define CUDA_SAFE_CALL( call) { \ + cudaError err = call; \ + if( cudaSuccess != err) { \ + fprintf(stderr, "Cuda error in file '%s' in line %i : %s.\n", \ + __FILE__, __LINE__, cudaGetErrorString( err) ); \ + exit(EXIT_FAILURE); \ + } } + +#define CHECKLASTERROR { \ + cudaError_t err = cudaGetLastError(); \ + if (err != cudaSuccess) { \ + fprintf(stderr, "Cuda error in file '%s' in line %i : %s.\n", \ + __FILE__, __LINE__, cudaGetErrorString( err) ); \ + exit(EXIT_FAILURE); \ + } } + + +#define SIZ 128 + +__global__ void truc(unsigned int * buf) { + if (threadIdx.x < SIZ) { + buf[threadIdx.x] = buf[threadIdx.x] << 5; + } + __syncthreads(); +} + +int testcuda() +{ + unsigned int* foo = (unsigned int*) malloc(SIZ * sizeof(unsigned int)); + for (int x = 0; x < SIZ; ++x) { + foo[x] = 1; + } + + unsigned int * recf = NULL; + CUDA_SAFE_CALL( cudaMalloc((void **) &recf, SIZ * sizeof(unsigned int)) ); + CUDA_SAFE_CALL(cudaMemcpy(recf, foo, SIZ * sizeof(unsigned int), cudaMemcpyHostToDevice)); + truc<<<1, SIZ>>>(recf); + CHECKLASTERROR + CUDA_SAFE_CALL(cudaMemcpy(foo, recf, SIZ * sizeof(unsigned int), cudaMemcpyDeviceToHost)); + printf("2^5 -> %u\n", foo[5]); + + CUDA_SAFE_CALL(cudaFree(recf)); + +/* +int deviceCount; +cudaGetDeviceCount(&deviceCount); +printf("device count %d\n", deviceCount); + +cudaDeviceProp dP; +cudaGetDeviceProperties(&dP, NULL); +//printf("Max threads per block: %d\n", dP.maxThreadsPerBlock); +//printf("Max Threads DIM: %d x %d x %d\n", dP.maxThreadsDim[0], dP.maxThreadsDim[1], dP.maxThreadsDim[2]); +//printf("Max Grid Size: %d x %d x %d\n", dP.maxGridSize[0], dP.maxGridSize[1], dP.maxGridSize[2]); + +cudaDeviceProp* pDeviceProp = &dP; + + printf( "\nDevice Name \t - %s ", pDeviceProp->name ); + printf( "\n**************************************"); + printf( "\nTotal Global Memory\t\t -%d KB", pDeviceProp->totalGlobalMem/1024 ); + printf( "\nShared memory available per block \t - %d KB", pDeviceProp->sharedMemPerBlock/1024 ); + printf( "\nNumber of registers per thread block \t - %d", pDeviceProp->regsPerBlock ); + printf( "\nWarp size in threads \t - %d", pDeviceProp->warpSize ); + printf( "\nMemory Pitch \t - %d bytes", pDeviceProp->memPitch ); + printf( "\nMaximum threads per block \t - %d", pDeviceProp->maxThreadsPerBlock ); + printf( "\nMaximum Thread Dimension (block) \t - %d %d %d", pDeviceProp->maxThreadsDim[0], pDeviceProp->maxThreadsDim[1], pDeviceProp->maxThreadsDim[2] ); + printf( "\nMaximum Thread Dimension (grid) \t - %d %d %d", pDeviceProp->maxGridSize[0], pDeviceProp->maxGridSize[1], pDeviceProp->maxGridSize[2] ); + printf( "\nTotal constant memory \t - %d bytes", pDeviceProp->totalConstMem ); + printf( "\nCUDA ver \t - %d.%d", pDeviceProp->major, pDeviceProp->minor ); + printf( "\nClock rate \t - %d KHz", pDeviceProp->clockRate ); + printf( "\nTexture Alignment \t - %d bytes", pDeviceProp->textureAlignment ); + printf( "\nDevice Overlap \t - %s", pDeviceProp-> deviceOverlap?"Allowed":"Not Allowed" ); + printf( "\nNumber of Multi processors \t - %d\n", pDeviceProp->multiProcessorCount ); +*/ + + return 0; +} + diff --git a/playground/cuda/test.h b/playground/cuda/test.h new file mode 100644 index 00000000..ccdb0965 --- /dev/null +++ b/playground/cuda/test.h @@ -0,0 +1 @@ +int testcuda(); diff --git a/playground/cuda/wscript b/playground/cuda/wscript new file mode 100644 index 00000000..f2592536 --- /dev/null +++ b/playground/cuda/wscript @@ -0,0 +1,31 @@ +#!/usr/bin/env python +# encoding: ISO8859-1 +# Thomas Nagy, 2010 + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_cxx') + +def configure(conf): + conf.load('compiler_cxx') # cuda does not compile in c mode + + # the tests will fail if the libraries cannot be found + # try pre_setting some variables, like this + # conf.env.LIBPATH_CUDA = ['c:\\foo\\bar\\lib'] + # conf.env.INCLUDES_CUDA = ['c:\\foo\\bar\\includes'] + + conf.load('cuda', tooldir='.') + +def build(bld): + + t = bld.program( + source = 'test.cu main.cpp', + target = 'app', + use = 'CUDA CUDART') + + #t.env.CUDAFLAGS = ['-deviceemu'] + # --ptxas-options="-v" + # --ptxas-options="-v -maxrregcount=10" + diff --git a/playground/cython/c_lib/lib.c b/playground/cython/c_lib/lib.c new file mode 100644 index 00000000..b6e86f0f --- /dev/null +++ b/playground/cython/c_lib/lib.c @@ -0,0 +1,8 @@ +#include "lib.h" +#include + + +void hello(void) +{ + printf("Hello, C-world!\n"); +} diff --git a/playground/cython/c_lib/lib.h b/playground/cython/c_lib/lib.h new file mode 100644 index 00000000..ef774764 --- /dev/null +++ b/playground/cython/c_lib/lib.h @@ -0,0 +1 @@ +void hello(void); diff --git a/playground/cython/cxx_lib/app.cxx b/playground/cython/cxx_lib/app.cxx new file mode 100644 index 00000000..b4578de2 --- /dev/null +++ b/playground/cython/cxx_lib/app.cxx @@ -0,0 +1,34 @@ +#include "Python.h" +#include + +#include "lib.h" +// cython function +#include "cy_cxxtest_api.h" + +int main(int argc, char** argv) +{ + std::cout << "::: cxx-app\n"; + Py_Initialize(); + if (!Py_IsInitialized()) { + std::cerr << ":: could not initialize python interpreter !\n"; + return 1; + } else { + std::cout << ":: python initialized\n"; + } + PyEval_InitThreads(); + if (!PyEval_ThreadsInitialized()) { + std::cerr << ":: could not init GIL !\n"; + return 1; + } + + if (import_cy_cxxtest()) { + std::cerr << "** could not import 'cy_cxxtest' module !\n"; + return 1; + } else { + std::cout << "::: successfully imported 'cy_cxxtest'\n"; + } + + cy_hello(); + std::cout << "::: cxx-app [done]\n"; + return 0; +} diff --git a/playground/cython/cxx_lib/lib.cxx b/playground/cython/cxx_lib/lib.cxx new file mode 100644 index 00000000..06531048 --- /dev/null +++ b/playground/cython/cxx_lib/lib.cxx @@ -0,0 +1,8 @@ +#include "lib.h" +#include + + +void hello(void) +{ + std::cout << "Hello, C++-world!" << std::endl; +} diff --git a/playground/cython/cxx_lib/lib.h b/playground/cython/cxx_lib/lib.h new file mode 100644 index 00000000..ef774764 --- /dev/null +++ b/playground/cython/cxx_lib/lib.h @@ -0,0 +1 @@ +void hello(void); diff --git a/playground/cython/src/cy_ctest.pxd b/playground/cython/src/cy_ctest.pxd new file mode 100644 index 00000000..1e8ec23b --- /dev/null +++ b/playground/cython/src/cy_ctest.pxd @@ -0,0 +1,2 @@ +cdef extern from "lib.h": + void hello() diff --git a/playground/cython/src/cy_ctest.pyx b/playground/cython/src/cy_ctest.pyx new file mode 100644 index 00000000..ddf4b0be --- /dev/null +++ b/playground/cython/src/cy_ctest.pyx @@ -0,0 +1,4 @@ +cimport cy_ctest + +def pyhello(): + cy_ctest.hello() diff --git a/playground/cython/src/cy_cxxtest.pxd b/playground/cython/src/cy_cxxtest.pxd new file mode 100644 index 00000000..1e8ec23b --- /dev/null +++ b/playground/cython/src/cy_cxxtest.pxd @@ -0,0 +1,2 @@ +cdef extern from "lib.h": + void hello() diff --git a/playground/cython/src/cy_cxxtest.pyx b/playground/cython/src/cy_cxxtest.pyx new file mode 100644 index 00000000..1102b97b --- /dev/null +++ b/playground/cython/src/cy_cxxtest.pyx @@ -0,0 +1,8 @@ +cimport cy_cxxtest + +def pyhello(): + cy_cxxtest.hello() + +cdef public api void cy_hello(): + print("hello cython-world!") + diff --git a/playground/cython/wscript b/playground/cython/wscript new file mode 100644 index 00000000..79e0a3ad --- /dev/null +++ b/playground/cython/wscript @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# encoding: ISO8859-1 +# Thomas Nagy, 2010 + +top = '.' +out = 'build' + +def options(ctx): + ctx.load('compiler_c') + ctx.load('compiler_cxx') + ctx.load('python') + ctx.load('cython') + +def configure(ctx): + ctx.load('compiler_c') + ctx.load('compiler_cxx') + ctx.load('python') + ctx.check_python_headers() + ctx.load('cython') + +def build(ctx): + # a C library + ctx(features = 'c cshlib', + source = 'c_lib/lib.c', + target = 'c_lib', + includes = 'c_lib') + + # a C++ library + ctx(features = 'cxx cxxshlib', + source = 'cxx_lib/lib.cxx', + target = 'cxx_lib', + includes = 'cxx_lib') + + # first try to build a C-based cython extension + ctx( + features = 'c cshlib pyext', + source = 'src/cy_ctest.pyx', + target = 'cy_ctest', + includes = 'c_lib', + use = 'c_lib') + + # then a C++-based one + ctx( + features = 'cxx cxxshlib pyext', + source = 'src/cy_cxxtest.pyx', + target = 'cy_cxxtest', + includes = 'cxx_lib', + use = 'cxx_lib') + + # a C++ application which uses a C function from a cython module + ctx( + features = 'cxx cxxprogram pyembed', + source = 'cxx_lib/app.cxx', + target = 'cy-app', + includes = 'cxx_lib src', + use = 'cxx_lib' + ) diff --git a/playground/daemon/daemon.py b/playground/daemon/daemon.py new file mode 100644 index 00000000..7938122c --- /dev/null +++ b/playground/daemon/daemon.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Matthias Jahn 2006 +# rewritten by Thomas Nagy 2009 + +""" +Start a new build as soon as something changes in the build directory. + +PyInotify, Fam, Gamin or time-threshold are used for the detection + +For now only PyInotify and time threshold are supported +Watching for new svn revisions could be added too +""" + +import select, errno, os, time +from waflib import Utils, Scripting, Logs, Build, Node, Context, Options + +w_pyinotify = w_fam = w_gamin = None +def check_support(): + global w_pyinotify, w_fam, w_gamin + try: + import pyinotify as w_pyinotify + except ImportError: + w_pyinotify = None + else: + try: + wm = w_pyinotify.WatchManager() + wm = w_pyinotify.Notifier(wm) + wm = None + except: + raise + w_pyinotify = None + + try: + import gamin as w_gamin + except ImportError: + w_gamin = None + else: + try: + test = w_gamin.WatchMonitor() + test.disconnect() + test = None + except: + w_gamin = None + + try: + import _fam as w_fam + except ImportError: + w_fam = None + else: + try: + test = w_fam.open() + test.close() + test = None + except: + w_fam = None + +def daemon(ctx): + """waf command: rebuild as soon as something changes""" + bld = None + while True: + bld = Context.create_context('build') + try: + bld.options = Options.options + bld.cmd = 'build' + bld.execute() + except ctx.errors.WafError as e: + print(e) + except KeyboardInterrupt: + Utils.pprint('RED', 'interrupted') + break + + try: + x = ctx.state + except AttributeError: + setattr(ctx, 'state', DirWatch()) + x = ctx.state + + x.wait(bld) + +def options(opt): + """So this shows how to add new commands from tools""" + Context.g_module.__dict__['daemon'] = daemon + +class DirWatch(object): + def __init__(self): + check_support() + if w_pyinotify: + self.sup = 'pyinotify' + elif w_gamin: + self.sup = 'gamin' + elif w_fam: + self.sup = 'fam' + else: + self.sup = 'dumb' + #self.sup = 'dumb' + + def wait(self, bld): + return getattr(self.__class__, 'wait_' + self.sup)(self, bld) + + def enumerate(self, node): + if os.path.exists(node.abspath()): + yield node.abspath() + try: + for x in node.children.values(): + for k in self.enumerate(x): + yield k + except AttributeError: + pass + raise StopIteration + + def wait_pyinotify(self, bld): + + class PE(w_pyinotify.ProcessEvent): + def stop(self, event): + self.notif.ev = True + self.notif.stop() + raise ValueError("stop for delete") + + process_IN_DELETE = stop + process_IN_CLOSE = stop + process_default = stop + + proc = PE() + wm = w_pyinotify.WatchManager() + notif = w_pyinotify.Notifier(wm, proc) + proc.notif = notif + + # well, we should add all the folders to watch here + for x in self.enumerate(bld.srcnode): + wm.add_watch(x, w_pyinotify.IN_DELETE | w_pyinotify.IN_CLOSE_WRITE) + + try: + # pyinotify uses an infinite loop ... not too nice, so we have to use an exception + notif.loop() + except ValueError: + pass + if not hasattr(notif, 'ev'): + raise KeyboardInterrupt + + def wait_dumb(self, bld): + time.sleep(5) + + def wait_gamin(self, bld): + time.sleep(5) + + def wait_fam(self, bld): + time.sleep(5) + diff --git a/playground/daemon/main.c b/playground/daemon/main.c new file mode 100644 index 00000000..fba61c6d --- /dev/null +++ b/playground/daemon/main.c @@ -0,0 +1,6 @@ + +int main() +{ + return 0; +} + diff --git a/playground/daemon/wscript b/playground/daemon/wscript new file mode 100644 index 00000000..502a5738 --- /dev/null +++ b/playground/daemon/wscript @@ -0,0 +1,25 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2009 (ita) + +""" +Use 'waf daemon' +""" + +VERSION='0.0.1' +APPNAME='cc_test' +top = '.' +out = 'build' + +def options(opt): + opt.load('daemon', tooldir='.') + +def configure(conf): + conf.load('gcc') + conf.load('daemon', tooldir='.') + +def build(bld): + bld.program( + source='main.c', + target='test') + diff --git a/playground/display/main.c b/playground/display/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/playground/display/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/playground/display/wscript b/playground/display/wscript new file mode 100644 index 00000000..117afee9 --- /dev/null +++ b/playground/display/wscript @@ -0,0 +1,51 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2011 (ita) + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + bld.program(source='main.c', target='app') + + customize_ze_outputs() + +def customize_ze_outputs(): + # first, display strings, people like them + from waflib import Utils, Logs + from waflib.Context import Context + def exec_command(self, cmd, **kw): + subprocess = Utils.subprocess + kw['shell'] = isinstance(cmd, str) + if isinstance(cmd, str): + Logs.info('%s' % cmd) + else: + Logs.info('%s' % ' '.join(cmd)) # here is the change + Logs.debug('runner_env: kw=%s' % kw) + try: + if self.logger: + self.logger.info(cmd) + kw['stdout'] = kw['stderr'] = subprocess.PIPE + p = subprocess.Popen(cmd, **kw) + (out, err) = p.communicate() + if out: + self.logger.debug('out: %s' % out.decode(sys.stdout.encoding or 'iso8859-1')) + if err: + self.logger.error('err: %s' % err.decode(sys.stdout.encoding or 'iso8859-1')) + return p.returncode + else: + p = subprocess.Popen(cmd, **kw) + return p.wait() + except OSError: + return -1 + Context.exec_command = exec_command + + # and change the outputs for tasks too + from waflib.Task import Task + def display(self): + return '' # no output on empty strings + Task.__str__ = display + diff --git a/playground/dynamic_build/main.c b/playground/dynamic_build/main.c new file mode 100644 index 00000000..a46866d9 --- /dev/null +++ b/playground/dynamic_build/main.c @@ -0,0 +1,4 @@ +int main() +{ + return 0; +} diff --git a/playground/dynamic_build/wscript b/playground/dynamic_build/wscript new file mode 100644 index 00000000..228c280e --- /dev/null +++ b/playground/dynamic_build/wscript @@ -0,0 +1,80 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +VERSION='0.0.1' +APPNAME='cc_test' + +""" +The build groups can be processed all at once (required for the progress bar) +or sequentially in a lazy manner. + +Each time one more .c file will be added during the build and compiled and linked in app + +$ waf distclean configure build build +'distclean' finished successfully (0.005s) +Setting top to : /dynamic_build +Setting out to : /dynamic_build/build +Checking for 'gcc' (c compiler) : ok +'configure' finished successfully (0.091s) + +Waf: Entering directory `/dynamic_build/build' +[1/1] foo_3.c: -> build/foo_3.c +[2/4] c: main.c -> build/main.c.1.o +[3/4] c: build/foo_3.c -> build/foo_3.c.1.o +[4/4] cprogram: build/main.c.1.o build/foo_3.c.1.o -> build/app +Waf: Leaving directory `/dynamic_build/build' +'build' finished successfully (5.169s) + +Waf: Entering directory `/dynamic_build/build' +[1/1] foo_17.c: -> build/foo_17.c +[3/5] c: build/foo_17.c -> build/foo_17.c.1.o +[5/5] cprogram: build/main.c.1.o build/foo_17.c.1.o build/foo_3.c.1.o -> build/app +Waf: Leaving directory `/dynamic_build/build' +'build' finished successfully (5.144s) +""" + +top = '.' + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + + +def build(bld): + """ + groups will be processed one by one during the build + the progress bar display will be inaccurate + """ + + bld.post_mode = Build.POST_LAZY + + import random + rnd = random.randint(0, 25) + bld( + rule = "sleep 2 && (echo 'int num%d = %d;' > ${TGT})" % (rnd, rnd), + target = 'foo_%d.c' % rnd, + ) + + bld.add_group() + + bld.program(source='main.c', target='app', dynamic_source='*.c') + +# support for the "dynamic_source" attribute follows + +from waflib import Build, Utils, TaskGen +@TaskGen.feature('c') +@TaskGen.before('process_source') +def dynamic_post(self): + """ + bld(dynamic_source='*.c', ..) will search for source files to add to the attribute 'source' + we could also call "eval" or whatever expression + """ + if not getattr(self, 'dynamic_source', None): + return + self.source = Utils.to_list(self.source) + self.source.extend(self.path.get_bld().ant_glob(self.dynamic_source)) + + diff --git a/playground/dynamic_headers/main.cpp b/playground/dynamic_headers/main.cpp new file mode 100644 index 00000000..5b6fe73d --- /dev/null +++ b/playground/dynamic_headers/main.cpp @@ -0,0 +1,5 @@ +#include "a.h" + +int main() { + return 0; +} diff --git a/playground/dynamic_headers/wscript b/playground/dynamic_headers/wscript new file mode 100644 index 00000000..78223b15 --- /dev/null +++ b/playground/dynamic_headers/wscript @@ -0,0 +1,77 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +VERSION='0.0.1' +APPNAME='dynamic_header_test' + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_cxx') + +def configure(conf): + conf.load('compiler_cxx') + +def build(bld): + bld.program(source='main.cpp', target='app', includes='.') + +# -------------------------------------------------------------------- +# Dynamic header creation, the file "a.h" from main.cpp is created +# during the build. You may use this code sample to create more complicated +# dynamic header generators + +from waflib import Task +from waflib.Tools.cxx import cxx + +class test(Task.Task): + run_str = 'touch ${TGT}' + +def runnable_status(self): + ret = super(cxx, self).runnable_status() + + try: + shared = self.generator.bld.shared_tasks + except AttributeError: + shared = self.generator.bld.shared_tasks = {} + + if ret != Task.ASK_LATER: + add = False + + # first pass over the unresolved files found by the scanner + for x in self.generator.bld.raw_deps[self.uid()]: + if x == 'a.h': + tgt = self.generator.path.find_or_declare(x) + try: + tsk = shared[tgt] + except KeyError: + tsk = shared[tgt] = self.generator.create_task('test', [], tgt) + self.set_run_after(tsk) + self.generator.bld.producer.outstanding.append(tsk) + add = True + if add: + # remove cache entries to force a clean scanner execution + delattr(self, 'cache_sig') + self.generator.bld.cache_nd = {} + del self.generator.bld.task_sigs[(self.uid(), 'imp')] # will have to rescan the C file + return self.runnable_status() + + # second pass over the existing nodes - corresponding tasks are still necessary + for x in self.generator.bld.node_deps[self.uid()]: + if x.name == 'a.h': + try: + tsk = shared[x] + except KeyError: + tsk = shared[x] = self.generator.create_task('test', [], x) + self.set_run_after(tsk) + self.generator.bld.producer.outstanding.append(tsk) + add = True + if add: + # no need to rescan anything, but recompute the signature after the dependent task is executed + delattr(self, 'cache_sig') + return self.runnable_status() + return ret + +cxx.runnable_status = runnable_status + diff --git a/playground/dynamic_recursive_tasks/bar.cpp b/playground/dynamic_recursive_tasks/bar.cpp new file mode 100644 index 00000000..ce2394fa --- /dev/null +++ b/playground/dynamic_recursive_tasks/bar.cpp @@ -0,0 +1,6 @@ +#include "truc.h" + +void bar() { + +} + diff --git a/playground/dynamic_recursive_tasks/bar.h b/playground/dynamic_recursive_tasks/bar.h new file mode 100644 index 00000000..aee2dc61 --- /dev/null +++ b/playground/dynamic_recursive_tasks/bar.h @@ -0,0 +1 @@ +void bar(); diff --git a/playground/dynamic_recursive_tasks/foo.cpp b/playground/dynamic_recursive_tasks/foo.cpp new file mode 100644 index 00000000..50bd0bdc --- /dev/null +++ b/playground/dynamic_recursive_tasks/foo.cpp @@ -0,0 +1,7 @@ +#include "bar.h" +#include "truc.h" + +void test() { + +} + diff --git a/playground/dynamic_recursive_tasks/foo.h b/playground/dynamic_recursive_tasks/foo.h new file mode 100644 index 00000000..14b3d723 --- /dev/null +++ b/playground/dynamic_recursive_tasks/foo.h @@ -0,0 +1,2 @@ +void test(); + diff --git a/playground/dynamic_recursive_tasks/main.cpp b/playground/dynamic_recursive_tasks/main.cpp new file mode 100644 index 00000000..3b50333a --- /dev/null +++ b/playground/dynamic_recursive_tasks/main.cpp @@ -0,0 +1,6 @@ +#include "foo.h" +#include "bar.h" + +int main() { + return 0; +} diff --git a/playground/dynamic_recursive_tasks/truc.cpp b/playground/dynamic_recursive_tasks/truc.cpp new file mode 100644 index 00000000..a8275bca --- /dev/null +++ b/playground/dynamic_recursive_tasks/truc.cpp @@ -0,0 +1,3 @@ +void truc() { + +} diff --git a/playground/dynamic_recursive_tasks/truc.h b/playground/dynamic_recursive_tasks/truc.h new file mode 100644 index 00000000..2efe441c --- /dev/null +++ b/playground/dynamic_recursive_tasks/truc.h @@ -0,0 +1 @@ +void truc(); diff --git a/playground/dynamic_recursive_tasks/wscript b/playground/dynamic_recursive_tasks/wscript new file mode 100644 index 00000000..ed485c53 --- /dev/null +++ b/playground/dynamic_recursive_tasks/wscript @@ -0,0 +1,78 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +VERSION='0.0.1' +APPNAME='crazy_test' + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_cxx') + +def configure(conf): + conf.load('compiler_cxx') + +def build(bld): + bld.program(source='main.cpp', target='app') + +""" +Only main.cpp is added to the program, then by looking at the include files, +a file is found. If a corresponding exists, +then a new c++ task is created to compile that file and to add it to the +program (modify the link task). + +The idea is to change the method runnable_status of the task. A more correct but less obvious +approach would be the creation of a specific c++ subclass, and using another +extension mapping function (@extension). +""" + +from waflib.Task import ASK_LATER +from waflib.Tools.cxx import cxx +def runnable_status(self): + ret = super(cxx, self).runnable_status() + self.more_tasks = [] + + # use a cache to avoid creating the same tasks + # for example, truc.cpp might be compiled twice + try: + shared = self.generator.bld.shared_tasks + except AttributeError: + shared = self.generator.bld.shared_tasks = {} + + if ret != ASK_LATER: + for x in self.generator.bld.node_deps[self.uid()]: + node = x.parent.get_src().find_resource(x.name.replace('.h', '.cpp')) + if node: + try: + tsk = shared[node] + except: + tsk = shared[node] = self.generator.cxx_hook(node) + + self.more_tasks.append(tsk) + + + # add the node created to the link task outputs + try: + link = self.generator.link_task + except AttributeError: + pass + else: + if not tsk.outputs[0] in link.inputs: + link.inputs.append(tsk.outputs[0]) + link.set_run_after(tsk) + + # any change in the order of the input nodes may cause a recompilation + link.inputs.sort(key=lambda x: x.abspath()) + + # if you want to modify some flags + # you *must* have the task recompute the signature + self.env.append_value('CXXFLAGS', '-O2') + delattr(self, 'cache_sig') + return super(cxx, self).runnable_status() + + return ret + +cxx.runnable_status = runnable_status + diff --git a/playground/erlang/hello.erl b/playground/erlang/hello.erl new file mode 100644 index 00000000..ae71681f --- /dev/null +++ b/playground/erlang/hello.erl @@ -0,0 +1,5 @@ +% what a weird language ... :-) + +-module(hello). +-export([hello_world/0]). +hello_world() -> io:fwrite("hello, world\n"). diff --git a/playground/erlang/wscript b/playground/erlang/wscript new file mode 100644 index 00000000..c594f711 --- /dev/null +++ b/playground/erlang/wscript @@ -0,0 +1,8 @@ +#! /usr/bin/env python + +def configure(conf): + conf.load('erlang') + +def build(bld): + bld(source='hello.erl') + diff --git a/playground/errcheck/typos/main.c b/playground/errcheck/typos/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/playground/errcheck/typos/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/playground/errcheck/typos/wscript b/playground/errcheck/typos/wscript new file mode 100644 index 00000000..6b37b933 --- /dev/null +++ b/playground/errcheck/typos/wscript @@ -0,0 +1,20 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_c') + opt.load('errcheck') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + bld(feature='c cprogram', source='main.c', target='app1') + bld.program(sources='main.c', target='app2') + bld.program(source='main.c', targets='app3') + bld.program(source='main.c', target='app4', include='.') + bld.program(source='main.c', target='app5', define='x=1') diff --git a/playground/errcheck/wrong_top/src/wscript_build b/playground/errcheck/wrong_top/src/wscript_build new file mode 100644 index 00000000..6936e14c --- /dev/null +++ b/playground/errcheck/wrong_top/src/wscript_build @@ -0,0 +1,3 @@ +""" +nothing to see here +""" diff --git a/playground/errcheck/wrong_top/wscript b/playground/errcheck/wrong_top/wscript new file mode 100644 index 00000000..e6416f53 --- /dev/null +++ b/playground/errcheck/wrong_top/wscript @@ -0,0 +1,15 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +""" +Setting the source directory below the current folder is a +bad idea in general +""" + +top = 'src' +out = 'build' + +def configure(conf): + pass + diff --git a/playground/exclusive_link/excl.py b/playground/exclusive_link/excl.py new file mode 100644 index 00000000..732534b6 --- /dev/null +++ b/playground/exclusive_link/excl.py @@ -0,0 +1,57 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2011 (ita) + +""" +Prevents link tasks from executing in parallel. This can be used to +improve the linker execution, which may use a lot of memory. + +The variable 'MAX' represents the tasks able to run +concurrently (just one by default). The variable 'count' +is the amount of tasks of one kind being executed. + +Different constraints can be enforced by changing the scope +of some variables. Remember that: + +* the counter could be set on the class level +* the MAX amount of concurrent tasks can be more than 1 +""" + +from waflib.Utils import threading +from waflib import Task +lock = threading.Lock() +count = 0 +MAX = 1 + +def make_exclusive(cls): + + old_runnable_status = cls.runnable_status + def runnable_status(self): + global count, lock, MAX + ret = Task.ASK_LATER + if count >= MAX: + return ret + ret = old_runnable_status(self) + if ret == Task.RUN_ME: + lock.acquire() + count += 1 + lock.release() + return ret + cls.runnable_status = runnable_status + + old_run = cls.run + def run(self): + global count, lock + try: + ret = old_run(self) + finally: + lock.acquire() + count -= 1 + lock.release() + return ret + cls.run = run + +for x in 'cprogram cxxprogram cshlib cxxshlib cstlib cxxstlib fcprogram fcshlib fcstlib'.split(): + if x in Task.classes: + make_exclusive(Task.classes[x]) + diff --git a/playground/exclusive_link/main.c b/playground/exclusive_link/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/playground/exclusive_link/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/playground/exclusive_link/wscript b/playground/exclusive_link/wscript new file mode 100644 index 00000000..919edf94 --- /dev/null +++ b/playground/exclusive_link/wscript @@ -0,0 +1,20 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2011 (ita) + +VERSION='0.0.1' +APPNAME='cc_test' + +top = '.' + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + conf.load('excl', tooldir='.') + +def build(bld): + for i in range(20): + bld.program(source='main.c', target='app_%d' % i) + diff --git a/playground/extern_makefile/Makefile b/playground/extern_makefile/Makefile new file mode 100644 index 00000000..6424bdbc --- /dev/null +++ b/playground/extern_makefile/Makefile @@ -0,0 +1,11 @@ +all: + echo "that's all" +build: + echo "build" +install: + echo "install" +uninstall: + echo "uninstall" +clean: + echo "clean" + diff --git a/playground/extern_makefile/just_make/Makefile b/playground/extern_makefile/just_make/Makefile new file mode 100644 index 00000000..a6159303 --- /dev/null +++ b/playground/extern_makefile/just_make/Makefile @@ -0,0 +1,3 @@ + +all: + touch ../build/just_make/foo.so diff --git a/playground/extern_makefile/just_make/wscript b/playground/extern_makefile/just_make/wscript new file mode 100644 index 00000000..510bd960 --- /dev/null +++ b/playground/extern_makefile/just_make/wscript @@ -0,0 +1,22 @@ +#! /usr/bin/env python + +def build(bld): + + # Call make for building, but keep waf for install/uninstall + # there is no 'clean' here + def make_all(tsk): + + # create the output folder in advance + d = tsk.generator.path + d.get_bld().mkdir() + ret = tsk.generator.bld.exec_command('make all', cwd=d.abspath()) + + # install the files by waf - it might be more maintainable to do it through make though + tsk.set_outputs(d.get_bld().ant_glob('*.so')) + tsk.generator.bld.install_files('${LIBDIR}', tsk.outputs, postpone=False) + return ret + + # the attribute 'always' is used to force the make execution, else + # the make command will be called only once + bld(rule=make_all, always=True, name='call make') + diff --git a/playground/extern_makefile/wscript b/playground/extern_makefile/wscript new file mode 100644 index 00000000..b6651c25 --- /dev/null +++ b/playground/extern_makefile/wscript @@ -0,0 +1,35 @@ +#! /usr/bin/env python + +def configure(conf): + conf.env.thecmd = 'all' + +def build(bld): + bld(rule='touch ${TGT}', target='bar.txt') + bld.recurse('just_make') + +from waflib.Build import BuildContext, InstallContext, UninstallContext, CleanContext + +class _build(BuildContext): + def compile(self): + ret = self.exec_command('make %s' % self.env.thecmd, cwd=self.path.abspath()) + if ret: + self.fatal('make returned %r' % ret) + super(_build, self).compile() + +class _clean(CleanContext): + def clean(self): + self.exec_command('make clean', cwd=self.path.abspath()) + super(_clean, self).clean() + +class _install(InstallContext): + def compile(self): + ret = self.exec_command('make install', cwd=self.path.abspath()) + if ret: + self.fatal('make install returned %r' % ret) + super(_install, self).compile() + +class _uninstall(UninstallContext): + def compile(self): + self.exec_command('make uninstall', cwd=self.path.abspath()) + super(_uninstall, self).compile() + diff --git a/playground/folder_hashing/fhash.py b/playground/folder_hashing/fhash.py new file mode 100644 index 00000000..1e848267 --- /dev/null +++ b/playground/folder_hashing/fhash.py @@ -0,0 +1,49 @@ +#! /usr/bin/env python +# encoding: utf-8 + +""" +Modification to handle folders as if they were files. + +Usually, the target folders are created automatically (Node.find_or_declare) +for files that need them so this is not really necessary. + +This modification incurs a performance penalty (computing hashes, +creating additional tasks, checking if the folders are there +vs just creating the folders if missing), and can conceal serious +errors (confusing files and folders for example). + +The build order will not look at the parent folder relationships, +we will need a testcase for this (overriding the function +Task.set_file_constraints is trivial) +""" + +import stat +from waflib import Utils, Task +from waflib.TaskGen import feature + +def h_file(filename): + """now folders can have a signature too""" + st = os.stat(filename) + if stat.S_ISDIR(st[stat.ST_MODE]): + return Utils.md5(filename).digest() + m = Utils.md5() + m.update(str(st.st_mtime)) + m.update(str(st.st_size)) + m.update(filename) + return m.digest() +Utils.h_file = h_file + +@feature('mkdir') +def make_target_folder(self): + """code provided as an example""" + try: + node = self.target + except AttributeError: + raise self.bld.errors.WafError('Missing target attribute on task generator %r' % self) + self.create_task('mkdir', [], node) + +class mkdir(Task.Task): + """calling node.mkdir() will be more efficient than creating folders""" + def run(self): + self.outputs[0].mkdir() + diff --git a/playground/folder_hashing/wscript b/playground/folder_hashing/wscript new file mode 100644 index 00000000..f66101fb --- /dev/null +++ b/playground/folder_hashing/wscript @@ -0,0 +1,12 @@ +#! /usr/bin/env python +# encoding: utf-8 + +def configure(conf): + conf.load('fhash', tooldir='.') + +def build(bld): + node = bld.path.get_bld().make_node('test/bar/stuff') + + bld(features='mkdir', target=node) + bld(rule='du ${SRC}', source=node) + diff --git a/playground/freeimage/fi.cpp b/playground/freeimage/fi.cpp new file mode 100644 index 00000000..614f1957 --- /dev/null +++ b/playground/freeimage/fi.cpp @@ -0,0 +1,19 @@ +#include +#include "FreeImage.h" + +int main() { + +#if defined(FREEIMAGE_LIB) || !defined(WIN32) + FreeImage_Initialise(FALSE); +#endif + + FIBITMAP* dib = FreeImage_Load(FIF_PNG, "img.png", PNG_DEFAULT); + printf("%dx%d", FreeImage_GetWidth(dib), FreeImage_GetHeight(dib)); + FreeImage_Unload(dib); + +#if defined(FREEIMAGE_LIB) || !defined(WIN32) + FreeImage_DeInitialise(); +#endif + + return 0; +} diff --git a/playground/freeimage/fip.cpp b/playground/freeimage/fip.cpp new file mode 100644 index 00000000..92a0603b --- /dev/null +++ b/playground/freeimage/fip.cpp @@ -0,0 +1,21 @@ +// run './waf --fip' to build this one + +#include +#include "FreeImagePlus.h" + +int main() { + +#if defined(FREEIMAGE_LIB) || !defined(WIN32) + FreeImage_Initialise(); +#endif + + fipImage img; + img.load("img.png"); + std::cout << img.getWidth() << "x" << img.getHeight() << std::endl; + +#if defined(FREEIMAGE_LIB) || !defined(WIN32) + FreeImage_DeInitialise(); +#endif + + return 0; +} diff --git a/playground/freeimage/wscript b/playground/freeimage/wscript new file mode 100644 index 00000000..15b37cda --- /dev/null +++ b/playground/freeimage/wscript @@ -0,0 +1,11 @@ +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_cxx freeimage') + +def configure(conf): + conf.load('compiler_cxx freeimage') + +def build(bld): + bld.program(source='fi.cpp', target='app', use='FREEIMAGE') diff --git a/playground/freeimage/wscript.fip b/playground/freeimage/wscript.fip new file mode 100644 index 00000000..0a886858 --- /dev/null +++ b/playground/freeimage/wscript.fip @@ -0,0 +1,20 @@ +# wscript to build FreeImagePlus on Windows with MinGW +# put it in FreeImage directory and run: +# python waf configure build install + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_cxx') + +def configure(conf): + conf.load('compiler_cxx') + +def build(bld): + bld.stlib(source=bld.path.ant_glob('Wrapper/FreeImagePlus/src/*'), + includes='Wrapper/FreeImagePlus Source', target='FreeImagePlus', + defines='NDEBUG WIN32 _WINDOWS _USRDLL FIP_EXPORTS _CRT_SECURE_NO_DEPRECATE', + use='Dist/FreeImage odbc32 odbccp32') + bld.install_as('Dist/FreeImagePlus.a', 'libFreeImagePlus.a') + bld.install_files('Dist', 'Wrapper/FreeImagePlus/FreeImagePlus.h') \ No newline at end of file diff --git a/playground/fsharp/main.fs b/playground/fsharp/main.fs new file mode 100644 index 00000000..1e7908d0 --- /dev/null +++ b/playground/fsharp/main.fs @@ -0,0 +1,4 @@ + +#light +module Main +let main = Printer.print_repeatedly 5 "hello, world" diff --git a/playground/fsharp/printer.fs b/playground/fsharp/printer.fs new file mode 100644 index 00000000..fd6377b6 --- /dev/null +++ b/playground/fsharp/printer.fs @@ -0,0 +1,4 @@ + +#light +module Printer +let print_repeatedly n str = for x in 1..n do printfn "%s" str diff --git a/playground/fsharp/wscript b/playground/fsharp/wscript new file mode 100644 index 00000000..0187e47e --- /dev/null +++ b/playground/fsharp/wscript @@ -0,0 +1,13 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2011 (ita) + +def options(opt): + opt.load('fsc') + +def configure(conf): + conf.load('fsc') + +def build(bld): + bld(features='fs', source='printer.fs main.fs', type='exe', gen='test.exe') + diff --git a/playground/fully_sequential_build/main.c b/playground/fully_sequential_build/main.c new file mode 100644 index 00000000..cbe2dc3d --- /dev/null +++ b/playground/fully_sequential_build/main.c @@ -0,0 +1,5 @@ + +int main() { + return 0; +} + diff --git a/playground/fully_sequential_build/wscript b/playground/fully_sequential_build/wscript new file mode 100644 index 00000000..9ebdfe28 --- /dev/null +++ b/playground/fully_sequential_build/wscript @@ -0,0 +1,25 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2011 (ita) + +""" +perform builds in a step-by-step fashion +""" + +VERSION='0.0.1' +APPNAME='fsb' + +top = '.' + +def options(opt): + opt.load('fsb') + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + bld.program(source='main.c', target='app') + bld.program(source='main.c', target='app2') + bld(rule='echo hello', source='wscript') + diff --git a/playground/gcj/gcj.py b/playground/gcj/gcj.py new file mode 100644 index 00000000..ddbb698e --- /dev/null +++ b/playground/gcj/gcj.py @@ -0,0 +1,88 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2008 (ita) + +""" +Native compilation using gcj + +highly experimental, and gcj sucks anyway +""" + +import os, re +from waflib.Configure import conf +from waflib import TaskGen, Task, Utils, Node +from waflib.TaskGen import feature, before, after +from waflib.Tools import ccroot + +def configure(conf): + conf.find_program('gcj', var='GCJ') + conf.env.GCJLINK = conf.env.GCJ + conf.env.GCJLINKFLAGS_gcj_shlib = ['-shared'] + conf.env.GCJFLAGS_gcj_shlib = ['-fPIC'] + +class gcj(Task.Task): + run_str = '${GCJ} ${GCJFLAGS} -classpath ${CLASSPATH} -c -o ${TGT} ${SRC}' + +class gcj_program(ccroot.link_task): + run_str = '${GCJLINK} ${GCJLINKFLAGS} ${SRC} -o ${TGT}' + color = 'YELLOW' + +class gcj_shlib(gcj_program): + pass + +ccroot.USELIB_VARS['gcj'] = set(['CLASSPATH', 'JAVACFLAGS', 'GCJFLAGS']) +ccroot.USELIB_VARS['gcj_program'] = set(['CLASSPATH', 'JAVACFLAGS', 'GCJLINKFLAGS']) +ccroot.USELIB_VARS['gcj_shlib'] = set(['CLASSPATH', 'JAVACFLAGS', 'GCJLINKFLAGS']) +feature('gcj_program', 'gcj_shlib')(ccroot.apply_link) +feature('gcj_program', 'gcj_shlib')(ccroot.propagate_uselib_vars) + +@feature('gcj') +@after('propagate_uselib_vars', 'apply_gcj') +def set_gcj_classpath(self): + lst = [isinstance(x, str) and x or x.abspath() for x in self.env.CLASSPATH] + self.env.CLASSPATH = os.pathsep.join(lst) + os.pathsep + +@feature('gcj') +@before('apply_java') +def apply_gcj(self): + if 'javac' in self.features: + self.bld.fatal('feature gcj_native is not compatible with javac %r' % self) + + srcdir = getattr(self, 'srcdir', '') + if isinstance(srcdir, Node.Node): + srcdir = [srcdir] + + tmp = [] + for x in Utils.to_list(srcdir): + if isinstance(x, Node.Node): + y = x + else: + y = self.path.find_dir(x) + if not y: + self.bld.fatal('Could not find the folder %s from %s' % (x, self.path)) + tmp.append(y) + + nodes = [] + for x in tmp: + nodes.extend(x.ant_glob('**/*.java')) + + if not getattr(self, 'gcjonce', None): + for x in nodes: + self.create_compiled_task('gcj', x) + +############################################################# +# gcj is still beta software +# and this workaround cannot work for shared object (-fPIC) + +class fix_dummy(Task.Task): + run_str = 'objcopy -L _ZGr8_$$_dummy ${SRC}' + before = ['gcj_program', 'gcj_shlib'] + +@feature('gcj') +@after('apply_gcj') +def gcj_developers_like_duplicate_dummy_symbols(self): + if self.env.FIX_DUMMY: + for tsk in self.compiled_tasks: + if isinstance(tsk, gcj): + self.create_task('fix_dummy', tsk.outputs[0]) + diff --git a/playground/gcj/more/es/Hi.java b/playground/gcj/more/es/Hi.java new file mode 100644 index 00000000..cb0a8906 --- /dev/null +++ b/playground/gcj/more/es/Hi.java @@ -0,0 +1,5 @@ +public class Hi { + public Hi() { + System.out.println("hi"); + } +} diff --git a/playground/gcj/src/es/Comp.java b/playground/gcj/src/es/Comp.java new file mode 100644 index 00000000..9c2a4ee7 --- /dev/null +++ b/playground/gcj/src/es/Comp.java @@ -0,0 +1,5 @@ +package es; + +public class Comp { + public static final String WAF = "-.-.-.-.-"; +} diff --git a/playground/gcj/src/es/Hello.java b/playground/gcj/src/es/Hello.java new file mode 100644 index 00000000..52032ebd --- /dev/null +++ b/playground/gcj/src/es/Hello.java @@ -0,0 +1,36 @@ +package es; // obligatory + +import es.Comp; + +public class Hello +{ + int m_var = 0; + public Hello() + { + this.m_var = 2; + } + + class MyHelperClass + { + MyHelperClass() { } + int someHelperMethod(int z, int q) { return 2; } + } + + public Object makeObj(String name) + { + final String objName = "My name is " + name; + + return new Object() { + public String toString() + { + return objName; + } + }; + } + + public static void main(String args[]) + { + System.out.println("Hello, world" + Comp.WAF); + } +} + diff --git a/playground/gcj/wscript b/playground/gcj/wscript new file mode 100644 index 00000000..00855929 --- /dev/null +++ b/playground/gcj/wscript @@ -0,0 +1,23 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2008 (ita) + +VERSION='0.0.2' +APPNAME='gcj_test' + +top = '.' +out = 'out' + +def configure(conf): + conf.load('gcj', tooldir='.') + conf.env.FIX_DUMMY = True + +def build(bld): + bld( + features = 'gcj gcj_program', + srcdir = 'src more', + target = 'test', + gcjlinkflags = '--main=es.Hello', + classpath = [bld.path.find_dir(x) for x in ('src', 'more')], + ) + diff --git a/playground/hide_output/wscript b/playground/hide_output/wscript new file mode 100644 index 00000000..48c99ba5 --- /dev/null +++ b/playground/hide_output/wscript @@ -0,0 +1,22 @@ +#! /usr/bin/env python + +""" +the feature 'hidden' is used to display the task output for make-like rules +""" + +def configure(conf): + pass + +def build(bld): + bld.env.FOO =['m', 'ncurses'] + bld.env.ST = '-L%s' + t = bld(rule='echo ${ST:FOO}', always=True, shell=1, features='hidden') + + +from waflib import Utils +from waflib.TaskGen import feature, after +@feature('hidden') +@after('apply_rule') +def hide_it(self): + self.tasks[0].__class__.log_display = Utils.nada + diff --git a/playground/implicit_order/a.c b/playground/implicit_order/a.c new file mode 100644 index 00000000..4b3f20fb --- /dev/null +++ b/playground/implicit_order/a.c @@ -0,0 +1 @@ +int ki = 33; diff --git a/playground/implicit_order/main.c b/playground/implicit_order/main.c new file mode 100644 index 00000000..75a458c8 --- /dev/null +++ b/playground/implicit_order/main.c @@ -0,0 +1,5 @@ +#include "faa.h" + +int main() { + return 0; +} diff --git a/playground/implicit_order/wscript b/playground/implicit_order/wscript new file mode 100644 index 00000000..925d9047 --- /dev/null +++ b/playground/implicit_order/wscript @@ -0,0 +1,63 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +VERSION='0.0.1' +APPNAME='cc_test' + +top = '.' + +import waflib.Configure +waflib.Configure.autoconfig = True + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + + bld(rule='echo "int ko = $$RANDOM;" > ${TGT}', target='faa.h', always=True, update_outputs=True, shell=True, name='z2') + bld.program(source='a.c main.c', target='foo', includes='.') + +# sort the tasks in reverse order to force the 'faa.h' creation in last position +from waflib import Task, Errors, Logs +old = Task.set_file_constraints +def meth(lst): + try: + lst.sort(cmp=lambda x, y: cmp(x.__class__.__name__, y.__class__.__name__)) + except: + lst.sort(key=lambda x: x.__class__.__name__) # python3 + old(lst) +Task.set_file_constraints = meth + +def are_implicit_nodes_ready(self): + """remove this method if/when the main one is enabled""" + bld = self.generator.bld + try: + cache = bld.dct_implicit_nodes + except: + bld.dct_implicit_nodes = cache = {} + + try: + dct = cache[bld.cur] + except KeyError: + dct = cache[bld.cur] = {} + for tsk in bld.cur_tasks: + for x in tsk.outputs: + dct[x] = tsk + + modified = False + for x in bld.node_deps.get(self.uid(), []): + if x in dct: + self.run_after.add(dct[x]) + modified = True + + if modified: + for tsk in self.run_after: + if not tsk.hasrun: + Logs.warn("task %r is not ready..." % self) + raise Errors.TaskNotReady('not ready') +Task.Task.are_implicit_nodes_ready = are_implicit_nodes_ready + diff --git a/playground/ldscript/main.c b/playground/ldscript/main.c new file mode 100644 index 00000000..089f5c78 --- /dev/null +++ b/playground/ldscript/main.c @@ -0,0 +1,3 @@ +void init() +{ +} diff --git a/playground/ldscript/myscript.ld b/playground/ldscript/myscript.ld new file mode 100644 index 00000000..73589cf7 --- /dev/null +++ b/playground/ldscript/myscript.ld @@ -0,0 +1,2 @@ +ENTRY(init) + diff --git a/playground/ldscript/wscript b/playground/ldscript/wscript new file mode 100644 index 00000000..1116134e --- /dev/null +++ b/playground/ldscript/wscript @@ -0,0 +1,37 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2009 (ita) + +VERSION='0.0.1' +APPNAME='cc_test' + +top = '.' +out = 'build' + +def configure(conf): + conf.load('gcc') + +def build(bld): + + bld.program( + source = 'main.c', + target = 'test', + ldscript = 'myscript.ld', + linkflags = ['-nostdlib'], + ) + +from waflib import Utils +from waflib.TaskGen import after, feature + +@after('apply_link') +@feature('cprogram', 'cshlib') +def process_ldscript(self): + if not getattr(self, 'ldscript', None) or self.env.CC_NAME != 'gcc': + return + + node = self.path.find_resource(self.ldscript) + if not node: + raise Utils.WafError('could not find %r' % self.ldscript) + self.link_task.env.append_value('LINKFLAGS', '-Wl,-T,%s' % node.abspath()) + self.link_task.dep_nodes.append(node) + diff --git a/playground/local_rpath/a.c b/playground/local_rpath/a.c new file mode 100644 index 00000000..2118dadc --- /dev/null +++ b/playground/local_rpath/a.c @@ -0,0 +1 @@ +int k=43234; diff --git a/playground/local_rpath/main.c b/playground/local_rpath/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/playground/local_rpath/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/playground/local_rpath/wscript b/playground/local_rpath/wscript new file mode 100644 index 00000000..e62afffe --- /dev/null +++ b/playground/local_rpath/wscript @@ -0,0 +1,26 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2011 (ita) + +""" +rpath processing example: +add all rpaths from local libraries + +(warning: do not use in production code...) +""" + +VERSION='0.0.1' +APPNAME='local_rpath' + +top = '.' + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c local_rpath') + +def build(bld): + bld.shlib(source='a.c', target='foo') + bld.program(source='main.c', target='bar', use='foo') + diff --git a/playground/makedeps/wscript b/playground/makedeps/wscript new file mode 100644 index 00000000..04ed2d67 --- /dev/null +++ b/playground/makedeps/wscript @@ -0,0 +1,39 @@ +#! /usr/bin/env python + +""" +A build iterator similar to "waf step" but following the dependencies of input/output files + +waf make --files=aa +waf clean make --files=cc +""" + +top = '.' +out = 'build' + +def options(opt): + opt.load('make') + +def configure(conf): + conf.load('make') + +def build(bld): + + x = bld.path.make_node('wscript') + + def xxx(**kw): + # this is just an alias, but aliases are convenient, use them! + kw['update_outputs'] = True + if not 'rule' in kw: + kw['rule'] = 'cp ${SRC} ${TGT}' + return bld(**kw) + + xxx(source=x, target=x.change_ext('.a'), name='a') + xxx(source=x.change_ext('.a'), target=x.change_ext('.aa'), name='aa') + + xxx(source=x, target=x.change_ext('.b'), name='b') + xxx(source=x.change_ext('.b'), target=x.change_ext('.bb'), name='bb') + + xxx(source=x, target=x.change_ext('.c'), name='c') + + xxx(rule='cat ${SRC} > ${TGT}', source=[x.change_ext('.bb'), x.change_ext('.aa')], target=[x.change_ext('.cc')], name='cc') + diff --git a/playground/netcache/Netcache.java b/playground/netcache/Netcache.java new file mode 100644 index 00000000..103ccc90 --- /dev/null +++ b/playground/netcache/Netcache.java @@ -0,0 +1,266 @@ +// Thomas Nagy, 2011 + +// TODO handle all exceptions properly + +import java.util.HashMap; +import java.util.Map; +import java.util.List; +import java.util.Date; +import java.util.ArrayList; +import java.util.Comparator; +import java.util.Collections; +import java.lang.Math; +import java.lang.StringBuilder; +import java.io.*; +import java.net.*; +import java.security.*; + +public class Netcache implements Runnable, Comparator { + private static int PORT = 51200; + private static String CACHEDIR = "/tmp/wafcache/"; + private static long MAX = 10l * 1024l * 1024l * 1024l; + private static double CLEANRATIO = 0.8; + private static int BUF = 16 * 8192; + + private static HashMap flist = null; + private Socket server = null; + + public Netcache(Socket server) { + this.server = server; + } + + public void run () { + try { + + while (true) { + InputStream in = server.getInputStream(); + OutputStream out = server.getOutputStream(); + + byte b[] = new byte[128]; + int off = 0; + while (off < b.length) { + off += in.read(b, off, b.length - off); + } + + //System.out.println(new String(b)); + String[] args = new String(b).split(","); + if (args[0].equals("LST")) { + lst(args, in, out); + } + else if (args[0].equals("PUT")) { + put(args, in, out); + } + else if (args[0].equals("GET")) { + get(args, in, out); + } + else if (args[0].equals("CLEAN")) { + clean(args, in, out); + } + else if (args[0].equals("BYE")) { + server.close(); + break; + } + else { + System.out.println("Invalid command " + new String(b)); + server.close(); + break; + } + } + } catch (IOException e) { + e.printStackTrace(); + } + } + + public void lst(String[] args, InputStream in, OutputStream out) throws IOException { + StringBuilder b = new StringBuilder(); + int k = 0; + synchronized(flist) { + for (String name : flist.keySet()) { + b.append(name); + if (k <= flist.size()) { + k++; + b.append("\n"); + } + } + } + + byte[] ret = b.toString().getBytes(); + String header = String.format("%-128s", String.format("%d,", ret.length)); + + out.write(header.getBytes()); + out.write(ret); + } + + public void put(String[] args, InputStream in, OutputStream out) throws IOException { + File cachedir = new File(CACHEDIR); + File temp = File.createTempFile("foo", ".suffix", cachedir); + + long size = new Long(args[3].trim()); + + //System.out.println("" + args[1] + " " + args[2] + " " + args[3] + " " + args.length); + + byte[] buf = new byte[BUF]; + long cnt = 0; + OutputStream w = new FileOutputStream(temp); + try { + while (cnt < size) { + int c = in.read(buf, 0, (int) Math.min(BUF, size-cnt)); + if (c == 0) { + throw new RuntimeException("Connection closed too early"); + } + w.write(buf, 0, c); + cnt += c; + } + } finally { + w.close(); + } + + /*if (cnt != size) { + System.out.println("error!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!"); + }*/ + + File parent = new File(new File(new File(CACHEDIR), args[1].substring(0, 2)), args[1]); + File dest = new File(parent, args[2]); + try { + dest.getParentFile().mkdirs(); + } catch (Exception e) { + } + + if (!temp.renameTo(dest)) { + throw new RuntimeException("Could not reanae the filee"); + } + + long total = 0; + for (File f : parent.listFiles()) { + total += f.length(); + } + + synchronized(flist) { + if (flist.containsKey(parent.getName())) { + flist.get(parent.getName())[0] = parent.lastModified(); + } + else + { + flist.put(parent.getName(), new Object[] {parent.lastModified(), total, parent.getName()}); + } + } + } + + public void get(String[] args, InputStream in, OutputStream out) throws IOException { + File f = new File(new File(new File(new File(CACHEDIR), args[1].substring(0, 2)), args[1]), args[2].trim()); + long fsize = -1; + try { + fsize = f.length(); + } catch (Exception e) { + // return -1 to the client + } + + String ret = String.format("%-128s", String.format("%d,", fsize)); + out.write(ret.getBytes()); + + byte[] buf = new byte[BUF]; + + long cnt = 0; + InputStream s = new FileInputStream(f); + try { + while (cnt < fsize) { + long c = s.read(buf); + cnt += c; + out.write(buf, 0, (int) c); + } + } finally { + s.close(); + } + + File parent = f.getParentFile(); + Date d = new Date(); + parent.setLastModified(d.getTime()); + synchronized(flist) { + flist.get(parent.getName())[0] = parent.lastModified(); + } + } + + public void clean(String[] args, InputStream in, OutputStream out) throws IOException { + synchronized(flist) { + long total = 0; + for (Map.Entry entry : flist.entrySet()) { + total += (Long) entry.getValue()[1]; + } + + List k = new ArrayList(flist.values()); + Collections.sort(k, this); + + int cur = 0; + while (total > MAX * CLEANRATIO) { + Object[] kk = k.get(cur); + + String name = (String) kk[2]; + File f = new File(new File(new File(CACHEDIR), name.substring(0, 2)), name); + //System.out.println("removing " + cur + " " + kk[0] + " " + kk[1] + " " + f.getAbsolutePath()); + rm(f); + + total -= (Long) kk[1]; + + flist.remove(name); + cur++; + } + } + } + + public static void init_flist() { + flist = new HashMap(); + synchronized(flist) { + File dir = new File(CACHEDIR); + try { + dir.mkdirs(); + } catch (Exception e) { + + } + + for (File d : dir.listFiles()) { + if (!d.isDirectory()) continue; + for (File sd : d.listFiles()) { + if (!sd.isDirectory()) continue; + long total = 0; + for (File f : sd.listFiles()) { + total += f.length(); + } + //System.out.println(sd.getName()); + flist.put(sd.getName(), new Object[] {sd.lastModified(), total, sd.getName()}); + } + } + } + } + + public int compare(Object[] a, Object[] b) { + return ((Long) a[0]).compareTo((Long) b[0]); + } + + public static void rm(File dir) { + if (dir.isDirectory()) { + for (File f: dir.listFiles()) + { + rm(f); + } + } + dir.delete(); + } + + public static void main(String[] args) { + try + { + init_flist(); + System.out.println("ready (" + flist.keySet().size() + " dirs)"); + ServerSocket sock = new ServerSocket(PORT); + while (true) + { + Netcache tmp = new Netcache(sock.accept()); + Thread t = new Thread(tmp); + t.start(); + } + } catch (IOException e) { + e.printStackTrace(); + } + } +} + diff --git a/playground/netcache/netcache_server.py b/playground/netcache/netcache_server.py new file mode 100755 index 00000000..3dedd8f3 --- /dev/null +++ b/playground/netcache/netcache_server.py @@ -0,0 +1,262 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy 2011 (ita) + +""" +A simple TCP server to cache files over the network. +The client is located in waflib/extras/netcache_client.py + +This server uses a LRU cache policy (remove least recently used files), which means +that there is no risk of filling up the entire filesystem. + +Security: +--------- ++ the LRU cache policy will prevent filesystem saturation ++ invalid queries will be rejected (no risk of reading/writing arbitrary files on the OS) +- attackers might poison the cache + +Performance: +------------ +The server seems to work pretty well (for me) at the moment. cPython is limited to only +one CPU core, but there is a Java version of this server (Netcache.java). Send your +performance results to the Waf mailing-list! + +Future ideas: +------------- +- File transfer integrity +- Use servers on different ports (eg: get->1200, put->51201) to enable firewall filtering +- Use different processes for get/put (performance improvement) +""" + +import os, re, tempfile, socket, threading, shutil +import SocketServer + +CACHEDIR = '/tmp/wafcache' +CONN = (socket.gethostname(), 51200) +HEADER_SIZE = 128 +BUF = 8192*16 +MAX = 50*1024*1024*1024 # in bytes +CLEANRATIO = 0.85 +CHARS = '0123456789abcdef' + +GET = 'GET' +PUT = 'PUT' +LST = 'LST' +BYE = 'BYE' +CLEAN = 'CLN' +RESET = 'RST' + +re_valid_query = re.compile('^[a-zA-Z0-9_, ]+$') + +flist = {} +def init_flist(): + """map the cache folder names to the timestamps and sizes""" + global flist + try: + os.makedirs(CACHEDIR) + except: + pass + flist = {} + for x in os.listdir(CACHEDIR): + if len(x) != 2: + continue + for y in os.listdir(os.path.join(CACHEDIR, x)): + path = os.path.join(CACHEDIR, x, y) + size = 0 + for z in os.listdir(path): + size += os.stat(os.path.join(path, z)).st_size + flist[y] = [os.stat(path).st_mtime, size] + +lock = threading.Lock() +def make_clean(): + global lock + # there is no need to spend a lot of time cleaning + # so one thread cleans and the others return immediately + + if lock.acquire(0): + try: + make_clean_unsafe() + finally: + lock.release() + +def make_clean_unsafe(): + global MAX, flist + # and do some cleanup if necessary + total = sum([x[1] for x in flist.values()]) + + #print("and the total is %d" % total) + if total >= MAX: + print("Trimming the cache since %r > %r" % (total, MAX)) + lst = [(p, v[0], v[1]) for (p, v) in flist.items()] + lst.sort(key=lambda x: x[1]) # sort by timestamp + lst.reverse() + + while total >= MAX * CLEANRATIO: + (k, t, s) = lst.pop() + shutil.rmtree(os.path.join(CACHEDIR, k[:2], k)) + total -= s + del flist[k] + +def reset(): + global MAX, flist + tmp = list(flist.keys()) + lock.acquire() + try: + flist = {} + finally: + lock.release() + for x in CHARS: + for y in CHARS: + try: + os.rename(os.path.join(CACHEDIR, x+y), os.path.join(CACHEDIR, x+y+'_rm')) + except: + pass + for x in CHARS: + for y in CHARS: + try: + shutil.rmtree(os.path.join(CACHEDIR, x+y+'_rm')) + except: + pass + + +def update(ssig): + """update the cache folder and make some space if necessary""" + global flist + # D, T, S : directory, timestamp, size + + # update the contents with the last folder created + cnt = 0 + d = os.path.join(CACHEDIR, ssig[:2], ssig) + for k in os.listdir(d): + cnt += os.stat(os.path.join(d, k)).st_size + + # the same thread will usually push the next files + try: + flist[ssig][1] = cnt + except: + flist[ssig] = [os.stat(d).st_mtime, cnt] + +class req(SocketServer.StreamRequestHandler): + def handle(self): + while 1: + try: + self.process_command() + except Exception as e: + print(e) + break + + def process_command(self): + query = self.rfile.read(HEADER_SIZE).strip() + #print "%r" % query + if not re_valid_query.match(query): + raise ValueError('Invalid query %r' % query) + + query = query.strip().split(',') + + if query[0] == GET: + self.get_file(query[1:]) + elif query[0] == PUT: + self.put_file(query[1:]) + elif query[0] == LST: + self.lst_file(query[1:]) + elif query[0] == CLEAN: + make_clean() + elif query[0] == RESET: + reset() + elif query[0] == BYE: + raise ValueError('Exit') + else: + raise ValueError('Invalid query %r' % query) + + def lst_file(self, query): + response = '\n'.join(flist.keys()) + params = [str(len(response)),''] + self.wfile.write(','.join(params).ljust(HEADER_SIZE)) + self.wfile.write(response) + + def get_file(self, query): + # get a file from the cache if it exists, else return 0 + tmp = os.path.join(CACHEDIR, query[0][:2], query[0], query[1]) + fsize = -1 + try: + fsize = os.stat(tmp).st_size + except Exception: + #print(e) + pass + else: + # cache was useful, update the last access for LRU + d = os.path.join(CACHEDIR, query[0][:2], query[0]) + os.utime(d, None) + flist[query[0]][0] = os.stat(d).st_mtime + params = [str(fsize)] + self.wfile.write(','.join(params).ljust(HEADER_SIZE)) + + if fsize < 0: + #print("file not found in cache %s" % query[0]) + return + f = open(tmp, 'rb') + try: + cnt = 0 + while cnt < fsize: + r = f.read(BUF) + self.wfile.write(r) + cnt += len(r) + finally: + f.close() + + def put_file(self, query): + # add a file to the cache, the third parameter is the file size + (fd, filename) = tempfile.mkstemp(dir=CACHEDIR) + try: + size = int(query[2]) + cnt = 0 + while cnt < size: + r = self.rfile.read(min(BUF, size-cnt)) + if not r: + raise ValueError('Connection closed') + os.write(fd, r) + cnt += len(r) + finally: + os.close(fd) + + + d = os.path.join(CACHEDIR, query[0][:2], query[0]) + try: + os.stat(d) + except: + try: + # obvious race condition here + os.makedirs(d) + except OSError: + pass + try: + os.rename(filename, os.path.join(d, query[1])) + except OSError: + pass # folder removed by the user, or another thread is pushing the same file + try: + update(query[0]) + except OSError: + pass + make_clean() + +class req_only_get(req): + def put_file(self, query): + self.wfile.write('ERROR,'.ljust(HEADER_SIZE)) + raise ValueError('Put is forbidden') + +class req_only_put(req): + def get_file(self, query): + self.wfile.write('ERROR,'.ljust(HEADER_SIZE)) + raise ValueError('Get is forbidden') + +def create_server(conn, cls): + SocketServer.ThreadingTCPServer.allow_reuse_address = True + server = SocketServer.ThreadingTCPServer(CONN, req) + server.timeout = 60 # seconds + server.serve_forever() + +if __name__ == '__main__': + init_flist() + print("ready (%r dirs)" % len(flist.keys())) + create_server(CONN, req) + diff --git a/playground/netcache/wscript b/playground/netcache/wscript new file mode 100644 index 00000000..9c522403 --- /dev/null +++ b/playground/netcache/wscript @@ -0,0 +1,39 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2011 (ita) + +# the following two variables are used by the target "waf dist" +APPNAME='cc_test' + +srcdir = '.' +blddir = 'build' + +def set_options(opt): + #opt.tool_options('compiler_cc') + pass + +def configure(conf): + #conf.check_tool('compiler_cc') + conf.check_tool('gcc') + conf.check_tool('netcache_client') + +def build(bld): + + # 1. A simple program + bld.new_task_gen( + features = 'cc cprogram', + source = 'main.c', + target = 'test_c_app', + uselib_local = 'my_static_lib', + includes = '. /usr/include') + + # 2. A simple static lib + bld.new_task_gen( + features = 'cc cstaticlib', + source = 'test_staticlib.c', + target='my_static_lib') + + # if we had subfolder we would do the following + #bld.add_subdirs('src') + + diff --git a/playground/objcopy/main.c b/playground/objcopy/main.c new file mode 100644 index 00000000..a25e6296 --- /dev/null +++ b/playground/objcopy/main.c @@ -0,0 +1,5 @@ + +int main() +{ + return 0; +} diff --git a/playground/objcopy/wscript b/playground/objcopy/wscript new file mode 100644 index 00000000..b897a9f9 --- /dev/null +++ b/playground/objcopy/wscript @@ -0,0 +1,18 @@ +#! /usr/bin/env python + +def options(ctx): + ctx.load('compiler_c') + +def configure(ctx): + ctx.load('compiler_c') + ctx.load('objcopy') + +def build(ctx): + ctx(features = 'c cprogram objcopy', + source = 'main.c', + target = 'app', +# objcopy_bfdname='srec', +# objcopy_target = 'alternative-name', +# objcopy_install_path = '${PREFIX}/some_dir', +# objcopy_flags = '--strip-all' + ) diff --git a/playground/package/wscript b/playground/package/wscript new file mode 100644 index 00000000..09d2cbfe --- /dev/null +++ b/playground/package/wscript @@ -0,0 +1,49 @@ +#! /usr/bin/env python + +top = '.' + +def configure(conf): + pass + +def build(bld): + bld(rule='touch ${TGT}', target='foo.txt') + bld.install_files('${PREFIX}/bin', 'foo.txt') + +# --------------------------- + +import shutil + +from waflib import Build +class package_cls(Build.InstallContext): + cmd = 'package' + fun = 'build' + + def init_dirs(self, *k, **kw): + super(package_cls, self).init_dirs(*k, **kw) + self.tmp = self.bldnode.make_node('package_tmp_dir') + try: + shutil.rmtree(self.tmp.abspath()) + except: + pass + self.tmp.mkdir() + self.options.destdir = self.tmp.abspath() + + def execute(self, *k, **kw): + back = self.options.destdir + try: + super(package_cls, self).execute(*k, **kw) + finally: + self.options.destdir = back + + files = self.tmp.ant_glob('**') + + # we could mess with multiple inheritance but that's probably unnecessary + from waflib import Scripting + ctx = Scripting.Dist() + ctx.files = files + ctx.tar_prefix = '' + ctx.base_path = self.tmp + ctx.archive() + + shutil.rmtree(self.tmp.abspath()) + diff --git a/playground/parallel_cmd/main.c b/playground/parallel_cmd/main.c new file mode 100644 index 00000000..7748be1f --- /dev/null +++ b/playground/parallel_cmd/main.c @@ -0,0 +1,8 @@ +#include "config.h" +#include + +int main() { + printf("A=%d\n", A); + return 0; +} + diff --git a/playground/parallel_cmd/wscript b/playground/parallel_cmd/wscript new file mode 100644 index 00000000..99307b59 --- /dev/null +++ b/playground/parallel_cmd/wscript @@ -0,0 +1,82 @@ +#! /usr/bin/env python +# encoding: utf-8 + +VERSION='0.0.1' +APPNAME='cc_test' + +top = '.' +out = 'build' + +""" +Call "waf build_all_at_once" + +The commands will be executed in parallel, but the processes +will be limited by a bounded semaphore to avoid excessive usage. +""" + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.setenv('debug') + conf.load('compiler_c') + conf.env.DEFINES = ["A=1"] + + conf.setenv('release', env=conf.env.derive()) + conf.env.CFLAGS = ['-O2'] + conf.env.DEFINES = ["A=2"] + +def build(bld): + if not bld.variant: + bld.fatal('call "waf build_debug" or "waf build_release", and try "waf --help"') + bld.program(source='main.c', target='app', includes='.') + + +from waflib.Build import BuildContext, CleanContext, InstallContext, UninstallContext + +for x in 'debug release'.split(): + for y in (BuildContext, CleanContext, InstallContext, UninstallContext): + name = y.__name__.replace('Context','').lower() + class tmp(y): + cmd = name + '_' + x + variant = x + +def buildall(ctx): + import waflib.Options + waflib.Options.commands.extend(['build_debug', 'build_release']) + + +# The following defines a command which builds all the variants at once + +from waflib import Build, Task, Options, Utils, Scripting + +Scripting.default_cmd = "build_all_at_once" + +class buildall_ctx(Build.BuildContext): + cmd = fun = "build_all_at_once" + variant = "" + def compile(self): pass + +def build_all_at_once(ctx): + sem = Utils.threading.Semaphore(Options.options.jobs) + def with_sem(f): + def f2(self): + sem.acquire() + f(self) + sem.release() + return f2 + Task.TaskBase.process = with_sem(Task.TaskBase.process) + + threads = [] + for var in ctx.all_envs: + if var == '': continue + cls = type(Build.BuildContext)(var, (Build.BuildContext,), {'cmd': var, 'variant': var}) + bld = cls(top_dir=ctx.top_dir, out_dir=ctx.out_dir) + bld.targets = ctx.targets + t = Utils.threading.Thread() + t.run = bld.execute + threads.append(t) + + for t in threads: t.start() + for t in threads: t.join() + diff --git a/playground/pep8/wscript b/playground/pep8/wscript new file mode 100644 index 00000000..8ffa95a8 --- /dev/null +++ b/playground/pep8/wscript @@ -0,0 +1,26 @@ +#!/usr/bin/env python +# encoding: utf-8 + +from waflib.TaskGen import extension + +top = '.' +out = 'build' + + +@extension('.py', 'wscript') +def check_syntax(self, node): + self.create_task('Pep8', node) + + +def options(opt): + opt.load('pep8') + + +def configure(conf): + # warning after this comment + + conf.load('pep8') + + +def build(bld): + bld(source='wscript') diff --git a/playground/redirect/wscript b/playground/redirect/wscript new file mode 100644 index 00000000..afb8428d --- /dev/null +++ b/playground/redirect/wscript @@ -0,0 +1,41 @@ +#! /usr/bin/env python + +""" +Run: "waf configure clean build" + +The program "app" writes to both stdout and stderr, it is executed +directly or through another python process (just for the fun of it) +""" + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + + def write(task): + task.outputs[0].write(''' + #include + #include + int main() { + printf("hi\\n"); + perror("hello\\n"); + return 0; + }''') + + bld(rule=write, target='main.c') + bld.program(source='main.c', target='app') + bld(rule='${SRC[0].abspath()}', source='app') + + import sys + bld( + rule = '${tsk.generator.template % (tsk.generator.python, tsk.inputs[0].abspath())}', + template = """%s -c "import subprocess;subprocess.Popen(%r).wait()" """, + python = sys.executable, + source = 'app') + diff --git a/playground/relocate/c/a.c b/playground/relocate/c/a.c new file mode 100644 index 00000000..5bdc2c54 --- /dev/null +++ b/playground/relocate/c/a.c @@ -0,0 +1 @@ +int k = 332; diff --git a/playground/relocate/c/main.c b/playground/relocate/c/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/playground/relocate/c/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/playground/relocate/c/wscript b/playground/relocate/c/wscript new file mode 100644 index 00000000..63370c72 --- /dev/null +++ b/playground/relocate/c/wscript @@ -0,0 +1,11 @@ +#! /usr/bin/env python + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('relocation compiler_c') + +def build(bld): + bld.shlib(source='a.c', target='one') + bld.program(source='main.c', target='two', use='one') diff --git a/playground/relocate/run.sh b/playground/relocate/run.sh new file mode 100755 index 00000000..d4a42008 --- /dev/null +++ b/playground/relocate/run.sh @@ -0,0 +1,18 @@ +#! /bin/bash + +# make a copy of a waf directory with the same name +# +# the tool 'relocation' (waflib/extras) adds some information +# so that a full rebuild is not performed when the dir changes + +rm -rf tmp +mkdir tmp + +pushd c +waf configure build +popd +cp -R c tmp/c + +cd tmp/c +waf configure build + diff --git a/playground/sas/data/mydata.csv b/playground/sas/data/mydata.csv new file mode 100644 index 00000000..0618a90b --- /dev/null +++ b/playground/sas/data/mydata.csv @@ -0,0 +1,5 @@ +"id","var1","var2" +1,5,"foo" +2,8,"bar" +3,6,"gnat" +4,5,"gnu" diff --git a/playground/sas/src/sastest.sas b/playground/sas/src/sastest.sas new file mode 100644 index 00000000..bf89615f --- /dev/null +++ b/playground/sas/src/sastest.sas @@ -0,0 +1,23 @@ +proc contents data=sashelp.retail varnum; +run; + +proc univariate data=sashelp.retail; + var sales; +run; + + +%let rootFldr = C:\waf\demos\sas\; +filename mydata "&rootFldr.data\mydata.csv"; + +data mydata; + infile mydata delimiter=',' dsd firstobs=2; + input + id + var1 + var2 $ + ; +run; + +proc print data=mydata; +run; + diff --git a/playground/sas/wscript b/playground/sas/wscript new file mode 100644 index 00000000..7514f3b6 --- /dev/null +++ b/playground/sas/wscript @@ -0,0 +1,23 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Mark Coggeshall 2010 + +VERSION = '1.0.0' +APPNAME = 'sastest' + +top = '.' +out = 'build' + +def configure(ctx): + ctx.load('sas') + +def build(ctx): + sastester = ctx( + features = 'sas', + name = 'SAStest', + type = 'sas', + source = 'src/sastest.sas', + logdir = 'log', + lstdir = 'lst', + deps = ['data/mydata.csv'] + ) diff --git a/playground/scala/example/Foo.scala b/playground/scala/example/Foo.scala new file mode 100644 index 00000000..5d866e9c --- /dev/null +++ b/playground/scala/example/Foo.scala @@ -0,0 +1,8 @@ +package example; + +object Foo { + def main(args: Array[String]) { + println("Hello, Foo!") + } +} + diff --git a/playground/scala/wscript b/playground/scala/wscript new file mode 100644 index 00000000..3b32ac94 --- /dev/null +++ b/playground/scala/wscript @@ -0,0 +1,37 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +""" +scala example +""" + +VERSION = '0.0.1' +APPNAME = 'scala_test' + +top = '.' + +def configure(conf): + conf.load('scala') + try: + conf.load('java') + except: + pass + +def build(bld): + + bld(features = 'scalac', # there are scala files to process + srcdir = '.', # folder containing the sources to compile + outdir = 'out', # folder where to output the classes (in the build directory) + classpath = ['.', '..'], + name = 'scala_one' + ) + + if bld.env.JAR: + bld( + features = 'jar', + basedir = 'out', + destfile = 'filez.jar', + use = 'scala_one' + ) + diff --git a/playground/slow_qt/foo.cpp b/playground/slow_qt/foo.cpp new file mode 100644 index 00000000..7ef1b75d --- /dev/null +++ b/playground/slow_qt/foo.cpp @@ -0,0 +1,24 @@ +// Thomas Nagy, 2011 + +#include + +#include "foo.h" + +Foo::Foo() : QWidget(NULL) { + +} + +class FooP : public QWidget { + Q_OBJECT + signals: + void test(); + public: + FooP(); +}; + +FooP::FooP() { + +} + +#include "foo_cpp_moc.cpp" + diff --git a/playground/slow_qt/foo.h b/playground/slow_qt/foo.h new file mode 100644 index 00000000..1c54ef61 --- /dev/null +++ b/playground/slow_qt/foo.h @@ -0,0 +1,16 @@ +// Thomas Nagy, 2011 + +#ifndef _FOO +#define _FOO + +#include + +class Foo : public QWidget { + Q_OBJECT + signals: + void test(); + public: + Foo(); +}; + +#endif diff --git a/playground/slow_qt/main.cpp b/playground/slow_qt/main.cpp new file mode 100644 index 00000000..3b138d6c --- /dev/null +++ b/playground/slow_qt/main.cpp @@ -0,0 +1,8 @@ +// Thomas Nagy, 2011 + +#include + +int main(int argc, char *argv[]) +{ + return 0; +} diff --git a/playground/slow_qt/wscript b/playground/slow_qt/wscript new file mode 100644 index 00000000..7335fc50 --- /dev/null +++ b/playground/slow_qt/wscript @@ -0,0 +1,33 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005, 2011 (ita) + +""" +Including the moc files *is* the best practice (KDE), not doing it is easy, +but makes the compilations about 30-40% slower on average. + +This is the slow version that creates _moc.cpp files (a bad idea!) +""" + +VERSION='0.0.2' +APPNAME='qt4_test2' + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_cxx qt4') + +def configure(conf): + conf.load('compiler_cxx qt4') + conf.load('slow_qt4') + +def build(bld): + bld( + features = 'qt4 cxx cxxprogram', + uselib = 'QTCORE QTGUI QTOPENGL QTSVG', + source = 'main.cpp foo.cpp', + includes = '.', + target = 'window', + ) + diff --git a/playground/stale_files/wscript b/playground/stale_files/wscript new file mode 100644 index 00000000..da407456 --- /dev/null +++ b/playground/stale_files/wscript @@ -0,0 +1,74 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +""" +Add a pre-build hook to remove all build files +which do not have a corresponding target + +This can be used for example to remove the targets +that have changed name without performing +a full 'waf clean' + +Of course, it will only work if there are no dynamically generated +nodes/tasks, in which case the method will have to be modified +to exclude some folders for example. +""" + +VERSION='0.0.1' +APPNAME='cc_test' + +top = '.' + +def options(opt): + opt.load('compiler_c') + opt.load('gnu_dirs') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + import random + if random.randint(0, 1): + bld(rule='touch ${TGT}', target='foo.h') + else: + bld(rule='touch ${TGT}', target='bar.h') + +from waflib import Logs +from waflib.Runner import Parallel +old = Parallel.refill_task_list +def refill_task_list(self): + iit = old(self) + bld = self.bld + if bld.options.targets and bld.options.targets != '*': + return iit + + # execute only once + if getattr(self, 'clean', False): + return iit + self.clean = True + + # obtain the nodes to use during the build + nodes = [] + for i in range(len(bld.groups)): + tasks = bld.get_tasks_group(i) + for x in tasks: + try: + nodes.extend(x.outputs) + except: + pass + + # recursion over the nodes to find the stale files + def iter(node): + if getattr(node, 'children', []): + for x in node.children.values(): + iter(x) + else: + if not node in nodes: + Logs.warn("stale file found -> %s" % node.abspath()) + node.delete() + iter(bld.bldnode) + return iit + +Parallel.refill_task_list = refill_task_list + diff --git a/playground/strip/main.c b/playground/strip/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/playground/strip/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/playground/strip/strip.py b/playground/strip/strip.py new file mode 100644 index 00000000..3e7662bd --- /dev/null +++ b/playground/strip/strip.py @@ -0,0 +1,32 @@ +#! /usr/bin/env python + +""" +Strip a program/library after it is created. Use this tool as an example. + +Usage:: + + bld.program(features='strip', source='main.c', target='foo') + +By using:: + + @TaskGen.feature('cprogram', 'cxxprogram', 'fcprogram') + +It is possible to have all the C, C++ and Fortran programs stripped automatically +""" + +def configure(conf): + conf.find_program('strip') + +from waflib import Task, TaskGen +class strip(Task.Task): + run_str = '${STRIP} ${SRC}' + color = 'BLUE' + +@TaskGen.feature('strip') +@TaskGen.after('apply_link') +def add_strip_task(self): + try: + link_task = self.link_task + except: + return + tsk = self.create_task('strip', self.link_task.outputs[0]) diff --git a/playground/strip/wscript b/playground/strip/wscript new file mode 100644 index 00000000..772eb531 --- /dev/null +++ b/playground/strip/wscript @@ -0,0 +1,12 @@ +#! /usr/bin/env python + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + conf.load('strip', tooldir='.') + +def build(bld): + bld.program(features='strip', source='main.c', target='app') + diff --git a/playground/syms/a.c b/playground/syms/a.c new file mode 100644 index 00000000..50dd22dd --- /dev/null +++ b/playground/syms/a.c @@ -0,0 +1,6 @@ +int mylib_aa() { + return 32; +} +int internal_aa(){ + return 16; +} diff --git a/playground/syms/b.c b/playground/syms/b.c new file mode 100644 index 00000000..c6a5eae1 --- /dev/null +++ b/playground/syms/b.c @@ -0,0 +1,6 @@ +int mylib_bb() { + return 48; +} +int internal_bb() { + return 64; +} diff --git a/playground/syms/main.c b/playground/syms/main.c new file mode 100644 index 00000000..3e9a7b99 --- /dev/null +++ b/playground/syms/main.c @@ -0,0 +1,8 @@ +#include +int mylib_aa(); +int mylib_bb(); +int main() { + printf("aa() = %d", mylib_aa()); + printf("bb() = %d", mylib_bb()); + return 0; +} diff --git a/playground/syms/wscript b/playground/syms/wscript new file mode 100644 index 00000000..bc40f712 --- /dev/null +++ b/playground/syms/wscript @@ -0,0 +1,11 @@ +#! /usr/bin/env python + +def options(ctx): + ctx.load('compiler_c') + +def configure(ctx): + ctx.load('compiler_c syms') + +def build(ctx): + ctx(features='c cshlib syms', source='a.c b.c', export_symbols_regex='mylib_.*', target='testlib') + ctx(features='c cprogram', source='main.c', target='app', use='testlib') diff --git a/playground/test_fail/fail.cpp b/playground/test_fail/fail.cpp new file mode 100644 index 00000000..6787fdca --- /dev/null +++ b/playground/test_fail/fail.cpp @@ -0,0 +1,2 @@ +// must fail +1+1; diff --git a/playground/test_fail/success.cpp b/playground/test_fail/success.cpp new file mode 100644 index 00000000..19236684 --- /dev/null +++ b/playground/test_fail/success.cpp @@ -0,0 +1 @@ +int main() {return 0;} diff --git a/playground/test_fail/wscript b/playground/test_fail/wscript new file mode 100644 index 00000000..b8305d63 --- /dev/null +++ b/playground/test_fail/wscript @@ -0,0 +1,41 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2011 (ita) + +""" +Map a compilation failure to a successs status. People playing with C++ templates +might need this. +""" + +top = '.' +out = 'build' + +def options(opt): + opt.load('compiler_cxx') + +def configure(conf): + conf.load('compiler_cxx') + +def build(bld): + bld.objects(source='success.cpp', target='ok') + bld.objects(source='fail.cpp', target='fail', features='fail') + +################################################################## +# the feature 'fail' is defined below + +from waflib.Tools.cxx import cxx +class cxxfail(cxx): + def run(self): + ret = super(cxxfail, self).run() + self.outputs[0].write('just a simulation') + return not ret + +def one_more_mapping(self, node): + return self.create_compiled_task('cxxfail', node) + +from waflib.TaskGen import feature, before +@before('process_source') +@feature('fail') +def remap_failure_to_success(self): + self.mappings['.cpp'] = one_more_mapping + diff --git a/playground/test_times/wscript b/playground/test_times/wscript new file mode 100644 index 00000000..ad7d7568 --- /dev/null +++ b/playground/test_times/wscript @@ -0,0 +1,55 @@ +#! /usr/bin/env python + +""" +Create a few c programs, measure the execution times on "waf times" +""" + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + + # add a task to create a .c file + from waflib.TaskGen import feature, before_method + @feature('foo') + @before_method('process_source') + def add_one_c_file(self): + node = self.path.find_or_declare('main%d.c' % self.num) + self.create_task('write_file', [], node) + self.source = [node] # add the .c file to the list of source + + # write a slow main.c file + from waflib.Task import Task + class write_file(Task): + def run(self): + self.outputs[0].write(''' +#include +int main() { + int i; + char buf[50]; + for (i=0; i < %d; ++i) { + sprintf(buf, "%%l\\n", i); + } + return 0; +}''' % 2 ** (13 + self.generator.num)) + + for i in range(10): + # create a few programs + bld(features='foo c cprogram', num=i, target='app%d' % i) + + if bld.cmd == 'times': + # measure the execution times when calling "waf times" + def measure(ctx): + for x in range(10): + tg = ctx.get_tgen_by_name('app%d' % x) + name = tg.link_task.outputs[0].abspath() + ctx.exec_command('time %s' % name, shell=True) + bld.add_post_fun(measure) + +from waflib.Build import BuildContext +class times(BuildContext): + cmd = 'times' + diff --git a/playground/updates/wscript b/playground/updates/wscript new file mode 100644 index 00000000..335d6b31 --- /dev/null +++ b/playground/updates/wscript @@ -0,0 +1,15 @@ +#! /usr/bin/env python + +""" +An example to show how multiple tasks can modify the same file +""" + +def configure(conf): + pass + +def build(bld): + bld.env.A = "test (change me) " + bld(rule="echo '${A}' > ${TGT}", target='foo.txt', name='foo') + bld(rule='echo `cat ${SRC} ${SRC}` > ${SRC}', source='foo.txt', name='one') + bld(rule='echo `cat ${SRC} ${SRC}` > ${SRC}', source='foo.txt', after=['one'], name='two') + diff --git a/playground/use/basic/a.c b/playground/use/basic/a.c new file mode 100644 index 00000000..75b29341 --- /dev/null +++ b/playground/use/basic/a.c @@ -0,0 +1 @@ +int k = 32; diff --git a/playground/use/basic/b.c b/playground/use/basic/b.c new file mode 100644 index 00000000..766d18af --- /dev/null +++ b/playground/use/basic/b.c @@ -0,0 +1 @@ +int u = 48; diff --git a/playground/use/basic/main.c b/playground/use/basic/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/playground/use/basic/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/playground/use/cycle/a.c b/playground/use/cycle/a.c new file mode 100644 index 00000000..75b29341 --- /dev/null +++ b/playground/use/cycle/a.c @@ -0,0 +1 @@ +int k = 32; diff --git a/playground/use/cycle/b.c b/playground/use/cycle/b.c new file mode 100644 index 00000000..99dc0e66 --- /dev/null +++ b/playground/use/cycle/b.c @@ -0,0 +1 @@ +int l = 33; diff --git a/playground/use/cycle/wscript_build b/playground/use/cycle/wscript_build new file mode 100644 index 00000000..da645569 --- /dev/null +++ b/playground/use/cycle/wscript_build @@ -0,0 +1,18 @@ +#! /usr/bin/env python + +""" +Cycles in use should be detected (raise an error before the build starts) +""" + +bld.shlib( + source = 'a.c', + target = 'CYC_1', + use = 'CYC_2' +) + +bld.shlib( + source = 'b.c', + target = 'CYC_2', + use = 'CYC_1' +).post() + diff --git a/playground/use/fakelibs/main.c b/playground/use/fakelibs/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/playground/use/fakelibs/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/playground/use/fakelibs/wscript b/playground/use/fakelibs/wscript new file mode 100644 index 00000000..7463d141 --- /dev/null +++ b/playground/use/fakelibs/wscript @@ -0,0 +1,3 @@ +def build(bld): + bld.read_shlib('m', paths=['/usr/lib64']) + bld.program(source='main.c', target='app', use='m') diff --git a/playground/use/headers/a/a.h b/playground/use/headers/a/a.h new file mode 100644 index 00000000..75b29341 --- /dev/null +++ b/playground/use/headers/a/a.h @@ -0,0 +1 @@ +int k = 32; diff --git a/playground/use/headers/a/wscript_build b/playground/use/headers/a/wscript_build new file mode 100644 index 00000000..ee02553a --- /dev/null +++ b/playground/use/headers/a/wscript_build @@ -0,0 +1 @@ +bld(export_includes='.', name='afa') diff --git a/playground/use/headers/b/main.c b/playground/use/headers/b/main.c new file mode 100644 index 00000000..582261b9 --- /dev/null +++ b/playground/use/headers/b/main.c @@ -0,0 +1,4 @@ +#include "a.h" +int main() { + return 0; +} diff --git a/playground/use/headers/b/wscript_build b/playground/use/headers/b/wscript_build new file mode 100644 index 00000000..708ef3fd --- /dev/null +++ b/playground/use/headers/b/wscript_build @@ -0,0 +1 @@ +bld.program(source='main.c', target='afb', use='afa') diff --git a/playground/use/headers/wscript b/playground/use/headers/wscript new file mode 100644 index 00000000..a9a2fa23 --- /dev/null +++ b/playground/use/headers/wscript @@ -0,0 +1,2 @@ +def build(bld): + bld.recurse('a b') diff --git a/playground/use/objects/a-test.c b/playground/use/objects/a-test.c new file mode 100644 index 00000000..bb3514c6 --- /dev/null +++ b/playground/use/objects/a-test.c @@ -0,0 +1,5 @@ +#include "a.h" + +int main() { + a(); +} diff --git a/playground/use/objects/a.c b/playground/use/objects/a.c new file mode 100644 index 00000000..3e562a77 --- /dev/null +++ b/playground/use/objects/a.c @@ -0,0 +1,7 @@ +#include "stdio.h" +#include + +void a() { + gzFile f = gzopen("test.gz", "wb9"); +} + diff --git a/playground/use/objects/a.h b/playground/use/objects/a.h new file mode 100644 index 00000000..902b3b4e --- /dev/null +++ b/playground/use/objects/a.h @@ -0,0 +1,2 @@ +extern void a(); + diff --git a/playground/use/objects/c.c b/playground/use/objects/c.c new file mode 100644 index 00000000..8418fbf4 --- /dev/null +++ b/playground/use/objects/c.c @@ -0,0 +1,5 @@ +#include "b.h" + +int main() { + b(); +} diff --git a/playground/use/objects/wscript_build b/playground/use/objects/wscript_build new file mode 100644 index 00000000..ce02a852 --- /dev/null +++ b/playground/use/objects/wscript_build @@ -0,0 +1,35 @@ +#!/usr/bin/env python + +""" +when linked, object files should bring the libraries (uselib) they refer to +""" + +bld.env.LIB_Z = ['z'] + +bld.objects( + source = 'a.c', + target = 'A', + use = 'Z', + ) + + +bld.program( + source = 'a-test.c', + target = 'a-test', + use = 'A', + ) + +""" +bld.objects( + source = 'b.c', + target = 'B', + use = 'A', + ) + +bld.program( + source = 'c.c', + target = 'C', + use = 'B', + ) +""" + diff --git a/playground/use/order/a.c b/playground/use/order/a.c new file mode 100644 index 00000000..cb836b42 --- /dev/null +++ b/playground/use/order/a.c @@ -0,0 +1,6 @@ +#include "a.h" + +int aaa() { + return 33; +} + diff --git a/playground/use/order/a.h b/playground/use/order/a.h new file mode 100644 index 00000000..53420d7b --- /dev/null +++ b/playground/use/order/a.h @@ -0,0 +1 @@ +int aaa(); diff --git a/playground/use/order/b.c b/playground/use/order/b.c new file mode 100644 index 00000000..c3165fdb --- /dev/null +++ b/playground/use/order/b.c @@ -0,0 +1,7 @@ + +#include "a.h" + +int bbb() { + return aaa(); +} + diff --git a/playground/use/order/main.c b/playground/use/order/main.c new file mode 100644 index 00000000..893ea64d --- /dev/null +++ b/playground/use/order/main.c @@ -0,0 +1,4 @@ +int main() { + bbb(); + return 0; +} diff --git a/playground/use/order/wscript_build b/playground/use/order/wscript_build new file mode 100644 index 00000000..e5435b58 --- /dev/null +++ b/playground/use/order/wscript_build @@ -0,0 +1,22 @@ +#! /usr/bin/env python + +""" +* the static lib order should be preserved +* the static libraries must be given in reverse order +""" + +bld.stlib( + source = 'a.c', + target = 'ST_A' +) + +bld.stlib( + source = 'b.c', + target = 'ST_B' +) + +bld.program( + source = 'main.c', + target = 'static_app', + use = 'ST_B ST_A' # this is the correct order for ld +) diff --git a/playground/use/transitivity/a.c b/playground/use/transitivity/a.c new file mode 100644 index 00000000..81dd9653 --- /dev/null +++ b/playground/use/transitivity/a.c @@ -0,0 +1,8 @@ +#include "stdio.h" +#include +#include "a.h" + +void a() { + gzFile f = gzopen("test.gz", "wb9"); +} + diff --git a/playground/use/transitivity/b.c b/playground/use/transitivity/b.c new file mode 100644 index 00000000..0e1a7b2f --- /dev/null +++ b/playground/use/transitivity/b.c @@ -0,0 +1,6 @@ +#include "a.h" + +void b () { + a(); +} + diff --git a/playground/use/transitivity/b.h b/playground/use/transitivity/b.h new file mode 100644 index 00000000..79657996 --- /dev/null +++ b/playground/use/transitivity/b.h @@ -0,0 +1,2 @@ +extern void b(); + diff --git a/playground/use/transitivity/c.c b/playground/use/transitivity/c.c new file mode 100644 index 00000000..1c0c1889 --- /dev/null +++ b/playground/use/transitivity/c.c @@ -0,0 +1,6 @@ +#include "a.h" +#include "b.h" + +int main() { + b(); +} diff --git a/playground/use/transitivity/subdir/a.h b/playground/use/transitivity/subdir/a.h new file mode 100644 index 00000000..902b3b4e --- /dev/null +++ b/playground/use/transitivity/subdir/a.h @@ -0,0 +1,2 @@ +extern void a(); + diff --git a/playground/use/transitivity/wscript_build b/playground/use/transitivity/wscript_build new file mode 100644 index 00000000..f6fbbf3b --- /dev/null +++ b/playground/use/transitivity/wscript_build @@ -0,0 +1,28 @@ +#!/usr/bin/env python + +""" +the objects should carry uselib variables (includes, libraries to link against) +to other objects and to the final link +""" + +bld.env.LIB_Z = ['z'] +bld.env.INCLUDES_Z = ['subdir'] + +bld.objects( + source = 'a.c', + target = 'AA', + use = 'Z', + ) + +bld.objects( + source = 'b.c', + target = 'BB', + use = 'AA', + ) + +bld.program( + source = 'c.c', + target = 'CC', + use = 'BB', + ) + diff --git a/playground/use/wscript b/playground/use/wscript new file mode 100644 index 00000000..58746b01 --- /dev/null +++ b/playground/use/wscript @@ -0,0 +1,17 @@ +#! /usr/bin/env python + +def options(ctx): + ctx.load('compiler_c') + +def configure(ctx): + ctx.load('compiler_c') + +def build(ctx): + sub = ctx.recurse + sub('fakelibs') + sub('objects') + sub('order') + sub('transitivity') + sub('headers') + #sub('cycle') # this must raise an error during the build + diff --git a/tests/apis/wscript b/tests/apis/wscript new file mode 100755 index 00000000..c6b2c2dc --- /dev/null +++ b/tests/apis/wscript @@ -0,0 +1,142 @@ +#! /usr/bin/env python3.1 + +import os, shutil +from waflib import Node, Build, Utils, Logs + +def tt(msg, result, expected): + color = 'RED' + if result == expected: + color = 'GREEN' + Logs.pprint(color, msg.ljust(20) + " %r" % result) + +def exists(path): + try: + os.stat(path) + except: + return 'no' + else: + return 'yes' + +def remove(path): + try: + try: + os.listdir(path) + except: + os.unlink(path) + else: + shutil.rmtree(path) + except: + pass + +def create(path): + try: + os.makedirs(path) + except: + os.listdir(path) + +def configure(ctx): + pass + +def test(ctx): + bld = Build.BuildContext() + + + # 1. absdir is wrong, keep the drive letter + # 2. split should use os.sep + # 3. replace / in d1 from d2 + # 4. use os.sep in find_node + absdir = os.getcwd().split(os.sep) + + dd = bld.root.make_node(absdir) + pp = dd.parent + + + tt('dir printed', repr(dd), os.getcwd()) + tt('parent', repr(pp), os.path.split(os.getcwd())[0]) + tt('path_from', dd.path_from(pp), os.path.split(os.getcwd())[1]) + tt('path_from (reverse)', pp.path_from(dd), '..') + tt('same path', pp.path_from(pp), '.') + tt('same_root', bld.root.path_from(bld.root), '.') + + tt('root height', bld.root.height(), 0) + tt('self height', dd.height(), len(absdir)) + + d1 = dd.make_node(['a', 'b']) + d2 = dd.make_node(['c', 'd']) + tt('compare height', d1.height() - pp.height(), 3) + + tt('d1 from d2', d1.path_from(d2), '../../a/b'.replace('/', os.sep)) + tt('d2 from d1', d2.path_from(d1), '../../c/d'.replace('/', os.sep)) + + + d1.parent.delete() + + tt('d1.parent exists', exists(d1.parent.abspath()), 'no') + tt('d1 exists', exists(d1.abspath()), 'no') + + d1.parent.mkdir() + d1.parent.mkdir() + + tt('d1.parent exists', exists(d1.parent.abspath()), 'yes') + tt('d1 exists', exists(d1.abspath()), 'no') + + + d1.mkdir() + kp = d1.make_node(['ah-ha']) + ini = "this is a test" + kp.write(ini) + kp.chmod(493) + fin = kp.read() + + tt('read and write text', fin, ini) + + rama = ['1234', '5', '6', '7'] + remove('1234') + create('/'.join(rama)) + rr = dd.find_node(rama) + tt('find a node', repr(rr), os.sep.join([os.getcwd()]+rama)) + + remove('src/build') + create('src/build') + + ss = dd.find_node(['src']) + bb = dd.find_node(['src', 'build']) + bld.top_dir = ss.abspath() + bld.out_dir = bb.abspath() + bld.init_dirs() + + #remove(dd.abspath() + '/' +"xyz") + tt('find ["xyz"]', dd.find_node(['xyz']), None) + + tt('bld.srcnode is src', bld.srcnode.is_src(), True) + tt('bld.srcnode is bld', bld.srcnode.is_bld(), False) + tt('bld.bldnode is src', bld.bldnode.is_src(), False) + tt('bld.bldnode is bld', bld.bldnode.is_bld(), True) + tt('bld.root is bld', bld.root.is_bld(), False) + tt('bld.root is src', bld.root.is_src(), False) + nf = bld.srcnode.make_node('abc') + nf.write("aha") + nf.get_bld_sig() + tt('find_resource src/abc', bld.srcnode.find_resource(['abc']), nf) + tt('find_or_declare src/abc', bld.srcnode.find_or_declare(['abc']), nf) + tt('src.get_bld()', bld.srcnode.get_bld(), bld.bldnode) + tt('bld.get_src()', bld.bldnode.get_src(), bld.srcnode) + + stupid_build = bld.bldnode.make_node(['abc']) + stupid_build.write("heheh") + tt('find_or_declare src/abc', bld.srcnode.find_or_declare(['abc']), stupid_build) + tt('find_resource src/abc', bld.srcnode.find_resource(['abc']), stupid_build) + + bld = Build.BuildContext() + bld.top_dir = ss.abspath() + bld.out_dir = bb.abspath() + bld.init_dirs() + + create('src/a.txt') + create('src/b.txt') + nd = bld.srcnode.make_node('c.txt') + nd.write("test") + + tt("ant_glob ->", len(bld.srcnode.ant_glob('*.txt', flat=False)), 1) + #print("ant_glob src ->", bld.srcnode.ant_glob('*.txt')) + diff --git a/tests/defines/main.c b/tests/defines/main.c new file mode 100644 index 00000000..062ce0e4 --- /dev/null +++ b/tests/defines/main.c @@ -0,0 +1,12 @@ +int main() { + return 0; +} + +#ifndef REQ + #error required +#endif + +#ifdef NREQ + #error not required +#endif + diff --git a/tests/defines/wscript b/tests/defines/wscript new file mode 100644 index 00000000..950af6c5 --- /dev/null +++ b/tests/defines/wscript @@ -0,0 +1,59 @@ +#! /usr/bin/env python + +top = '.' +out = 'build' + +from waflib import Node, Build, Utils, Logs + +def tt(msg, expected, result): + color = 'RED' + if result == expected: + color = 'GREEN' + Logs.pprint(color, msg.ljust(20) + " %r" % result) + + +FRAG1 = '''#include \nint main() { printf(somestring); return 0; }\n''' +FRAG2 = '''#ifdef MMM\nint main() { return 0; }\n#endif\n''' + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + """the configuration should not raise any assert""" + + conf.load('compiler_c') + + conf.define('AAA', 1) + tt('define AAA', "['AAA=1']", repr(conf.env.DEFINES)) + + conf.undefine('AAA') + tt('undefine AAA', [], conf.env.DEFINES) + + conf.define('BB', 32) + conf.define('CC', 'test') + conf.define('inline', 'inline', quote=False) + + conf.write_config_header() + tt('empty config header', [], conf.env.DEFINES) + + conf.define('somestring', 'test') + conf.check(fragment=FRAG1, define_name='MMM', mandatory=False) + tt('is_defined(MMM)', True, conf.is_defined('MMM')) + conf.check(fragment=FRAG2, define_name='NNN', mandatory=False) + tt('defines are propagated to tests', True, conf.is_defined('NNN')) + + conf.undefine('AAA') + conf.write_config_header('config.2.h', remove=False) + tt('defines are not removed', 3, len(conf.env.DEFINES)) + + tt('have_define', 'HAVE_FOO', conf.have_define('FOO')) + + conf.env.DEFINES = [] + conf.define('AAA', 1) + conf.define('AAA', 2) + tt('AAA', '2', conf.get_define('AAA')) + tt('defines count', 1, len(conf.env.DEFINES)) + +def build(bld): + pass + diff --git a/tests/diff_top_src/main.c b/tests/diff_top_src/main.c new file mode 100644 index 00000000..faa3427a --- /dev/null +++ b/tests/diff_top_src/main.c @@ -0,0 +1,6 @@ +#include + +int main() { + printf("Howdy!\n"); + return 0; +} diff --git a/tests/different_top/wscript b/tests/different_top/wscript new file mode 100644 index 00000000..5ae63782 --- /dev/null +++ b/tests/different_top/wscript @@ -0,0 +1,18 @@ +#! /usr/bin/env python + +top = '../diff_top_src' +out = 'build' + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + bld.program( + source = bld.srcnode.ant_glob('*.c'), + target = 'main', + ) + +# vim:ft=python:noet diff --git a/tests/headers/main.c b/tests/headers/main.c new file mode 100644 index 00000000..332b4d1a --- /dev/null +++ b/tests/headers/main.c @@ -0,0 +1,2 @@ +//#include "bar.h" +int main() { return 0; } diff --git a/tests/headers/wscript b/tests/headers/wscript new file mode 100644 index 00000000..743a7853 --- /dev/null +++ b/tests/headers/wscript @@ -0,0 +1,46 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2011 (ita) + +""" +One test for headers +Just call 'waf clean test' +""" + +VERSION='0.0.1' +APPNAME='cc_test' + +top = '.' + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c ') + +def build(bld): + bld.program(source='main.c', includes='. uhu', target='bar') + +# --------------------------------------------------------- + +import shutil +from waflib import Options + +def test(ctx): + Options.commands += ['test1', 'build', 'test2', 'build'] + +def test1(ctx): + d = ctx.path.make_node('uhu') + d.mkdir() + + bar = d.make_node('bar.h') + bar.write('int k = 32;\n') + main = ctx.path.make_node('main.c') + main.write('#include "bar.h"\nint main() { return 0; }\n') + +def test2(ctx): + d = ctx.path.find_node('uhu') + if d: + shutil.rmtree(d.abspath()) + main = ctx.path.make_node('main.c') + main.write('//#include "bar.h"\nint main() { return 0; }\n') diff --git a/tests/nodes/README.txt b/tests/nodes/README.txt new file mode 100644 index 00000000..353e4612 --- /dev/null +++ b/tests/nodes/README.txt @@ -0,0 +1,27 @@ +Node concepts, without a particular order +----------------------------------------- + +when finding a source dir: + 1. eliminate src files that do not exist anymore + 2. look in os.listdir or try os.stat + +when declaring a build file: + 1. construct the folder structure in the build dir to avoid the manual mkdirs + 2. create the nodes for the src dir too if the folders exist + +when looking for a resource: + 1. return either a source file or a build file, the build file will have the priority + 2. find the intermediate source and build nodes + +The calls to os.listdir should be cached somehowa cache for os.listdir + +Eliminate source dir nodes when they do not exist anymore + +When build nodes do not exist, do not delete them but remove the signatures + +Using the testcase +------------------ + +Execute +../waf distclean configure build + diff --git a/tests/nodes/main.c b/tests/nodes/main.c new file mode 100644 index 00000000..cb3f7482 --- /dev/null +++ b/tests/nodes/main.c @@ -0,0 +1,3 @@ +int main() { + return 0; +} diff --git a/tests/nodes/src/src.txt b/tests/nodes/src/src.txt new file mode 100644 index 00000000..0ddf2bae --- /dev/null +++ b/tests/nodes/src/src.txt @@ -0,0 +1 @@ +i diff --git a/tests/nodes/wscript b/tests/nodes/wscript new file mode 100644 index 00000000..c2228ba8 --- /dev/null +++ b/tests/nodes/wscript @@ -0,0 +1,68 @@ +#! /usr/bin/env python3 + +top = '.' +out = 'bin' + +def options(opt): + opt.load('compiler_c') + +def configure(conf): + conf.load('compiler_c') + +def build(bld): + + bld(rule='echo hi') + bld(rule='touch ${TGT}', target='foo.txt') + + + bld(rule='touch ${TGT}', target='aaa') + t = bld(rule='touch ${TGT}', target='bbb', source='aaa') + t = bld(rule='touch ${TGT}', target='ccc', source='bbb') + t = bld(rule='touch ${TGT}', target='ddd', source='ccc') + t.create_task('foo') + + #print( 'path from srcnode', bld.path.find_or_declare('aaa').path_from(bld.bldnode) ) + + bld.install_files('/tmp/bar', 'wscript') + + bld(features='c cprogram', source='main.c', target='app') + + +def init(self): + print('init') + +def shutdown(self): + print('shutdown') + +def bar(ctx): + print("command ok") + +from waflib import Task + +# TODO update_outputs is a decorator too .. now +@Task.always_run +class foo_task(Task.Task): + def run(self): + print("running foo") + +print("classes", Task.classes) + +#----------------------------------------------- +# variant example below +# call "../waf debug" +# + +import os +from waflib import Build, Options + +class debug_context(Build.BuildContext): + cmd = 'debug' + fun = 'build' + variant = 'test' + def __init__(self, start=None): + Build.BuildContext.__init__(self, start=start) + +# looks evil +#def debug(bld): +# build(bld) + diff --git a/utils/genbench.py b/utils/genbench.py new file mode 100755 index 00000000..972a9be3 --- /dev/null +++ b/utils/genbench.py @@ -0,0 +1,344 @@ +#!/usr/bin/python +# encoding: utf-8 + +import sys +import os.path +from random import Random +random = Random(0) # initialise with seed to have reproductible benches + +# for example: ./genbench.py /tmp/build 50 100 15 5 + +HELP_USAGE = """Usage: generate_libs.py root libs classes internal external. + root - Root directory where to create libs. + libs - Number of libraries (libraries only depend on those with smaller numbers) + classes - Number of classes per library + internal - Number of includes per file referring to that same library + external - Number of includes per file pointing to other libraries + +To try the waf part, do: +waf configure build -p -j5 + +To test the autotools part, do: +touch README AUTHORS NEWS ChangeLog && +autoreconf --install --symlink --verbose && +mkdir autotools-build-dir && +cd autotools-build-dir && +../configure --disable-shared CXXFLAGS=-Wall && +time make -j4 --silent && +time make -j4 --silent +""" + +def lib_name(i): + return "lib_" + str(i) + +def createHeader(name): + filename = name + ".h" + handle = file(filename, "w" ) + + guard = name + '_h_' + handle.write ('#ifndef ' + guard + '\n'); + handle.write ('#define ' + guard + '\n\n'); + + handle.write ('class ' + name + ' {\n'); + handle.write ('public:\n'); + handle.write (' ' + name + '();\n'); + handle.write (' ~' + name + '();\n'); + handle.write ('};\n\n'); + + handle.write ('#endif\n'); + + +def createCPP(name, lib_number, classes_per_lib, internal_includes, external_includes): + filename = name + ".cpp" + handle = file(filename, "w" ) + + header= name + ".h" + handle.write ('#include "' + header + '"\n'); + + includes = random.sample(range(classes_per_lib), internal_includes) + for i in includes: + handle.write ('#include "class_' + str(i) + '.h"\n') + + if (lib_number > 0): + includes = random.sample(range(classes_per_lib), external_includes) + lib_list = range(lib_number) + for i in includes: + libname = 'lib_' + str(random.choice(lib_list)) + handle.write ('#include <' + libname + '/' + 'class_' + str(i) + '.h>\n') + + handle.write ('\n'); + handle.write (name + '::' + name + '() {}\n'); + handle.write (name + '::~' + name + '() {}\n'); + + +def createSConscript(lib_number, classes): + handle = file("SConscript", "w"); + handle.write("Import('env')\n") + handle.write('list = Split("""\n'); + for i in range(classes): + handle.write(' class_' + str(i) + '.cpp\n') + handle.write(' """)\n\n') + handle.write('env.StaticLibrary("lib_' + str(lib_number) + '", list)\n\n') + +def createLibCMakeLists(lib_number, classes): + handle = file("CMakeLists.txt", "w") + handle.write("""add_library(lib_%s STATIC %s)\n""" % (str(lib_number), ' '.join(('class_%s' % str(i) for i in range(classes))))) + +def createLibMakefile(lib_number, classes): + handle = file("Makefile", "w"); + handle.write ("""COMPILER = g++ +INC = -I.. +CCFLAGS = -g -Wall $(INC) +ARCHIVE = ar +DEPEND = makedepend +.SUFFIXES: .o .cpp + +""") + handle.write ("lib = lib_" + str(lib_number) + ".a\n") + handle.write ("src = \\\n") + for i in range(classes): + handle.write('class_' + str(i) + '.cpp \\\n') + handle.write (""" + +objects = $(patsubst %.cpp, %.o, $(src)) + +all: depend $(lib) + +$(lib): $(objects) + $(ARCHIVE) cr $@ $^ + touch $@ + +.cpp.o: + $(COMPILER) $(CCFLAGS) -c $< + +clean: + @rm $(objects) $(lib) 2> /dev/null + +depend: + @$(DEPEND) $(INC) $(src) + +""") + +def createLibJamFile(lib_number, classes): + handle = file("Jamfile", "w") + handle.write ("SubDir TOP lib_" + str(lib_number) + " ;\n\n") + handle.write ("SubDirHdrs $(INCLUDES) ;\n\n") + handle.write ("Library lib_" + str(lib_number) + " :\n") + for i in range(classes): + handle.write(' class_' + str(i) + '.cpp\n') + handle.write (' ;\n') + +def createVCProjFile(lib_number, classes): + handle = file("lib_" + str(lib_number) + ".vcproj", "w") + handle.write(""" + + + + + + + + + + + + + + +""") + + for i in range(classes): + handle.write(' \n') + + handle.write(""" + + + + +""") + +def createLibrary(lib_number, classes, internal_includes, external_includes): + name = "lib_" + str(lib_number) + setDir(name) + for i in range(classes): + classname = "class_" + str(i) + createHeader(classname) + createCPP(classname, lib_number, classes, internal_includes, external_includes) + createSConscript(lib_number, classes) + createLibCMakeLists(lib_number, classes) + createLibMakefile(lib_number, classes) + createAutotools(lib_number, classes) + + os.chdir("..") + +def createCMakeLists(libs): + handle = file("CMakeLists.txt", "w") + handle.write("""project('profiling-test') +cmake_minimum_required(VERSION 2.8) + +include_directories(${CMAKE_SOURCE_DIR}) +""") + + for i in range(libs): + handle.write("""add_subdirectory(lib_%s)\n""" % str(i)) + +def createSConstruct(libs): + handle = file("SConstruct", "w"); + handle.write("""env = Environment(CPPFLAGS=['-Wall'], CPPDEFINES=['LINUX'], CPPPATH=[Dir('#')])\n""") + handle.write("""env.Decider('timestamp-newer')\n""") + handle.write("""env.SetOption('implicit_cache', True)\n""") + handle.write("""env.SourceCode('.', None)\n""") + + for i in range(libs): + handle.write("""env.SConscript("lib_%s/SConscript", exports=['env'])\n""" % str(i)) + +def createFullMakefile(libs): + handle = file("Makefile", "w") + + handle.write('subdirs = \\\n') + for i in range(libs): + handle.write('lib_' + str(i) + '\\\n') + handle.write(""" + +all: $(subdirs) + @for i in $(subdirs); do \ + $(MAKE) -C $$i all; done + +clean: + @for i in $(subdirs); do \ + (cd $$i; $(MAKE) clean); done + +depend: + @for i in $(subdirs); do \ + (cd $$i; $(MAKE) depend); done +""") + +def createFullJamfile(libs): + handle = file("Jamfile", "w") + handle.write ("SubDir TOP ;\n\n") + + for i in range(libs): + handle.write('SubInclude TOP ' + lib_name(i) + ' ;\n') + + handle = file("Jamrules", "w") + handle.write('INCLUDES = $(TOP) ;\n') + +WT = """#! /usr/bin/env python +# encoding: utf-8 + +VERSION = '0.0.2' +APPNAME = 'build_bench' +top = '.' +out = 'out' + +def configure(conf): + conf.load('g++') + +def build(bld): + for i in range(%d): + filez = ' '.join(['lib_%%d/class_%%d.cpp' %% (i, j) for j in range(%d)]) + bld.stlib( + source = filez, + target = 'lib_%%d' %% i, + includes = '.', # include the top-level + ) +""" + +def createWtop(libs, classes): + f = open('wscript', 'w') + f.write(WT % (libs, classes)) + f.close() + +def createFullSolution(libs): + handle = file("solution.sln", "w") + handle.write("Microsoft Visual Studio Solution File, Format Version 8.00\n") + + for i in range(libs): + project_name = lib_name(i) + '\\' + lib_name(i) + '.vcproj' + handle.write('Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "' + lib_name(i) + + '", "' + project_name + '", "{CF495178-8865-4D20-939D-AAA' + str(i) + '}"\n') + handle.write('EndProject\n') + +def createAutotoolsTop(libs): + handle = file("configure.ac", "w") + handle.write('''\ +AC_INIT([bench], [1.0.0]) +AC_CONFIG_AUX_DIR([autotools-aux]) +AM_INIT_AUTOMAKE([subdir-objects nostdinc no-define tar-pax dist-bzip2]) +AM_PROG_LIBTOOL +AC_CONFIG_HEADERS([config.h]) +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT +''') + + handle = file("Makefile.am", "w") + handle.write('''\ +AM_CPPFLAGS = -I$(srcdir) +lib_LTLIBRARIES = +''') + for i in range(libs): handle.write('include lib_%s/Makefile.am\n' % str(i)) + +def createAutotools(lib_number, classes): + + handle = file("Makefile.am", "w") + handle.write('''\ +lib_LTLIBRARIES += lib%s.la +lib%s_la_SOURCES =''' % (str(lib_number), str(lib_number))) + for i in range(classes): handle.write(' lib_%s/class_%s.cpp' % (str(lib_number), str(i))) + handle.write('\n') + +def setDir(dir): + if (not os.path.exists(dir)): + os.mkdir(dir) + os.chdir(dir) + +def main(argv): + if len(argv) != 6: + print HELP_USAGE + return + + root_dir = argv[1] + libs = int(argv[2]) + classes = int(argv[3]) + internal_includes = int(argv[4]) + external_includes = int(argv[5]) + + setDir(root_dir) + for i in range(libs): + createLibrary(i, classes, internal_includes, external_includes) + + createSConstruct(libs) + createCMakeLists(libs) + createFullMakefile(libs) + createWtop(libs, classes) + createAutotoolsTop(libs) + +if __name__ == "__main__": + main( sys.argv ) + + diff --git a/utils/launcher/LICENSE b/utils/launcher/LICENSE new file mode 100644 index 00000000..a4147d2b --- /dev/null +++ b/utils/launcher/LICENSE @@ -0,0 +1,25 @@ +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. diff --git a/utils/launcher/README.md b/utils/launcher/README.md new file mode 100644 index 00000000..62c3787b --- /dev/null +++ b/utils/launcher/README.md @@ -0,0 +1,42 @@ +#Waf-launcher +This is a simple wrapper for the +[waf build system](http://code.google.com/p/waf/) + +Since many windows users does not have python installed by default, +the exe file from this project can be included along with the copy of +waf to wrap waf to install python on demand. + +The requirements is only .Net 2.0 whics is either Windows Server 2003 R2 +and up, or Windows Vista and up. There is a good chance it is installed +in many OEM installs of Windows XP as well + +##How to compile +use either waf or monodevelop (wscript and project files is in the repository) + +##How to use + +Assume we have a repository, myrepo, where wscript and waf already exists. + + myrepo/waf + myrepo/wscript + +now copy waf-launcher/bin/Release/waf.exe to the repository + + myrepo/waf.exe + +Linux users can continue to use `./waf`, just like +Windows users can continue to use `python waf` + +For Windows users there is now a second way to invoke waf by writing +either `waf.exe` or simply just `waf`. + +When the Windows users does that the following happens: + +1. It tries "python waf" +2. If 1 failed it tries "C:\python27\python.exe waf" +3. If 2 failed it ask for permission to install python +4. If pemission is given it tries to install python silently +5. step 1 and 2 it done again +6. If this still fails we exit with an error + +Any arguments to `waf.exe` is passed on to `waf` diff --git a/utils/launcher/waf-launcher.sln b/utils/launcher/waf-launcher.sln new file mode 100644 index 00000000..71bf3a9b --- /dev/null +++ b/utils/launcher/waf-launcher.sln @@ -0,0 +1,21 @@ + +Microsoft Visual Studio Solution File, Format Version 10.00 +# Visual Studio 2008 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "waf-launcher", "waf-launcher\waf-launcher.csproj", "{63E79792-BE8F-4AE5-9A99-99B1AE4D96E3}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|x86 = Debug|x86 + Release|x86 = Release|x86 + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {63E79792-BE8F-4AE5-9A99-99B1AE4D96E3}.Debug|x86.ActiveCfg = Debug|x86 + {63E79792-BE8F-4AE5-9A99-99B1AE4D96E3}.Debug|x86.Build.0 = Debug|x86 + {63E79792-BE8F-4AE5-9A99-99B1AE4D96E3}.Release|x86.ActiveCfg = Release|x86 + {63E79792-BE8F-4AE5-9A99-99B1AE4D96E3}.Release|x86.Build.0 = Release|x86 + EndGlobalSection + GlobalSection(MonoDevelopProperties) = preSolution + StartupItem = waf-launcher\waf-launcher.csproj + version = 1.0 + EndGlobalSection +EndGlobal diff --git a/utils/launcher/waf-launcher/AssemblyInfo.cs b/utils/launcher/waf-launcher/AssemblyInfo.cs new file mode 100644 index 00000000..328d4c8f --- /dev/null +++ b/utils/launcher/waf-launcher/AssemblyInfo.cs @@ -0,0 +1,27 @@ +using System.Reflection; +using System.Runtime.CompilerServices; + +// Information about this assembly is defined by the following attributes. +// Change them to the values specific to your project. + +[assembly: AssemblyTitle("waf-launcher")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// The assembly version has the format "{Major}.{Minor}.{Build}.{Revision}". +// The form "{Major}.{Minor}.*" will automatically update the build and revision, +// and "{Major}.{Minor}.{Build}.*" will update just the revision. + +[assembly: AssemblyVersion("1.0.*")] + +// The following attributes are used to specify the signing key for the assembly, +// if desired. See the Mono documentation for more information about signing. + +//[assembly: AssemblyDelaySign(false)] +//[assembly: AssemblyKeyFile("")] + diff --git a/utils/launcher/waf-launcher/Main.cs b/utils/launcher/waf-launcher/Main.cs new file mode 100644 index 00000000..780254f7 --- /dev/null +++ b/utils/launcher/waf-launcher/Main.cs @@ -0,0 +1,99 @@ +using System; +using System.Diagnostics; + +namespace waflauncher +{ + class MainClass + { + public static System.Diagnostics.Process exec(string command,params string[] args) { + String argstring = String.Join(" ",args); + System.Diagnostics.ProcessStartInfo startinfo = new System.Diagnostics.ProcessStartInfo(command,argstring); + startinfo.UseShellExecute = false; + System.Diagnostics.Process p; + try { + p = Process.Start(startinfo); + } catch (System.ComponentModel.Win32Exception){ + return null; + } + p.WaitForExit(); + return p; + } + + public static int Main (string[] args) + { + //I run waf and if not succesful we try on-the-fly install of python + if(!runWaf(args)){ + //but first we ask the user if it's okay to install software on their computer + if(mayInstall()){ + //I install python and try running waf yet another time + installPython(); + if(!runWaf(args)){ + //If it still fails something has gone horrible wrong + Console.WriteLine("Python not fully working"); + return 1; + } + } else { + Console.WriteLine("Not automatically installing Python"); + Console.WriteLine("Please download and install http://www.python.org/ftp/python/2.7.1/python-2.7.1.msi"); + Console.WriteLine("or if you have python installed make sure it is on %PATH%"); + Console.WriteLine("or run this command again and answer yes"); + } + } + return 0; + } + + public static bool mayInstall() { + Console.Write("Download and install python [Y/n]? "); + ConsoleKeyInfo a = Console.ReadKey(); + Console.WriteLine(); + switch(a.KeyChar){ + case 'Y': + case 'y': + case '\n': + case '\r': + return true; + //If unsure default to not doing it + default: + return false; + } + } + + public static String getwafDir(){ + //This changes the current directory to the place where the exe exists + System.Reflection.Assembly a = System.Reflection.Assembly.GetEntryAssembly(); + String path = System.IO.Path.GetDirectoryName(a.Location); + return path + System.IO.Path.DirectorySeparatorChar; + } + + public static bool runWaf(string[] args){ + Process p = exec("python", getwafDir() + "waf", String.Join(" ",args)); + //If command could be execeuted return true + if (p != null) return true; + //If not try with the direct path to the default installation which is where installPython() will install it to + //This is done since the %PATH% variable might not be setup to include python + + List versions = new List() { "27", "32", "26", "31", "25", "30" }; + foreach (String v in versions) { + p = exec("C:\\Python"+v+"\\python.exe", "waf", String.Join(" ",args)); + if (p != null) return true; + } + return false; + } + + public static void installPython(){ + //Make a filename to download python to + String filename = System.IO.Path.GetTempPath() + Char.ToString(System.IO.Path.DirectorySeparatorChar) + "python-2.7.1.msi"; + + System.Net.WebClient web = new System.Net.WebClient(); + Console.WriteLine ("Downloading python 2.7"); + web.DownloadFile("http://www.python.org/ftp/python/2.7.1/python-2.7.1.msi",filename); + Console.WriteLine ("python2.7 downloaded to " + filename); + + Console.WriteLine ("Installing python"); + //filename must be qouted or else msiexec will fail + exec("msiexec","/qn","/i","\"" +filename + "\""); + Console.WriteLine ("Python is now installed"); + } + } +} + diff --git a/utils/launcher/waf-launcher/waf-launcher.csproj b/utils/launcher/waf-launcher/waf-launcher.csproj new file mode 100644 index 00000000..9fcca85f --- /dev/null +++ b/utils/launcher/waf-launcher/waf-launcher.csproj @@ -0,0 +1,50 @@ + + + + Debug + x86 + 9.0.21022 + 2.0 + {63E79792-BE8F-4AE5-9A99-99B1AE4D96E3} + Exe + waflauncher + waf + v3.5 + 1.0 + + + true + full + false + bin\Debug + DEBUG + prompt + 4 + x86 + true + + + none + false + bin\Release + prompt + 4 + x86 + true + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/utils/launcher/wscript b/utils/launcher/wscript new file mode 100644 index 00000000..0c55eeed --- /dev/null +++ b/utils/launcher/wscript @@ -0,0 +1,7 @@ +out = 'waf-launcher/bin/Release' + +def configure(config): + config.load('cs') + +def build(bld): + bld(features='cs',source='waf-launcher/Main.cs', type='exe',gen='waf.exe') diff --git a/utils/waf.bat b/utils/waf.bat new file mode 100644 index 00000000..9ca79c08 --- /dev/null +++ b/utils/waf.bat @@ -0,0 +1,100 @@ +@echo off + +rem from issue #964 + +Setlocal EnableDelayedExpansion + +rem Check Windows Version +set TOKEN=tokens=3* +ver | findstr /i "5\.0\." > nul +if %ERRORLEVEL% EQU 0 SET TOKEN=tokens=3* +ver | findstr /i "5\.1\." > nul +if %ERRORLEVEL% EQU 0 SET TOKEN=tokens=3* +ver | findstr /i "5\.2\." > nul +if %ERRORLEVEL% EQU 0 SET TOKEN=tokens=3* +ver | findstr /i "6\.0\." > nul +if %ERRORLEVEL% EQU 0 SET TOKEN=tokens=2* +ver | findstr /i "6\.1\." > nul +if %ERRORLEVEL% EQU 0 SET TOKEN=tokens=2* + +rem Start calculating PYTHON and PYTHON_DIR +set PYTHON= +set PYTHON_DIR= + +Setlocal EnableDelayedExpansion + +set PYTHON_DIR_OK=FALSE +set REGPATH= + +for %%i in (3.7 3.6 3.5 3.4 3.3 3.2 3.1 3.0 2.7 2.6 2.5 2.4 2.3) do ( +for %%j in (HKCU HKLM) do ( +for %%k in (SOFTWARE\Wow6432Node SOFTWARE) do ( +for %%l in (Python\PythonCore IronPython) do ( +set REG_PYTHON_EXE=python.exe +if "%%l"=="IronPython" ( +set REG_PYTHON_EXE=ipy.exe +) + +@echo on + +set REGPATH=%%j\%%k\%%l\%%i\InstallPath +rem @echo Regpath !REGPATH! +REG QUERY "!REGPATH!" /ve 1>nul 2>nul +if !ERRORLEVEL! equ 0 ( + for /F "%TOKEN% delims= " %%A IN ('REG QUERY "!REGPATH!" /ve') do @set REG_PYTHON_DIR=%%B + if exist !REG_PYTHON_DIR! ( + set REG_PYTHON=!REG_PYTHON_DIR!!REG_PYTHON_EXE! + rem set PYTHON_DIR_OK=TRUE + if "!PYTHON_DIR_OK!"=="FALSE" ( + set PYTHON_DIR=!REG_PYTHON_DIR! + set PYTHON=!REG_PYTHON! + set PYTHON_DIR_OK=TRUE + ) + + rem set PYTHON_DIR_OK=FALSE + rem @echo Find !REG_PYTHON! + rem goto finished + ) +) + +echo off + +) +rem for l +) +rem for k +) +rem for j +) +rem for i + + + +:finished + +Endlocal & SET PYTHON_DIR=%PYTHON_DIR% & SET PYTHON=%PYTHON% + +if "%PYTHON_DIR%" == "" ( +rem @echo No Python dir +set PYTHON=python +goto running +) + +rem @echo %PYTHON_DIR% + +if "%PYTHON%" == "" ( +rem @echo No Python +set PYTHON=python +goto running +) + +set PYTHON_INCLUDE=%PYTHON_DIR%include +set PYTHON_LIB=%PYTHON_DIR%libs\python27.lib +set PATH=%PYTHON_DIR%;%PYTHON_DIR%Scripts;%PYTHON_DIR%Tools\Scripts;%PATH% + +:running + +@echo Using %PYTHON% + +"%PYTHON%" -x "%~dp0waf" %* & Endlocal & exit /b + diff --git a/waf-light b/waf-light new file mode 100755 index 00000000..9064a928 --- /dev/null +++ b/waf-light @@ -0,0 +1,164 @@ +#!/usr/bin/env python +# encoding: ISO8859-1 +# Thomas Nagy, 2005-2011 + +""" +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +""" + +import os, sys + +VERSION="1.6.8" +REVISION="x" +INSTALL="x" +C1='x' +C2='x' +cwd = os.getcwd() +join = os.path.join + +if sys.hexversion<0x206000f: + raise ImportError('Python >= 2.6 is required to create the waf file') + +WAF='waf' +def b(x): + return x +if sys.hexversion>0x300000f: + WAF='waf3' + def b(x): + return x.encode() + +def err(m): + print(('\033[91mError: %s\033[0m' % m)) + sys.exit(1) + +def unpack_wafdir(dir): + f = open(sys.argv[0],'rb') + c = 'corrupt archive (%d)' + while 1: + line = f.readline() + if not line: err('run waf-light from a folder containing waflib') + if line == b('#==>\n'): + txt = f.readline() + if not txt: err(c % 1) + if f.readline() != b('#<==\n'): err(c % 2) + break + if not txt: err(c % 3) + txt = txt[1:-1].replace(b(C1), b('\n')).replace(b(C2), b('\r')) + + import shutil, tarfile + try: shutil.rmtree(dir) + except OSError: pass + try: + for x in ['Tools', 'extras']: + os.makedirs(join(dir, 'waflib', x)) + except OSError: + err("Cannot unpack waf lib into %s\nMove waf into a writeable directory" % dir) + + os.chdir(dir) + tmp = 't.bz2' + t = open(tmp,'wb') + t.write(txt) + t.close() + + try: + t = tarfile.open(tmp) + except: + try: + os.system('bunzip2 t.bz2') + t = tarfile.open('t') + tmp = 't' + except: + os.chdir(cwd) + try: shutil.rmtree(dir) + except OSError: pass + err("Waf cannot be unpacked, check that bzip2 support is present") + + for x in t: t.extract(x) + t.close() + + for x in ['Tools', 'extras']: + os.chmod(join('waflib',x), 493) + + if sys.hexversion<0x300000f: + sys.path = [join(dir, 'waflib')] + sys.path + import fixpy2 + fixpy2.fixdir(dir) + + os.unlink(tmp) + os.chdir(cwd) + + try: dir = unicode(dir, 'mbcs') + except: pass + try: + from ctypes import windll + windll.kernel32.SetFileAttributesW(dir, 2) + except: + pass + +def test(dir): + try: + os.stat(join(dir, 'waflib')) + return os.path.abspath(dir) + except OSError: + pass + +def find_lib(): + name = sys.argv[0] + base = os.path.dirname(os.path.abspath(name)) + + #devs use $WAFDIR + w=test(os.environ.get('WAFDIR', '')) + if w: return w + + #waf-light + if name.endswith('waf-light'): + w = test(base) + if w: return w + err('waf-light requires waflib -> export WAFDIR=/folder') + + dirname = '%s-%s-%s' % (WAF, VERSION, REVISION) + for i in [INSTALL,'/usr','/usr/local','/opt']: + w = test(i + '/lib/' + dirname) + if w: return w + + #waf-local + dir = join(base, (sys.platform != 'win32' and '.' or '') + dirname) + w = test(dir) + if w: return w + + #unpack + unpack_wafdir(dir) + return dir + +wafdir = find_lib() +sys.path.insert(0, wafdir) + +if __name__ == '__main__': + import waflib.extras.compat15#PRELUDE + from waflib import Scripting + Scripting.waf_entry_point(cwd, VERSION, wafdir) + diff --git a/waflib/Build.py b/waflib/Build.py new file mode 100644 index 00000000..95419dd7 --- /dev/null +++ b/waflib/Build.py @@ -0,0 +1,1296 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 (ita) + +""" +Classes related to the build phase (build, clean, install, step, etc) + +The inheritance tree is the following: + +""" + +import os, sys, errno, re, shutil +try: import cPickle +except: import pickle as cPickle +from waflib import Runner, TaskGen, Utils, ConfigSet, Task, Logs, Options, Context, Errors +import waflib.Node + +CACHE_DIR = 'c4che' +"""Location of the cache files""" + +CACHE_SUFFIX = '_cache.py' +"""Suffix for the cache files""" + +INSTALL = 1337 +"""Positive value '->' install, see :py:attr:`waflib.Build.BuildContext.is_install`""" + +UNINSTALL = -1337 +"""Negative value '<-' uninstall, see :py:attr:`waflib.Build.BuildContext.is_install`""" + +SAVED_ATTRS = 'root node_deps raw_deps task_sigs'.split() +"""Build class members to save between the runs (root, node_deps, raw_deps, task_sigs)""" + +CFG_FILES = 'cfg_files' +"""Files from the build directory to hash before starting the build (``config.h`` written during the configuration)""" + +POST_AT_ONCE = 0 +"""Post mode: all task generators are posted before the build really starts""" + +POST_LAZY = 1 +"""Post mode: post the task generators group after group""" + +POST_BOTH = 2 +"""Post mode: post the task generators at once, then re-check them for each group""" + +class BuildContext(Context.Context): + '''executes the build''' + + cmd = 'build' + variant = '' + + def __init__(self, **kw): + super(BuildContext, self).__init__(**kw) + + self.is_install = 0 + """Non-zero value when installing or uninstalling file""" + + self.top_dir = kw.get('top_dir', Context.top_dir) + + self.run_dir = kw.get('run_dir', Context.run_dir) + + self.post_mode = POST_AT_ONCE + """post the task generators at once, group-by-group, or both""" + + # output directory - may be set until the nodes are considered + self.out_dir = kw.get('out_dir', Context.out_dir) + + self.cache_dir = kw.get('cache_dir', None) + if not self.cache_dir: + self.cache_dir = self.out_dir + os.sep + CACHE_DIR + + # map names to environments, the '' must be defined + self.all_envs = {} + + # ======================================= # + # cache variables + + self.task_sigs = {} + """Signatures of the tasks (persists between build executions)""" + + self.node_deps = {} + """Dict of node dependencies found by :py:meth:`waflib.Task.Task.scan` (persists between build executions)""" + + self.raw_deps = {} + """Dict of custom data returned by :py:meth:`waflib.Task.Task.scan` (persists between build executions)""" + + # list of folders that are already scanned + # so that we do not need to stat them one more time + self.cache_dir_contents = {} + + self.task_gen_cache_names = {} + + self.launch_dir = Context.launch_dir + + self.jobs = Options.options.jobs + self.targets = Options.options.targets + self.keep = Options.options.keep + self.cache_global = Options.cache_global + self.nocache = Options.options.nocache + self.progress_bar = Options.options.progress_bar + + ############ stuff below has not been reviewed + + # Manual dependencies. + self.deps_man = Utils.defaultdict(list) + """Manual dependencies set by :py:meth:`waflib.Build.BuildContext.add_manual_dependency`""" + + # just the structure here + self.current_group = 0 + """ + Current build group + """ + + self.groups = [] + """ + List containing lists of task generators + """ + self.group_names = {} + """ + Map group names to the group lists. See :py:meth:`waflib.Build.BuildContext.add_group` + """ + + def get_variant_dir(self): + """Getter for the variant_dir attribute""" + if not self.variant: + return self.out_dir + return os.path.join(self.out_dir, self.variant) + variant_dir = property(get_variant_dir, None) + + def __call__(self, *k, **kw): + """ + Create a task generator and add it to the current build group. The following forms are equivalent:: + + def build(bld): + tg = bld(a=1, b=2) + + def build(bld): + tg = bld() + tg.a = 1 + tg.b = 2 + + def build(bld): + tg = TaskGen.task_gen(a=1, b=2) + bld.add_to_group(tg, None) + + :param group: group name to add the task generator to + :type group: string + """ + kw['bld'] = self + ret = TaskGen.task_gen(*k, **kw) + self.task_gen_cache_names = {} # reset the cache, each time + self.add_to_group(ret, group=kw.get('group', None)) + return ret + + def __copy__(self): + """Implemented to prevents copies of build contexts (raises an exception)""" + raise Errors.WafError('build contexts are not supposed to be copied') + + def install_files(self, *k, **kw): + """Actual implementation provided by :py:meth:`waflib.Build.InstallContext.install_files`""" + pass + + def install_as(self, *k, **kw): + """Actual implementation provided by :py:meth:`waflib.Build.InstallContext.install_as`""" + pass + + def symlink_as(self, *k, **kw): + """Actual implementation provided by :py:meth:`waflib.Build.InstallContext.symlink_as`""" + pass + + def load_envs(self): + """ + The configuration command creates files of the form ``build/c4che/NAMEcache.py``. This method + creates a :py:class:`waflib.ConfigSet.ConfigSet` instance for each ``NAME`` by reading those + files. The config sets are then stored in the dict :py:attr:`waflib.Build.BuildContext.allenvs`. + """ + try: + lst = Utils.listdir(self.cache_dir) + except OSError as e: + if e.errno == errno.ENOENT: + raise Errors.WafError('The project was not configured: run "waf configure" first!') + else: + raise + + if not lst: + raise Errors.WafError('The cache directory is empty: reconfigure the project') + + for fname in lst: + if fname.endswith(CACHE_SUFFIX): + env = ConfigSet.ConfigSet(os.path.join(self.cache_dir, fname)) + name = fname[:-len(CACHE_SUFFIX)] + self.all_envs[name] = env + + for f in env[CFG_FILES]: + newnode = self.root.find_resource(f) + try: + h = Utils.h_file(newnode.abspath()) + except (IOError, AttributeError): + Logs.error('cannot find %r' % f) + h = Utils.SIG_NIL + newnode.sig = h + + def init_dirs(self): + """ + Initialize the project directory and the build directory by creating the nodes + :py:attr:`waflib.Build.BuildContext.srcnode` and :py:attr:`waflib.Build.BuildContext.bldnode` + corresponding to ``top_dir`` and ``variant_dir`` respectively. The ``bldnode`` directory will be + created if it does not exist. + """ + + if not (os.path.isabs(self.top_dir) and os.path.isabs(self.out_dir)): + raise Errors.WafError('The project was not configured: run "waf configure" first!') + + self.path = self.srcnode = self.root.find_dir(self.top_dir) + self.bldnode = self.root.make_node(self.variant_dir) + self.bldnode.mkdir() + + def execute(self): + """ + Restore the data from previous builds and call :py:meth:`waflib.Build.BuildContext.execute_build`. Overrides from :py:func:`waflib.Context.Context.execute` + """ + self.restore() + if not self.all_envs: + self.load_envs() + + self.execute_build() + + def execute_build(self): + """ + Execute the build by: + + * reading the scripts (see :py:meth:`waflib.Context.Context.recurse`) + * calling :py:meth:`waflib.Build.BuildContext.pre_build` to call user build functions + * calling :py:meth:`waflib.Build.BuildContext.compile` to process the tasks + * calling :py:meth:`waflib.Build.BuildContext.post_build` to call user build functions + """ + + Logs.info("Waf: Entering directory `%s'" % self.variant_dir) + self.recurse([self.run_dir]) + self.pre_build() + + # display the time elapsed in the progress bar + self.timer = Utils.Timer() + + if self.progress_bar: + sys.stderr.write(Logs.colors.cursor_off) + try: + self.compile() + finally: + if self.progress_bar == 1: + c = len(self.returned_tasks) or 1 + self.to_log(self.progress_line(c, c, Logs.colors.BLUE, Logs.colors.NORMAL)) + print('') + sys.stdout.flush() + sys.stderr.write(Logs.colors.cursor_on) + Logs.info("Waf: Leaving directory `%s'" % self.variant_dir) + self.post_build() + + def restore(self): + """ + Load the data from a previous run, sets the attributes listed in :py:const:`waflib.Build.SAVED_ATTRS` + """ + try: + env = ConfigSet.ConfigSet(os.path.join(self.cache_dir, 'build.config.py')) + except (IOError, OSError): + pass + else: + if env['version'] < Context.HEXVERSION: + raise Errors.WafError('Version mismatch! reconfigure the project') + for t in env['tools']: + self.setup(**t) + + f = None + try: + dbfn = os.path.join(self.variant_dir, Context.DBFILE) + try: + f = open(dbfn, 'rb') + except (IOError, EOFError): + # handle missing file/empty file + Logs.debug('build: could not load the build cache %s (missing)' % dbfn) + else: + try: + waflib.Node.pickle_lock.acquire() + waflib.Node.Nod3 = self.node_class + try: + data = cPickle.load(f) + except Exception as e: + Logs.debug('build: could not pickle the build cache %s: %r' % (dbfn, e)) + else: + for x in SAVED_ATTRS: + setattr(self, x, data[x]) + finally: + waflib.Node.pickle_lock.release() + finally: + if f: + f.close() + + self.init_dirs() + + def store(self): + """ + Store the data for next runs, sets the attributes listed in :py:const:`waflib.Build.SAVED_ATTRS`. Uses a temporary + file to avoid problems on ctrl+c. + """ + + data = {} + for x in SAVED_ATTRS: + data[x] = getattr(self, x) + db = os.path.join(self.variant_dir, Context.DBFILE) + + try: + waflib.Node.pickle_lock.acquire() + waflib.Node.Nod3 = self.node_class + + f = None + try: + f = open(db + '.tmp', 'wb') + cPickle.dump(data, f) + finally: + if f: + f.close() + finally: + waflib.Node.pickle_lock.release() + + try: + st = os.stat(db) + os.unlink(db) + if not Utils.is_win32: # win32 has no chown but we're paranoid + os.chown(db + '.tmp', st.st_uid, st.st_gid) + except (AttributeError, OSError): + pass + + # do not use shutil.move (copy is not thread-safe) + os.rename(db + '.tmp', db) + + def compile(self): + """ + Run the build by creating an instance of :py:class:`waflib.Runner.Parallel` + The cache file is not written if the build is up to date (no task executed). + """ + Logs.debug('build: compile()') + + # use another object to perform the producer-consumer logic (reduce the complexity) + self.producer = Runner.Parallel(self, self.jobs) + self.producer.biter = self.get_build_iterator() + self.returned_tasks = [] # not part of the API yet + try: + self.producer.start() + except KeyboardInterrupt: + self.store() + raise + else: + if self.producer.dirty: + self.store() + + if self.producer.error: + raise Errors.BuildError(self.producer.error) + + def setup(self, tool, tooldir=None, funs=None): + """ + Import waf tools, used to import those accessed during the configuration:: + + def configure(conf): + conf.load('glib2') + + def build(bld): + pass # glib2 is imported implicitly + + :param tool: tool list + :type tool: list + :param tooldir: optional tool directory (sys.path) + :type tooldir: list of string + :param funs: unused variable + """ + if isinstance(tool, list): + for i in tool: self.setup(i, tooldir) + return + + module = Context.load_tool(tool, tooldir) + if hasattr(module, "setup"): module.setup(self) + + def get_env(self): + """Getter for the env property""" + try: + return self.all_envs[self.variant] + except KeyError: + return self.all_envs[''] + def set_env(self, val): + """Setter for the env property""" + self.all_envs[self.variant] = val + + env = property(get_env, set_env) + + def add_manual_dependency(self, path, value): + """ + Adds a dependency from a node object to a value:: + + def build(bld): + bld.add_manual_dependency( + bld.path.find_resource('wscript'), + bld.root.find_resource('/etc/fstab')) + + :param path: file path + :type path: string or :py:class:`waflib.Node.Node` + :param value: value to depend on + :type value: :py:class:`waflib.Node.Node`, string, or function returning a string + """ + if isinstance(path, waflib.Node.Node): + node = path + elif os.path.isabs(path): + node = self.root.find_resource(path) + else: + node = self.path.find_resource(path) + self.deps_man[id(node)].append(value) + + def launch_node(self): + """Returns the launch directory as a :py:class:`waflib.Node.Node` object""" + try: + # private cache + return self.p_ln + except AttributeError: + self.p_ln = self.root.find_dir(self.launch_dir) + return self.p_ln + + def hash_env_vars(self, env, vars_lst): + """ + Hash configuration set variables:: + + def build(bld): + bld.hash_env_vars(bld.env, ['CXX', 'CC']) + + :param env: Configuration Set + :type env: :py:class:`waflib.ConfigSet.ConfigSet` + :param vars_lst: list of variables + :type vars_list: list of string + """ + + if not env.table: + env = env.parent + if not env: + return Utils.SIG_NIL + + idx = str(id(env)) + str(vars_lst) + try: + cache = self.cache_env + except AttributeError: + cache = self.cache_env = {} + else: + try: + return self.cache_env[idx] + except KeyError: + pass + + lst = [env[a] for a in vars_lst] + ret = Utils.h_list(lst) + Logs.debug('envhash: %s %r', Utils.to_hex(ret), lst) + + cache[idx] = ret + + return ret + + def get_tgen_by_name(self, name): + """ + Retrieves a task generator from its name or its target name + the name must be unique:: + + def build(bld): + tg = bld(name='foo') + tg == bld.get_tgen_by_name('foo') + """ + cache = self.task_gen_cache_names + if not cache: + # create the index lazily + for g in self.groups: + for tg in g: + try: + cache[tg.name] = tg + except AttributeError: + # raised if not a task generator, which should be uncommon + pass + try: + return cache[name] + except KeyError: + raise Errors.WafError('Could not find a task generator for the name %r' % name) + + def progress_line(self, state, total, col1, col2): + """ + Compute the progress bar used by ``waf -p`` + """ + n = len(str(total)) + + Utils.rot_idx += 1 + ind = Utils.rot_chr[Utils.rot_idx % 4] + + pc = (100.*state)/total + eta = str(self.timer) + fs = "[%%%dd/%%%dd][%%s%%2d%%%%%%s][%s][" % (n, n, ind) + left = fs % (state, total, col1, pc, col2) + right = '][%s%s%s]' % (col1, eta, col2) + + cols = Logs.get_term_cols() - len(left) - len(right) + 2*len(col1) + 2*len(col2) + if cols < 7: cols = 7 + + ratio = ((cols*state)//total) - 1 + + bar = ('='*ratio+'>').ljust(cols) + msg = Utils.indicator % (left, bar, right) + + return msg + + def declare_chain(self, *k, **kw): + """ + Wrapper for :py:func:`waflib.TaskGen.declare_chain` provided for convenience + """ + return TaskGen.declare_chain(*k, **kw) + + def pre_build(self): + """Execute user-defined methods before the build starts, see :py:meth:`waflib.Build.BuildContext.add_pre_fun`""" + for m in getattr(self, 'pre_funs', []): + m(self) + + def post_build(self): + """Executes the user-defined methods after the build is successful, see :py:meth:`waflib.Build.BuildContext.add_post_fun`""" + for m in getattr(self, 'post_funs', []): + m(self) + + def add_pre_fun(self, meth): + """ + Bind a method to execute after the scripts are read and before the build starts:: + + def mycallback(bld): + print("Hello, world!") + + def build(bld): + bld.add_pre_fun(mycallback) + """ + try: + self.pre_funs.append(meth) + except AttributeError: + self.pre_funs = [meth] + + def add_post_fun(self, meth): + """ + Bind a method to execute immediately after the build is successful:: + + def call_ldconfig(bld): + bld.exec_command('/sbin/ldconfig') + + def build(bld): + if bld.cmd == 'install': + bld.add_pre_fun(call_ldconfig) + """ + try: + self.post_funs.append(meth) + except AttributeError: + self.post_funs = [meth] + + def get_group(self, x): + """ + Get the group x, or return the current group if x is None + + :param x: name or number or None + :type x: string, int or None + """ + if not self.groups: + self.add_group() + if x is None: + return self.groups[self.current_group] + if x in self.group_names: + return self.group_names[x] + return self.groups[x] + + def add_to_group(self, tgen, group=None): + """add a task or a task generator for the build""" + # paranoid + assert(isinstance(tgen, TaskGen.task_gen) or isinstance(tgen, Task.TaskBase)) + tgen.bld = self + self.get_group(group).append(tgen) + + def get_group_name(self, g): + """name for the group g (utility)""" + if not isinstance(g, list): + g = self.groups[g] + for x in self.group_names: + if id(self.group_names[x]) == id(g): + return x + return '' + + def get_group_idx(self, tg): + """ + Index of the group containing the task generator given as argument:: + + def build(bld): + tg = bld(name='nada') + 0 == bld.get_group_idx(tg) + + :param tg: Task generator object + :type tg: :py:class:`waflib.TaskGen.task_gen` + """ + se = id(tg) + for i in range(len(self.groups)): + for t in self.groups[i]: + if id(t) == se: + return i + return None + + def add_group(self, name=None, move=True): + """ + Add a new group of tasks/task generators. By default the new group becomes the default group for new task generators. + + :param name: name for this group + :type name: string + :param move: set the group created as default group (True by default) + :type move: bool + """ + #if self.groups and not self.groups[0].tasks: + # error('add_group: an empty group is already present') + if name and name in self.group_names: + Logs.error('add_group: name %s already present' % name) + g = [] + self.group_names[name] = g + self.groups.append(g) + if move: + self.current_group = len(self.groups) - 1 + + def set_group(self, idx): + """ + Set the current group to be idx: now new task generators will be added to this group by default:: + + def build(bld): + bld(rule='touch ${TGT}', target='foo.txt') + bld.add_group() # now the current group is 1 + bld(rule='touch ${TGT}', target='bar.txt') + bld.set_group(0) # now the current group is 0 + bld(rule='touch ${TGT}', target='truc.txt') # build truc.txt before bar.txt + + :param idx: group name or group index + :type idx: string or int + """ + if isinstance(idx, str): + g = self.group_names[idx] + for i in range(len(self.groups)): + if id(g) == id(self.groups[i]): + self.current_group = i + else: + self.current_group = idx + + def total(self): + """ + Approximate task count: this value may be inaccurate if task generators are posted lazily (see :py:attr:`waflib.Build.BuildContext.post_mode`). + The value :py:attr:`waflib.Runner.Parallel.total` is updated during the task execution. + """ + total = 0 + for group in self.groups: + for tg in group: + try: + total += len(tg.tasks) + except AttributeError: + total += 1 + return total + + def get_targets(self): + """ + Return the task generator corresponding to the 'targets' list, used by :py:meth:`waflib.Build.BuildContext.get_build_iterator`:: + + $ waf --targets=myprogram,myshlib + """ + to_post = [] + min_grp = 0 + for name in self.targets.split(','): + tg = self.get_tgen_by_name(name) + if not tg: + raise Errors.WafError('target %r does not exist' % name) + + m = self.get_group_idx(tg) + if m > min_grp: + min_grp = m + to_post = [tg] + elif m == min_grp: + to_post.append(tg) + return (min_grp, to_post) + + def post_group(self): + """ + Post the task generators from the group indexed by self.cur, used by :py:meth:`waflib.Build.BuildContext.get_build_iterator` + """ + if self.targets == '*': + for tg in self.groups[self.cur]: + try: + f = tg.post + except AttributeError: + pass + else: + f() + elif self.targets: + if self.cur < self._min_grp: + for tg in self.groups[self.cur]: + try: + f = tg.post + except AttributeError: + pass + else: + f() + else: + for tg in self._exact_tg: + tg.post() + else: + ln = self.launch_node() + for tg in self.groups[self.cur]: + try: + f = tg.post + except AttributeError: + pass + else: + if tg.path.is_child_of(ln): + f() + + def get_tasks_group(self, idx): + """ + Return all the tasks for the group of num idx, used by :py:meth:`waflib.Build.BuildContext.get_build_iterator` + """ + tasks = [] + for tg in self.groups[idx]: + # TODO a try-except might be more efficient + if isinstance(tg, Task.TaskBase): + tasks.append(tg) + else: + tasks.extend(tg.tasks) + return tasks + + def get_build_iterator(self): + """ + Creates a generator object that returns lists of tasks executable in parallel (yield) + + :return: tasks which can be executed immediatly + :rtype: list of :py:class:`waflib.Task.TaskBase` + """ + self.cur = 0 + + if self.targets and self.targets != '*': + (self._min_grp, self._exact_tg) = self.get_targets() + + global lazy_post + if self.post_mode != POST_LAZY: + while self.cur < len(self.groups): + self.post_group() + self.cur += 1 + self.cur = 0 + + while self.cur < len(self.groups): + # first post the task generators for the group + if self.post_mode != POST_AT_ONCE: + self.post_group() + + # then extract the tasks + tasks = self.get_tasks_group(self.cur) + # if the constraints are set properly (ext_in/ext_out, before/after) + # the call to set_file_constraints may be removed (can be a 15% penalty on no-op rebuilds) + # (but leave set_file_constraints for the installation step) + # + # if the tasks have only files, set_file_constraints is required but set_precedence_constraints is not necessary + # + Task.set_file_constraints(tasks) + Task.set_precedence_constraints(tasks) + + self.cur_tasks = tasks + self.cur += 1 + if not tasks: # return something else the build will stop + continue + yield tasks + while 1: + yield [] + + + #def install_dir(self, path, env=None): + # """ + # Create empty folders for the installation (very rarely used) TODO + # """ + # return + +class inst(Task.Task): + """ + Special task used for installing files and symlinks, it behaves both like a task + and like a task generator + """ + color = 'CYAN' + + def post(self): + """ + Same interface as in :py:meth:`waflib.TaskGen.task_gen.post` + """ + buf = [] + for x in self.source: + if isinstance(x, waflib.Node.Node): + y = x + else: + y = self.path.find_resource(x) + if not y: + idx = self.generator.bld.get_group_idx(self) + for tg in self.generator.bld.groups[idx]: + if not isinstance(tg, inst) and id(tg) != id(self): + tg.post() + y = self.path.find_resource(x) + if y: + break + else: + raise Errors.WafError('could not find %r in %r' % (x, self.path)) + buf.append(y) + self.inputs = buf + + def runnable_status(self): + """ + Installation tasks are always executed, so this method returns either :py:const:`waflib.Task.ASK_LATER` or :py:const:`waflib.Task.RUN_ME`. + """ + ret = super(inst, self).runnable_status() + if ret == Task.SKIP_ME: + return Task.RUN_ME + return ret + + def __str__(self): + """Return an empty string to disable the display""" + return '' + + def run(self): + """The attribute 'exec_task' holds the method to execute""" + return self.generator.exec_task() + + def get_install_path(self, destdir=True): + """ + Installation path obtained from ``self.dest`` and prefixed by the destdir. + The variables such as '${PREFIX}/bin' are substituted. + """ + dest = Utils.subst_vars(self.dest, self.env) + dest = dest.replace('/', os.sep) + if destdir and Options.options.destdir: + dest = os.path.join(Options.options.destdir, os.path.splitdrive(dest)[1].lstrip(os.sep)) + return dest + + def exec_install_files(self): + """ + Predefined method for installing files + """ + destpath = self.get_install_path() + if not destpath: + raise Errors.WafError('unknown installation path %r' % self.generator) + for x, y in zip(self.source, self.inputs): + if self.relative_trick: + destfile = os.path.join(destpath, y.path_from(self.path)) + Utils.check_dir(os.path.dirname(destfile)) + else: + destfile = os.path.join(destpath, y.name) + self.generator.bld.do_install(y.abspath(), destfile, self.chmod) + + def exec_install_as(self): + """ + Predefined method for installing one file with a given name + """ + destfile = self.get_install_path() + self.generator.bld.do_install(self.inputs[0].abspath(), destfile, self.chmod) + + def exec_symlink_as(self): + """ + Predefined method for installing a symlink + """ + destfile = self.get_install_path() + self.generator.bld.do_link(self.link, destfile) + +class InstallContext(BuildContext): + '''installs the targets on the system''' + cmd = 'install' + + def __init__(self, **kw): + super(InstallContext, self).__init__(**kw) + + # list of targets to uninstall for removing the empty folders after uninstalling + self.uninstall = [] + self.is_install = INSTALL + + def do_install(self, src, tgt, chmod=Utils.O644): + """ + Copy a file from src to tgt with given file permissions. The actual copy is not performed + if the source and target file have the same size and the same timestamps. When the copy occurs, + the file is first removed and then copied (prevent stale inodes). + + This method is overridden in :py:meth:`waflib.Build.UninstallContext.do_install` to remove the file. + + :param src: file name as absolute path + :type src: string + :param tgt: file destination, as absolute path + :type tgt: string + :param chmod: installation mode + :type chmod: int + """ + d, _ = os.path.split(tgt) + if not d: + raise Errors.WafError('Invalid installation given %r->%r' % (src, tgt)) + Utils.check_dir(d) + + srclbl = src.replace(self.srcnode.abspath() + os.sep, '') + if not Options.options.force: + # check if the file is already there to avoid a copy + try: + st1 = os.stat(tgt) + st2 = os.stat(src) + except OSError: + pass + else: + # same size and identical timestamps -> make no copy + if st1.st_mtime + 2 >= st2.st_mtime and st1.st_size == st2.st_size: + if not self.progress_bar: + Logs.info('- install %s (from %s)' % (tgt, srclbl)) + return False + + if not self.progress_bar: + Logs.info('+ install %s (from %s)' % (tgt, srclbl)) + + # following is for shared libs and stale inodes (-_-) + try: + os.remove(tgt) + except OSError: + pass + + try: + shutil.copy2(src, tgt) + os.chmod(tgt, chmod) + except IOError: + try: + os.stat(src) + except (OSError, IOError): + Logs.error('File %r does not exist' % src) + raise Errors.WafError('Could not install the file %r' % tgt) + + def do_link(self, src, tgt): + """ + Create a symlink from tgt to src. + + This method is overridden in :py:meth:`waflib.Build.UninstallContext.do_link` to remove the symlink. + + :param src: file name as absolute path + :type src: string + :param tgt: file destination, as absolute path + :type tgt: string + """ + d, _ = os.path.split(tgt) + Utils.check_dir(d) + + link = False + if not os.path.islink(tgt): + link = True + elif os.readlink(tgt) != src: + link = True + + if link: + try: os.remove(tgt) + except OSError: pass + if not self.progress_bar: + Logs.info('+ symlink %s (to %s)' % (tgt, src)) + os.symlink(src, tgt) + else: + if not self.progress_bar: + Logs.info('- symlink %s (to %s)' % (tgt, src)) + + def run_task_now(self, tsk, postpone): + """ + This method is called by :py:meth:`waflib.Build.InstallContext.install_files`, + :py:meth:`waflib.Build.InstallContext.install_as` and :py:meth:`waflib.Build.InstallContext.symlink_as` immediately + after the installation task is created. Its role is to force the immediate execution if necessary, that is when + ``postpone=False`` was given. + """ + tsk.post() + if not postpone: + if tsk.runnable_status() == Task.ASK_LATER: + raise self.WafError('cannot post the task %r' % tsk) + tsk.run() + + def install_files(self, dest, files, env=None, chmod=Utils.O644, relative_trick=False, cwd=None, add=True, postpone=True): + """ + Create a task to install files on the system:: + + def build(bld): + bld.install_files('${DATADIR}', self.path.find_resource('wscript')) + + :param dest: absolute path of the destination directory + :type dest: string + :param files: input files + :type files: list of strings or list of nodes + :param env: configuration set for performing substitutions in dest + :type env: Configuration set + :param relative_trick: preserve the folder hierarchy when installing whole folders + :type relative_trick: bool + :param cwd: parent node for searching srcfile, when srcfile is not a :py:class:`waflib.Node.Node` + :type cwd: :py:class:`waflib.Node.Node` + :param add: add the task created to a build group - set ``False`` only if the installation task is created after the build has started + :type add: bool + :param postpone: execute the task immediately to perform the installation + :type postpone: bool + """ + tsk = inst(env=env or self.env) + tsk.bld = self + tsk.path = cwd or self.path + tsk.chmod = chmod + if isinstance(files, waflib.Node.Node): + tsk.source = [files] + else: + tsk.source = Utils.to_list(files) + tsk.dest = dest + tsk.exec_task = tsk.exec_install_files + tsk.relative_trick = relative_trick + if add: self.add_to_group(tsk) + self.run_task_now(tsk, postpone) + return tsk + + def install_as(self, dest, srcfile, env=None, chmod=Utils.O644, cwd=None, add=True, postpone=True): + """ + Create a task to install a file on the system with a different name:: + + def build(bld): + bld.install_as('${PREFIX}/bin', 'myapp', chmod=Utils.O755) + + :param dest: absolute path of the destination file + :type dest: string + :param srcfile: input file + :type srcfile: string or node + :param cwd: parent node for searching srcfile, when srcfile is not a :py:class:`waflib.Node.Node` + :type cwd: :py:class:`waflib.Node.Node` + :param env: configuration set for performing substitutions in dest + :type env: Configuration set + :param add: add the task created to a build group - set ``False`` only if the installation task is created after the build has started + :type add: bool + :param postpone: execute the task immediately to perform the installation + :type postpone: bool + """ + tsk = inst(env=env or self.env) + tsk.bld = self + tsk.path = cwd or self.path + tsk.chmod = chmod + tsk.source = [srcfile] + tsk.dest = dest + tsk.exec_task = tsk.exec_install_as + if add: self.add_to_group(tsk) + self.run_task_now(tsk, postpone) + return tsk + + def symlink_as(self, dest, src, env=None, cwd=None, add=True, postpone=True): + """ + Create a task to install a symlink:: + + def build(bld): + bld.symlink_as('${PREFIX}/lib/libfoo.so', 'libfoo.so.1.2.3') + + :param dest: absolute path of the symlink + :type dest: string + :param src: absolute or relative path of the link + :type src: string + :param env: configuration set for performing substitutions in dest + :type env: Configuration set + :param add: add the task created to a build group - set ``False`` only if the installation task is created after the build has started + :type add: bool + :param postpone: execute the task immediately to perform the installation + :type postpone: bool + """ + + if Utils.is_win32: + # symlinks *cannot* work on that platform + return + + tsk = inst(env=env or self.env) + tsk.bld = self + tsk.dest = dest + tsk.path = cwd or self.path + tsk.source = [] + tsk.link = src + tsk.exec_task = tsk.exec_symlink_as + if add: self.add_to_group(tsk) + self.run_task_now(tsk, postpone) + return tsk + +class UninstallContext(InstallContext): + '''removes the targets installed''' + cmd = 'uninstall' + + def __init__(self, **kw): + super(UninstallContext, self).__init__(**kw) + self.is_install = UNINSTALL + + def do_install(self, src, tgt, chmod=Utils.O644): + """See :py:meth:`waflib.Build.InstallContext.do_install`""" + if not self.progress_bar: + Logs.info('- remove %s' % tgt) + + self.uninstall.append(tgt) + try: + os.remove(tgt) + except OSError as e: + if e.errno != errno.ENOENT: + if not getattr(self, 'uninstall_error', None): + self.uninstall_error = True + Logs.warn('build: some files could not be uninstalled (retry with -vv to list them)') + if Logs.verbose > 1: + Logs.warn('could not remove %s (error code %r)' % (e.filename, e.errno)) + + # TODO ita refactor this into a post build action to uninstall the folders (optimization) + while tgt: + tgt = os.path.dirname(tgt) + try: + os.rmdir(tgt) + except OSError: + break + + def do_link(self, src, tgt): + """See :py:meth:`waflib.Build.InstallContext.do_link`""" + try: + if not self.progress_bar: + Logs.info('- unlink %s' % tgt) + os.remove(tgt) + except OSError: + pass + + # TODO ita refactor this into a post build action to uninstall the folders (optimization)? + while tgt: + tgt = os.path.dirname(tgt) + try: + os.rmdir(tgt) + except OSError: + break + + def execute(self): + """ + See :py:func:`waflib.Context.Context.execute` + """ + try: + # do not execute any tasks + def runnable_status(self): + return Task.SKIP_ME + setattr(Task.Task, 'runnable_status_back', Task.Task.runnable_status) + setattr(Task.Task, 'runnable_status', runnable_status) + + super(UninstallContext, self).execute() + finally: + setattr(Task.Task, 'runnable_status', Task.Task.runnable_status_back) + +class CleanContext(BuildContext): + '''cleans the project''' + cmd = 'clean' + def execute(self): + """ + See :py:func:`waflib.Context.Context.execute` + """ + self.restore() + if not self.all_envs: + self.load_envs() + + self.recurse([self.run_dir]) + try: + self.clean() + finally: + self.store() + + def clean(self): + """clean the data and some files in the build dir .. well, TODO""" + Logs.debug('build: clean called') + + if self.bldnode != self.srcnode: + # would lead to a disaster if top == out + lst = [self.root.find_or_declare(f) for f in self.env[CFG_FILES]] + for n in self.bldnode.ant_glob('**/*', excl='lock* *conf_check_*/** config.log c4che/*'): + if n in lst: + continue + n.delete() + self.root.children = {} + + for v in 'node_deps task_sigs raw_deps'.split(): + setattr(self, v, {}) + +class ListContext(BuildContext): + '''lists the targets to execute''' + + cmd = 'list' + def execute(self): + """ + See :py:func:`waflib.Context.Context.execute`. + """ + self.restore() + if not self.all_envs: + self.load_envs() + + self.recurse([self.run_dir]) + self.pre_build() + + # display the time elapsed in the progress bar + self.timer = Utils.Timer() + + for g in self.groups: + for tg in g: + try: + f = tg.post + except AttributeError: + pass + else: + f() + + try: + # force the cache initialization + self.get_tgen_by_name('') + except: + pass + lst = list(self.task_gen_cache_names.keys()) + lst.sort() + for k in lst: + Logs.pprint('GREEN', k) + +class StepContext(BuildContext): + '''executes tasks in a step-by-step fashion, for debugging''' + cmd = 'step' + + def __init__(self, **kw): + super(StepContext, self).__init__(**kw) + self.files = Options.options.files + + def compile(self): + """ + Compile the tasks matching the input/output files given (regular expression matching). Derived from :py:meth:`waflib.Build.BuildContext.compile`:: + + $ waf step --files=foo.c,bar.c,in:truc.c,out:bar.o + $ waf step --files=in:foo.cpp.1.o # link task only + + """ + if not self.files: + Logs.warn('Add a pattern for the debug build, for example "waf step --files=main.c,app"') + BuildContext.compile(self) + return + + for g in self.groups: + for tg in g: + try: + f = tg.post + except AttributeError: + pass + else: + f() + + for pat in self.files.split(','): + matcher = self.get_matcher(pat) + for tg in g: + if isinstance(tg, Task.TaskBase): + lst = [tg] + else: + lst = tg.tasks + for tsk in lst: + do_exec = False + for node in getattr(tsk, 'inputs', []): + if matcher(node, output=False): + do_exec = True + break + for node in getattr(tsk, 'outputs', []): + if matcher(node, output=True): + do_exec = True + break + if do_exec: + ret = tsk.run() + Logs.info('%s -> exit %r' % (str(tsk), ret)) + + def get_matcher(self, pat): + # this returns a function + inn = True + out = True + if pat.startswith('in:'): + out = False + pat = pat.replace('in:', '') + elif pat.startswith('out:'): + inn = False + pat = pat.replace('out:', '') + + anode = self.root.find_node(pat) + pattern = None + if not anode: + if not pat.startswith('^'): + pat = '^.+?%s' % pat + if not pat.endswith('$'): + pat = '%s$' % pat + pattern = re.compile(pat) + + def match(node, output): + if output == True and not out: + return False + if output == False and not inn: + return False + + if anode: + return anode == node + else: + return pattern.match(node.abspath()) + return match + +BuildContext.store = Utils.nogc(BuildContext.store) +BuildContext.restore = Utils.nogc(BuildContext.restore) + diff --git a/waflib/ConfigSet.py b/waflib/ConfigSet.py new file mode 100644 index 00000000..c98040b3 --- /dev/null +++ b/waflib/ConfigSet.py @@ -0,0 +1,332 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 (ita) + +""" + +ConfigSet: a special dict + +The values put in :py:class:`ConfigSet` must be lists +""" + +import copy, re +from waflib import Logs, Utils +re_imp = re.compile('^(#)*?([^#=]*?)\ =\ (.*?)$', re.M) + +class ConfigSet(object): + """ + A dict that honor serialization and parent relationships. The serialization format + is human-readable (python-like) and performed by using eval() and repr(). + For high performance prefer pickle. Do not store functions as they are not serializable. + + The values can be accessed by attributes or by keys:: + + from waflib.ConfigSet import ConfigSet + env = ConfigSet() + env.FOO = 'test' + env['FOO'] = 'test' + """ + __slots__ = ('table', 'parent') + def __init__(self, filename=None): + self.table = {} + """ + Internal dict holding the object values + """ + #self.parent = None + + if filename: + self.load(filename) + + def __contains__(self, key): + """ + Enable the *in* syntax:: + + if 'foo' in env: + print env['foo'] + """ + if key in self.table: return True + try: return self.parent.__contains__(key) + except AttributeError: return False # parent may not exist + + def keys(self): + """Dict interface (unknown purpose)""" + keys = set() + cur = self + while cur: + keys.update(cur.table.keys()) + cur = getattr(cur, 'parent', None) + keys = list(keys) + keys.sort() + return keys + + def __str__(self): + """Text representation of the ConfigSet (for debugging purposes)""" + return "\n".join(["%r %r" % (x, self.__getitem__(x)) for x in self.keys()]) + + def __getitem__(self, key): + """ + Dictionary interface: get value from key:: + + def configure(conf): + conf.env['foo'] = {} + print(env['foo']) + """ + try: + while 1: + x = self.table.get(key, None) + if not x is None: + return x + self = self.parent + except AttributeError: + return [] + + def __setitem__(self, key, value): + """ + Dictionary interface: get value from key + """ + self.table[key] = value + + def __delitem__(self, key): + """ + Dictionary interface: get value from key + """ + self[key] = [] + + def __getattr__(self, name): + """ + Attribute access provided for convenience. The following forms are equivalent:: + + def configure(conf): + conf.env.value + conf.env['value'] + """ + if name in self.__slots__: + return object.__getattr__(self, name) + else: + return self[name] + + def __setattr__(self, name, value): + """ + Attribute access provided for convenience. The following forms are equivalent:: + + def configure(conf): + conf.env.value = x + env['value'] = x + """ + if name in self.__slots__: + object.__setattr__(self, name, value) + else: + self[name] = value + + def __delattr__(self, name): + """ + Attribute access provided for convenience. The following forms are equivalent:: + + def configure(conf): + del env.value + del env['value'] + """ + if name in self.__slots__: + object.__delattr__(self, name) + else: + del self[name] + + def derive(self): + """ + Returns a new ConfigSet deriving from self. The copy returned + will be a shallow copy:: + + from waflib.ConfigSet import ConfigSet + env = ConfigSet() + env.append_value('CFLAGS', ['-O2']) + child = env.derive() + child.CFLAGS.append('test') # warning! this will modify 'env' + child.CFLAGS = ['-O3'] # new list, ok + child.append_value('CFLAGS', ['-O3']) # ok + + Use :py:func:`ConfigSet.detach` to detach the child from the parent. + """ + newenv = ConfigSet() + newenv.parent = self + return newenv + + def detach(self): + """ + Detach self from its parent (if existing) + + Modifying the parent :py:class:`ConfigSet` will not change the current object + Modifying this :py:class:`ConfigSet` will not modify the parent one. + """ + tbl = self.get_merged_dict() + try: + delattr(self, 'parent') + except AttributeError: + pass + else: + keys = tbl.keys() + for x in keys: + tbl[x] = copy.deepcopy(tbl[x]) + self.table = tbl + + def get_flat(self, key): + """ + Return a value as a string. If the input is a list, the value returned is space-separated. + + :param key: key to use + :type key: string + """ + s = self[key] + if isinstance(s, str): return s + return ' '.join(s) + + def _get_list_value_for_modification(self, key): + """ + Return a list value for further modification. + + The list may be modified inplace and there is no need to do this afterwards:: + + self.table[var] = value + """ + try: + value = self.table[key] + except KeyError: + try: value = self.parent[key] + except AttributeError: value = [] + if isinstance(value, list): + value = value[:] + else: + value = [value] + else: + if not isinstance(value, list): + value = [value] + self.table[key] = value + return value + + def append_value(self, var, val): + """ + Appends a value to the specified config key:: + + def build(bld): + bld.env.append_value('CFLAGS', ['-O2']) + + The value must be a list or a tuple + """ + current_value = self._get_list_value_for_modification(var) + if isinstance(val, str): # if there were string everywhere we could optimize this + val = [val] + current_value.extend(val) + + def prepend_value(self, var, val): + """ + Prepends a value to the specified item:: + + def configure(conf): + conf.env.prepend_value('CFLAGS', ['-O2']) + + The value must be a list or a tuple + """ + if isinstance(val, str): + val = [val] + self.table[var] = val + self._get_list_value_for_modification(var) + + def append_unique(self, var, val): + """ + Append a value to the specified item only if it's not already present:: + + def build(bld): + bld.env.append_unique('CFLAGS', ['-O2', '-g']) + + The value must be a list or a tuple + """ + if isinstance(val, str): + val = [val] + current_value = self._get_list_value_for_modification(var) + + for x in val: + if x not in current_value: + current_value.append(x) + + def get_merged_dict(self): + """ + Compute the merged dictionary from the fusion of self and all its parent + + :rtype: a ConfigSet object + """ + table_list = [] + env = self + while 1: + table_list.insert(0, env.table) + try: env = env.parent + except AttributeError: break + merged_table = {} + for table in table_list: + merged_table.update(table) + return merged_table + + def store(self, filename): + """ + Write the :py:class:`ConfigSet` data into a file. See :py:meth:`ConfigSet.load` for reading such files. + + :param filename: file to use + :type filename: string + """ + f = None + try: + f = open(filename, 'w') + merged_table = self.get_merged_dict() + keys = list(merged_table.keys()) + keys.sort() + for k in keys: + if k != 'undo_stack': + f.write('%s = %r\n' % (k, merged_table[k])) + finally: + if f: + f.close() + + def load(self, filename): + """ + Retrieve the :py:class:`ConfigSet` data from a file. See :py:meth:`ConfigSet.store` for writing such files + + :param filename: file to use + :type filename: string + """ + tbl = self.table + code = Utils.readf(filename) + for m in re_imp.finditer(code): + g = m.group + tbl[g(2)] = eval(g(3)) + Logs.debug('env: %s' % str(self.table)) + + def update(self, d): + """ + Dictionary interface: replace values from another dict + + :param d: object to use the value from + :type d: dict-like object + """ + for k, v in d.items(): + self[k] = v + + def stash(self): + """ + Store the object state, to provide a kind of transaction support:: + + env = ConfigSet() + env.stash() + try: + env.append_value('CFLAGS', '-O3') + call_some_method(env) + finally: + env.revert() + + The history is kept in a stack, and is lost during the serialization by :py:meth:`ConfigSet.store` + """ + self.undo_stack = self.undo_stack + [self.table] + self.table = self.table.copy() + + def revert(self): + """ + Reverts the object to a previous state. See :py:meth:`ConfigSet.stash` + """ + self.table = self.undo_stack.pop(-1) + diff --git a/waflib/Configure.py b/waflib/Configure.py new file mode 100644 index 00000000..3d9d5d8f --- /dev/null +++ b/waflib/Configure.py @@ -0,0 +1,568 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 (ita) + +""" +Configuration system + +A :py:class:`waflib.Configure.ConfigurationContext` instance is created when ``waf configure`` is called, it is used to: + +* create data dictionaries (ConfigSet instances) +* store the list of modules to import +* hold configuration routines such as ``find_program``, etc +""" + +import os, shlex, sys, time +from waflib import ConfigSet, Utils, Options, Logs, Context, Build, Errors + +try: + from urllib import request +except: + from urllib import urlopen +else: + urlopen = request.urlopen + +BREAK = 'break' +"""In case of a configuration error, break""" + +CONTINUE = 'continue' +"""In case of a configuration error, continue""" + +WAF_CONFIG_LOG = 'config.log' +"""Name of the configuration log file""" + +autoconfig = False +"""Execute the configuration automatically""" + +conf_template = '''# project %(app)s configured on %(now)s by +# waf %(wafver)s (abi %(abi)s, python %(pyver)x on %(systype)s) +# using %(args)s +#''' + +def download_check(node): + """ + Hook to check for the tools which are downloaded. Replace with your function if necessary. + """ + pass + +def download_tool(tool, force=False, ctx=None): + """ + Download a Waf tool from the remote repository defined in :py:const:`waflib.Context.remote_repo`:: + + $ waf configure --download + """ + for x in Utils.to_list(Context.remote_repo): + for sub in Utils.to_list(Context.remote_locs): + url = '/'.join((x, sub, tool + '.py')) + try: + web = urlopen(url) + try: + if web.getcode() != 200: + continue + except AttributeError: + pass + except Exception: + # on python3 urlopen throws an exception + # python 2.3 does not have getcode and throws an exception to fail + continue + else: + tmp = ctx.root.make_node(os.sep.join((Context.waf_dir, 'waflib', 'extras', tool + '.py'))) + tmp.write(web.read()) + Logs.warn('Downloaded %s from %s' % (tool, url)) + download_check(tmp) + try: + module = Context.load_tool(tool) + except: + Logs.warn('The tool %s from %s is unusable' % (tool, url)) + try: + tmp.delete() + except: + pass + continue + return module + raise Errors.WafError('Could not load the Waf tool') + +class ConfigurationContext(Context.Context): + '''configures the project''' + + cmd = 'configure' + + error_handlers = [] + """ + Additional functions to handle configuration errors + """ + + def __init__(self, **kw): + super(ConfigurationContext, self).__init__(**kw) + self.environ = dict(os.environ) + self.all_envs = {} + + self.top_dir = None + self.out_dir = None + + self.tools = [] # tools loaded in the configuration, and that will be loaded when building + + self.hash = 0 + self.files = [] + + self.tool_cache = [] + + self.setenv('') + + def setenv(self, name, env=None): + """ + Set a new config set for conf.env + + The name is the filename prefix to save to ``c4che/NAME_cache.py``, and it + is also used as *variants* by the build commands. + Though related to variants, whatever kind of data may be stored in the config set:: + + def configure(cfg): + cfg.env.ONE = 1 + cfg.setenv('foo') + cfg.env.ONE = 2 + + def build(bld): + 2 == bld.env_of_name('foo').ONE + + :param name: name of the configuration set + :type name: string + :param env: ConfigSet to copy, or an empty ConfigSet is created + :type env: :py:class:`waflib.ConfigSet.ConfigSet` + """ + if not env: + env = ConfigSet.ConfigSet() + self.prepare_env(env) + else: + env = env.derive() + self.all_envs[name] = env + self.variant = name + + def get_env(self): + """Getter for the env property""" + return self.all_envs[self.variant] + def set_env(self, val): + """Setter for the env property""" + self.all_envs[self.variant] = val + + env = property(get_env, set_env) + + def init_dirs(self): + """ + Initialize the project directory and the build directory + """ + + top = self.top_dir + if not top: + top = Options.options.top + if not top: + top = getattr(Context.g_module, Context.TOP, None) + if not top: + top = self.path.abspath() + top = os.path.abspath(top) + + self.srcnode = (os.path.isabs(top) and self.root or self.path).find_dir(top) + assert(self.srcnode) + + out = self.out_dir + if not out: + out = Options.options.out + if not out: + out = getattr(Context.g_module, Context.OUT, None) + if not out: + out = Options.lockfile.replace('.lock-waf', '') + + self.bldnode = (os.path.isabs(out) and self.root or self.path).make_node(out) + self.bldnode.mkdir() + + if not os.path.isdir(self.bldnode.abspath()): + conf.fatal('could not create the build directory %s' % self.bldnode.abspath()) + + def execute(self): + """ + See :py:func:`waflib.Context.Context.execute` + """ + self.init_dirs() + + self.cachedir = self.bldnode.make_node(Build.CACHE_DIR) + self.cachedir.mkdir() + + path = os.path.join(self.bldnode.abspath(), WAF_CONFIG_LOG) + self.logger = Logs.make_logger(path, 'cfg') + + app = getattr(Context.g_module, 'APPNAME', '') + if app: + ver = getattr(Context.g_module, 'VERSION', '') + if ver: + app = "%s (%s)" % (app, ver) + + now = time.ctime() + pyver = sys.hexversion + systype = sys.platform + args = " ".join(sys.argv) + wafver = Context.WAFVERSION + abi = Context.ABI + self.to_log(conf_template % vars()) + + self.msg('Setting top to', self.srcnode.abspath()) + self.msg('Setting out to', self.bldnode.abspath()) + + if id(self.srcnode) == id(self.bldnode): + Logs.warn('Setting top == out (remember to use "update_outputs")') + elif id(self.path) != id(self.srcnode): + if self.srcnode.is_child_of(self.path): + Logs.warn('Are you certain that you do not want to set top="." ?') + + super(ConfigurationContext, self).execute() + + self.store() + + Context.top_dir = self.srcnode.abspath() + Context.out_dir = self.bldnode.abspath() + + # this will write a configure lock so that subsequent builds will + # consider the current path as the root directory (see prepare_impl). + # to remove: use 'waf distclean' + env = ConfigSet.ConfigSet() + env['argv'] = sys.argv + env['options'] = Options.options.__dict__ + + env.run_dir = Context.run_dir + env.top_dir = Context.top_dir + env.out_dir = Context.out_dir + + # conf.hash & conf.files hold wscript files paths and hash + # (used only by Configure.autoconfig) + env['hash'] = self.hash + env['files'] = self.files + env['environ'] = dict(self.environ) + + if not self.env.NO_LOCK_IN_RUN: + env.store(Context.run_dir + os.sep + Options.lockfile) + if not self.env.NO_LOCK_IN_TOP: + env.store(Context.top_dir + os.sep + Options.lockfile) + if not self.env.NO_LOCK_IN_OUT: + env.store(Context.out_dir + os.sep + Options.lockfile) + + def prepare_env(self, env): + """ + Insert *PREFIX*, *BINDIR* and *LIBDIR* values into ``env`` + + :type env: :py:class:`waflib.ConfigSet.ConfigSet` + :param env: a ConfigSet, usually ``conf.env`` + """ + if not env.PREFIX: + env.PREFIX = os.path.abspath(os.path.expanduser(Options.options.prefix)) + if not env.BINDIR: + env.BINDIR = Utils.subst_vars('${PREFIX}/bin', env) + if not env.LIBDIR: + env.LIBDIR = Utils.subst_vars('${PREFIX}/lib', env) + + def store(self): + """Save the config results into the cache file""" + n = self.cachedir.make_node('build.config.py') + n.write('version = 0x%x\ntools = %r\n' % (Context.HEXVERSION, self.tools)) + + if not self.all_envs: + self.fatal('nothing to store in the configuration context!') + + for key in self.all_envs: + tmpenv = self.all_envs[key] + tmpenv.store(os.path.join(self.cachedir.abspath(), key + Build.CACHE_SUFFIX)) + + def load(self, input, tooldir=None, funs=None, download=True): + """ + Load Waf tools, which will be imported whenever a build is started. + + :param input: waf tools to import + :type input: list of string + :param tooldir: paths for the imports + :type tooldir: list of string + :param funs: functions to execute from the waf tools + :type funs: list of string + :param download: whether to download the tool from the waf repository + :type download: bool + """ + + tools = Utils.to_list(input) + if tooldir: tooldir = Utils.to_list(tooldir) + for tool in tools: + # avoid loading the same tool more than once with the same functions + # used by composite projects + + mag = (tool, id(self.env), funs) + if mag in self.tool_cache: + self.to_log('(tool %s is already loaded, skipping)' % tool) + continue + self.tool_cache.append(mag) + + module = None + try: + module = Context.load_tool(tool, tooldir) + except ImportError as e: + if Options.options.download: + module = download_tool(tool, ctx=self) + if not module: + self.fatal('Could not load the Waf tool %r or download a suitable replacement from the repository (sys.path %r)\n%s' % (tool, sys.path, e)) + else: + self.fatal('Could not load the Waf tool %r from %r (try the --download option?):\n%s' % (tool, sys.path, e)) + except Exception as e: + self.to_log('imp %r (%r & %r)' % (tool, tooldir, funs)) + self.to_log(Utils.ex_stack()) + raise + + if funs is not None: + self.eval_rules(funs) + else: + func = getattr(module, 'configure', None) + if func: + if type(func) is type(Utils.readf): func(self) + else: self.eval_rules(func) + + self.tools.append({'tool':tool, 'tooldir':tooldir, 'funs':funs}) + + def post_recurse(self, node): + """ + Records the path and a hash of the scripts visited, see :py:meth:`waflib.Context.Context.post_recurse` + + :param node: script + :type node: :py:class:`waflib.Node.Node` + """ + super(ConfigurationContext, self).post_recurse(node) + self.hash = hash((self.hash, node.read('rb'))) + self.files.append(node.abspath()) + + def eval_rules(self, rules): + """ + Execute the configuration tests. The method :py:meth:`waflib.Configure.ConfigurationContext.err_handler` + is used to process the eventual exceptions + + :param rules: list of configuration method names + :type rules: list of string + """ + self.rules = Utils.to_list(rules) + for x in self.rules: + f = getattr(self, x) + if not f: self.fatal("No such method '%s'." % x) + try: + f() + except Exception as e: + ret = self.err_handler(x, e) + if ret == BREAK: + break + elif ret == CONTINUE: + continue + else: + raise + + def err_handler(self, fun, error): + """ + Error handler for the configuration tests, the default is to let the exception raise + + :param fun: configuration test + :type fun: method + :param error: exception + :type error: exception + """ + pass + +def conf(f): + """ + Decorator: attach new configuration functions to :py:class:`waflib.Build.BuildContext` and + :py:class:`waflib.Configure.ConfigurationContext`. The methods bound will accept a parameter + named 'mandatory' to disable the configuration errors:: + + def configure(conf): + conf.find_program('abc', mandatory=False) + + :param f: method to bind + :type f: function + """ + def fun(*k, **kw): + mandatory = True + if 'mandatory' in kw: + mandatory = kw['mandatory'] + del kw['mandatory'] + + try: + return f(*k, **kw) + except Errors.ConfigurationError as e: + if mandatory: + raise e + + setattr(ConfigurationContext, f.__name__, fun) + setattr(Build.BuildContext, f.__name__, fun) + return f + +@conf +def add_os_flags(self, var, dest=None): + """ + Import operating system environment values into ``conf.env`` dict:: + + def configure(conf): + conf.add_os_flags('CFLAGS') + + :param var: variable to use + :type var: string + :param dest: destination variable, by default the same as var + :type dest: string + """ + # do not use 'get' to make certain the variable is not defined + try: self.env.append_value(dest or var, shlex.split(self.environ[var])) + except KeyError: pass + +@conf +def cmd_to_list(self, cmd): + """ + Detect if a command is written in pseudo shell like ``ccache g++`` and return a list. + + :param cmd: command + :type cmd: a string or a list of string + """ + if isinstance(cmd, str) and cmd.find(' '): + try: + os.stat(cmd) + except OSError: + return shlex.split(cmd) + else: + return [cmd] + return cmd + +@conf +def check_waf_version(self, mini='1.6.0', maxi='1.7.0'): + """ + check for the waf version + + Versions should be supplied as hex. 0x01000000 means 1.0.0, + 0x010408 means 1.4.8, etc. + + :type mini: number, tuple or string + :param mini: Minimum required version + :type maxi: number, tuple or string + :param maxi: Maximum allowed version + """ + self.start_msg('Checking for waf version in %s-%s' % (str(mini), str(maxi))) + ver = Context.HEXVERSION + if Utils.num2ver(mini) > ver: + self.fatal('waf version should be at least %r (%r found)' % (Utils.num2ver(mini), ver)) + + if Utils.num2ver(maxi) < ver: + self.fatal('waf version should be at most %r (%r found)' % (Utils.num2ver(maxi), ver)) + self.end_msg('ok') + +@conf +def find_file(self, filename, path_list=[]): + """ + Find a file in a list of paths + + :param filename: name of the file to search for + :param path_list: list of directories to search + :return: the first occurrence filename or '' if filename could not be found + """ + for n in Utils.to_list(filename): + for d in Utils.to_list(path_list): + p = os.path.join(d, n) + if os.path.exists(p): + return p + self.fatal('Could not find %r' % filename) + +@conf +def find_program(self, filename, **kw): + """ + Search for a program on the operating system + + When var is used, you may set os.environ[var] to help find a specific program version, for example:: + + $ VALAC=/usr/bin/valac_test waf configure + + :param path_list: paths to use for searching + :type param_list: list of string + :param var: store the result to conf.env[var], by default use filename.upper() + :type var: string + :param ext: list of extensions for the binary (do not add an extension for portability) + :type ext: list of string + """ + + exts = kw.get('exts', Utils.is_win32 and '.exe,.com,.bat,.cmd' or ',.sh,.pl,.py') + + environ = kw.get('environ', os.environ) + + ret = '' + filename = Utils.to_list(filename) + + var = kw.get('var', '') + if not var: + var = filename[0].upper() + + if self.env[var]: + ret = self.env[var] + elif var in environ: + ret = environ[var] + + path_list = kw.get('path_list', '') + if not ret: + if path_list: + path_list = Utils.to_list(path_list) + else: + path_list = environ.get('PATH', '').split(os.pathsep) + + if not isinstance(filename, list): + filename = [filename] + + for a in exts.split(','): + if ret: + break + for b in filename: + if ret: + break + for c in path_list: + if ret: + break + x = os.path.expanduser(os.path.join(c, b + a)) + if os.path.isfile(x): + ret = x + + if not ret and Utils.winreg: + ret = Utils.get_registry_app_path(Utils.winreg.HKEY_CURRENT_USER, filename) + if not ret and Utils.winreg: + ret = Utils.get_registry_app_path(Utils.winreg.HKEY_LOCAL_MACHINE, filename) + + self.msg('Checking for program ' + ','.join(filename), ret or False) + self.to_log('find program=%r paths=%r var=%r -> %r' % (filename, path_list, var, ret)) + + if not ret: + self.fatal(kw.get('errmsg', '') or 'Could not find the program %s' % ','.join(filename)) + + if var: + self.env[var] = ret + return ret + + +@conf +def find_perl_program(self, filename, path_list=[], var=None, environ=None, exts=''): + """ + Search for a perl program on the operating system + + :param filename: file to search for + :type filename: string + :param path_list: list of paths to look into + :type path_list: list of string + :param var: store the results into *conf.env.var* + :type var: string + :param environ: operating system environment to pass to :py:func:`waflib.Configure.find_program` + :type environ: dict + :param exts: extensions given to :py:func:`waflib.Configure.find_program` + :type exts: list + """ + + try: + app = self.find_program(filename, path_list=path_list, var=var, environ=environ, exts=exts) + except: + self.find_program('perl', var='PERL') + app = self.find_file(filename, os.environ['PATH'].split(os.pathsep)) + if not app: + raise + if var: + self.env[var] = Utils.to_list(self.env['PERL']) + [app] + self.msg('Checking for %r' % filename, app) + diff --git a/waflib/Context.py b/waflib/Context.py new file mode 100644 index 00000000..c0d10733 --- /dev/null +++ b/waflib/Context.py @@ -0,0 +1,599 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +""" +Classes and functions required for waf commands +""" + +import os, imp, sys +from waflib import Utils, Errors, Logs +import waflib.Node + +# the following 3 constants are updated on each new release (do not touch) +HEXVERSION=0x1060800 +"""Constant updated on new releases""" + +WAFVERSION="1.6.8" +"""Constant updated on new releases""" + +WAFREVISION="11517" +"""Constant updated on new releases""" + +ABI = 98 +"""Version of the build data cache file format (used in :py:const:`waflib.Context.DBFILE`)""" + +DBFILE = '.wafpickle-%d' % ABI +"""Name of the pickle file for storing the build data""" + +APPNAME = 'APPNAME' +"""Default application name (used by ``waf dist``)""" + +VERSION = 'VERSION' +"""Default application version (used by ``waf dist``)""" + +TOP = 'top' +"""The variable name for the top-level directory in wscript files""" + +OUT = 'out' +"""The variable name for the output directory in wscript files""" + +WSCRIPT_FILE = 'wscript' +"""Name of the waf script files""" + + +launch_dir = '' +"""Directory from which waf has been called""" +run_dir = '' +"""Location of the wscript file to use as the entry point""" +top_dir = '' +"""Location of the project directory (top), if the project was configured""" +out_dir = '' +"""Location of the build directory (out), if the project was configured""" +waf_dir = '' +"""Directory containing the waf modules""" + +local_repo = '' +"""Local repository containing additional Waf tools (plugins)""" +remote_repo = 'http://waf.googlecode.com/svn/' +""" +Remote directory containing downloadable waf tools. The missing tools can be downloaded by using:: + + $ waf configure --download +""" + +remote_locs = ['branches/waf-%s/waflib/extras' % WAFVERSION, 'trunk/waflib/extras', 'trunk/waflib/Tools'] +""" +Remote directories for use with :py:const:`waflib.Context.remote_repo` +""" + +g_module = None +""" +Module representing the main wscript file (see :py:const:`waflib.Context.run_dir`) +""" + +STDOUT = 1 +STDERR = -1 +BOTH = 0 + +classes = [] +""" +List of :py:class:`waflib.Context.Context` subclasses that can be used as waf commands. The classes +are added automatically by a metaclass. +""" + + +def create_context(cmd_name, *k, **kw): + """ + Create a new :py:class:`waflib.Context.Context` instance corresponding to the given command. + Used in particular by :py:func:`waflib.Scripting.run_command` + + :param cmd_name: command + :type cmd_name: string + :param k: arguments to give to the context class initializer + :type k: list + :param k: keyword arguments to give to the context class initializer + :type k: dict + """ + global classes + for x in classes: + if x.cmd == cmd_name: + return x(*k, **kw) + ctx = Context(*k, **kw) + ctx.fun = cmd_name + return ctx + +class store_context(type): + """ + Metaclass for storing the command classes into the list :py:const:`waflib.Context.classes` + Context classes must provide an attribute 'cmd' representing the command to execute + """ + def __init__(cls, name, bases, dict): + super(store_context, cls).__init__(name, bases, dict) + name = cls.__name__ + + if name == 'ctx' or name == 'Context': + return + + try: + cls.cmd + except AttributeError: + raise Errors.WafError('Missing command for the context class %r (cmd)' % name) + + if not getattr(cls, 'fun', None): + cls.fun = cls.cmd + + global classes + classes.insert(0, cls) + +ctx = store_context('ctx', (object,), {}) +"""Base class for the :py:class:`waflib.Context.Context` classes""" + +class Context(ctx): + """ + Default context for waf commands, and base class for new command contexts. + + Context objects are passed to top-level functions:: + + def foo(ctx): + print(ctx.__class__.__name__) # waflib.Context.Context + + Subclasses must define the attribute 'cmd': + + :param cmd: command to execute as in ``waf cmd`` + :type cmd: string + :param fun: function name to execute when the command is called + :type fun: string + + .. inheritance-diagram:: waflib.Context.Context waflib.Build.BuildContext waflib.Build.InstallContext waflib.Build.UninstallContext waflib.Build.StepContext waflib.Build.ListContext waflib.Configure.ConfigurationContext waflib.Scripting.Dist waflib.Scripting.DistCheck waflib.Build.CleanContext + + """ + + errors = Errors + """ + Shortcut to :py:mod:`waflib.Errors` provided for convenience + """ + + tools = {} + """ + A cache for modules (wscript files) read by :py:meth:`Context.Context.load` + """ + + def __init__(self, **kw): + try: + rd = kw['run_dir'] + except KeyError: + global run_dir + rd = run_dir + + # binds the context to the nodes in use to avoid a context singleton + class node_class(waflib.Node.Node): + pass + self.node_class = node_class + self.node_class.__module__ = "waflib.Node" + self.node_class.__name__ = "Nod3" + self.node_class.ctx = self + + self.root = self.node_class('', None) + self.cur_script = None + self.path = self.root.find_dir(rd) + + self.stack_path = [] + self.exec_dict = {'ctx':self, 'conf':self, 'bld':self, 'opt':self} + self.logger = None + + def __hash__(self): + """ + Return a hash value for storing context objects in dicts or sets. The value is not persistent. + + :return: hash value + :rtype: int + """ + return id(self) + + def load(self, tool_list, *k, **kw): + """ + Load a Waf tool as a module, and try calling the function named :py:const:`waflib.Context.Context.fun` from it. + A ``tooldir`` value may be provided as a list of module paths. + + :type tool_list: list of string or space-separated string + :param tool_list: list of Waf tools to use + """ + tools = Utils.to_list(tool_list) + path = Utils.to_list(kw.get('tooldir', '')) + + for t in tools: + module = load_tool(t, path) + fun = getattr(module, kw.get('name', self.fun), None) + if fun: + fun(self) + + def execute(self): + """ + Execute the command. Redefine this method in subclasses. + """ + global g_module + self.recurse([os.path.dirname(g_module.root_path)]) + + def pre_recurse(self, node): + """ + Method executed immediately before a folder is read by :py:meth:`waflib.Context.Context.recurse`. The node given is set + as an attribute ``self.cur_script``, and as the current path ``self.path`` + + :param node: script + :type node: :py:class:`waflib.Node.Node` + """ + self.stack_path.append(self.cur_script) + + self.cur_script = node + self.path = node.parent + + def post_recurse(self, node): + """ + Restore ``self.cur_script`` and ``self.path`` right after :py:meth:`waflib.Context.Context.recurse` terminates. + + :param node: script + :type node: :py:class:`waflib.Node.Node` + """ + self.cur_script = self.stack_path.pop() + if self.cur_script: + self.path = self.cur_script.parent + + def recurse(self, dirs, name=None, mandatory=True, once=True): + """ + Run user code from the supplied list of directories. + The directories can be either absolute, or relative to the directory + of the wscript file. The methods :py:meth:`waflib.Context.Context.pre_recurse` and :py:meth:`waflib.Context.Context.post_recurse` + are called immediately before and after a script has been executed. + + :param dirs: List of directories to visit + :type dirs: list of string or space-separated string + :param name: Name of function to invoke from the wscript + :type name: string + :param mandatory: whether sub wscript files are required to exist + :type mandatory: bool + :param once: read the script file once for a particular context + :type once: bool + """ + try: + cache = self.recurse_cache + except: + cache = self.recurse_cache = {} + + for d in Utils.to_list(dirs): + + if not os.path.isabs(d): + # absolute paths only + d = os.path.join(self.path.abspath(), d) + + WSCRIPT = os.path.join(d, WSCRIPT_FILE) + WSCRIPT_FUN = WSCRIPT + '_' + (name or self.fun) + + node = self.root.find_node(WSCRIPT_FUN) + if node and (not once or node not in cache): + cache[node] = True + self.pre_recurse(node) + try: + function_code = node.read('rU') + exec(compile(function_code, node.abspath(), 'exec'), self.exec_dict) + finally: + self.post_recurse(node) + elif not node: + node = self.root.find_node(WSCRIPT) + if node and (not once or node not in cache): + cache[node] = True + self.pre_recurse(node) + try: + wscript_module = load_module(node.abspath()) + user_function = getattr(wscript_module, (name or self.fun), None) + if not user_function: + if not mandatory: + continue + raise Errors.WafError('No function %s defined in %s' % (name or self.fun, node.abspath())) + user_function(self) + finally: + self.post_recurse(node) + elif not node: + if not mandatory: + continue + raise Errors.WafError('No wscript file in directory %s' % d) + + def exec_command(self, cmd, **kw): + """ + Execute a command and return the exit status. If the context has the attribute 'log', + capture and log the process stderr/stdout for logging purposes:: + + def run(tsk): + ret = tsk.generator.bld.exec_command('touch foo.txt') + return ret + + Do not confuse this method with :py:meth:`waflib.Context.Context.cmd_and_log` which is used to + return the standard output/error values. + + :param cmd: command argument for subprocess.Popen + :param kw: keyword arguments for subprocess.Popen + """ + subprocess = Utils.subprocess + kw['shell'] = isinstance(cmd, str) + Logs.debug('runner: %r' % cmd) + Logs.debug('runner_env: kw=%s' % kw) + + try: + if self.logger: + # warning: may deadlock with a lot of output (subprocess limitation) + + self.logger.info(cmd) + + kw['stdout'] = kw['stderr'] = subprocess.PIPE + p = subprocess.Popen(cmd, **kw) + (out, err) = p.communicate() + if out: + self.logger.debug('out: %s' % out.decode(sys.stdout.encoding or 'iso8859-1')) + if err: + self.logger.error('err: %s' % err.decode(sys.stdout.encoding or 'iso8859-1')) + return p.returncode + else: + p = subprocess.Popen(cmd, **kw) + return p.wait() + except OSError: + return -1 + + def cmd_and_log(self, cmd, **kw): + """ + Execute a command and return stdout if the execution is successful. + An exception is thrown when the exit status is non-0. In that case, both stderr and stdout + will be bound to the WafError object:: + + def configure(conf): + out = conf.cmd_and_log(['echo', 'hello'], output=waflib.Context.STDOUT, quiet=waflib.Context.BOTH) + (out, err) = conf.cmd_and_log(['echo', 'hello'], output=waflib.Context.BOTH) + try: + conf.cmd_and_log(['which', 'someapp'], output=waflib.Context.BOTH) + except Exception as e: + print(e.stdout, e.stderr) + + :param cmd: args for subprocess.Popen + :param kw: keyword arguments for subprocess.Popen + """ + subprocess = Utils.subprocess + kw['shell'] = isinstance(cmd, str) + Logs.debug('runner: %r' % cmd) + + if 'quiet' in kw: + quiet = kw['quiet'] + del kw['quiet'] + else: + quiet = None + + if 'output' in kw: + to_ret = kw['output'] + del kw['output'] + else: + to_ret = STDOUT + + kw['stdout'] = kw['stderr'] = subprocess.PIPE + if quiet is None: + self.to_log(cmd) + try: + p = subprocess.Popen(cmd, **kw) + (out, err) = p.communicate() + except Exception as e: + try: + self.to_log(str(err)) + except: + pass + raise Errors.WafError('Execution failure', ex=e) + + if not isinstance(out, str): + out = out.decode(sys.stdout.encoding or 'iso8859-1') + if not isinstance(err, str): + err = err.decode(sys.stdout.encoding or 'iso8859-1') + + if out and quiet != STDOUT and quiet != BOTH: + self.to_log('out: %s' % out) + if err and quiet != STDERR and quiet != BOTH: + self.to_log('err: %s' % err) + + if p.returncode: + e = Errors.WafError('command %r returned %r' % (cmd, p.returncode)) + e.returncode = p.returncode + e.stderr = err + e.stdout = out + raise e + + if to_ret == BOTH: + return (out, err) + elif to_ret == STDERR: + return err + return out + + def fatal(self, msg, ex=None): + """ + Raise a configuration error to interrupt the execution immediately:: + + def configure(conf): + conf.fatal('a requirement is missing') + + :param msg: message to display + :type msg: string + :param ex: optional exception object + :type ex: exception + """ + if self.logger: + self.logger.info('from %s: %s' % (self.path.abspath(), msg)) + try: + msg = '%s\n(complete log in %s)' % (msg, self.logger.handlers[0].baseFilename) + except: + pass + raise self.errors.ConfigurationError(msg, ex=ex) + + def to_log(self, msg): + """ + Log some information to the logger (if present), or to stderr. If the message is empty, + it is not printed:: + + def build(bld): + bld.to_log('starting the build') + + When in doubt, override this method, or provide a logger on the context class. + + :param msg: message + :type msg: string + """ + if not msg: + return + if self.logger: + self.logger.info(msg) + else: + sys.stderr.write(str(msg)) + sys.stderr.flush() + + + def msg(self, msg, result, color=None): + """ + Print a configuration message of the form ``msg: result``. + The second part of the message will be in colors. The output + can be disabled easly by setting ``in_msg`` to a positive value:: + + def configure(conf): + self.in_msg = 1 + conf.msg('Checking for library foo', 'ok') + # no output + + :param msg: message to display to the user + :type msg: string + :param result: result to display + :type result: string or boolean + :param color: color to use, see :py:const:`waflib.Logs.colors_lst` + :type color: string + """ + self.start_msg(msg) + + if not isinstance(color, str): + color = result and 'GREEN' or 'YELLOW' + + self.end_msg(result, color) + + def start_msg(self, msg): + """ + Print the beginning of a 'Checking for xxx' message. See :py:meth:`waflib.Context.Context.msg` + """ + try: + if self.in_msg: + self.in_msg += 1 + return + except: + self.in_msg = 0 + self.in_msg += 1 + + try: + self.line_just = max(self.line_just, len(msg)) + except AttributeError: + self.line_just = max(40, len(msg)) + for x in (self.line_just * '-', msg): + self.to_log(x) + Logs.pprint('NORMAL', "%s :" % msg.ljust(self.line_just), sep='') + + def end_msg(self, result, color=None): + """Print the end of a 'Checking for' message. See :py:meth:`waflib.Context.Context.msg`""" + self.in_msg -= 1 + if self.in_msg: + return + + defcolor = 'GREEN' + if result == True: + msg = 'ok' + elif result == False: + msg = 'not found' + defcolor = 'YELLOW' + else: + msg = str(result) + + self.to_log(msg) + Logs.pprint(color or defcolor, msg) + + + def load_special_tools(self, var, ban=[]): + global waf_dir + lst = self.root.find_node(waf_dir).find_node('waflib/extras').ant_glob(var) + for x in lst: + if not x.name in ban: + load_tool(x.name.replace('.py', '')) + +cache_modules = {} +""" +Dictionary holding already loaded modules, keyed by their absolute path. +The modules are added automatically by :py:func:`waflib.Context.load_module` +""" + +def load_module(path): + """ + Load a source file as a python module. + + :param path: file path + :type path: string + :return: Loaded Python module + :rtype: module + """ + try: + return cache_modules[path] + except KeyError: + pass + + module = imp.new_module(WSCRIPT_FILE) + try: + code = Utils.readf(path, m='rU') + except (IOError, OSError): + raise Errors.WafError('Could not read the file %r' % path) + + module_dir = os.path.dirname(path) + sys.path.insert(0, module_dir) + + exec(compile(code, path, 'exec'), module.__dict__) + sys.path.remove(module_dir) + + cache_modules[path] = module + + return module + +def load_tool(tool, tooldir=None): + """ + Import a Waf tool (python module), and store it in the dict :py:const:`waflib.Context.Context.tools` + + :type tool: string + :param tool: Name of the tool + :type tooldir: list + :param tooldir: List of directories to search for the tool module + """ + tool = tool.replace('++', 'xx') + tool = tool.replace('java', 'javaw') + tool = tool.replace('compiler_cc', 'compiler_c') + + if tooldir: + assert isinstance(tooldir, list) + sys.path = tooldir + sys.path + try: + __import__(tool) + ret = sys.modules[tool] + Context.tools[tool] = ret + return ret + finally: + for d in tooldir: + sys.path.remove(d) + else: + global waf_dir + try: + os.stat(os.path.join(waf_dir, 'waflib', 'extras', tool + '.py')) + d = 'waflib.extras.%s' % tool + except: + try: + os.stat(os.path.join(waf_dir, 'waflib', 'Tools', tool + '.py')) + d = 'waflib.Tools.%s' % tool + except: + d = tool # user has messed with sys.path + + __import__(d) + ret = sys.modules[d] + Context.tools[tool] = ret + return ret + diff --git a/waflib/Errors.py b/waflib/Errors.py new file mode 100644 index 00000000..104f7d82 --- /dev/null +++ b/waflib/Errors.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +""" +Exceptions used in the Waf code +""" + +import traceback, sys + +class WafError(Exception): + """Base class for all Waf errors""" + def __init__(self, msg='', ex=None): + """ + :param msg: error message + :type msg: string + :param ex: exception causing this error (optional) + :type ex: exception + """ + self.msg = msg + assert not isinstance(msg, Exception) + + self.stack = [] + if ex: + if not msg: + self.msg = str(ex) + if isinstance(ex, WafError): + self.stack = ex.stack + else: + self.stack = traceback.extract_tb(sys.exc_info()[2]) + self.stack += traceback.extract_stack()[:-1] + self.verbose_msg = ''.join(traceback.format_list(self.stack)) + + def __str__(self): + return str(self.msg) + +class BuildError(WafError): + """ + Errors raised during the build and install phases + """ + def __init__(self, error_tasks=[]): + """ + :param error_tasks: tasks that could not complete normally + :type error_tasks: list of task objects + """ + self.tasks = error_tasks + WafError.__init__(self, self.format_error()) + + def format_error(self): + """format the error messages from the tasks that failed""" + lst = ['Build failed'] + for tsk in self.tasks: + txt = tsk.format_error() + if txt: lst.append(txt) + return '\n'.join(lst) + +class ConfigurationError(WafError): + """ + Configuration exception raised in particular by :py:meth:`waflib.Context.Context.fatal` + """ + pass + +class TaskRescan(WafError): + """task-specific exception type, trigger a signature recomputation""" + pass + +class TaskNotReady(WafError): + """task-specific exception type, raised when the task signature cannot be computed""" + pass + diff --git a/waflib/Logs.py b/waflib/Logs.py new file mode 100644 index 00000000..a88ef0a9 --- /dev/null +++ b/waflib/Logs.py @@ -0,0 +1,274 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 (ita) + +""" +logging, colors, terminal width and pretty-print +""" + +import os, re, traceback, sys + +_nocolor = os.environ.get('NOCOLOR', 'no') not in ('no', '0', 'false') +try: + if not _nocolor: + import waflib.ansiterm +except: + # optional module for colors on win32, just ignore if it cannot be imported + pass + +import logging # do it after + +LOG_FORMAT = "%(asctime)s %(c1)s%(zone)s%(c2)s %(message)s" +HOUR_FORMAT = "%H:%M:%S" + +zones = '' +verbose = 0 + +colors_lst = { +'USE' : True, +'BOLD' :'\x1b[01;1m', +'RED' :'\x1b[01;31m', +'GREEN' :'\x1b[32m', +'YELLOW':'\x1b[33m', +'PINK' :'\x1b[35m', +'BLUE' :'\x1b[01;34m', +'CYAN' :'\x1b[36m', +'NORMAL':'\x1b[0m', +'cursor_on' :'\x1b[?25h', +'cursor_off' :'\x1b[?25l', +} + +got_tty = not os.environ.get('TERM', 'dumb') in ['dumb', 'emacs'] +if got_tty: + try: + got_tty = sys.stderr.isatty() + except AttributeError: + got_tty = False + +if (not got_tty and os.environ.get('TERM', 'dumb') != 'msys') or _nocolor: + colors_lst['USE'] = False + +def get_term_cols(): + return 80 + +# If console packages are available, replace the dummy function with a real +# implementation +try: + import struct, fcntl, termios +except ImportError: + pass +else: + if got_tty: + def get_term_cols_real(): + """ + Private use only. + """ + + dummy_lines, cols = struct.unpack("HHHH", \ + fcntl.ioctl(sys.stderr.fileno(),termios.TIOCGWINSZ , \ + struct.pack("HHHH", 0, 0, 0, 0)))[:2] + return cols + # try the function once to see if it really works + try: + get_term_cols_real() + except: + pass + else: + get_term_cols = get_term_cols_real + +get_term_cols.__doc__ = """ + Get the console width in characters. + + :return: the number of characters per line + :rtype: int + """ + +def get_color(cl): + if not colors_lst['USE']: return '' + return colors_lst.get(cl, '') + +class color_dict(object): + """attribute-based color access, eg: colors.PINK""" + def __getattr__(self, a): + return get_color(a) + def __call__(self, a): + return get_color(a) + +colors = color_dict() + +re_log = re.compile(r'(\w+): (.*)', re.M) +class log_filter(logging.Filter): + """ + The waf logs are of the form 'name: message', and can be filtered by 'waf --zones=name'. + For example, the following:: + + from waflib import Logs + Logs.debug('test: here is a message') + + Will be displayed only when executing:: + + $ waf --zones=test + """ + def __init__(self, name=None): + pass + + def filter(self, rec): + """ + filter a record, adding the colors automatically + + * error: red + * warning: yellow + + :param rec: message to record + """ + + rec.c1 = colors.PINK + rec.c2 = colors.NORMAL + rec.zone = rec.module + if rec.levelno >= logging.INFO: + if rec.levelno >= logging.ERROR: + rec.c1 = colors.RED + elif rec.levelno >= logging.WARNING: + rec.c1 = colors.YELLOW + else: + rec.c1 = colors.GREEN + return True + + m = re_log.match(rec.msg) + if m: + rec.zone = m.group(1) + rec.msg = m.group(2) + + if zones: + return getattr(rec, 'zone', '') in zones or '*' in zones + elif not verbose > 2: + return False + return True + +class formatter(logging.Formatter): + """Simple log formatter which handles colors""" + def __init__(self): + logging.Formatter.__init__(self, LOG_FORMAT, HOUR_FORMAT) + + def format(self, rec): + """Messages in warning, error or info mode are displayed in color by default""" + if rec.levelno >= logging.WARNING or rec.levelno == logging.INFO: + try: + msg = rec.msg.decode('utf-8') + except: + msg = rec.msg + return '%s%s%s' % (rec.c1, msg, rec.c2) + return logging.Formatter.format(self, rec) + +log = None +"""global logger for Logs.debug, Logs.error, etc""" + +def debug(*k, **kw): + """ + Wrap logging.debug, the output is filtered for performance reasons + """ + if verbose: + k = list(k) + k[0] = k[0].replace('\n', ' ') + global log + log.debug(*k, **kw) + +def error(*k, **kw): + """ + Wrap logging.errors, display the origin of the message when '-vv' is set + """ + global log + log.error(*k, **kw) + if verbose > 2: + st = traceback.extract_stack() + if st: + st = st[:-1] + buf = [] + for filename, lineno, name, line in st: + buf.append(' File "%s", line %d, in %s' % (filename, lineno, name)) + if line: + buf.append(' %s' % line.strip()) + if buf: log.error("\n".join(buf)) + +def warn(*k, **kw): + """ + Wrap logging.warn + """ + global log + log.warn(*k, **kw) + +def info(*k, **kw): + """ + Wrap logging.info + """ + global log + log.info(*k, **kw) + +def init_log(): + """ + Initialize the loggers globally + """ + global log + log = logging.getLogger('waflib') + log.handlers = [] + log.filters = [] + hdlr = logging.StreamHandler() + hdlr.setFormatter(formatter()) + log.addHandler(hdlr) + log.addFilter(log_filter()) + log.setLevel(logging.DEBUG) + +def make_logger(path, name): + """ + Create a simple logger, which is often used to redirect the context command output:: + + from waflib import Logs + bld.logger = Logs.make_logger('test.log', 'build') + bld.check(header_name='sadlib.h', features='cxx cprogram', mandatory=False) + bld.logger = None + + :param path: file name to write the log output to + :type path: string + :param name: logger name (loggers are reused) + :type name: string + """ + logger = logging.getLogger(name) + hdlr = logging.FileHandler(path, 'w') + formatter = logging.Formatter('%(message)s') + hdlr.setFormatter(formatter) + logger.addHandler(hdlr) + logger.setLevel(logging.DEBUG) + return logger + +def make_mem_logger(name, to_log, size=10000): + """ + Create a memory logger to avoid writing concurrently to the main logger + """ + from logging.handlers import MemoryHandler + logger = logging.getLogger(name) + hdlr = MemoryHandler(size, target=to_log) + formatter = logging.Formatter('%(message)s') + hdlr.setFormatter(formatter) + logger.addHandler(hdlr) + logger.memhandler = hdlr + logger.setLevel(logging.DEBUG) + return logger + +def pprint(col, str, label='', sep='\n'): + """ + Print messages in color immediately on stderr:: + + from waflib import Logs + Logs.pprint('RED', 'Something bad just happened') + + :param col: color name to use in :py:const:`Logs.colors_lst` + :type col: string + :param str: message to display + :type str: string or a value that can be printed by %s + :param label: a message to add after the colored output + :type label: string + :param sep: a string to append at the end (line separator) + :type sep: string + """ + sys.stderr.write("%s%s%s %s%s" % (colors(col), str, colors.NORMAL, label, sep)) + diff --git a/waflib/Node.py b/waflib/Node.py new file mode 100644 index 00000000..bbd6c587 --- /dev/null +++ b/waflib/Node.py @@ -0,0 +1,836 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 (ita) + +""" +Node: filesystem structure, contains lists of nodes + +#. Each file/folder is represented by exactly one node. + +#. Some potential class properties are stored on :py:class:`waflib.Build.BuildContext` : nodes to depend on, etc. + Unused class members can increase the `.wafpickle` file size sensibly. + +#. Node objects should never be created directly, use + the methods :py:func:`Node.make_node` or :py:func:`Node.find_node` + +#. The methods :py:func:`Node.find_resource`, :py:func:`Node.find_dir` :py:func:`Node.find_or_declare` should be + used when a build context is present + +#. Each instance of :py:class:`waflib.Context.Context` has a unique :py:class:`Node` subclass. + (:py:class:`waflib.Node.Nod3`, see the :py:class:`waflib.Context.Context` initializer). A reference to the context owning a node is held as self.ctx +""" + +import os, re, sys, shutil +from waflib import Utils, Errors + +exclude_regs = ''' +**/*~ +**/#*# +**/.#* +**/%*% +**/._* +**/CVS +**/CVS/** +**/.cvsignore +**/SCCS +**/SCCS/** +**/vssver.scc +**/.svn +**/.svn/** +**/BitKeeper +**/.git +**/.git/** +**/.gitignore +**/.bzr +**/.bzrignore +**/.bzr/** +**/.hg +**/.hg/** +**/_MTN +**/_MTN/** +**/.arch-ids +**/{arch} +**/_darcs +**/_darcs/** +**/.DS_Store''' +""" +Ant patterns for files and folders to exclude while doing the +recursive traversal in :py:meth:`waflib.Node.Node.ant_glob` +""" + +# TODO optimize split_path by performing a replacement when unpacking? + +def split_path(path): + """ + Split a path by os.sep (This is not os.path.split) + + :param path: path to split + :type path: string + :rtype: list of string + :return: the path, split + """ + return path.split('/') + +def split_path_cygwin(path): + if path.startswith('//'): + ret = path.split('/')[2:] + ret[0] = '/' + ret[0] + return ret + return path.split('/') + +re_sp = re.compile('[/\\\\]') +def split_path_win32(path): + if path.startswith('\\\\'): + ret = re.split(re_sp, path)[2:] + ret[0] = '\\' + ret[0] + return ret + return re.split(re_sp, path) + +if sys.platform == 'cygwin': + split_path = split_path_cygwin +elif Utils.is_win32: + split_path = split_path_win32 + +class Node(object): + """ + This class is organized in two parts + + * The basic methods meant for filesystem access (compute paths, create folders, etc) + * The methods bound to a :py:class:`waflib.Build.BuildContext` (require ``bld.srcnode`` and ``bld.bldnode``) + """ + + __slots__ = ('name', 'sig', 'children', 'parent', 'cache_abspath', 'cache_isdir') + def __init__(self, name, parent): + self.name = name + self.parent = parent + + if parent: + if name in parent.children: + raise Errors.WafError('node %s exists in the parent files %r already' % (name, parent)) + parent.children[name] = self + + def __setstate__(self, data): + "Deserializes from data" + self.name = data[0] + self.parent = data[1] + if data[2] is not None: + self.children = data[2] + if data[3] is not None: + self.sig = data[3] + + def __getstate__(self): + "Serialize the node info" + return (self.name, self.parent, getattr(self, 'children', None), getattr(self, 'sig', None)) + + def __str__(self): + "String representation (name), for debugging purposes" + return self.name + + def __repr__(self): + "String representation (abspath), for debugging purposes" + return self.abspath() + + def __hash__(self): + "Node hash, used for storage in dicts. This hash is not persistent." + return id(self) + + def __eq__(self, node): + "Node comparison, based on the IDs" + return id(self) == id(node) + + def __copy__(self): + "Implemented to prevent nodes from being copied (raises an exception)" + raise Errors.WafError('nodes are not supposed to be copied') + + def read(self, flags='r'): + """ + Return the contents of the file represented by this node:: + + def build(bld): + bld.path.find_node('wscript').read() + + :type fname: string + :param fname: Path to file + :type m: string + :param m: Open mode + :rtype: string + :return: File contents + """ + return Utils.readf(self.abspath(), flags) + + def write(self, data, flags='w'): + """ + Write some text to the physical file represented by this node:: + + def build(bld): + bld.path.make_node('foo.txt').write('Hello, world!') + + :type data: string + :param data: data to write + :type flags: string + :param flags: Write mode + """ + f = None + try: + f = open(self.abspath(), flags) + f.write(data) + finally: + if f: + f.close() + + def chmod(self, val): + """ + Change file/dir permissions:: + + def build(bld): + bld.path.chmod(493) # 0755 + """ + os.chmod(self.abspath(), val) + + def delete(self): + """Delete the file/folder physically (but not the node)""" + try: + if getattr(self, 'children', None): + shutil.rmtree(self.abspath()) + else: + os.unlink(self.abspath()) + except: + pass + + try: + delattr(self, 'children') + except: + pass + + def suffix(self): + """Return the file extension""" + k = max(0, self.name.rfind('.')) + return self.name[k:] + + def height(self): + """Depth in the folder hierarchy from the filesystem root or from all the file drives""" + d = self + val = -1 + while d: + d = d.parent + val += 1 + return val + + def listdir(self): + """List the folder contents""" + lst = Utils.listdir(self.abspath()) + lst.sort() + return lst + + def mkdir(self): + """Create a folder represented by this node""" + if getattr(self, 'cache_isdir', None): + return + + try: + self.parent.mkdir() + except: + pass + + if self.name: + try: + os.makedirs(self.abspath()) + except OSError: + pass + + if not os.path.isdir(self.abspath()): + raise Errors.WafError('Could not create the directory %s' % self.abspath()) + + try: + self.children + except: + self.children = {} + + self.cache_isdir = True + + def find_node(self, lst): + """ + Find a node on the file system (files or folders), create intermediate nodes as needed + + :param lst: path + :type lst: string or list of string + """ + + if isinstance(lst, str): + lst = [x for x in split_path(lst) if x and x != '.'] + + cur = self + for x in lst: + if x == '..': + cur = cur.parent or cur + continue + + try: + if x in cur.children: + cur = cur.children[x] + continue + except: + cur.children = {} + + # optimistic: create the node first then look if it was correct to do so + cur = self.__class__(x, cur) + try: + os.stat(cur.abspath()) + except: + del cur.parent.children[x] + return None + + ret = cur + + try: + while not getattr(cur.parent, 'cache_isdir', None): + cur = cur.parent + cur.cache_isdir = True + except AttributeError: + pass + + return ret + + def make_node(self, lst): + """ + Find or create a node without looking on the filesystem + + :param lst: path + :type lst: string or list of string + """ + if isinstance(lst, str): + lst = [x for x in split_path(lst) if x and x != '.'] + + cur = self + for x in lst: + if x == '..': + cur = cur.parent or cur + continue + + if getattr(cur, 'children', {}): + if x in cur.children: + cur = cur.children[x] + continue + else: + cur.children = {} + cur = self.__class__(x, cur) + return cur + + def search(self, lst): + """ + Search for a node without looking on the filesystem + + :param lst: path + :type lst: string or list of string + """ + if isinstance(lst, str): + lst = [x for x in split_path(lst) if x and x != '.'] + + cur = self + try: + for x in lst: + if x == '..': + cur = cur.parent or cur + else: + cur = cur.children[x] + return cur + except: + pass + + def path_from(self, node): + """ + Path of this node seen from the other:: + + def build(bld): + n1 = bld.path.find_node('foo/bar/xyz.txt') + n2 = bld.path.find_node('foo/stuff/') + n1.path_from(n2) # './bar/xyz.txt' + + :param node: path to use as a reference + :type node: :py:class:`waflib.Node.Node` + """ + + c1 = self + c2 = node + + c1h = c1.height() + c2h = c2.height() + + lst = [] + up = 0 + + while c1h > c2h: + lst.append(c1.name) + c1 = c1.parent + c1h -= 1 + + while c2h > c1h: + up += 1 + c2 = c2.parent + c2h -= 1 + + while id(c1) != id(c2): + lst.append(c1.name) + up += 1 + + c1 = c1.parent + c2 = c2.parent + + for i in range(up): + lst.append('..') + lst.reverse() + return os.sep.join(lst) or '.' + + def abspath(self): + """ + Absolute path. A cache is kept in the context as ``cache_node_abspath`` + """ + try: + return self.cache_abspath + except: + pass + # think twice before touching this (performance + complexity + correctness) + + if os.sep == '/': + if not self.parent: + val = os.sep + elif not self.parent.name: + val = os.sep + self.name + else: + val = self.parent.abspath() + os.sep + self.name + else: + if not self.parent: + val = '' + elif not self.parent.name: + val = self.name + os.sep + else: + val = self.parent.abspath().rstrip(os.sep) + os.sep + self.name + + self.cache_abspath = val + return val + + def is_child_of(self, node): + """ + Does this node belong to the subtree node?:: + + def build(bld): + node = bld.path.find_node('wscript') + node.is_child_of(bld.path) # True + + :param node: path to use as a reference + :type node: :py:class:`waflib.Node.Node` + """ + p = self + diff = self.height() - node.height() + while diff > 0: + diff -= 1 + p = p.parent + return id(p) == id(node) + + def ant_iter(self, accept=None, maxdepth=25, pats=[], dir=False, src=True, remove=True): + """ + Semi-private and recursive method used by ant_glob. + + :param accept: function used for accepting/rejecting a node, returns the patterns that can be still accepted in recursion + :type accept: function + :param maxdepth: maximum depth in the filesystem (25) + :type maxdepth: int + :param pats: list of patterns to accept and list of patterns to exclude + :type pats: tuple + :param dir: return folders too (False by default) + :type dir: bool + :param src: return files (True by default) + :type src: bool + :param remove: remove files/folders that do not exist (True by default) + :type remove: bool + """ + dircont = self.listdir() + dircont.sort() + + try: + lst = set(self.children.keys()) + if remove: + for x in lst - set(dircont): + del self.children[x] + except: + self.children = {} + + for name in dircont: + npats = accept(name, pats) + if npats and npats[0]: + accepted = [] in npats[0] + + node = self.make_node([name]) + + isdir = os.path.isdir(node.abspath()) + if accepted: + if isdir: + if dir: + yield node + else: + if src: + yield node + + if getattr(node, 'cache_isdir', None) or isdir: + node.cache_isdir = True + if maxdepth: + for k in node.ant_iter(accept=accept, maxdepth=maxdepth - 1, pats=npats, dir=dir, src=src): + yield k + raise StopIteration + + def ant_glob(self, *k, **kw): + """ + This method is used for finding files across folders. It behaves like ant patterns: + + * ``**/*`` find all files recursively + * ``**/*.class`` find all files ending by .class + * ``..`` find files having two dot characters + + For example:: + + def configure(cfg): + cfg.path.ant_glob('**/*.cpp') # find all .cpp files + cfg.root.ant_glob('etc/*.txt') # using the filesystem root can be slow + cfg.path.ant_glob('*.cpp', excl=['*.c'], src=True, dir=False) + + For more information see http://ant.apache.org/manual/dirtasks.html + + The nodes that correspond to files and folders that do not exist will be removed + + :param incl: ant patterns or list of patterns to include + :type incl: string or list of strings + :param excl: ant patterns or list of patterns to exclude + :type excl: string or list of strings + :param dir: return folders too (False by default) + :type dir: bool + :param src: return files (True by default) + :type src: bool + :param remove: remove files/folders that do not exist (True by default) + :type remove: bool + :param maxdepth: maximum depth of recursion + :type maxdepth: int + """ + + src = kw.get('src', True) + dir = kw.get('dir', False) + + excl = kw.get('excl', exclude_regs) + incl = k and k[0] or kw.get('incl', '**') + + def to_pat(s): + lst = Utils.to_list(s) + ret = [] + for x in lst: + x = x.replace('\\', '/').replace('//', '/') + if x.endswith('/'): + x += '**' + lst2 = x.split('/') + accu = [] + for k in lst2: + if k == '**': + accu.append(k) + else: + k = k.replace('.', '[.]').replace('*','.*').replace('?', '.').replace('+', '\\+') + k = '^%s$' % k + try: + #print "pattern", k + accu.append(re.compile(k)) + except Exception as e: + raise Errors.WafError("Invalid pattern: %s" % k, e) + ret.append(accu) + return ret + + def filtre(name, nn): + ret = [] + for lst in nn: + if not lst: + pass + elif lst[0] == '**': + ret.append(lst) + if len(lst) > 1: + if lst[1].match(name): + ret.append(lst[2:]) + else: + ret.append([]) + elif lst[0].match(name): + ret.append(lst[1:]) + return ret + + def accept(name, pats): + nacc = filtre(name, pats[0]) + nrej = filtre(name, pats[1]) + if [] in nrej: + nacc = [] + return [nacc, nrej] + + ret = [x for x in self.ant_iter(accept=accept, pats=[to_pat(incl), to_pat(excl)], maxdepth=25, dir=dir, src=src, remove=kw.get('remove', True))] + if kw.get('flat', False): + return ' '.join([x.path_from(self) for x in ret]) + + return ret + + def find_nodes(self, find_dirs=True, find_files=True, match_fun=lambda x: True): + # FIXME not part of the stable API: find_node vs find_nodes? consistency with argument names on other functions? + x = """ + Recursively finds nodes:: + + def configure(cnf): + cnf.find_nodes() + + :param find_dirs: whether to return directories + :param find_files: whether to return files + :param match_fun: matching function, taking a node as parameter + :rtype generator + :return: a generator that iterates over all the requested files + """ + files = self.listdir() + for f in files: + node = self.make_node([f]) + if os.path.isdir(node.abspath()): + if find_dirs and match_fun(node): + yield node + gen = node.find_nodes(find_dirs, find_files, match_fun) + for g in gen: + yield g + else: + if find_files and match_fun(node): + yield node + + + # -------------------------------------------------------------------------------- + # the following methods require the source/build folders (bld.srcnode/bld.bldnode) + # using a subclass is a possibility, but is that really necessary? + # -------------------------------------------------------------------------------- + + def is_src(self): + """ + True if the node is below the source directory + note: !is_src does not imply is_bld() + + :rtype: bool + """ + cur = self + x = id(self.ctx.srcnode) + y = id(self.ctx.bldnode) + while cur.parent: + if id(cur) == y: + return False + if id(cur) == x: + return True + cur = cur.parent + return False + + def is_bld(self): + """ + True if the node is below the build directory + note: !is_bld does not imply is_src + + :rtype: bool + """ + cur = self + y = id(self.ctx.bldnode) + while cur.parent: + if id(cur) == y: + return True + cur = cur.parent + return False + + def get_src(self): + """ + Return the equivalent src node (or self if not possible) + + :rtype: :py:class:`waflib.Node.Node` + """ + cur = self + x = id(self.ctx.srcnode) + y = id(self.ctx.bldnode) + lst = [] + while cur.parent: + if id(cur) == y: + lst.reverse() + return self.ctx.srcnode.make_node(lst) + if id(cur) == x: + return self + lst.append(cur.name) + cur = cur.parent + return self + + def get_bld(self): + """ + Return the equivalent bld node (or self if not possible) + + :rtype: :py:class:`waflib.Node.Node` + """ + cur = self + x = id(self.ctx.srcnode) + y = id(self.ctx.bldnode) + lst = [] + while cur.parent: + if id(cur) == y: + return self + if id(cur) == x: + lst.reverse() + return self.ctx.bldnode.make_node(lst) + lst.append(cur.name) + cur = cur.parent + return self + + def find_resource(self, lst): + """ + Try to find a declared build node or a source file + + :param lst: path + :type lst: string or list of string + """ + if isinstance(lst, str): + lst = [x for x in split_path(lst) if x and x != '.'] + + node = self.get_bld().search(lst) + if not node: + self = self.get_src() + node = self.search(lst) + if not node: + node = self.find_node(lst) + try: + pat = node.abspath() + if os.path.isdir(pat): + return None + except: + pass + return node + + def find_or_declare(self, lst): + """ + if 'self' is in build directory, try to return an existing node + if no node is found, go to the source directory + try to find an existing node in the source directory + if no node is found, create it in the build directory + + :param lst: path + :type lst: string or list of string + """ + if isinstance(lst, str): + lst = [x for x in split_path(lst) if x and x != '.'] + + node = self.get_bld().search(lst) + if node: + if not os.path.isfile(node.abspath()): + node.sig = None + try: + node.parent.mkdir() + except: + pass + return node + self = self.get_src() + node = self.find_node(lst) + if node: + if not os.path.isfile(node.abspath()): + node.sig = None + try: + node.parent.mkdir() + except: + pass + return node + node = self.get_bld().make_node(lst) + node.parent.mkdir() + return node + + def find_dir(self, lst): + """ + Search for a folder in the filesystem + + :param lst: path + :type lst: string or list of string + """ + if isinstance(lst, str): + lst = [x for x in split_path(lst) if x and x != '.'] + + node = self.find_node(lst) + try: + if not os.path.isdir(node.abspath()): + return None + except (OSError, AttributeError): + # the node might be None, and raise an AttributeError + return None + return node + + # helpers for building things + def change_ext(self, ext, ext_in=None): + """ + :return: A build node of the same path, but with a different extension + :rtype: :py:class:`waflib.Node.Node` + """ + name = self.name + if ext_in is None: + k = name.rfind('.') + if k >= 0: + name = name[:k] + ext + else: + name = name + ext + else: + name = name[:- len(ext_in)] + ext + + return self.parent.find_or_declare([name]) + + def nice_path(self, env=None): + """ + Return the path seen from the launch directory. It is often used for printing nodes in the console to open + files easily. + + :param env: unused, left for compatibility with waf 1.5 + """ + return self.path_from(self.ctx.launch_node()) + + def bldpath(self): + "Path seen from the build directory default/src/foo.cpp" + return self.path_from(self.ctx.bldnode) + + def srcpath(self): + "Path seen from the source directory ../src/foo.cpp" + return self.path_from(self.ctx.srcnode) + + def relpath(self): + "If a file in the build directory, bldpath, else srcpath" + cur = self + x = id(self.ctx.bldnode) + while cur.parent: + if id(cur) == x: + return self.bldpath() + cur = cur.parent + return self.srcpath() + + def bld_dir(self): + "Build path without the file name" + return self.parent.bldpath() + + def bld_base(self): + "Build path without the extension: src/dir/foo(.cpp)" + s = os.path.splitext(self.name)[0] + return self.bld_dir() + os.sep + s + + def get_bld_sig(self): + """ + Node signature, assuming the file is in the build directory + """ + try: + ret = self.ctx.hash_cache[id(self)] + except KeyError: + pass + except AttributeError: + self.ctx.hash_cache = {} + else: + return ret + + if not self.is_bld() or self.ctx.bldnode is self.ctx.srcnode: + self.sig = Utils.h_file(self.abspath()) + self.ctx.hash_cache[id(self)] = ret = self.sig + return ret + +pickle_lock = Utils.threading.Lock() +"""Lock mandatory for thread-safe node serialization""" + +class Nod3(Node): + """Mandatory subclass for thread-safe node serialization""" + pass # do not remove + + diff --git a/waflib/Options.py b/waflib/Options.py new file mode 100644 index 00000000..5943f63f --- /dev/null +++ b/waflib/Options.py @@ -0,0 +1,248 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Scott Newton, 2005 (scottn) +# Thomas Nagy, 2006-2010 (ita) + +""" +Support for waf command-line options + +Provides default command-line options, +as well as custom ones, used by the ``options`` wscript function. + +""" + +import os, tempfile, optparse, sys, re +from waflib import Logs, Utils, Context + +cmds = 'distclean configure build install clean uninstall check dist distcheck'.split() +""" +Constant representing the default waf commands displayed in:: + + $ waf --help + +""" + +options = {} +""" +A dictionary representing the command-line options:: + + $ waf --foo=bar + +""" + +commands = [] +""" +List of commands to execute extracted from the command-line. This list is consumed during the execution, see :py:func:`waflib.Scripting.run_commands`. +""" + +lockfile = os.environ.get('WAFLOCK', '.lock-wafbuild%s' % sys.platform) +try: cache_global = os.path.abspath(os.environ['WAFCACHE']) +except KeyError: cache_global = '' +platform = Utils.unversioned_sys_platform() + + +class opt_parser(optparse.OptionParser): + """ + Command-line options parser. + """ + def __init__(self, ctx): + optparse.OptionParser.__init__(self, conflict_handler="resolve", version='waf %s (%s)' % (Context.WAFVERSION, Context.WAFREVISION)) + + self.formatter.width = Logs.get_term_cols() + p = self.add_option + self.ctx = ctx + + jobs = ctx.jobs() + p('-j', '--jobs', dest='jobs', default=jobs, type='int', help='amount of parallel jobs (%r)' % jobs) + p('-k', '--keep', dest='keep', default=0, action='count', help='keep running happily even if errors are found') + p('-v', '--verbose', dest='verbose', default=0, action='count', help='verbosity level -v -vv or -vvv [default: 0]') + p('--nocache', dest='nocache', default=False, action='store_true', help='ignore the WAFCACHE (if set)') + p('--zones', dest='zones', default='', action='store', help='debugging zones (task_gen, deps, tasks, etc)') + + gr = optparse.OptionGroup(self, 'configure options') + self.add_option_group(gr) + + gr.add_option('-o', '--out', action='store', default='', help='build dir for the project', dest='out') + gr.add_option('-t', '--top', action='store', default='', help='src dir for the project', dest='top') + + default_prefix = os.environ.get('PREFIX') + if not default_prefix: + if platform == 'win32': + d = tempfile.gettempdir() + default_prefix = d[0].upper() + d[1:] + # win32 preserves the case, but gettempdir does not + else: + default_prefix = '/usr/local/' + gr.add_option('--prefix', dest='prefix', default=default_prefix, help='installation prefix [default: %r]' % default_prefix) + gr.add_option('--download', dest='download', default=False, action='store_true', help='try to download the tools if missing') + + + gr = optparse.OptionGroup(self, 'build and install options') + self.add_option_group(gr) + + gr.add_option('-p', '--progress', dest='progress_bar', default=0, action='count', help= '-p: progress bar; -pp: ide output') + gr.add_option('--targets', dest='targets', default='', action='store', help='task generators, e.g. "target1,target2"') + + gr = optparse.OptionGroup(self, 'step options') + self.add_option_group(gr) + gr.add_option('--files', dest='files', default='', action='store', help='files to process, by regexp, e.g. "*/main.c,*/test/main.o"') + + default_destdir = os.environ.get('DESTDIR', '') + gr = optparse.OptionGroup(self, 'install/uninstall options') + self.add_option_group(gr) + gr.add_option('--destdir', help='installation root [default: %r]' % default_destdir, default=default_destdir, dest='destdir') + gr.add_option('-f', '--force', dest='force', default=False, action='store_true', help='force file installation') + + def get_usage(self): + """ + Return the message to print on ``waf --help`` + """ + cmds_str = {} + for cls in Context.classes: + if not cls.cmd or cls.cmd == 'options': + continue + + s = cls.__doc__ or '' + cmds_str[cls.cmd] = s + + if Context.g_module: + for (k, v) in Context.g_module.__dict__.items(): + if k in ['options', 'init', 'shutdown']: + continue + + if type(v) is type(Context.create_context): + if v.__doc__ and not k.startswith('_'): + cmds_str[k] = v.__doc__ + + just = 0 + for k in cmds_str: + just = max(just, len(k)) + + lst = [' %s: %s' % (k.ljust(just), v) for (k, v) in cmds_str.items()] + lst.sort() + ret = '\n'.join(lst) + + return '''waf [commands] [options] + +Main commands (example: ./waf build -j4) +%s +''' % ret + + +class OptionsContext(Context.Context): + """ + Collect custom options from wscript files and parses the command line. + Set the global :py:const:`waflib.Options.commands` and :py:const:`waflib.Options.options` values. + """ + + cmd = 'options' + fun = 'options' + + def __init__(self, **kw): + super(OptionsContext, self).__init__(**kw) + + self.parser = opt_parser(self) + """Instance of :py:class:`waflib.Options.opt_parser`""" + + self.option_groups = {} + + def jobs(self): + """ + Find the amount of cpu cores to set the default amount of tasks executed in parallel. At + runtime the options can be obtained from :py:const:`waflib.Options.options` :: + + from waflib.Options import options + njobs = options.jobs + + :return: the amount of cpu cores + :rtype: int + """ + count = int(os.environ.get('JOBS', 0)) + if count < 1: + if 'NUMBER_OF_PROCESSORS' in os.environ: + # on Windows, use the NUMBER_OF_PROCESSORS environment variable + count = int(os.environ.get('NUMBER_OF_PROCESSORS', 1)) + else: + # on everything else, first try the POSIX sysconf values + if hasattr(os, 'sysconf_names'): + if 'SC_NPROCESSORS_ONLN' in os.sysconf_names: + count = int(os.sysconf('SC_NPROCESSORS_ONLN')) + elif 'SC_NPROCESSORS_CONF' in os.sysconf_names: + count = int(os.sysconf('SC_NPROCESSORS_CONF')) + elif os.name not in ('nt', 'java'): + tmp = self.cmd_and_log(['sysctl', '-n', 'hw.ncpu']) + if re.match('^[0-9]+$', tmp): + count = int(tmp) + if count < 1: + count = 1 + elif count > 1024: + count = 1024 + return count + + def add_option(self, *k, **kw): + """ + Wrapper for optparse.add_option:: + + def options(ctx): + ctx.add_option('-u', '--use', dest='use', default=False, action='store_true', + help='a boolean option') + """ + self.parser.add_option(*k, **kw) + + def add_option_group(self, *k, **kw): + """ + Wrapper for optparse.add_option_group:: + + def options(ctx): + ctx.add_option_group('some options') + gr.add_option('-u', '--use', dest='use', default=False, action='store_true') + """ + try: + gr = self.option_groups[k[0]] + except: + gr = self.parser.add_option_group(*k, **kw) + self.option_groups[k[0]] = gr + return gr + + def get_option_group(self, opt_str): + """ + Wrapper for optparse.get_option_group:: + + def options(ctx): + gr = ctx.get_option_group('configure options') + gr.add_option('-o', '--out', action='store', default='', + help='build dir for the project', dest='out') + + """ + try: + return self.option_groups[opt_str] + except KeyError: + for group in self.parser.option_groups: + if group.title == opt_str: + return group + return None + + def parse_args(self, _args=None): + """ + Parse arguments from a list (not bound to the command-line). + + :param _args: arguments + :type _args: list of strings + """ + global options, commands + (options, leftover_args) = self.parser.parse_args(args=_args) + commands = leftover_args + + if options.destdir: + options.destdir = os.path.abspath(os.path.expanduser(options.destdir)) + + if options.verbose >= 1: + self.load('errcheck') + + def execute(self): + """ + See :py:func:`waflib.Context.Context.execute` + """ + super(OptionsContext, self).execute() + self.parse_args() + diff --git a/waflib/Runner.py b/waflib/Runner.py new file mode 100644 index 00000000..85d2abbd --- /dev/null +++ b/waflib/Runner.py @@ -0,0 +1,360 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 (ita) + +""" +Runner.py: Task scheduling and execution + +""" + +import random, atexit +try: + from queue import Queue +except: + from Queue import Queue +from waflib import Utils, Task, Errors + +GAP = 10 +""" +Wait for free tasks if there are at least ``GAP * njobs`` in queue +""" + +class TaskConsumer(Utils.threading.Thread): + """ + Task consumers belong to a pool of workers + + They wait for tasks in the queue and then use ``task.process(...)`` + """ + def __init__(self): + Utils.threading.Thread.__init__(self) + self.ready = Queue() + """ + Obtain :py:class:`waflib.Task.TaskBase` instances from this queue. + """ + self.setDaemon(1) + self.start() + + def run(self): + """ + Loop over the tasks to execute + """ + try: + self.loop() + except: + pass + + def loop(self): + """ + Obtain tasks from :py:attr:`waflib.Runner.TaskConsumer.ready` and call + :py:meth:`waflib.Task.TaskBase.process`. If the object is a function, execute it. + """ + while 1: + tsk = self.ready.get() + if not isinstance(tsk, Task.TaskBase): + tsk(self) + else: + tsk.process() + +pool = Queue() +""" +Pool of task consumer objects +""" + +def get_pool(): + """ + Obtain a task consumer from :py:attr:`waflib.Runner.pool`. + Do not forget to put it back by using :py:func:`waflib.Runner.put_pool` + and reset properly (original waiting queue). + + :rtype: :py:class:`waflib.Runner.TaskConsumer` + """ + try: + return pool.get(False) + except: + return TaskConsumer() + +def put_pool(x): + """ + Return a task consumer to the thread pool :py:attr:`waflib.Runner.pool` + + :param x: task consumer object + :type x: :py:class:`waflib.Runner.TaskConsumer` + """ + pool.put(x) + +def _free_resources(): + global pool + lst = [] + while pool.qsize(): + lst.append(pool.get()) + for x in lst: + x.ready.put(None) + for x in lst: + x.join() + pool = None +atexit.register(_free_resources) + +class Parallel(object): + """ + Schedule the tasks obtained from the build context for execution. + """ + def __init__(self, bld, j=2): + """ + The initialization requires a build context reference + for computing the total number of jobs. + """ + + self.numjobs = j + """ + Number of consumers in the pool + """ + + self.bld = bld + """ + Instance of :py:class:`waflib.Build.BuildContext` + """ + + self.outstanding = [] + """List of :py:class:`waflib.Task.TaskBase` that may be ready to be executed""" + + self.frozen = [] + """List of :py:class:`waflib.Task.TaskBase` that cannot be executed immediately""" + + self.out = Queue(0) + """List of :py:class:`waflib.Task.TaskBase` returned by the task consumers""" + + self.count = 0 + """Amount of tasks that may be processed by :py:class:`waflib.Runner.TaskConsumer`""" + + self.processed = 1 + """Amount of tasks processed""" + + self.stop = False + """Error flag to stop the build""" + + self.error = [] + """Tasks that could not be executed""" + + self.biter = None + """Task iterator which must give groups of parallelizable tasks when calling ``next()``""" + + self.dirty = False + """Flag to indicate that tasks have been executed, and that the build cache must be saved (call :py:meth:`waflib.Build.BuildContext.store`)""" + + def get_next_task(self): + """ + Obtain the next task to execute. + + :rtype: :py:class:`waflib.Task.TaskBase` + """ + if not self.outstanding: + return None + return self.outstanding.pop(0) + + def postpone(self, tsk): + """ + A task cannot be executed at this point, put it in the list :py:attr:`waflib.Runner.Parallel.frozen`. + + :param tsk: task + :type tsk: :py:class:`waflib.Task.TaskBase` + """ + if random.randint(0, 1): + self.frozen.insert(0, tsk) + else: + self.frozen.append(tsk) + + def refill_task_list(self): + """ + Put the next group of tasks to execute in :py:attr:`waflib.Runner.Parallel.outstanding`. + """ + while self.count > self.numjobs * GAP: + self.get_out() + + while not self.outstanding: + if self.count: + self.get_out() + elif self.frozen: + try: + cond = self.deadlock == self.processed + except: + pass + else: + if cond: + msg = 'check the build order for the tasks' + for tsk in self.frozen: + if not tsk.run_after: + msg = 'check the methods runnable_status' + break + lst = [] + for tsk in self.frozen: + lst.append('%s\t-> %r' % (repr(tsk), [id(x) for x in tsk.run_after])) + raise Errors.WafError('Deadlock detected: %s%s' % (msg, ''.join(lst))) + self.deadlock = self.processed + + if self.frozen: + self.outstanding += self.frozen + self.frozen = [] + elif not self.count: + self.outstanding.extend(next(self.biter)) + self.total = self.bld.total() + break + + def add_more_tasks(self, tsk): + """ + Tasks may be added dynamically during the build by binding them to the task :py:attr:`waflib.Task.TaskBase.more_tasks` + + :param tsk: task + :type tsk: :py:attr:`waflib.Task.TaskBase` + """ + if getattr(tsk, 'more_tasks', None): + self.outstanding += tsk.more_tasks + self.total += len(tsk.more_tasks) + + def get_out(self): + """ + Obtain one task returned from the task consumers, and update the task count. Add more tasks if necessary through + :py:attr:`waflib.Runner.Parallel.add_more_tasks`. + + :rtype: :py:attr:`waflib.Task.TaskBase` + """ + tsk = self.out.get() + if not self.stop: + self.add_more_tasks(tsk) + self.count -= 1 + self.dirty = True + + def error_handler(self, tsk): + """ + Called when a task cannot be executed. The flag :py:attr:`waflib.Runner.Parallel.stop` is set, unless + the build is executed with:: + + $ waf build -k + + :param tsk: task + :type tsk: :py:attr:`waflib.Task.TaskBase` + """ + if not self.bld.keep: + self.stop = True + self.error.append(tsk) + + def add_task(self, tsk): + """ + Pass a task to a consumer. + + :param tsk: task + :type tsk: :py:attr:`waflib.Task.TaskBase` + """ + try: + self.pool + except AttributeError: + self.init_task_pool() + self.ready.put(tsk) + + def init_task_pool(self): + # lazy creation, and set a common pool for all task consumers + pool = self.pool = [get_pool() for i in range(self.numjobs)] + self.ready = Queue(0) + def setq(consumer): + consumer.ready = self.ready + for x in pool: + x.ready.put(setq) + return pool + + def free_task_pool(self): + # return the consumers, setting a different queue for each of them + def setq(consumer): + consumer.ready = Queue(0) + self.out.put(self) + try: + pool = self.pool + except: + pass + else: + for x in pool: + self.ready.put(setq) + for x in pool: + self.get_out() + for x in pool: + put_pool(x) + self.pool = [] + + def start(self): + """ + Give tasks to :py:class:`waflib.Runner.TaskConsumer` instances until the build finishes or the ``stop`` flag is set. + If only one job is used, then execute the tasks one by one, without consumers. + """ + + self.total = self.bld.total() + + while not self.stop: + + self.refill_task_list() + + # consider the next task + tsk = self.get_next_task() + if not tsk: + if self.count: + # tasks may add new ones after they are run + continue + else: + # no tasks to run, no tasks running, time to exit + break + + if tsk.hasrun: + # if the task is marked as "run", just skip it + self.processed += 1 + continue + + if self.stop: # stop immediately after a failure was detected + break + + try: + st = tsk.runnable_status() + except Exception: + self.processed += 1 + if not self.stop and self.bld.keep: + tsk.hasrun = Task.SKIPPED + if self.bld.keep == 1: + # if -k stop at the first exception, if -kk try to go as far as possible + self.stop = True + continue + tsk.err_msg = Utils.ex_stack() + tsk.hasrun = Task.EXCEPTION + self.error_handler(tsk) + continue + + if st == Task.ASK_LATER: + self.postpone(tsk) + # TODO optimize this + if self.outstanding: + for x in tsk.run_after: + if x in self.outstanding: + self.outstanding.remove(x) + self.outstanding.insert(0, x) + elif st == Task.SKIP_ME: + self.processed += 1 + tsk.hasrun = Task.SKIPPED + self.add_more_tasks(tsk) + else: + # run me: put the task in ready queue + tsk.position = (self.processed, self.total) + self.count += 1 + tsk.master = self + self.processed += 1 + + if self.numjobs == 1: + tsk.process() + else: + self.add_task(tsk) + + # self.count represents the tasks that have been made available to the consumer threads + # collect all the tasks after an error else the message may be incomplete + while self.error and self.count: + self.get_out() + + #print loop + assert (self.count == 0 or self.stop) + + # free the task pool, if any + self.free_task_pool() + diff --git a/waflib/Scripting.py b/waflib/Scripting.py new file mode 100644 index 00000000..00ec9230 --- /dev/null +++ b/waflib/Scripting.py @@ -0,0 +1,561 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 (ita) + +"Module called for configuring, compiling and installing targets" + +import os, shutil, traceback, errno, sys, stat +from waflib import Utils, Configure, Logs, Options, ConfigSet, Context, Errors, Build, Node + +build_dir_override = None + +no_climb_commands = ['configure'] + +default_cmd = "build" + +def waf_entry_point(current_directory, version, wafdir): + """ + This is the main entry point, all Waf execution starts here. + + :param current_directory: absolute path representing the current directory + :type current_directory: string + :param version: version number + :type version: string + :param wafdir: absolute path representing the directory of the waf library + :type wafdir: string + """ + + Logs.init_log() + + if Context.WAFVERSION != version: + Logs.error('Waf script %r and library %r do not match (directory %r)' % (version, Context.WAFVERSION, wafdir)) + sys.exit(1) + + if '--version' in sys.argv: + ctx = Context.create_context('options') + ctx.curdir = current_directory + ctx.parse_args() + sys.exit(0) + + Context.waf_dir = wafdir + Context.launch_dir = current_directory + + # if 'configure' is in the commands, do not search any further + no_climb = os.environ.get('NOCLIMB', None) + if not no_climb: + for k in no_climb_commands: + if k in sys.argv: + no_climb = True + break + + # try to find a lock file (if the project was configured) + # at the same time, store the first wscript file seen + cur = current_directory + while cur: + lst = os.listdir(cur) + if Options.lockfile in lst: + env = ConfigSet.ConfigSet() + try: + env.load(os.path.join(cur, Options.lockfile)) + ino = os.stat(cur)[stat.ST_INO] + except Exception: + pass + else: + # check if the folder was not moved + for x in [env.run_dir, env.top_dir, env.out_dir]: + if Utils.is_win32: + if cur == x: + load = True + break + else: + # if the filesystem features symlinks, compare the inode numbers + try: + ino2 = os.stat(x)[stat.ST_INO] + except: + pass + else: + if ino == ino2: + load = True + break + else: + Logs.warn('invalid lock file in %s' % cur) + load = False + + if load: + Context.run_dir = env.run_dir + Context.top_dir = env.top_dir + Context.out_dir = env.out_dir + break + + if not Context.run_dir: + if Context.WSCRIPT_FILE in lst: + Context.run_dir = cur + + next = os.path.dirname(cur) + if next == cur: + break + cur = next + + if no_climb: + break + + if not Context.run_dir: + if '-h' in sys.argv or '--help' in sys.argv: + Logs.warn('No wscript file found: the help message may be incomplete') + ctx = Context.create_context('options') + ctx.curdir = current_directory + ctx.parse_args() + sys.exit(0) + Logs.error('Waf: Run from a directory containing a file named %r' % Context.WSCRIPT_FILE) + sys.exit(1) + + try: + os.chdir(Context.run_dir) + except OSError: + Logs.error('Waf: The folder %r is unreadable' % Context.run_dir) + sys.exit(1) + + try: + set_main_module(Context.run_dir + os.sep + Context.WSCRIPT_FILE) + except Errors.WafError as e: + Logs.pprint('RED', e.verbose_msg) + Logs.error(str(e)) + sys.exit(1) + except Exception as e: + Logs.error('Waf: The wscript in %r is unreadable' % Context.run_dir, e) + traceback.print_exc(file=sys.stdout) + sys.exit(2) + + parse_options() + + """ + import cProfile, pstats + cProfile.runctx("import Scripting; Scripting.run_commands()", {}, {}, 'profi.txt') + p = pstats.Stats('profi.txt') + p.sort_stats('time').print_stats(25) + """ + try: + run_commands() + except Errors.WafError as e: + if Logs.verbose > 1: + Logs.pprint('RED', e.verbose_msg) + Logs.error(e.msg) + sys.exit(1) + except Exception as e: + traceback.print_exc(file=sys.stdout) + sys.exit(2) + except KeyboardInterrupt: + Logs.pprint('RED', 'Interrupted') + sys.exit(68) + #""" + +def set_main_module(file_path): + """ + Read the main wscript file into :py:const:`waflib.Context.Context.g_module` and + bind default functions such as ``init``, ``dist``, ``distclean`` if not defined. + Called by :py:func:`waflib.Scripting.waf_entry_point` during the initialization. + + :param file_path: absolute path representing the top-level wscript file + :type file_path: string + """ + Context.g_module = Context.load_module(file_path) + Context.g_module.root_path = file_path + + # note: to register the module globally, use the following: + # sys.modules['wscript_main'] = g_module + + def set_def(obj): + name = obj.__name__ + if not name in Context.g_module.__dict__: + setattr(Context.g_module, name, obj) + for k in [update, dist, distclean, distcheck, update]: + set_def(k) + # add dummy init and shutdown functions if they're not defined + if not 'init' in Context.g_module.__dict__: + Context.g_module.init = Utils.nada + if not 'shutdown' in Context.g_module.__dict__: + Context.g_module.shutdown = Utils.nada + if not 'options' in Context.g_module.__dict__: + Context.g_module.options = Utils.nada + +def parse_options(): + """ + Parse the command-line options and initialize the logging system. + Called by :py:func:`waflib.Scripting.waf_entry_point` during the initialization. + """ + Context.create_context('options').execute() + + if not Options.commands: + Options.commands = [default_cmd] + + # process some internal Waf options + Logs.verbose = Options.options.verbose + Logs.init_log() + + if Options.options.zones: + Logs.zones = Options.options.zones.split(',') + if not Logs.verbose: + Logs.verbose = 1 + elif Logs.verbose > 0: + Logs.zones = ['runner'] + + if Logs.verbose > 2: + Logs.zones = ['*'] + +def run_command(cmd_name): + """ + Execute a single command. Called by :py:func:`waflib.Scripting.run_commands`. + + :param cmd_name: command to execute, like ``build`` + :type cmd_name: string + """ + ctx = Context.create_context(cmd_name) + ctx.options = Options.options # provided for convenience + ctx.cmd = cmd_name + ctx.execute() + return ctx + +def run_commands(): + """ + Execute the commands that were given on the command-line. + Called by :py:func:`waflib.Scripting.waf_entry_point` during the initialization, and executed + after :py:func:`waflib.Scripting.parse_options`. + """ + run_command('init') + while Options.commands: + cmd_name = Options.commands.pop(0) + + timer = Utils.Timer() + run_command(cmd_name) + if not Options.options.progress_bar: + elapsed = ' (%s)' % str(timer) + Logs.info('%r finished successfully%s' % (cmd_name, elapsed)) + run_command('shutdown') + +########################################################################################### + +def _can_distclean(name): + # WARNING: this method may disappear anytime + for k in '.o .moc .exe'.split(): + if name.endswith(k): + return True + return False + +def distclean_dir(dirname): + """ + Distclean function called in the particular case when:: + + top == out + + :param dirname: absolute path of the folder to clean + :type dirname: string + """ + for (root, dirs, files) in os.walk(dirname): + for f in files: + if _can_distclean(f): + fname = root + os.sep + f + try: + os.unlink(fname) + except: + Logs.warn('could not remove %r' % fname) + + for x in [Context.DBFILE, 'config.log']: + try: + os.unlink(x) + except: + pass + + try: + shutil.rmtree('c4che') + except: + pass + +def distclean(ctx): + '''removes the build directory''' + lst = os.listdir('.') + for f in lst: + if f == Options.lockfile: + try: + proj = ConfigSet.ConfigSet(f) + except: + Logs.warn('could not read %r' % f) + continue + + if proj['out_dir'] != proj['top_dir']: + try: + shutil.rmtree(proj['out_dir']) + except IOError: + pass + except OSError as e: + if e.errno != errno.ENOENT: + Logs.warn('project %r cannot be removed' % proj[Context.OUT]) + else: + distclean_dir(proj['out_dir']) + + for k in (proj['out_dir'], proj['top_dir'], proj['run_dir']): + try: + os.remove(os.path.join(k, Options.lockfile)) + except OSError as e: + if e.errno != errno.ENOENT: + Logs.warn('file %r cannot be removed' % f) + + # remove the local waf cache + if f.startswith('.waf-') and not Options.commands: + shutil.rmtree(f, ignore_errors=True) + +class Dist(Context.Context): + """ + Create an archive containing the project source code:: + + $ waf dist + """ + cmd = 'dist' + fun = 'dist' + algo = 'tar.bz2' + ext_algo = {} + + def execute(self): + """ + See :py:func:`waflib.Context.Context.execute` + """ + self.recurse([os.path.dirname(Context.g_module.root_path)]) + self.archive() + + def archive(self): + """ + Create the archive. + """ + import tarfile + + arch_name = self.get_arch_name() + + try: + self.base_path + except: + self.base_path = self.path + + node = self.base_path.make_node(arch_name) + try: + node.delete() + except: + pass + + files = self.get_files() + + if self.algo.startswith('tar.'): + tar = tarfile.open(arch_name, 'w:' + self.algo.replace('tar.', '')) + + for x in files: + tinfo = tar.gettarinfo(name=x.abspath(), arcname=self.get_tar_prefix() + '/' + x.path_from(self.base_path)) + tinfo.uid = 0 + tinfo.gid = 0 + tinfo.uname = 'root' + tinfo.gname = 'root' + + fu = None + try: + fu = open(x.abspath(), 'rb') + tar.addfile(tinfo, fileobj=fu) + finally: + fu.close() + + tar.close() + elif self.algo == 'zip': + import zipfile + zip = zipfile.ZipFile(arch_name, 'w', compression=zipfile.ZIP_DEFLATED) + + for x in files: + archive_name = self.get_base_name() + '/' + x.path_from(self.base_path) + zip.write(x.abspath(), archive_name, zipfile.ZIP_DEFLATED) + zip.close() + else: + self.fatal('Valid algo types are tar.bz2, tar.gz or zip') + + try: + from hashlib import sha1 as sha + except ImportError: + from sha import sha + try: + digest = " (sha=%r)" % sha(node.read()).hexdigest() + except: + digest = '' + + Logs.info('New archive created: %s%s' % (self.arch_name, digest)) + + def get_tar_prefix(self): + try: + return self.tar_prefix + except: + return self.get_base_name() + + def get_arch_name(self): + """ + Return the name of the archive to create. Change the default value by setting *arch_name*:: + + def dist(ctx): + ctx.arch_name = 'ctx.tar.bz2' + + :rtype: string + """ + try: + self.arch_name + except: + self.arch_name = self.get_base_name() + '.' + self.ext_algo.get(self.algo, self.algo) + return self.arch_name + + def get_base_name(self): + """ + Return the default name of the main directory in the archive, which is set to *appname-version*. + Set the attribute *base_name* to change the default value:: + + def dist(ctx): + ctx.base_name = 'files' + + :rtype: string + """ + try: + self.base_name + except: + appname = getattr(Context.g_module, Context.APPNAME, 'noname') + version = getattr(Context.g_module, Context.VERSION, '1.0') + self.base_name = appname + '-' + version + return self.base_name + + def get_excl(self): + """ + Return the patterns to exclude for finding the files in the top-level directory. Set the attribute *excl* + to change the default value:: + + def dist(ctx): + ctx.excl = 'build **/*.o **/*.class' + + :rtype: string + """ + try: + return self.excl + except: + self.excl = Node.exclude_regs + ' **/waf-1.6.* **/.waf-1.6* **/*~ **/*.rej **/*.orig **/*.pyc **/*.pyo **/*.bak **/*.swp **/.lock-w*' + nd = self.root.find_node(Context.out_dir) + if nd: + self.excl += ' ' + nd.path_from(self.base_path) + return self.excl + + def get_files(self): + """ + The files to package are searched automatically by :py:func:`waflib.Node.Node.ant_glob`. Set + *files* to prevent this behaviour:: + + def dist(ctx): + ctx.files = ctx.path.find_node('wscript') + + The files are searched from the directory 'base_path', to change it, set:: + + def dist(ctx): + ctx.base_path = path + + :rtype: list of :py:class:`waflib.Node.Node` + """ + try: + files = self.files + except: + files = self.base_path.ant_glob('**/*', excl=self.get_excl()) + return files + + +def dist(ctx): + '''makes a tarball for redistributing the sources''' + pass + +class DistCheck(Dist): + """ + Create an archive of the project, and try to build the project in a temporary directory:: + + $ waf distcheck + """ + + fun = 'distcheck' + cmd = 'distcheck' + + def execute(self): + """ + See :py:func:`waflib.Context.Context.execute` + """ + self.recurse([os.path.dirname(Context.g_module.root_path)]) + self.archive() + self.check() + + def check(self): + """ + Create the archive, uncompress it and try to build the project + """ + import tempfile, tarfile + + t = None + try: + t = tarfile.open(self.get_arch_name()) + for x in t: + t.extract(x) + finally: + if t: + t.close() + + instdir = tempfile.mkdtemp('.inst', self.get_base_name()) + ret = Utils.subprocess.Popen([sys.argv[0], 'configure', 'install', 'uninstall', '--destdir=' + instdir], cwd=self.get_base_name()).wait() + if ret: + raise Errors.WafError('distcheck failed with code %i' % ret) + + if os.path.exists(instdir): + raise Errors.WafError('distcheck succeeded, but files were left in %s' % instdir) + + shutil.rmtree(self.get_base_name()) + + +def distcheck(ctx): + '''checks if the project compiles (tarball from 'dist')''' + pass + +def update(ctx): + '''updates the plugins from the *waflib/extras* directory''' + lst = Options.options.files.split(',') + if not lst: + lst = [x for x in Utils.listdir(Context.waf_dir + '/waflib/extras') if x.endswith('.py')] + for x in lst: + tool = x.replace('.py', '') + try: + Configure.download_tool(tool, force=True, ctx=ctx) + except Errors.WafError: + Logs.error('Could not find the tool %s in the remote repository' % x) + +def autoconfigure(execute_method): + """ + Decorator used to set the commands that can be configured automatically + """ + def execute(self): + if not Configure.autoconfig: + return execute_method(self) + + env = ConfigSet.ConfigSet() + do_config = False + try: + env.load(os.path.join(Context.top_dir, Options.lockfile)) + except Exception: + Logs.warn('Configuring the project') + do_config = True + else: + if env.run_dir != Context.run_dir: + do_config = True + else: + h = 0 + for f in env['files']: + h = hash((h, Utils.readf(f, 'rb'))) + do_config = h != env.hash + + if do_config: + Options.commands.insert(0, self.cmd) + Options.commands.insert(0, 'configure') + return + + return execute_method(self) + return execute +Build.BuildContext.execute = autoconfigure(Build.BuildContext.execute) + diff --git a/waflib/Task.py b/waflib/Task.py new file mode 100644 index 00000000..499c9f61 --- /dev/null +++ b/waflib/Task.py @@ -0,0 +1,1234 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 (ita) + +""" +Tasks represent atomic operations such as processes. +""" + +import os, shutil, re, tempfile +from waflib import Utils, Logs, Errors + +# task states +NOT_RUN = 0 +"""The task was not executed yet""" + +MISSING = 1 +"""The task has been executed but the files have not been created""" + +CRASHED = 2 +"""The task execution returned a non-zero exit status""" + +EXCEPTION = 3 +"""An exception occured in the task execution""" + +SKIPPED = 8 +"""The task did not have to be executed""" + +SUCCESS = 9 +"""The task was successfully executed""" + +ASK_LATER = -1 +"""The task is not ready to be executed""" + +SKIP_ME = -2 +"""The task does not need to be executed""" + +RUN_ME = -3 +"""The task must be executed""" + +COMPILE_TEMPLATE_SHELL = ''' +def f(tsk): + env = tsk.env + gen = tsk.generator + bld = gen.bld + wd = getattr(tsk, 'cwd', None) + p = env.get_flat + tsk.last_cmd = cmd = \'\'\' %s \'\'\' % s + return tsk.exec_command(cmd, cwd=wd, env=env.env or None) +''' + +COMPILE_TEMPLATE_NOSHELL = ''' +def f(tsk): + env = tsk.env + gen = tsk.generator + bld = gen.bld + wd = getattr(tsk, 'cwd', None) + def to_list(xx): + if isinstance(xx, str): return [xx] + return xx + tsk.last_cmd = lst = [] + %s + lst = [x for x in lst if x] + return tsk.exec_command(lst, cwd=wd, env=env.env or None) +''' + +def cache_outputs(cls): + """ + Task class decorator applied to all task classes by default unless they define the attribute 'nocache':: + + from waflib import Task + class foo(Task.Task): + nocache = True + + If bld.cache_global is defined and if the task instances produces output nodes, + the files will be copied into a folder in the cache directory + + The files may also be retrieved from that folder, if it exists + """ + m1 = cls.run + def run(self): + bld = self.generator.bld + if bld.cache_global and not bld.nocache: + if self.can_retrieve_cache(): + return 0 + return m1(self) + cls.run = run + + m2 = cls.post_run + def post_run(self): + bld = self.generator.bld + ret = m2(self) + if bld.cache_global and not bld.nocache: + self.put_files_cache() + return ret + cls.post_run = post_run + + return cls + + +classes = {} +"class tasks created by user scripts or Waf tools are kept in this dict name -> class object" + +class store_task_type(type): + """ + Metaclass: store the task types into :py:const:`waflib.Task.classes`. + The attribute 'run_str' will be processed to compute a method 'run' on the task class + The decorator :py:func:`waflib.Task.cache_outputs` is also applied to the class + """ + def __init__(cls, name, bases, dict): + super(store_task_type, cls).__init__(name, bases, dict) + name = cls.__name__ + + if name.endswith('_task'): + name = name.replace('_task', '') + if name != 'evil' and name != 'TaskBase': + global classes + + if getattr(cls, 'run_str', None): + # if a string is provided, convert it to a method + (f, dvars) = compile_fun(cls.run_str, cls.shell) + cls.hcode = cls.run_str + cls.run_str = None + cls.run = f + cls.vars = list(set(cls.vars + dvars)) + cls.vars.sort() + elif getattr(cls, 'run', None) and not 'hcode' in cls.__dict__: + # getattr(cls, 'hcode') would look in the upper classes + cls.hcode = Utils.h_fun(cls.run) + + if not getattr(cls, 'nocache', None): + cls = cache_outputs(cls) + + classes[name] = cls + +evil = store_task_type('evil', (object,), {}) +"Base class provided to avoid writing a metaclass, so the code can run in python 2.6 and 3.x unmodified" + +class TaskBase(evil): + """ + Base class for all Waf tasks, which should be seen as an interface. + For illustration purposes, instances of this class will execute the attribute + 'fun' in :py:meth:`waflib.Task.TaskBase.run`. When in doubt, create + subclasses of :py:class:`waflib.Task.Task` instead. + + Subclasses should override these methods: + + #. __str__: string to display to the user + #. runnable_status: ask the task if it should be run, skipped, or if we have to ask later + #. run: let threads execute the task + #. post_run: let threads update the data regarding the task (cache) + """ + + color = 'GREEN' + """Color for the console display, see :py:const:`waflib.Logs.colors_lst`""" + + ext_in = [] + """File extensions that objects of this task class might use""" + + ext_out = [] + """File extensions that objects of this task class might create""" + + before = [] + """List of task class names to execute before instances of this class""" + + after = [] + """List of task class names to execute after instances of this class""" + + hcode = '' + """String representing an additional hash for the class representation""" + + def __init__(self, *k, **kw): + """ + The base task class requires a task generator, which will be itself if missing + """ + self.hasrun = NOT_RUN + try: + self.generator = kw['generator'] + except KeyError: + self.generator = self + + def __repr__(self): + "for debugging purposes" + return '\n\t{task %r: %s %s}' % (self.__class__.__name__, id(self), str(getattr(self, 'fun', ''))) + + def __str__(self): + "string to display to the user" + if hasattr(self, 'fun'): + return 'executing: %s\n' % self.fun.__name__ + return self.__class__.__name__ + '\n' + + def __hash__(self): + "Very fast hashing scheme but not persistent (replace/implement in subclasses and see :py:meth:`waflib.Task.Task.uid`)" + return id(self) + + def exec_command(self, cmd, **kw): + """ + Wrapper for :py:meth:`waflib.Context.Context.exec_command` which sets a current working directory to ``build.variant_dir`` + + :return: the return code + :rtype: int + """ + bld = self.generator.bld + try: + if not kw.get('cwd', None): + kw['cwd'] = bld.cwd + except AttributeError: + bld.cwd = kw['cwd'] = bld.variant_dir + return bld.exec_command(cmd, **kw) + + def runnable_status(self): + """ + State of the task + + :return: a task state in :py:const:`waflib.Task.RUN_ME`, :py:const:`waflib.Task.SKIP_ME` or :py:const:`waflib.Task.ASK_LATER`. + :rtype: int + """ + return RUN_ME + + def process(self): + """ + Assume that the task has had a new attribute ``master`` which is an instance of :py:class:`waflib.Runner.Parallel`. + Execute the task and then put it back in the queue :py:attr:`waflib.Runner.Parallel.out` (may be replaced by subclassing). + """ + m = self.master + if m.stop: + m.out.put(self) + return + + # remove the task signature immediately before it is executed + # in case of failure the task will be executed again + try: + del self.generator.bld.task_sigs[self.uid()] + except: + pass + + try: + self.generator.bld.returned_tasks.append(self) + self.log_display(self.generator.bld) + ret = self.run() + except Exception: + self.err_msg = Utils.ex_stack() + self.hasrun = EXCEPTION + + # TODO cleanup + m.error_handler(self) + m.out.put(self) + return + + if ret: + self.err_code = ret + self.hasrun = CRASHED + else: + try: + self.post_run() + except Errors.WafError: + pass + except Exception: + self.err_msg = Utils.ex_stack() + self.hasrun = EXCEPTION + else: + self.hasrun = SUCCESS + if self.hasrun != SUCCESS: + m.error_handler(self) + + m.out.put(self) + + def run(self): + """ + Execute the task (executed by threads). Override in subclasses. + + :rtype: int + """ + if hasattr(self, 'fun'): + return self.fun(self) + return 0 + + def post_run(self): + "Update the cache files (executed by threads). Override in subclasses." + pass + + def log_display(self, bld): + "Write the execution status on the context logger" + bld.to_log(self.display()) + + def display(self): + """ + Return an execution status for the console, the progress bar, or the IDE output. + + :rtype: string + """ + col1 = Logs.colors(self.color) + col2 = Logs.colors.NORMAL + master = self.master + + def cur(): + # the current task position, computed as late as possible + tmp = -1 + if hasattr(master, 'ready'): + tmp -= master.ready.qsize() + return master.processed + tmp + + if self.generator.bld.progress_bar == 1: + return self.generator.bld.progress_line(cur(), master.total, col1, col2) + + if self.generator.bld.progress_bar == 2: + ela = str(self.generator.bld.timer) + try: + ins = ','.join([n.name for n in self.inputs]) + except AttributeError: + ins = '' + try: + outs = ','.join([n.name for n in self.outputs]) + except AttributeError: + outs = '' + return '|Total %s|Current %s|Inputs %s|Outputs %s|Time %s|\n' % (master.total, cur(), ins, outs, ela) + + s = str(self) + if not s: + return None + + total = master.total + n = len(str(total)) + fs = '[%%%dd/%%%dd] %%s%%s%%s' % (n, n) + return fs % (cur(), total, col1, s, col2) + + def attr(self, att, default=None): + """ + Retrieve an attribute from the instance or from the class. + + :param att: variable name + :type att: string + :param default: default value + """ + ret = getattr(self, att, self) + if ret is self: return getattr(self.__class__, att, default) + return ret + + def hash_constraints(self): + """ + Identify a task type for all the constraints relevant for the scheduler: precedence, file production + + :return: a hash value + :rtype: string + """ + cls = self.__class__ + tup = (str(cls.before), str(cls.after), str(cls.ext_in), str(cls.ext_out), cls.__name__, cls.hcode) + h = hash(tup) + return h + + def format_error(self): + """ + Error message to display to the user when a build fails + + :rtype: string + """ + msg = getattr(self, 'last_cmd', '') + if getattr(self, "err_msg", None): + return self.err_msg + elif self.hasrun == CRASHED: + try: + return ' -> task failed (exit status %r): %r\n%r' % (self.err_code, self, msg) + except AttributeError: + return ' -> task failed: %r\n%r' % (self, msg) + elif self.hasrun == MISSING: + return ' -> missing files: %r\n%r' % (self, msg) + else: + return '?' + + def colon(self, var1, var2): + """ + private function for the moment + + used for scriptlet expressions such as ${FOO_ST:FOO}, for example, if + env.FOO_ST = ['-a', '-b'] + env.FOO = ['1', '2'] + then the result will be ['-a', '-b', '1', '-a', '-b', '2'] + """ + tmp = self.env[var1] + if isinstance(var2, str): + it = self.env[var2] + else: + it = var2 + if isinstance(tmp, str): + return [tmp % x for x in it] + else: + if Logs.verbose and not tmp and it: + Logs.warn('Missing env variable %r for task %r (generator %r)' % (var1, self, self.generator)) + lst = [] + for y in it: + lst.extend(tmp) + lst.append(y) + return lst + +class Task(TaskBase): + """ + This class deals with the filesystem (:py:class:`waflib.Node.Node`). The method :py:class:`waflib.Task.Task.runnable_status` + uses a hash value (from :py:class:`waflib.Task.Task.signature`) which is persistent from build to build. When the value changes, + the task has to be executed. The method :py:class:`waflib.Task.Task.post_run` will assign the task signature to the output + nodes (if present). + """ + vars = [] + """Variables to depend on (class attribute used for :py:meth:`waflib.Task.Task.sig_vars`)""" + + shell = False + """Execute the command with the shell (class attribute)""" + + def __init__(self, *k, **kw): + TaskBase.__init__(self, *k, **kw) + + self.env = kw['env'] + """ConfigSet object (make sure to provide one)""" + + self.inputs = [] + """List of input nodes, which represent the files used by the task instance""" + + self.outputs = [] + """List of output nodes, which represent the files created by the task instance""" + + self.dep_nodes = [] + """List of additional nodes to depend on""" + + self.run_after = set([]) + """Set of tasks that must be executed before this one""" + + # Additionally, you may define the following + #self.dep_vars = 'PREFIX DATADIR' + + def __str__(self): + "string to display to the user" + env = self.env + src_str = ' '.join([a.nice_path(env) for a in self.inputs]) + tgt_str = ' '.join([a.nice_path(env) for a in self.outputs]) + if self.outputs: sep = ' -> ' + else: sep = '' + return '%s: %s%s%s\n' % (self.__class__.__name__.replace('_task', ''), src_str, sep, tgt_str) + + def __repr__(self): + "for debugging purposes" + return "".join(['\n\t{task %r: ' % id(self), self.__class__.__name__, " ", ",".join([x.name for x in self.inputs]), " -> ", ",".join([x.name for x in self.outputs]), '}']) + + def uid(self): + """ + Return an identifier used to determine if tasks are up-to-date. Since the + identifier will be stored between executions, it must be: + + - unique: no two tasks return the same value (for a given build context) + - the same for a given task instance + + By default, the node paths, the class name, and the function are used + as inputs to compute a hash. + + The pointer to the object (python built-in 'id') will change between build executions, + and must be avoided in such hashes. + + :return: hash value + :rtype: string + """ + try: + return self.uid_ + except AttributeError: + # this is not a real hot zone, but we want to avoid surprizes here + m = Utils.md5() + up = m.update + up(self.__class__.__name__.encode()) + for x in self.inputs + self.outputs: + up(x.abspath().encode()) + self.uid_ = m.digest() + return self.uid_ + + def set_inputs(self, inp): + """ + Append the nodes to the *inputs* + + :param inp: input nodes + :type inp: node or list of nodes + """ + if isinstance(inp, list): self.inputs += inp + else: self.inputs.append(inp) + + def set_outputs(self, out): + """ + Append the nodes to the *outputs* + + :param out: output nodes + :type out: node or list of nodes + """ + if isinstance(out, list): self.outputs += out + else: self.outputs.append(out) + + def set_run_after(self, task): + """ + Run this task only after *task*. Affect :py:meth:`waflib.Task.runnable_status` + + :param task: task + :type task: :py:class:`waflib.Task.Task` + """ + # TODO: handle lists too? + assert isinstance(task, TaskBase) + self.run_after.add(task) + + def signature(self): + """ + Task signatures are stored between build executions, they are use to track the changes + made to the input nodes (not to the outputs!). The signature hashes data from various sources: + + * explicit dependencies: files listed in the inputs (list of node objects) :py:meth:`waflib.Task.Task.sig_explicit_deps` + * implicit dependencies: list of nodes returned by scanner methods (when present) :py:meth:`waflib.Task.Task.sig_implicit_deps` + * hashed data: variables/values read from task.__class__.vars/task.env :py:meth:`waflib.Task.Task.sig_vars` + + If the signature is expected to give a different result, clear the cache kept in ``self.cache_sig``:: + + from waflib import Task + class cls(Task.Task): + def signature(self): + sig = super(Task.Task, self).signature() + delattr(self, 'cache_sig') + return super(Task.Task, self).signature() + """ + try: return self.cache_sig + except AttributeError: pass + + self.m = Utils.md5() + self.m.update(self.hcode.encode()) + + # explicit deps + self.sig_explicit_deps() + + # env vars + self.sig_vars() + + # implicit deps / scanner results + if self.scan: + try: + self.sig_implicit_deps() + except Errors.TaskRescan: + return self.signature() + + ret = self.cache_sig = self.m.digest() + return ret + + def runnable_status(self): + """ + Override :py:meth:`waflib.Task.TaskBase.runnable_status` to determine if the task is ready + to be run (:py:attr:`waflib.Task.Task.run_after`) + """ + #return 0 # benchmarking + + for t in self.run_after: + if not t.hasrun: + return ASK_LATER + + bld = self.generator.bld + + # first compute the signature + try: + new_sig = self.signature() + except Errors.TaskNotReady: + return ASK_LATER + + # compare the signature to a signature computed previously + key = self.uid() + try: + prev_sig = bld.task_sigs[key] + except KeyError: + Logs.debug("task: task %r must run as it was never run before or the task code changed" % self) + return RUN_ME + + # compare the signatures of the outputs + for node in self.outputs: + try: + if node.sig != new_sig: + return RUN_ME + except AttributeError: + Logs.debug("task: task %r must run as the output nodes do not exist" % self) + return RUN_ME + + if new_sig != prev_sig: + return RUN_ME + return SKIP_ME + + def post_run(self): + """ + Called after successful execution to update the cache data :py:class:`waflib.Node.Node` sigs + and :py:attr:`waflib.Build.BuildContext.task_sigs`. + + The node signature is obtained from the task signature, but the output nodes may also get the signature + of their contents. See the class decorator :py:func:`waflib.Task.update_outputs` if you need this behaviour. + """ + bld = self.generator.bld + sig = self.signature() + + for node in self.outputs: + # check if the node exists .. + try: + os.stat(node.abspath()) + except OSError: + self.hasrun = MISSING + self.err_msg = '-> missing file: %r' % node.abspath() + raise Errors.WafError(self.err_msg) + + # important, store the signature for the next run + node.sig = sig + + bld.task_sigs[self.uid()] = self.cache_sig + + def sig_explicit_deps(self): + """ + Used by :py:meth:`waflib.Task.Task.signature`, hash :py:attr:`waflib.Task.Task.inputs` + and :py:attr:`waflib.Task.Task.dep_nodes` signatures. + + :rtype: hash value + """ + bld = self.generator.bld + upd = self.m.update + + # the inputs + for x in self.inputs + self.dep_nodes: + try: + upd(x.get_bld_sig()) + except (AttributeError, TypeError): + raise Errors.WafError('Missing node signature for %r (required by %r)' % (x, self)) + + # manual dependencies, they can slow down the builds + if bld.deps_man: + additional_deps = bld.deps_man + for x in self.inputs + self.outputs: + try: + d = additional_deps[id(x)] + except KeyError: + continue + + for v in d: + if isinstance(v, bld.root.__class__): + try: + v = v.get_bld_sig() + except AttributeError: + raise Errors.WafError('Missing node signature for %r (required by %r)' % (v, self)) + elif hasattr(v, '__call__'): + v = v() # dependency is a function, call it + upd(v) + + return self.m.digest() + + def sig_vars(self): + """ + Used by :py:meth:`waflib.Task.Task.signature`, hash :py:attr:`waflib.Task.Task.env` variables/values + + :rtype: hash value + """ + bld = self.generator.bld + env = self.env + upd = self.m.update + + # dependencies on the environment vars + act_sig = bld.hash_env_vars(env, self.__class__.vars) + upd(act_sig) + + # additional variable dependencies, if provided + dep_vars = getattr(self, 'dep_vars', None) + if dep_vars: + upd(bld.hash_env_vars(env, dep_vars)) + + return self.m.digest() + + scan = None + """ + This method, when provided, returns a tuple containing: + + * a list of nodes corresponding to real files + * a list of names for files not found in path_lst + + For example:: + + from waflib.Task import Task + class mytask(Task): + def scan(self, node): + return ((), ()) + + The first and second lists are stored in :py:attr:`waflib.Build.BuildContext.node_deps` and + :py:attr:`waflib.Build.BuildContext.raw_deps` respectively. + """ + + def sig_implicit_deps(self): + """ + Used by :py:meth:`waflib.Task.Task.signature` hashes node signatures obtained by scanning for dependencies (:py:meth:`waflib.Task.Task.scan`). + + The exception :py:class:`waflib.Errors.TaskRescan` is thrown + when a file has changed. When this occurs, :py:meth:`waflib.Task.Task.signature` is called + once again, and this method will be executed once again, this time calling :py:meth:`waflib.Task.Task.scan` + for searching the dependencies. + + :rtype: hash value + """ + + bld = self.generator.bld + + # get the task signatures from previous runs + key = self.uid() + prev = bld.task_sigs.get((key, 'imp'), []) + + # for issue #379 + if prev: + try: + if prev == self.compute_sig_implicit_deps(): + return prev + except: + # when a file was renamed (IOError usually), remove the stale nodes (headers in folders without source files) + # this will break the order calculation for headers created during the build in the source directory (should be uncommon) + # the behaviour will differ when top != out + for x in bld.node_deps.get(self.uid(), []): + if x.is_child_of(bld.srcnode): + try: + os.stat(x.abspath()) + except: + try: + del x.parent.children[x.name] + except: + pass + del bld.task_sigs[(key, 'imp')] + raise Errors.TaskRescan('rescan') + + # no previous run or the signature of the dependencies has changed, rescan the dependencies + (nodes, names) = self.scan() + if Logs.verbose: + Logs.debug('deps: scanner for %s returned %s %s' % (str(self), str(nodes), str(names))) + + # store the dependencies in the cache + bld.node_deps[key] = nodes + bld.raw_deps[key] = names + + # might happen + self.are_implicit_nodes_ready() + + # recompute the signature and return it + try: + bld.task_sigs[(key, 'imp')] = sig = self.compute_sig_implicit_deps() + except: + if Logs.verbose: + for k in bld.node_deps.get(self.uid(), []): + try: + k.get_bld_sig() + except: + Logs.warn('Missing signature for node %r (may cause rebuilds)' % k) + else: + return sig + + def compute_sig_implicit_deps(self): + """ + Used by :py:meth:`waflib.Task.Task.sig_implicit_deps` for computing the actual hash of the + :py:class:`waflib.Node.Node` returned by the scanner. + + :return: hash value + :rtype: string + """ + + upd = self.m.update + + bld = self.generator.bld + + self.are_implicit_nodes_ready() + + # scanner returns a node that does not have a signature + # just *ignore* the error and let them figure out from the compiler output + # waf -k behaviour + for k in bld.node_deps.get(self.uid(), []): + upd(k.get_bld_sig()) + return self.m.digest() + + def are_implicit_nodes_ready(self): + """ + For each node returned by the scanner, see if there is a task behind it, and force the build order + + The performance impact on null builds is nearly invisible (1.66s->1.86s), but this is due to + agressive caching (1.86s->28s) + """ + bld = self.generator.bld + try: + cache = bld.dct_implicit_nodes + except: + bld.dct_implicit_nodes = cache = {} + + try: + dct = cache[bld.cur] + except KeyError: + dct = cache[bld.cur] = {} + for tsk in bld.cur_tasks: + for x in tsk.outputs: + dct[x] = tsk + + modified = False + for x in bld.node_deps.get(self.uid(), []): + if x in dct: + self.run_after.add(dct[x]) + modified = True + + if modified: + for tsk in self.run_after: + if not tsk.hasrun: + #print "task is not ready..." + raise Errors.TaskNotReady('not ready') + + def can_retrieve_cache(self): + """ + Used by :py:meth:`waflib.Task.cache_outputs` + + Retrieve build nodes from the cache + update the file timestamps to help cleaning the least used entries from the cache + additionally, set an attribute 'cached' to avoid re-creating the same cache files + + Suppose there are files in `cache/dir1/file1` and `cache/dir2/file2`: + + #. read the timestamp of dir1 + #. try to copy the files + #. look at the timestamp again, if it has changed, the data may have been corrupt (cache update by another process) + #. should an exception occur, ignore the data + """ + + if not getattr(self, 'outputs', None): + return None + + sig = self.signature() + ssig = Utils.to_hex(self.uid()) + Utils.to_hex(sig) + + # first try to access the cache folder for the task + dname = os.path.join(self.generator.bld.cache_global, ssig) + try: + t1 = os.stat(dname).st_mtime + except OSError: + return None + + for node in self.outputs: + orig = os.path.join(dname, node.name) + try: + shutil.copy2(orig, node.abspath()) + # mark the cache file as used recently (modified) + os.utime(orig, None) + except (OSError, IOError): + Logs.debug('task: failed retrieving file') + return None + + # is it the same folder? + try: + t2 = os.stat(dname).st_mtime + except OSError: + return None + + if t1 != t2: + return None + + for node in self.outputs: + node.sig = sig + if self.generator.bld.progress_bar < 1: + self.generator.bld.to_log('restoring from cache %r\n' % node.abspath()) + + self.cached = True + return True + + def put_files_cache(self): + """ + Used by :py:func:`waflib.Task.cache_outputs` to store the build files in the cache + """ + + # file caching, if possible + # try to avoid data corruption as much as possible + if getattr(self, 'cached', None): + return None + if not getattr(self, 'outputs', None): + return None + + sig = self.signature() + ssig = Utils.to_hex(self.uid()) + Utils.to_hex(sig) + dname = os.path.join(self.generator.bld.cache_global, ssig) + tmpdir = tempfile.mkdtemp(prefix=self.generator.bld.cache_global + os.sep + 'waf') + + try: + shutil.rmtree(dname) + except: + pass + + try: + for node in self.outputs: + dest = os.path.join(tmpdir, node.name) + shutil.copy2(node.abspath(), dest) + except (OSError, IOError): + try: + shutil.rmtree(tmpdir) + except: + pass + else: + try: + os.rename(tmpdir, dname) + except OSError: + try: + shutil.rmtree(tmpdir) + except: + pass + else: + try: + os.chmod(dname, Utils.O755) + except: + pass + +def is_before(t1, t2): + """ + Return a non-zero value if task t1 is to be executed before task t2:: + + t1.ext_out = '.h' + t2.ext_in = '.h' + t2.after = ['t1'] + t1.before = ['t2'] + waflib.Task.is_before(t1, t2) # True + + :param t1: task + :type t1: :py:class:`waflib.Task.TaskBase` + :param t2: task + :type t2: :py:class:`waflib.Task.TaskBase` + """ + to_list = Utils.to_list + for k in to_list(t2.ext_in): + if k in to_list(t1.ext_out): + return 1 + + if t1.__class__.__name__ in to_list(t2.after): + return 1 + + if t2.__class__.__name__ in to_list(t1.before): + return 1 + + return 0 + +def set_file_constraints(tasks): + """ + Adds tasks to the task 'run_after' attribute based on the task inputs and outputs + + :param tasks: tasks + :type tasks: list of :py:class:`waflib.Task.TaskBase` + """ + ins = Utils.defaultdict(set) + outs = Utils.defaultdict(set) + for x in tasks: + for a in getattr(x, 'inputs', []) + getattr(x, 'dep_nodes', []): + ins[id(a)].add(x) + for a in getattr(x, 'outputs', []): + outs[id(a)].add(x) + + links = set(ins.keys()).intersection(outs.keys()) + for k in links: + for a in ins[k]: + a.run_after.update(outs[k]) + +def set_precedence_constraints(tasks): + """ + Add tasks to the task 'run_after' attribute based on the after/before/ext_out/ext_in attributes + + :param tasks: tasks + :type tasks: list of :py:class:`waflib.Task.TaskBase` + """ + cstr_groups = Utils.defaultdict(list) + for x in tasks: + h = x.hash_constraints() + cstr_groups[h].append(x) + + keys = list(cstr_groups.keys()) + maxi = len(keys) + + # this list should be short + for i in range(maxi): + t1 = cstr_groups[keys[i]][0] + for j in range(i + 1, maxi): + t2 = cstr_groups[keys[j]][0] + + # add the constraints based on the comparisons + if is_before(t1, t2): + a = i + b = j + elif is_before(t2, t1): + a = j + b = i + else: + continue + for x in cstr_groups[keys[b]]: + x.run_after.update(cstr_groups[keys[a]]) + +def funex(c): + """ + Compile a function by 'exec' + + :param c: function to compile + :type c: string + :return: the function 'f' declared in the input string + :rtype: function + """ + dc = {} + exec(c, dc) + return dc['f'] + +reg_act = re.compile(r"(?P\\)|(?P\$\$)|(?P\$\{(?P\w+)(?P.*?)\})", re.M) +def compile_fun_shell(line): + """ + Create a compiled function to execute a process with the shell + WARNING: this method may disappear anytime, so use compile_fun instead + """ + + extr = [] + def repl(match): + g = match.group + if g('dollar'): return "$" + elif g('backslash'): return '\\\\' + elif g('subst'): extr.append((g('var'), g('code'))); return "%s" + return None + + line = reg_act.sub(repl, line) or line + + parm = [] + dvars = [] + app = parm.append + for (var, meth) in extr: + if var == 'SRC': + if meth: app('tsk.inputs%s' % meth) + else: app('" ".join([a.path_from(bld.bldnode) for a in tsk.inputs])') + elif var == 'TGT': + if meth: app('tsk.outputs%s' % meth) + else: app('" ".join([a.path_from(bld.bldnode) for a in tsk.outputs])') + elif meth: + if meth.startswith(':'): + m = meth[1:] + if m == 'SRC': + m = '[a.path_from(bld.bldnode) for a in tsk.inputs]' + elif m == 'TGT': + m = '[a.path_from(bld.bldnode) for a in tsk.outputs]' + elif m[:3] not in ('tsk', 'gen', 'bld'): + dvars.extend([var, meth[1:]]) + m = '%r' % m + app('" ".join(tsk.colon(%r, %s))' % (var, m)) + else: + app('%s%s' % (var, meth)) + else: + if not var in dvars: dvars.append(var) + app("p('%s')" % var) + if parm: parm = "%% (%s) " % (',\n\t\t'.join(parm)) + else: parm = '' + + c = COMPILE_TEMPLATE_SHELL % (line, parm) + + Logs.debug('action: %s' % c) + return (funex(c), dvars) + +def compile_fun_noshell(line): + """ + Create a compiled function to execute a process without the shell + WARNING: this method may disappear anytime, so use compile_fun instead + """ + extr = [] + def repl(match): + g = match.group + if g('dollar'): return "$" + elif g('subst'): extr.append((g('var'), g('code'))); return "<<|@|>>" + return None + + line2 = reg_act.sub(repl, line) + params = line2.split('<<|@|>>') + assert(extr) + + buf = [] + dvars = [] + app = buf.append + for x in range(len(extr)): + params[x] = params[x].strip() + if params[x]: + app("lst.extend(%r)" % params[x].split()) + (var, meth) = extr[x] + if var == 'SRC': + if meth: app('lst.append(tsk.inputs%s)' % meth) + else: app("lst.extend([a.path_from(bld.bldnode) for a in tsk.inputs])") + elif var == 'TGT': + if meth: app('lst.append(tsk.outputs%s)' % meth) + else: app("lst.extend([a.path_from(bld.bldnode) for a in tsk.outputs])") + elif meth: + if meth.startswith(':'): + m = meth[1:] + if m == 'SRC': + m = '[a.path_from(bld.bldnode) for a in tsk.inputs]' + elif m == 'TGT': + m = '[a.path_from(bld.bldnode) for a in tsk.outputs]' + elif m[:3] not in ('tsk', 'gen', 'bld'): + dvars.extend([var, m]) + m = '%r' % m + app('lst.extend(tsk.colon(%r, %s))' % (var, m)) + else: + app('lst.extend(gen.to_list(%s%s))' % (var, meth)) + else: + app('lst.extend(to_list(env[%r]))' % var) + if not var in dvars: dvars.append(var) + + if extr: + if params[-1]: + app("lst.extend(%r)" % params[-1].split()) + fun = COMPILE_TEMPLATE_NOSHELL % "\n\t".join(buf) + Logs.debug('action: %s' % fun) + return (funex(fun), dvars) + +def compile_fun(line, shell=False): + """ + Parse a string expression such as "${CC} ${SRC} -o ${TGT}" and return a pair containing: + + * the function created (compiled) for use as :py:meth:`waflib.Task.TaskBase.run` + * the list of variables that imply a dependency from self.env + + for example:: + + from waflib.Task import compile_fun + compile_fun('cxx', '${CXX} -o ${TGT[0]} ${SRC} -I ${SRC[0].parent.bldpath()}') + + def build(bld): + bld(source='wscript', rule='echo "foo\\${SRC[0].name}\\bar"') + + The env variables (CXX, ..) on the task must not hold dicts (order) + The reserved keywords *TGT* and *SRC* represent the task input and output nodes + + """ + if line.find('<') > 0 or line.find('>') > 0 or line.find('&&') > 0: + shell = True + + if shell: + return compile_fun_shell(line) + else: + return compile_fun_noshell(line) + +def task_factory(name, func=None, vars=None, color='GREEN', ext_in=[], ext_out=[], before=[], after=[], shell=False, scan=None): + """ + Return a new task subclass with the function ``run`` compiled from the line given. + Provided for compatibility with waf 1.4-1.5, when we did not use metaclasses to register new objects. + + :param func: method run + :type func: string or function + :param vars: list of variables to hash + :type vars: list of string + :param color: color to use + :type color: string + :param shell: when *func* is a string, enable/disable the use of the shell + :type shell: bool + :param scan: method scan + :type scan: function + :rtype: :py:class:`waflib.Task.Task` + """ + + params = { + 'vars': vars or [], # function arguments are static, and this one may be modified by the class + 'color': color, + 'name': name, + 'ext_in': Utils.to_list(ext_in), + 'ext_out': Utils.to_list(ext_out), + 'before': Utils.to_list(before), + 'after': Utils.to_list(after), + 'shell': shell, + 'scan': scan, + } + + if isinstance(func, str): + params['run_str'] = func + else: + params['run'] = func + + cls = type(Task)(name, (Task,), params) + global classes + classes[name] = cls + return cls + + +def always_run(cls): + """ + Task class decorator + + Set all task instances of this class to be executed whenever a build is started + The task signature is calculated, but the result of the comparation between + task signatures is bypassed + """ + old = cls.runnable_status + def always(self): + ret = old(self) + if ret == SKIP_ME: + ret = RUN_ME + return ret + cls.runnable_status = always + return cls + +def update_outputs(cls): + """ + Task class decorator + + If you want to create files in the source directory. For example, to keep *foo.txt* in the source + directory, create it first and declare:: + + def build(bld): + bld(rule='cp ${SRC} ${TGT}', source='wscript', target='foo.txt', update_outputs=True) + """ + old_post_run = cls.post_run + def post_run(self): + old_post_run(self) + for node in self.outputs: + node.sig = Utils.h_file(node.abspath()) + self.generator.bld.task_sigs[node.abspath()] = self.uid() # issue #1017 + cls.post_run = post_run + + + old_runnable_status = cls.runnable_status + def runnable_status(self): + status = old_runnable_status(self) + if status != RUN_ME: + return status + + try: + # by default, we check that the output nodes have the signature of the task + # perform a second check, returning 'SKIP_ME' as we are expecting that + # the signatures do not match + bld = self.generator.bld + prev_sig = bld.task_sigs[self.uid()] + if prev_sig == self.signature(): + for x in self.outputs: + if not x.sig or bld.task_sigs[x.abspath()] != self.uid(): + return RUN_ME + return SKIP_ME + except KeyError: + pass + except IndexError: + pass + except AttributeError: + pass + return RUN_ME + cls.runnable_status = runnable_status + + return cls + + diff --git a/waflib/TaskGen.py b/waflib/TaskGen.py new file mode 100644 index 00000000..571b78d1 --- /dev/null +++ b/waflib/TaskGen.py @@ -0,0 +1,740 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 (ita) + +""" +Task generators + +The class :py:class:`waflib.TaskGen.task_gen` encapsulates the creation of task objects (low-level code) +The instances can have various parameters, but the creation of task nodes (Task.py) +is always postponed. To achieve this, various methods are called from the method "apply" + + +""" + +import copy, re +from waflib import Task, Utils, Logs, Errors, ConfigSet + +feats = Utils.defaultdict(set) +"""remember the methods declaring features""" + +class task_gen(object): + """ + Instances of this class create :py:class:`waflib.Task.TaskBase` when + calling the method :py:meth:`waflib.TaskGen.task_gen.post` from the main thread. + A few notes: + + * The methods to call (*self.meths*) can be specified dynamically (removing, adding, ..) + * The 'features' are used to add methods to self.meths and then execute them + * The attribute 'path' is a node representing the location of the task generator + * The tasks created are added to the attribute *tasks* + * The attribute 'idx' is a counter of task generators in the same path + """ + + mappings = {} + prec = Utils.defaultdict(list) + + def __init__(self, *k, **kw): + """ + The task generator objects predefine various attributes (source, target) for possible + processing by process_rule (make-like rules) or process_source (extensions, misc methods) + + The tasks are stored on the attribute 'tasks'. They are created by calling methods + listed in self.meths *or* referenced in the attribute features + A topological sort is performed to ease the method re-use. + + The extra key/value elements passed in kw are set as attributes + """ + + # so we will have to play with directed acyclic graphs + # detect cycles, etc + self.source = '' + self.target = '' + + self.meths = [] + """ + List of method names to execute (it is usually a good idea to avoid touching this) + """ + + self.prec = Utils.defaultdict(list) + """ + Precedence table for sorting the methods in self.meths + """ + + self.mappings = {} + """ + List of mappings {extension -> function} for processing files by extension + """ + + self.features = [] + """ + List of feature names for bringing new methods in + """ + + self.tasks = [] + """ + List of tasks created. + """ + + if not 'bld' in kw: + # task generators without a build context :-/ + self.env = ConfigSet.ConfigSet() + self.idx = 0 + self.path = None + else: + self.bld = kw['bld'] + self.env = self.bld.env.derive() + self.path = self.bld.path # emulate chdir when reading scripts + + # provide a unique id + try: + self.idx = self.bld.idx[id(self.path)] = self.bld.idx.get(id(self.path), 0) + 1 + except AttributeError: + self.bld.idx = {} + self.idx = self.bld.idx[id(self.path)] = 1 + + for key, val in kw.items(): + setattr(self, key, val) + + def __str__(self): + """for debugging purposes""" + return "" % (self.name, self.path.abspath()) + + def __repr__(self): + """for debugging purposes""" + lst = [] + for x in self.__dict__.keys(): + if x not in ['env', 'bld', 'compiled_tasks', 'tasks']: + lst.append("%s=%s" % (x, repr(getattr(self, x)))) + return "bld(%s) in %s" % (", ".join(lst), self.path.abspath()) + + def get_name(self): + """ + If not set, the name is computed from the target name:: + + def build(bld): + x = bld(name='foo') + x.get_name() # foo + y = bld(target='bar') + y.get_name() # bar + + :rtype: string + :return: name of this task generator + """ + try: + return self._name + except AttributeError: + if isinstance(self.target, list): + lst = [str(x) for x in self.target] + name = self._name = ','.join(lst) + else: + name = self._name = str(self.target) + return name + def set_name(self, name): + self._name = name + + name = property(get_name, set_name) + + def to_list(self, val): + """ + Ensure that a parameter is a list + + :type val: string or list of string + :param val: input to return as a list + :rtype: list + """ + if isinstance(val, str): return val.split() + else: return val + + def post(self): + """ + Create task objects. The following operations are performed: + + #. The body of this method is called only once and sets the attribute ``posted`` + #. The attribute ``features`` is used to add more methods in ``self.meths`` + #. The methods are sorted by the precedence table ``self.prec`` or `:waflib:attr:waflib.TaskGen.task_gen.prec` + #. The methods are then executed in order + #. The tasks created are added to :py:attr:`waflib.TaskGen.task_gen.tasks` + """ + + # we could add a decorator to let the task run once, but then python 2.3 will be difficult to support + if getattr(self, 'posted', None): + #error("OBJECT ALREADY POSTED" + str( self)) + return False + self.posted = True + + keys = set(self.meths) + + # add the methods listed in the features + self.features = Utils.to_list(self.features) + for x in self.features + ['*']: + st = feats[x] + if not st: + if not x in Task.classes: + Logs.warn('feature %r does not exist - bind at least one method to it' % x) + keys.update(list(st)) # ironpython 2.7 wants the cast to list + + # copy the precedence table + prec = {} + prec_tbl = self.prec or task_gen.prec + for x in prec_tbl: + if x in keys: + prec[x] = prec_tbl[x] + + # elements disconnected + tmp = [] + for a in keys: + for x in prec.values(): + if a in x: break + else: + tmp.append(a) + + # TODO waf 1.7 + #tmp.sort() + + # topological sort + out = [] + while tmp: + e = tmp.pop() + if e in keys: out.append(e) + try: + nlst = prec[e] + except KeyError: + pass + else: + del prec[e] + for x in nlst: + for y in prec: + if x in prec[y]: + break + else: + tmp.append(x) + + if prec: + raise Errors.WafError('Cycle detected in the method execution %r' % prec) + out.reverse() + self.meths = out + + # then we run the methods in order + Logs.debug('task_gen: posting %s %d' % (self, id(self))) + for x in out: + try: + v = getattr(self, x) + except AttributeError: + raise Errors.WafError('%r is not a valid task generator method' % x) + Logs.debug('task_gen: -> %s (%d)' % (x, id(self))) + v() + + Logs.debug('task_gen: posted %s' % self.name) + return True + + def get_hook(self, node): + """ + :param node: Input file to process + :type node: :py:class:`waflib.Tools.Node.Node` + :return: A method able to process the input node by looking at the extension + :rtype: function + """ + name = node.name + for k in self.mappings: + if name.endswith(k): + return self.mappings[k] + for k in task_gen.mappings: + if name.endswith(k): + return task_gen.mappings[k] + raise Errors.WafError("File %r has no mapping in %r (did you forget to load a waf tool?)" % (node, task_gen.mappings.keys())) + + def create_task(self, name, src=None, tgt=None): + """ + Wrapper for creating task objects easily + + :param name: task class name + :type name: string + :param src: input nodes + :type src: list of :py:class:`waflib.Tools.Node.Node` + :param tgt: output nodes + :type tgt: list of :py:class:`waflib.Tools.Node.Node` + :return: A task object + :rtype: :py:class:`waflib.Task.TaskBase` + """ + task = Task.classes[name](env=self.env.derive(), generator=self) + if src: + task.set_inputs(src) + if tgt: + task.set_outputs(tgt) + self.tasks.append(task) + return task + + def clone(self, env): + """ + Make a copy of a task generator. Once the copy is made, it is necessary to ensure that the + task generator does not create the same output files as the original, or the same files may + be compiled twice. + + :param env: A configuration set + :type env: :py:class:`waflib.ConfigSet.ConfigSet` + :return: A copy + :rtype: :py:class:`waflib.TaskGen.task_gen` + """ + newobj = self.bld() + for x in self.__dict__: + if x in ['env', 'bld']: + continue + elif x in ['path', 'features']: + setattr(newobj, x, getattr(self, x)) + else: + setattr(newobj, x, copy.copy(getattr(self, x))) + + newobj.posted = False + if isinstance(env, str): + newobj.env = self.bld.all_envs[env].derive() + else: + newobj.env = env.derive() + + return newobj + +def declare_chain(name='', rule=None, reentrant=True, color='BLUE', + ext_in=[], ext_out=[], before=[], after=[], decider=None, scan=None, install_path=None, shell=False): + """ + Create a new mapping and a task class for processing files by extension. + See Tools/flex.py for an example. + + :param name: name for the task class + :type name: string + :param rule: function to execute or string to be compiled in a function + :type rule: string or function + :param reentrant: re-inject the output file in the process + :type reentrant: bool + :param color: color for the task output + :type color: string + :param ext_in: execute the task only after the files of such extensions are created + :type ext_in: list of string + :param ext_out: execute the task only before files of such extensions are processed + :type ext_out: list of string + :param before: execute instances of this task before classes of the given names + :type before: list of string + :param after: execute instances of this task after classes of the given names + :type after: list of string + :param decider: if present, use it to create the output nodes for the task + :type decider: function + :param scan: scanner function for the task + :type scan: function + :param install_path: installation path for the output nodes + :type install_path: string + """ + ext_in = Utils.to_list(ext_in) + ext_out = Utils.to_list(ext_out) + if not name: + name = rule + cls = Task.task_factory(name, rule, color=color, ext_in=ext_in, ext_out=ext_out, before=before, after=after, scan=scan, shell=shell) + + def x_file(self, node): + ext = decider and decider(self, node) or cls.ext_out + if ext_in: + _ext_in = ext_in[0] + out_source = [node.change_ext(x, ext_in=_ext_in) for x in ext] + if reentrant: + for i in range(reentrant): + self.source.append(out_source[i]) + tsk = self.create_task(name, node, out_source) + if install_path: + self.bld.install_files(install_path, out_source) + return tsk + + for x in cls.ext_in: + task_gen.mappings[x] = x_file + return x_file + +def taskgen_method(func): + """ + Decorator: register a method as a task generator method. + The function must accept a task generator as first parameter:: + + from waflib.TaskGen import taskgen_method + @taskgen_method + def mymethod(self): + pass + + :param func: task generator method to add + :type func: function + :rtype: function + """ + setattr(task_gen, func.__name__, func) + return func + +def feature(*k): + """ + Decorator: register a task generator method that will be executed when the + object attribute 'feature' contains the corresponding key(s):: + + from waflib.Task import feature + @feature('myfeature') + def myfunction(self): + print('that is my feature!') + def build(bld): + bld(features='myfeature') + + :param k: feature names + :type k: list of string + """ + def deco(func): + setattr(task_gen, func.__name__, func) + for name in k: + feats[name].update([func.__name__]) + return func + return deco + +def before_method(*k): + """ + Decorator: register a task generator method which will be executed + before the functions of given name(s):: + + from waflib.TaskGen import feature, before + @feature('myfeature') + @before_method('fun2') + def fun1(self): + print('feature 1!') + @feature('myfeature') + def fun2(self): + print('feature 2!') + def build(bld): + bld(features='myfeature') + + :param k: method names + :type k: list of string + """ + def deco(func): + setattr(task_gen, func.__name__, func) + for fun_name in k: + if not func.__name__ in task_gen.prec[fun_name]: + task_gen.prec[fun_name].append(func.__name__) + #task_gen.prec[fun_name].sort() + return func + return deco +before = before_method + +def after_method(*k): + """ + Decorator: register a task generator method which will be executed + after the functions of given name(s):: + + from waflib.TaskGen import feature, after + @feature('myfeature') + @after_method('fun2') + def fun1(self): + print('feature 1!') + @feature('myfeature') + def fun2(self): + print('feature 2!') + def build(bld): + bld(features='myfeature') + + :param k: method names + :type k: list of string + """ + def deco(func): + setattr(task_gen, func.__name__, func) + for fun_name in k: + if not fun_name in task_gen.prec[func.__name__]: + task_gen.prec[func.__name__].append(fun_name) + #task_gen.prec[func.__name__].sort() + return func + return deco +after = after_method + +def extension(*k): + """ + Decorator: register a task generator method which will be invoked during + the processing of source files for the extension given:: + + from waflib import Task + class mytask(Task): + run_str = 'cp ${SRC} ${TGT}' + @extension('.moo') + def create_maa_file(self, node): + self.create_task('mytask', node, node.change_ext('.maa')) + def build(bld): + bld(source='foo.moo') + """ + def deco(func): + setattr(task_gen, func.__name__, func) + for x in k: + task_gen.mappings[x] = func + return func + return deco + +# --------------------------------------------------------------- +# The following methods are task generator methods commonly used +# they are almost examples, the rest of waf core does not depend on them + +@taskgen_method +def to_nodes(self, lst, path=None): + """ + Convert the input list into a list of nodes. + It is used by :py:func:`waflib.TaskGen.process_source` and :py:func:`waflib.TaskGen.process_rule`. + It is designed for source files, for folders, see :py:func:`waflib.Tools.ccroot.to_incnodes`: + + :param lst: input list + :type lst: list of string and nodes + :param path: path from which to search the nodes (by default, :py:attr:`waflib.TaskGen.task_gen.path`) + :type path: :py:class:`waflib.Tools.Node.Node` + :rtype: list of :py:class:`waflib.Tools.Node.Node` + """ + tmp = [] + path = path or self.path + find = path.find_resource + + if isinstance(lst, self.path.__class__): + lst = [lst] + + # either a list or a string, convert to a list of nodes + for x in Utils.to_list(lst): + if isinstance(x, str): + node = find(x) + if not node: + raise Errors.WafError("source not found: %r in %r" % (x, self)) + else: + node = x + tmp.append(node) + return tmp + +@feature('*') +def process_source(self): + """ + Process each element in the attribute ``source`` by extension. + + #. The *source* list is converted through :py:meth:`waflib.TaskGen.to_nodes` to a list of :py:class:`waflib.Node.Node` first. + #. File extensions are mapped to methods having the signature: ``def meth(self, node)`` by :py:meth:`waflib.TaskGen.extension` + #. The method is retrieved through :py:meth:`waflib.TaskGen.task_gen.get_hook` + #. When called, the methods may modify self.source to append more source to process + #. The mappings can map an extension or a filename (see the code below) + """ + self.source = self.to_nodes(getattr(self, 'source', [])) + for node in self.source: + self.get_hook(node)(self, node) + +@feature('*') +@before_method('process_source') +def process_rule(self): + """ + Process the attribute ``rule``. When present, :py:meth:`waflib.TaskGen.process_source` is disabled:: + + def build(bld): + bld(rule='cp ${SRC} ${TGT}', source='wscript', target='bar.txt') + """ + if not getattr(self, 'rule', None): + return + + # create the task class + name = str(getattr(self, 'name', None) or self.target or self.rule) + cls = Task.task_factory(name, self.rule, + getattr(self, 'vars', []), + shell=getattr(self, 'shell', True), color=getattr(self, 'color', 'BLUE')) + + # now create one instance + tsk = self.create_task(name) + + if getattr(self, 'target', None): + if isinstance(self.target, str): + self.target = self.target.split() + if not isinstance(self.target, list): + self.target = [self.target] + for x in self.target: + if isinstance(x, str): + tsk.outputs.append(self.path.find_or_declare(x)) + else: + x.parent.mkdir() # if a node was given, create the required folders + tsk.outputs.append(x) + if getattr(self, 'install_path', None): + # from waf 1.5 + # although convenient, it does not 1. allow to name the target file and 2. symlinks + # TODO remove in waf 1.7 + self.bld.install_files(self.install_path, tsk.outputs) + + if getattr(self, 'source', None): + tsk.inputs = self.to_nodes(self.source) + # bypass the execution of process_source by setting the source to an empty list + self.source = [] + + if getattr(self, 'scan', None): + cls.scan = self.scan + elif getattr(self, 'deps', None): + def scan(self): + nodes = [] + for x in self.generator.to_list(self.generator.deps): + node = self.generator.path.find_resource(x) + if not node: + self.generator.bld.fatal('Could not find %r (was it declared?)' % x) + nodes.append(node) + return [nodes, []] + cls.scan = scan + + if getattr(self, 'cwd', None): + tsk.cwd = self.cwd + + # TODO remove on_results in waf 1.7 + if getattr(self, 'update_outputs', None) or getattr(self, 'on_results', None): + Task.update_outputs(cls) + + if getattr(self, 'always', None): + Task.always_run(cls) + + for x in ['after', 'before', 'ext_in', 'ext_out']: + setattr(cls, x, getattr(self, x, [])) + +@feature('seq') +def sequence_order(self): + """ + Add a strict sequential constraint between the tasks generated by task generators. + It works because task generators are posted in order. + It will not post objects which belong to other folders. + + Example:: + + bld(features='javac seq') + bld(features='jar seq') + + To start a new sequence, set the attribute seq_start, for example:: + + obj = bld(features='seq') + obj.seq_start = True + + Note that the method is executed in last position. This is more an + example than a widely-used solution. + """ + if self.meths and self.meths[-1] != 'sequence_order': + self.meths.append('sequence_order') + return + + if getattr(self, 'seq_start', None): + return + + # all the tasks previously declared must be run before these + if getattr(self.bld, 'prev', None): + self.bld.prev.post() + for x in self.bld.prev.tasks: + for y in self.tasks: + y.set_run_after(x) + + self.bld.prev = self + + +re_m4 = re.compile('@(\w+)@', re.M) + +class subst_pc(Task.Task): + """ + Create *.pc* files from *.pc.in*. The task is executed whenever an input variable used + in the substitution changes. + """ + + def run(self): + "Substitutes variables in a .in file" + + code = self.inputs[0].read() + + # replace all % by %% to prevent errors by % signs + code = code.replace('%', '%%') + + # extract the vars foo into lst and replace @foo@ by %(foo)s + lst = [] + def repl(match): + g = match.group + if g(1): + lst.append(g(1)) + return "%%(%s)s" % g(1) + return '' + code = re_m4.sub(repl, code) + + try: + d = self.generator.dct + except AttributeError: + d = {} + for x in lst: + tmp = getattr(self.generator, x, '') or self.env.get_flat(x) or self.env.get_flat(x.upper()) + d[x] = str(tmp) + + self.outputs[0].write(code % d) + self.generator.bld.raw_deps[self.uid()] = self.dep_vars = lst + + # make sure the signature is updated + try: delattr(self, 'cache_sig') + except AttributeError: pass + + def sig_vars(self): + """ + Compute a hash (signature) of the variables used in the substitution + """ + bld = self.generator.bld + env = self.env + upd = self.m.update + + # raw_deps: persistent custom values returned by the scanner + vars = self.generator.bld.raw_deps.get(self.uid(), []) + + # hash both env vars and task generator attributes + act_sig = bld.hash_env_vars(env, vars) + upd(act_sig) + + lst = [getattr(self.generator, x, '') for x in vars] + upd(Utils.h_list(lst)) + + return self.m.digest() + +@extension('.pc.in') +def add_pcfile(self, node): + """ + Process *.pc.in* files to *.pc*. Install the results to ``${PREFIX}/lib/pkgconfig/`` + + def build(bld): + bld(source='foo.pc.in', install_path='${LIBDIR}/pkgconfig/') + """ + tsk = self.create_task('subst_pc', node, node.change_ext('.pc', '.pc.in')) + self.bld.install_files(getattr(self, 'install_path', '${LIBDIR}/pkgconfig/'), tsk.outputs) + +class subst(subst_pc): + pass + +@feature('subst') +@before_method('process_source', 'process_rule') +def process_subst(self): + """ + Define a transformation that substitutes the contents of *source* files to *target* files:: + + def build(bld): + bld( + features='subst', + source='foo.c.in', + target='foo.c', + install_path='${LIBDIR}/pkgconfig', + VAR = 'val' + ) + + The input files are supposed to contain macros of the form *@VAR@*, where *VAR* is an argument + of the task generator object. + + This method overrides the processing by :py:meth:`waflib.TaskGen.process_source`. + """ + src = self.to_nodes(getattr(self, 'source', [])) + tgt = getattr(self, 'target', []) + if isinstance(tgt, self.path.__class__): + tgt = [tgt] + tgt = [isinstance(x, self.path.__class__) and x or self.path.find_or_declare(x) for x in Utils.to_list(tgt)] + + if len(src) != len(tgt): + raise Errors.WafError('invalid source or target for %r' % self) + + for x, y in zip(src, tgt): + if not (x and y): + raise Errors.WafError('invalid source or target for %r' % self) + tsk = self.create_task('subst', x, y) + for a in ('after', 'before', 'ext_in', 'ext_out'): + val = getattr(self, a, None) + if val: + setattr(tsk, a, val) + + inst_to = getattr(self, 'install_path', None) + if inst_to: + self.bld.install_files(inst_to, tgt, chmod=getattr(self, 'chmod', Utils.O644)) + + self.source = [] + diff --git a/waflib/Tools/__init__.py b/waflib/Tools/__init__.py new file mode 100644 index 00000000..c8a3c349 --- /dev/null +++ b/waflib/Tools/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 (ita) diff --git a/waflib/Tools/ar.py b/waflib/Tools/ar.py new file mode 100644 index 00000000..bf488e2f --- /dev/null +++ b/waflib/Tools/ar.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) +# Ralf Habacker, 2006 (rh) + +""" +The **ar** program creates static libraries. This tool is almost always loaded +from others (C, C++, D, etc) for static library support. +""" + +from waflib.Configure import conf + +@conf +def find_ar(conf): + """Configuration helper used by C/C++ tools to enable the support for static libraries""" + conf.load('ar') + +def configure(conf): + """Find the ar program and set the default flags in ``conf.env.ARFLAGS``""" + conf.find_program('ar', var='AR') + conf.env.ARFLAGS = 'rcs' + diff --git a/waflib/Tools/asm.py b/waflib/Tools/asm.py new file mode 100644 index 00000000..43918f9d --- /dev/null +++ b/waflib/Tools/asm.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2008-2010 (ita) + +""" +Assembly support, used by tools such as gas and nasm + +To declare targets using assembly:: + + def configure(conf): + conf.load('gcc gas') + + def build(bld): + bld( + features='c cstlib asm', + source = 'test.S', + target = 'asmtest') + + bld( + features='asm asmprogram', + source = 'test.S', + target = 'asmtest') + +Support for pure asm programs and libraries should also work:: + + def configure(conf): + conf.load('nasm') + conf.find_program('ld', 'ASLINK') + + def build(bld): + bld( + features='asm asmprogram', + source = 'test.S', + target = 'asmtest') +""" + +import os, sys +from waflib import Task, Utils +import waflib.Task +from waflib.Tools.ccroot import link_task, stlink_task +from waflib.TaskGen import extension, feature + +class asm(Task.Task): + """ + Compile asm files by gas/nasm/yasm/... + """ + color = 'BLUE' + run_str = '${AS} ${ASFLAGS} ${CPPPATH_ST:INCPATHS} ${AS_SRC_F}${SRC} ${AS_TGT_F}${TGT}' + +@extension('.s', '.S', '.asm', '.ASM', '.spp', '.SPP') +def asm_hook(self, node): + """ + Bind the asm extension to the asm task + + :param node: input file + :type node: :py:class:`waflib.Node.Node` + """ + return self.create_compiled_task('asm', node) + +class asmprogram(link_task): + "Link object files into a c program" + run_str = '${ASLINK} ${AS_TGT_F}${TGT} ${SRC}' + ext_out = ['.bin'] + inst_to = '${BINDIR}' + chmod = Utils.O755 + +class asmshlib(asmprogram): + "Link object files into a c shared library" + inst_to = '${LIBDIR}' + +class asmstlib(stlink_task): + "Link object files into a c static library" + pass # do not remove + diff --git a/waflib/Tools/bison.py b/waflib/Tools/bison.py new file mode 100644 index 00000000..9b903174 --- /dev/null +++ b/waflib/Tools/bison.py @@ -0,0 +1,49 @@ +#!/usr/bin/env python +# encoding: utf-8 +# John O'Meara, 2006 +# Thomas Nagy 2009-2010 (ita) + +""" +The **bison** program is a code generator which creates C or C++ files. +The generated files are compiled into object files. +""" + +from waflib import Task +from waflib.TaskGen import extension + +class bison(Task.Task): + """Compile bison files""" + color = 'BLUE' + run_str = '${BISON} ${BISONFLAGS} ${SRC[0].abspath()} -o ${TGT[0].name}' + ext_out = ['.h'] # just to make sure + +@extension('.y', '.yc', '.yy') +def big_bison(self, node): + """ + Create a bison task, which must be executed from the directory of the output file. + """ + has_h = '-d' in self.env['BISONFLAGS'] + + outs = [] + if node.name.endswith('.yc'): + outs.append(node.change_ext('.tab.cc')) + if has_h: + outs.append(node.change_ext('.tab.hh')) + else: + outs.append(node.change_ext('.tab.c')) + if has_h: + outs.append(node.change_ext('.tab.h')) + + tsk = self.create_task('bison', node, outs) + tsk.cwd = node.parent.get_bld().abspath() + + # and the c/cxx file must be compiled too + self.source.append(outs[0]) + +def configure(conf): + """ + Detect the *bison* program + """ + conf.find_program('bison', var='BISON') + conf.env.BISONFLAGS = ['-d'] + diff --git a/waflib/Tools/c.py b/waflib/Tools/c.py new file mode 100644 index 00000000..a136fb98 --- /dev/null +++ b/waflib/Tools/c.py @@ -0,0 +1,40 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +"Base for c programs/libraries" + +from waflib import TaskGen, Task, Utils +from waflib.Tools import c_preproc +from waflib.Tools.ccroot import link_task, stlink_task + +@TaskGen.extension('.c') +def c_hook(self, node): + "Bind the c file extension to the creation of a :py:class:`waflib.Tools.c.c` instance" + return self.create_compiled_task('c', node) + +class c(Task.Task): + "Compile C files into object files" + run_str = '${CC} ${ARCH_ST:ARCH} ${CFLAGS} ${CPPFLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${CPPPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} ${CC_SRC_F}${SRC} ${CC_TGT_F}${TGT}' + vars = ['CCDEPS'] # unused variable to depend on, just in case + ext_in = ['.h'] # set the build order easily by using ext_out=['.h'] + scan = c_preproc.scan + +Task.classes['cc'] = cc = c # compat, remove in waf 1.7 + +class cprogram(link_task): + "Link object files into a c program" + run_str = '${LINK_CC} ${CCLNK_SRC_F}${SRC} ${CCLNK_TGT_F}${TGT[0].abspath()} ${LINKFLAGS} ${RPATH_ST:RPATH} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${FRAMEWORK_ST:FRAMEWORK} ${ARCH_ST:ARCH} ${STLIB_MARKER} ${STLIBPATH_ST:STLIBPATH} ${STLIB_ST:STLIB} ${SHLIB_MARKER} ${LIBPATH_ST:LIBPATH} ${LIB_ST:LIB}' + ext_out = ['.bin'] + vars = ['LINKDEPS'] + inst_to = '${BINDIR}' + chmod = Utils.O755 + +class cshlib(cprogram): + "Link object files into a c shared library" + inst_to = '${LIBDIR}' + +class cstlib(stlink_task): + "Link object files into a c static library" + pass # do not remove + diff --git a/waflib/Tools/c_aliases.py b/waflib/Tools/c_aliases.py new file mode 100644 index 00000000..0e4059c0 --- /dev/null +++ b/waflib/Tools/c_aliases.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 (ita) + +"base for all c/c++ programs and libraries" + +import os, sys, re +from waflib import Utils, Build +from waflib.Configure import conf + +def get_extensions(lst): + """ + :param lst: files to process + :list lst: list of string or :py:class:`waflib.Node.Node` + :return: list of file extensions + :rtype: list of string + """ + ret = [] + for x in Utils.to_list(lst): + try: + if not isinstance(x, str): + x = x.name + ret.append(x[x.rfind('.') + 1:]) + except: + pass + return ret + +def sniff_features(**kw): + """ + Look at the source files and return the features for a task generator (mainly cc and cxx):: + + snif_features(source=['foo.c', 'foo.cxx'], type='shlib') + # returns ['cxx', 'c', 'cxxshlib', 'cshlib'] + + :param source: source files to process + :type source: list of string or :py:class:`waflib.Node.Node` + :param type: object type in *program*, *shlib* or *stlib* + :type type: string + :return: the list of features for a task generator processing the source files + :rtype: list of string + """ + exts = get_extensions(kw['source']) + type = kw['_type'] + feats = [] + + # watch the order, cxx will have the precedence + if 'cxx' in exts or 'cpp' in exts or 'c++' in exts or 'cc' in exts or 'C' in exts: + feats.append('cxx') + + if 'c' in exts or 'vala' in exts: + feats.append('c') + + if 'd' in exts: + feats.append('d') + + if 'java' in exts: + feats.append('java') + + if 'java' in exts: + return 'java' + + if type in ['program', 'shlib', 'stlib']: + for x in feats: + if x in ['cxx', 'd', 'c']: + feats.append(x + type) + + return feats + +def set_features(kw, _type): + kw['_type'] = _type + kw['features'] = Utils.to_list(kw.get('features', [])) + Utils.to_list(sniff_features(**kw)) + +@conf +def program(bld, *k, **kw): + """ + Alias for creating programs by looking at the file extensions:: + + def build(bld): + bld.program(source='foo.c', target='app') + # equivalent to: + # bld(features='c cprogram', source='foo.c', target='app') + + """ + set_features(kw, 'program') + return bld(*k, **kw) + +@conf +def shlib(bld, *k, **kw): + """ + Alias for creating shared libraries by looking at the file extensions:: + + def build(bld): + bld.shlib(source='foo.c', target='app') + # equivalent to: + # bld(features='c cshlib', source='foo.c', target='app') + + """ + set_features(kw, 'shlib') + return bld(*k, **kw) + +@conf +def stlib(bld, *k, **kw): + """ + Alias for creating static libraries by looking at the file extensions:: + + def build(bld): + bld.stlib(source='foo.cpp', target='app') + # equivalent to: + # bld(features='cxx cxxstlib', source='foo.cpp', target='app') + + """ + set_features(kw, 'stlib') + return bld(*k, **kw) + +@conf +def objects(bld, *k, **kw): + """ + Alias for creating object files by looking at the file extensions:: + + def build(bld): + bld.objects(source='foo.c', target='app') + # equivalent to: + # bld(features='c', source='foo.c', target='app') + + """ + set_features(kw, 'objects') + return bld(*k, **kw) + diff --git a/waflib/Tools/c_config.py b/waflib/Tools/c_config.py new file mode 100644 index 00000000..8a8af4ce --- /dev/null +++ b/waflib/Tools/c_config.py @@ -0,0 +1,1190 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 (ita) + +""" +C/C++/D configuration helpers +""" + +import os, imp, sys, re, shlex, shutil +from waflib import Build, Utils, Configure, Task, Options, Logs, TaskGen, Errors, ConfigSet, Runner +from waflib.TaskGen import before_method, after_method, feature +from waflib.Configure import conf + +WAF_CONFIG_H = 'config.h' +"""default name for the config.h file""" + +DEFKEYS = 'define_key' +INCKEYS = 'include_key' + +cfg_ver = { + 'atleast-version': '>=', + 'exact-version': '==', + 'max-version': '<=', +} + +SNIP_FUNCTION = ''' + int main() { + void *p; + p=(void*)(%s); + return 0; +} +''' +"""Code template for checking for functions""" + +SNIP_TYPE = ''' +int main() { + if ((%(type_name)s *) 0) return 0; + if (sizeof (%(type_name)s)) return 0; +} +''' +"""Code template for checking for types""" + +SNIP_CLASS = ''' +int main() { + if ( +} +''' + +SNIP_EMPTY_PROGRAM = ''' +int main() { + return 0; +} +''' + +SNIP_FIELD = ''' +int main() { + char *off; + off = (char*) &((%(type_name)s*)0)->%(field_name)s; + return (size_t) off < sizeof(%(type_name)s); +} +''' + +MACRO_TO_DESTOS = { +'__linux__' : 'linux', +'__GNU__' : 'gnu', +'__FreeBSD__' : 'freebsd', +'__NetBSD__' : 'netbsd', +'__OpenBSD__' : 'openbsd', +'__sun' : 'sunos', +'__hpux' : 'hpux', +'__sgi' : 'irix', +'_AIX' : 'aix', +'__CYGWIN__' : 'cygwin', +'__MSYS__' : 'msys', +'_UWIN' : 'uwin', +'_WIN64' : 'win32', +'_WIN32' : 'win32', +'__POWERPC__' : 'powerpc', +'__QNX__' : 'qnx' +} + +MACRO_TO_DEST_CPU = { +'__x86_64__' : 'x86_64', +'__i386__' : 'x86', +'__ia64__' : 'ia', +'__mips__' : 'mips', +'__sparc__' : 'sparc', +'__alpha__' : 'alpha', +'__arm__' : 'arm', +'__hppa__' : 'hppa', +'__powerpc__' : 'powerpc', +} + +@conf +def parse_flags(self, line, uselib, env=None): + """ + Parse the flags from the input lines, and add them to the relevant use variables:: + + def configure(conf): + conf.parse_flags('-O3', uselib_store='FOO') + # conf.env.CXXFLAGS_FOO = ['-O3'] + # conf.env.CFLAGS_FOO = ['-O3'] + + :param line: flags + :type line: string + :param uselib: where to add the flags + :type uselib: string + :param env: config set or conf.env by default + :type env: :py:class:`waflib.ConfigSet.ConfigSet` + """ + + assert(isinstance(line, str)) + + env = env or self.env + + # append_unique is not always possible + # for example, apple flags may require both -arch i386 and -arch ppc + + app = env.append_value + appu = env.append_unique + #lst = shlex.split(line) + # issue #811 + lex = shlex.shlex(line, posix=False) + lex.whitespace_split = True + lex.commenters = '' + lst = list(lex) + + while lst: + x = lst.pop(0) + st = x[:2] + ot = x[2:] + + if st == '-I' or st == '/I': + if not ot: ot = lst.pop(0) + appu('INCLUDES_' + uselib, [ot]) + elif st == '-include': + tmp = [x, lst.pop(0)] + app('CFLAGS', tmp) + app('CXXFLAGS', tmp) + elif st == '-D' or (self.env.CXX_NAME == 'msvc' and st == '/D'): # not perfect but.. + if not ot: ot = lst.pop(0) + app('DEFINES_' + uselib, [ot]) + elif st == '-l': + if not ot: ot = lst.pop(0) + appu('LIB_' + uselib, [ot]) + elif st == '-L': + if not ot: ot = lst.pop(0) + appu('LIBPATH_' + uselib, [ot]) + elif x == '-pthread' or x.startswith('+') or x.startswith('-std'): + app('CFLAGS_' + uselib, [x]) + app('CXXFLAGS_' + uselib, [x]) + app('LINKFLAGS_' + uselib, [x]) + elif x == '-framework': + appu('FRAMEWORK_' + uselib, [lst.pop(0)]) + elif x.startswith('-F'): + appu('FRAMEWORKPATH_' + uselib, [x[2:]]) + elif x.startswith('-Wl'): + app('LINKFLAGS_' + uselib, [x]) + elif x.startswith('-m') or x.startswith('-f') or x.startswith('-dynamic'): + app('CFLAGS_' + uselib, [x]) + app('CXXFLAGS_' + uselib, [x]) + elif x.startswith('-bundle'): + app('LINKFLAGS_' + uselib, [x]) + elif x.startswith('-undefined'): + arg = lst.pop(0) + app('LINKFLAGS_' + uselib, [x, arg]) + elif x.startswith('-arch') or x.startswith('-isysroot'): + tmp = [x, lst.pop(0)] + app('CFLAGS_' + uselib, tmp) + app('CXXFLAGS_' + uselib, tmp) + app('LINKFLAGS_' + uselib, tmp) + elif x.endswith('.a') or x.endswith('.so') or x.endswith('.dylib'): + appu('LINKFLAGS_' + uselib, [x]) # not cool, #762 + +@conf +def ret_msg(self, f, kw): + if isinstance(f, str): + return f + return f(kw) + +@conf +def validate_cfg(self, kw): + """ + Search for the program *pkg-config* if missing, and validate the parameters to pass to + :py:func:`waflib.Tools.c_config.exec_cfg`. + + :param path: the **-config program to use** (default is *pkg-config*) + :type path: list of string + :param msg: message to display to describe the test executed + :type msg: string + :param okmsg: message to display when the test is successful + :type okmsg: string + :param errmsg: message to display in case of error + :type errmsg: string + """ + if not 'path' in kw: + if not self.env.PKGCONFIG: + self.find_program('pkg-config', var='PKGCONFIG') + kw['path'] = self.env.PKGCONFIG + + # pkg-config version + if 'atleast_pkgconfig_version' in kw: + if not 'msg' in kw: + kw['msg'] = 'Checking for pkg-config version >= %r' % kw['atleast_pkgconfig_version'] + return + + if not 'okmsg' in kw: + kw['okmsg'] = 'yes' + if not 'errmsg' in kw: + kw['errmsg'] = 'not found' + + if 'modversion' in kw: + if not 'msg' in kw: + kw['msg'] = 'Checking for %r version' % kw['modversion'] + return + + # checking for the version of a module, for the moment, one thing at a time + for x in cfg_ver.keys(): + y = x.replace('-', '_') + if y in kw: + if not 'package' in kw: + raise ValueError('%s requires a package' % x) + + if not 'msg' in kw: + kw['msg'] = 'Checking for %r %s %s' % (kw['package'], cfg_ver[x], kw[y]) + return + + if not 'msg' in kw: + kw['msg'] = 'Checking for %r' % (kw['package'] or kw['path']) + +@conf +def exec_cfg(self, kw): + """ + Execute the program *pkg-config*: + + * if atleast_pkgconfig_version is given, check that pkg-config has the version n and return + * if modversion is given, then return the module version + * else, execute the *-config* program with the *args* and *variables* given, and set the flags on the *conf.env.FLAGS_name* variable + + :param atleast_pkgconfig_version: minimum pkg-config version to use (disable other tests) + :type atleast_pkgconfig_version: string + :param package: package name, for example *gtk+-2.0* + :type package: string + :param uselib_store: if the test is successful, define HAVE\_*name*. It is also used to define *conf.env.FLAGS_name* variables. + :type uselib_store: string + :param modversion: if provided, return the version of the given module and define *name*\_VERSION + :type modversion: string + :param args: arguments to give to *package* when retrieving flags + :type args: list of string + :param variables: return the values of particular variables + :type variables: list of string + :param define_variable: additional variables to define (also in conf.env.PKG_CONFIG_DEFINES) + :type define_variable: dict(string: string) + """ + + # pkg-config version + if 'atleast_pkgconfig_version' in kw: + cmd = [kw['path'], '--atleast-pkgconfig-version=%s' % kw['atleast_pkgconfig_version']] + self.cmd_and_log(cmd) + if not 'okmsg' in kw: + kw['okmsg'] = 'yes' + return + + # checking for the version of a module + for x in cfg_ver: + y = x.replace('-', '_') + if y in kw: + self.cmd_and_log([kw['path'], '--%s=%s' % (x, kw[y]), kw['package']]) + if not 'okmsg' in kw: + kw['okmsg'] = 'yes' + self.define(self.have_define(kw.get('uselib_store', kw['package'])), 1, 0) + break + + # retrieving the version of a module + if 'modversion' in kw: + version = self.cmd_and_log([kw['path'], '--modversion', kw['modversion']]).strip() + self.define('%s_VERSION' % Utils.quote_define_name(kw.get('uselib_store', kw['modversion'])), version) + return version + + lst = [kw['path']] + + defi = kw.get('define_variable', None) + if not defi: + defi = self.env.PKG_CONFIG_DEFINES or {} + for key, val in defi.items(): + lst.append('--define-variable=%s=%s' % (key, val)) + + if kw['package']: + lst.extend(Utils.to_list(kw['package'])) + + # retrieving variables of a module + if 'variables' in kw: + env = kw.get('env', self.env) + uselib = kw.get('uselib_store', kw['package'].upper()) + vars = Utils.to_list(kw['variables']) + for v in vars: + val = self.cmd_and_log(lst + ['--variable=' + v]).strip() + var = '%s_%s' % (uselib, v) + env[var] = val + if not 'okmsg' in kw: + kw['okmsg'] = 'yes' + return + + if 'args' in kw: + lst += Utils.to_list(kw['args']) + # so we assume the command-line will output flags to be parsed afterwards + ret = self.cmd_and_log(lst) + if not 'okmsg' in kw: + kw['okmsg'] = 'yes' + + self.define(self.have_define(kw.get('uselib_store', kw['package'])), 1, 0) + self.parse_flags(ret, kw.get('uselib_store', kw['package'].upper()), kw.get('env', self.env)) + return ret + +@conf +def check_cfg(self, *k, **kw): + """ + Check for configuration flags using a **-config**-like program (pkg-config, sdl-config, etc). + Encapsulate the calls to :py:func:`waflib.Tools.c_config.validate_cfg` and :py:func:`waflib.Tools.c_config.exec_cfg` + + A few examples:: + + def configure(conf): + conf.load('compiler_c') + conf.check_cfg(package='glib-2.0', args='--libs --cflags') + conf.check_cfg(package='glib-2.0', uselib_store='GLIB', atleast_version='2.10.0', + args='--cflags --libs') + conf.check_cfg(package='pango') + conf.check_cfg(package='pango', uselib_store='MYPANGO', args=['--cflags', '--libs']) + conf.check_cfg(package='pango', + args=['pango >= 0.1.0', 'pango < 9.9.9', '--cflags', '--libs'], + msg="Checking for 'pango 0.1.0'") + conf.check_cfg(path='sdl-config', args='--cflags --libs', package='', uselib_store='SDL') + conf.check_cfg(path='mpicc', args='--showme:compile --showme:link', + package='', uselib_store='OPEN_MPI', mandatory=False) + + """ + if k: + lst = k[0].split() + kw['package'] = lst[0] + kw['args'] = ' '.join(lst[1:]) + + self.validate_cfg(kw) + if 'msg' in kw: + self.start_msg(kw['msg']) + ret = None + try: + ret = self.exec_cfg(kw) + except self.errors.WafError as e: + if 'errmsg' in kw: + self.end_msg(kw['errmsg'], 'YELLOW') + if Logs.verbose > 1: + raise + else: + self.fatal('The configuration failed') + else: + kw['success'] = ret + if 'okmsg' in kw: + self.end_msg(self.ret_msg(kw['okmsg'], kw)) + + return ret + +@conf +def validate_c(self, kw): + """ + pre-check the parameters that will be given to run_c_code + + :param env: an optional environment (modified -> provide a copy) + :type env: :py:class:`waflib.ConfigSet.ConfigSet` + :param compiler: c or cxx (tries to guess what is best) + :type compiler: string + :param type: cprogram, cshlib, cstlib - not required if *features are given directly* + :type type: binary to create + :param feature: desired features for the task generator that will execute the test, for example ``cxx cxxstlib`` + :type feature: list of string + :param fragment: provide a piece of code for the test (default is to let the system create one) + :type fragment: string + :param uselib_store: define variables after the test is executed (IMPORTANT!) + :type uselib_store: string + :param use: parameters to use for building (just like the normal *use* keyword) + :type use: list of string + :param define_name: define to set when the check is over + :type define_name: string + :param execute: execute the resulting binary + :type execute: bool + :param define_ret: if execute is set to True, use the execution output in both the define and the return value + :type define_ret: bool + :param header_name: check for a particular header + :type header_name: string + :param auto_add_header_name: if header_name was set, add the headers in env.INCKEYS so the next tests will include these headers + :type auto_add_header_name: bool + """ + + if not 'env' in kw: + kw['env'] = self.env.derive() + env = kw['env'] + + if not 'compiler' in kw and not 'features' in kw: + kw['compiler'] = 'c' + if env['CXX_NAME'] and Task.classes.get('cxx', None): + kw['compiler'] = 'cxx' + if not self.env['CXX']: + self.fatal('a c++ compiler is required') + else: + if not self.env['CC']: + self.fatal('a c compiler is required') + + if not 'compile_mode' in kw: + kw['compile_mode'] = 'c' + if 'cxx' in Utils.to_list(kw.get('features',[])) or kw.get('compiler', '') == 'cxx': + kw['compile_mode'] = 'cxx' + + if not 'type' in kw: + kw['type'] = 'cprogram' + + if not 'features' in kw: + kw['features'] = [kw['compile_mode'], kw['type']] # "cprogram c" + else: + kw['features'] = Utils.to_list(kw['features']) + + if not 'compile_filename' in kw: + kw['compile_filename'] = 'test.c' + ((kw['compile_mode'] == 'cxx') and 'pp' or '') + + + def to_header(dct): + if 'header_name' in dct: + dct = Utils.to_list(dct['header_name']) + return ''.join(['#include <%s>\n' % x for x in dct]) + return '' + + #OSX + if 'framework_name' in kw: + fwkname = kw['framework_name'] + if not 'uselib_store' in kw: + kw['uselib_store'] = fwkname.upper() + + if not kw.get('no_header', False): + if not 'header_name' in kw: + kw['header_name'] = [] + fwk = '%s/%s.h' % (fwkname, fwkname) + if kw.get('remove_dot_h', None): + fwk = fwk[:-2] + kw['header_name'] = Utils.to_list(kw['header_name']) + [fwk] + + kw['msg'] = 'Checking for framework %s' % fwkname + kw['framework'] = fwkname + #kw['frameworkpath'] = set it yourself + + if 'function_name' in kw: + fu = kw['function_name'] + if not 'msg' in kw: + kw['msg'] = 'Checking for function %s' % fu + kw['code'] = to_header(kw) + SNIP_FUNCTION % fu + if not 'uselib_store' in kw: + kw['uselib_store'] = fu.upper() + if not 'define_name' in kw: + kw['define_name'] = self.have_define(fu) + + elif 'type_name' in kw: + tu = kw['type_name'] + if not 'header_name' in kw: + kw['header_name'] = 'stdint.h' + if 'field_name' in kw: + field = kw['field_name'] + kw['code'] = to_header(kw) + SNIP_FIELD % {'type_name' : tu, 'field_name' : field} + if not 'msg' in kw: + kw['msg'] = 'Checking for field %s in %s' % (field, tu) + if not 'define_name' in kw: + kw['define_name'] = self.have_define((tu + '_' + field).upper()) + else: + kw['code'] = to_header(kw) + SNIP_TYPE % {'type_name' : tu} + if not 'msg' in kw: + kw['msg'] = 'Checking for type %s' % tu + if not 'define_name' in kw: + kw['define_name'] = self.have_define(tu.upper()) + + elif 'header_name' in kw: + if not 'msg' in kw: + kw['msg'] = 'Checking for header %s' % kw['header_name'] + + l = Utils.to_list(kw['header_name']) + assert len(l)>0, 'list of headers in header_name is empty' + + kw['code'] = to_header(kw) + SNIP_EMPTY_PROGRAM + + if not 'uselib_store' in kw: + kw['uselib_store'] = l[0].upper() + + if not 'define_name' in kw: + kw['define_name'] = self.have_define(l[0]) + + if 'lib' in kw: + if not 'msg' in kw: + kw['msg'] = 'Checking for library %s' % kw['lib'] + if not 'uselib_store' in kw: + kw['uselib_store'] = kw['lib'].upper() + + if 'stlib' in kw: + if not 'msg' in kw: + kw['msg'] = 'Checking for static library %s' % kw['stlib'] + if not 'uselib_store' in kw: + kw['uselib_store'] = kw['stlib'].upper() + + if 'fragment' in kw: + # an additional code fragment may be provided to replace the predefined code + # in custom headers + kw['code'] = kw['fragment'] + if not 'msg' in kw: + kw['msg'] = 'Checking for code snippet' + if not 'errmsg' in kw: + kw['errmsg'] = 'no' + + for (flagsname,flagstype) in [('cxxflags','compiler'), ('cflags','compiler'), ('linkflags','linker')]: + if flagsname in kw: + if not 'msg' in kw: + kw['msg'] = 'Checking for %s flags %s' % (flagstype, kw[flagsname]) + if not 'errmsg' in kw: + kw['errmsg'] = 'no' + + if not 'execute' in kw: + kw['execute'] = False + if kw['execute']: + kw['features'].append('test_exec') + + if not 'errmsg' in kw: + kw['errmsg'] = 'not found' + + if not 'okmsg' in kw: + kw['okmsg'] = 'yes' + + if not 'code' in kw: + kw['code'] = SNIP_EMPTY_PROGRAM + + # if there are headers to append automatically to the next tests + if self.env[INCKEYS]: + kw['code'] = '\n'.join(['#include <%s>' % x for x in self.env[INCKEYS]]) + '\n' + kw['code'] + + if not kw.get('success'): kw['success'] = None + + if 'define_name' in kw: + self.undefine(kw['define_name']) + + assert 'msg' in kw, 'invalid parameters, read http://freehackers.org/~tnagy/wafbook/single.html#config_helpers_c' + +@conf +def post_check(self, *k, **kw): + "Set the variables after a test executed in :py:func:`waflib.Tools.c_config.check` was run successfully" + + is_success = 0 + if kw['execute']: + if kw['success'] is not None: + if kw.get('define_ret', False): + is_success = kw['success'] + else: + is_success = (kw['success'] == 0) + else: + is_success = (kw['success'] == 0) + + if 'define_name' in kw: + # TODO simplify? + if 'header_name' in kw or 'function_name' in kw or 'type_name' in kw or 'fragment' in kw: + nm = kw['define_name'] + if kw['execute'] and kw.get('define_ret', None) and isinstance(is_success, str): + self.define(kw['define_name'], is_success, quote=kw.get('quote', 1)) + else: + self.define_cond(kw['define_name'], is_success) + else: + self.define_cond(kw['define_name'], is_success) + + if 'header_name' in kw: + if kw.get('auto_add_header_name', False): + self.env.append_value(INCKEYS, Utils.to_list(kw['header_name'])) + + if is_success and 'uselib_store' in kw: + from waflib.Tools import ccroot + + # TODO see get_uselib_vars from ccroot.py + _vars = set([]) + for x in kw['features']: + if x in ccroot.USELIB_VARS: + _vars |= ccroot.USELIB_VARS[x] + + for k in _vars: + lk = k.lower() + if k == 'INCLUDES': lk = 'includes' + if k == 'DEFINES': lk = 'defines' + if lk in kw: + val = kw[lk] + # remove trailing slash + if isinstance(val, str): + val = val.rstrip(os.path.sep) + self.env.append_unique(k + '_' + kw['uselib_store'], val) + return is_success + +@conf +def check(self, *k, **kw): + """ + Perform a configuration test by calling :py:func:`waflib.Tools.c_config.run_c_code`. + For the complete list of parameters, see :py:func:`waflib.Tools.c_config.validate_c`. + To force a specific compiler, prefer the methods :py:func:`waflib.Tools.c_config.check_cxx` or :py:func:`waflib.Tools.c_config.check_cc` + """ + self.validate_c(kw) + self.start_msg(kw['msg']) + ret = None + try: + ret = self.run_c_code(*k, **kw) + except self.errors.ConfigurationError as e: + self.end_msg(kw['errmsg'], 'YELLOW') + if Logs.verbose > 1: + raise + else: + self.fatal('The configuration failed') + else: + kw['success'] = ret + self.end_msg(self.ret_msg(kw['okmsg'], kw)) + + ret = self.post_check(*k, **kw) + if not ret: + self.fatal('The configuration failed %r' % ret) + return ret + +class test_exec(Task.Task): + """ + A task for executing a programs after they are built. See :py:func:`waflib.Tools.c_config.test_exec_fun`. + """ + color = 'PINK' + def run(self): + if getattr(self.generator, 'rpath', None): + if getattr(self.generator, 'define_ret', False): + self.generator.bld.retval = self.generator.bld.cmd_and_log([self.inputs[0].abspath()]) + else: + self.generator.bld.retval = self.generator.bld.exec_command([self.inputs[0].abspath()]) + else: + env = self.env.env or {} + env.update(dict(os.environ)) + for var in ('LD_LIBRARY_PATH', 'DYLD_LIBRARY_PATH', 'PATH'): + env[var] = self.inputs[0].parent.abspath() + os.path.pathsep + env.get(var, '') + if getattr(self.generator, 'define_ret', False): + self.generator.bld.retval = self.generator.bld.cmd_and_log([self.inputs[0].abspath()], env=env) + else: + self.generator.bld.retval = self.generator.bld.exec_command([self.inputs[0].abspath()], env=env) + +@feature('test_exec') +@after_method('apply_link') +def test_exec_fun(self): + """ + The feature **test_exec** is used to create a task that will to execute the binary + created (link task output) during the build. The exit status will be set + on the build context, so only one program may have the feature *test_exec*. + This is used by configuration tests:: + + def configure(conf): + conf.check(execute=True) + """ + self.create_task('test_exec', self.link_task.outputs[0]) + +CACHE_RESULTS = 1 +COMPILE_ERRORS = 2 + +@conf +def run_c_code(self, *k, **kw): + """ + Create a temporary build context to execute a build. A reference to that build + context is kept on self.test_bld for debugging purposes. + The parameters given in the arguments to this function are passed as arguments for + a single task generator created in the build. Only three parameters are obligatory: + + :param features: features to pass to a task generator created in the build + :type features: list of string + :param compile_filename: file to create for the compilation (default: *test.c*) + :type compile_filename: string + :param code: code to write in the filename to compile + :type code: string + + Though this function returns *0* by default, the build may set an attribute named *retval* on the + build context object to return a particular value. See :py:func:`waflib.Tools.c_config.test_exec_fun` for example. + + This function also provides a limited cache. To use it, provide the following option:: + + def options(opt): + opt.add_option('--confcache', dest='confcache', default=0, + action='count', help='Use a configuration cache') + + And execute the configuration with the following command-line:: + + $ waf configure --confcache + + """ + + lst = [str(v) for (p, v) in kw.items() if p != 'env'] + h = Utils.h_list(lst) + dir = self.bldnode.abspath() + os.sep + (not Utils.is_win32 and '.' or '') + 'conf_check_' + Utils.to_hex(h) + + try: + os.makedirs(dir) + except: + pass + + try: + os.stat(dir) + except: + self.fatal('cannot use the configuration test folder %r' % dir) + + cachemode = getattr(Options.options, 'confcache', None) + if cachemode == CACHE_RESULTS: + try: + proj = ConfigSet.ConfigSet(os.path.join(dir, 'cache_run_c_code')) + ret = proj['cache_run_c_code'] + except: + pass + else: + if isinstance(ret, str) and ret.startswith('Test does not build'): + self.fatal(ret) + return ret + + bdir = os.path.join(dir, 'testbuild') + + if not os.path.exists(bdir): + os.makedirs(bdir) + + self.test_bld = bld = Build.BuildContext(top_dir=dir, out_dir=bdir) + bld.init_dirs() + bld.progress_bar = 0 + bld.targets = '*' + + if kw['compile_filename']: + node = bld.srcnode.make_node(kw['compile_filename']) + node.write(kw['code']) + + bld.logger = self.logger + bld.all_envs.update(self.all_envs) # not really necessary + bld.env = kw['env'] + + o = bld(features=kw['features'], source=kw['compile_filename'], target='testprog') + + for k, v in kw.items(): + setattr(o, k, v) + + self.to_log("==>\n%s\n<==" % kw['code']) + + # compile the program + bld.targets = '*' + + ret = -1 + try: + try: + bld.compile() + except Errors.WafError: + ret = 'Test does not build: %s' % Utils.ex_stack() + self.fatal(ret) + else: + ret = getattr(bld, 'retval', 0) + finally: + # cache the results each time + proj = ConfigSet.ConfigSet() + proj['cache_run_c_code'] = ret + proj.store(os.path.join(dir, 'cache_run_c_code')) + + return ret + +@conf +def check_cxx(self, *k, **kw): + """ + Same as :py:func:`waflib.Tools.c_config.check` but default to the *c++* programming language + """ + kw['compiler'] = 'cxx' + return self.check(*k, **kw) + +@conf +def check_cc(self, *k, **kw): + """ + Same as :py:func:`waflib.Tools.c_config.check` but default to the *c* programming language + """ + kw['compiler'] = 'c' + return self.check(*k, **kw) + +@conf +def define(self, key, val, quote=True): + """ + Store a single define and its state into conf.env.DEFINES + + :param key: define name + :type key: string + :param val: value + :type val: int or string + :param quote: enclose strings in quotes (yes by default) + :type quote: bool + """ + assert key and isinstance(key, str) + + if isinstance(val, int) or isinstance(val, float): + s = '%s=%s' + else: + s = quote and '%s="%s"' or '%s=%s' + app = s % (key, str(val)) + + ban = key + '=' + lst = self.env['DEFINES'] + for x in lst: + if x.startswith(ban): + lst[lst.index(x)] = app + break + else: + self.env.append_value('DEFINES', app) + + self.env.append_unique(DEFKEYS, key) + +@conf +def undefine(self, key): + """ + Remove a define from conf.env.DEFINES + + :param key: define name + :type key: string + """ + assert key and isinstance(key, str) + + ban = key + '=' + lst = [x for x in self.env['DEFINES'] if not x.startswith(ban)] + self.env['DEFINES'] = lst + self.env.append_unique(DEFKEYS, key) + +@conf +def define_cond(self, key, val): + """ + Conditionally define a name:: + + def configure(conf): + conf.define_cond('A', True) + # equivalent to: + # if val: conf.define('A', 1) + # else: conf.undefine('A') + + :param key: define name + :type key: string + :param val: value + :type val: int or string + """ + assert key and isinstance(key, str) + + if val: + self.define(key, 1) + else: + self.undefine(key) + +@conf +def is_defined(self, key): + """ + :param key: define name + :type key: string + :return: True if the define is set + :rtype: bool + """ + assert key and isinstance(key, str) + + ban = key + '=' + for x in self.env['DEFINES']: + if x.startswith(ban): + return True + return False + +@conf +def get_define(self, key): + """ + :param key: define name + :type key: string + :return: the value of a previously stored define or None if it is not set + """ + assert key and isinstance(key, str) + + ban = key + '=' + for x in self.env['DEFINES']: + if x.startswith(ban): + return x[len(ban):] + return None + +@conf +def have_define(self, key): + """ + :param key: define name + :type key: string + :return: the input key prefixed by *HAVE_* and substitute any invalid characters. + :rtype: string + """ + return self.__dict__.get('HAVE_PAT', 'HAVE_%s') % Utils.quote_define_name(key) + +@conf +def write_config_header(self, configfile='', guard='', top=False, env=None, defines=True, headers=False, remove=True): + """ + Write a configuration header containing defines and includes:: + + def configure(cnf): + cnf.define('A', 1) + cnf.write_config_header('config.h') + + :param configfile: relative path to the file to create + :type configfile: string + :param env: config set to read the definitions from (default is conf.env) + :type env: :py:class:`waflib.ConfigSet.ConfigSet` + :param top: write the configuration header from the build directory (default is from the current path) + :type top: bool + :param defines: add the defines (yes by default) + :type defines: bool + :param headers: add #include in the file + :type headers: bool + :param remove: remove the defines after they are added (yes by default) + :type remove: bool + """ + if not configfile: configfile = WAF_CONFIG_H + waf_guard = guard or '_%s_WAF' % Utils.quote_define_name(configfile) + + node = top and self.bldnode or self.path.get_bld() + node = node.make_node(configfile) + node.parent.mkdir() + + lst = ['/* WARNING! All changes made to this file will be lost! */\n'] + lst.append('#ifndef %s\n#define %s\n' % (waf_guard, waf_guard)) + lst.append(self.get_config_header(defines, headers)) + lst.append('\n#endif /* %s */\n' % waf_guard) + + node.write('\n'.join(lst)) + + env = env or self.env + + # config files are not removed on "waf clean" + env.append_unique(Build.CFG_FILES, [node.abspath()]) + + if remove: + for key in self.env[DEFKEYS]: + self.undefine(key) + self.env[DEFKEYS] = [] + +@conf +def get_config_header(self, defines=True, headers=False): + """ + Create the contents of a ``config.h`` file from the defines and includes + set in conf.env.define_key / conf.env.include_key. No include guards are added. + + :param defines: write the defines values + :type defines: bool + :param headers: write the headers + :type headers: bool + :return: the contents of a ``config.h`` file + :rtype: string + """ + lst = [] + if headers: + for x in self.env[INCKEYS]: + lst.append('#include <%s>' % x) + + if defines: + for x in self.env[DEFKEYS]: + if self.is_defined(x): + val = self.get_define(x) + lst.append('#define %s %s' % (x, val)) + else: + lst.append('/* #undef %s */' % x) + return "\n".join(lst) + +@conf +def cc_add_flags(conf): + """ + Read the CFLAGS/CPPFLAGS from os.environ and add to conf.env.CFLAGS + """ + conf.add_os_flags('CPPFLAGS', 'CFLAGS') + conf.add_os_flags('CFLAGS') + +@conf +def cxx_add_flags(conf): + """ + Read the CXXFLAGS/CPPFLAGS and add to conf.env.CXXFLAGS + """ + conf.add_os_flags('CPPFLAGS', 'CXXFLAGS') + conf.add_os_flags('CXXFLAGS') + +@conf +def link_add_flags(conf): + """ + Read the LINKFLAGS/LDFLAGS and add to conf.env.LDFLAGS + """ + conf.add_os_flags('LINKFLAGS') + conf.add_os_flags('LDFLAGS', 'LINKFLAGS') + +@conf +def cc_load_tools(conf): + """ + Load the c tool + """ + if not conf.env.DEST_OS: + conf.env.DEST_OS = Utils.unversioned_sys_platform() + conf.load('c') + +@conf +def cxx_load_tools(conf): + """ + Load the cxx tool + """ + if not conf.env.DEST_OS: + conf.env.DEST_OS = Utils.unversioned_sys_platform() + conf.load('cxx') + +@conf +def get_cc_version(conf, cc, gcc=False, icc=False): + """ + Run the preprocessor to determine the compiler version + + The variables CC_VERSION, DEST_OS, DEST_BINFMT and DEST_CPU will be set in *conf.env* + """ + cmd = cc + ['-dM', '-E', '-'] + try: + p = Utils.subprocess.Popen(cmd, stdin=Utils.subprocess.PIPE, stdout=Utils.subprocess.PIPE, stderr=Utils.subprocess.PIPE) + p.stdin.write('\n'.encode()) + out = p.communicate()[0] + except: + conf.fatal('could not determine the compiler version %r' % cmd) + + if not isinstance(out, str): + out = out.decode(sys.stdout.encoding) + + if gcc: + if out.find('__INTEL_COMPILER') >= 0: + conf.fatal('The intel compiler pretends to be gcc') + if out.find('__GNUC__') < 0: + conf.fatal('Could not determine the compiler type') + + if icc and out.find('__INTEL_COMPILER') < 0: + conf.fatal('Not icc/icpc') + + k = {} + if icc or gcc: + out = out.split('\n') + import shlex + + for line in out: + lst = shlex.split(line) + if len(lst)>2: + key = lst[1] + val = lst[2] + k[key] = val + + def isD(var): + return var in k + + def isT(var): + return var in k and k[var] != '0' + + # Some documentation is available at http://predef.sourceforge.net + # The names given to DEST_OS must match what Utils.unversioned_sys_platform() returns. + if not conf.env.DEST_OS: + conf.env.DEST_OS = '' + for i in MACRO_TO_DESTOS: + if isD(i): + conf.env.DEST_OS = MACRO_TO_DESTOS[i] + break + else: + if isD('__APPLE__') and isD('__MACH__'): + conf.env.DEST_OS = 'darwin' + elif isD('__unix__'): # unix must be tested last as it's a generic fallback + conf.env.DEST_OS = 'generic' + + if isD('__ELF__'): + conf.env.DEST_BINFMT = 'elf' + elif isD('__WINNT__') or isD('__CYGWIN__'): + conf.env.DEST_BINFMT = 'pe' + conf.env.LIBDIR = conf.env['PREFIX'] + '/bin' + elif isD('__APPLE__'): + conf.env.DEST_BINFMT = 'mac-o' + + if not conf.env.DEST_BINFMT: + # Infer the binary format from the os name. + conf.env.DEST_BINFMT = Utils.destos_to_binfmt(conf.env.DEST_OS) + + for i in MACRO_TO_DEST_CPU: + if isD(i): + conf.env.DEST_CPU = MACRO_TO_DEST_CPU[i] + break + + Logs.debug('ccroot: dest platform: ' + ' '.join([conf.env[x] or '?' for x in ('DEST_OS', 'DEST_BINFMT', 'DEST_CPU')])) + if icc: + ver = k['__INTEL_COMPILER'] + conf.env['CC_VERSION'] = (ver[:-2], ver[-2], ver[-1]) + else: + conf.env['CC_VERSION'] = (k['__GNUC__'], k['__GNUC_MINOR__'], k['__GNUC_PATCHLEVEL__']) + return k + +@conf +def get_xlc_version(conf, cc): + """Get the compiler version""" + + version_re = re.compile(r"IBM XL C/C\+\+.*, V(?P\d*)\.(?P\d*)", re.I).search + cmd = cc + ['-qversion'] + + try: + out, err = conf.cmd_and_log(cmd, output=0) + except Errors.WafError: + conf.fatal('Could not find xlc %r' % cmd) + if out: match = version_re(out) + else: match = version_re(err) + if not match: + conf.fatal('Could not determine the XLC version.') + k = match.groupdict() + conf.env['CC_VERSION'] = (k['major'], k['minor']) + +# ============ the --as-needed flag should added during the configuration, not at runtime ========= + +@conf +def add_as_needed(self): + """ + Add ``--as-needed`` to the *LINKFLAGS* + """ + if self.env.DEST_BINFMT == 'elf' and 'gcc' in (self.env.CXX_NAME, self.env.CC_NAME): + self.env.append_unique('LINKFLAGS', '--as-needed') + +# ============ parallel configuration + +class cfgtask(Task.TaskBase): + """ + A task that executes configuration tests + make sure that the checks write to conf.env in a thread-safe manner + + for the moment it only executes conf.check + """ + def display(self): + return '' + + def runnable_status(self): + return Task.RUN_ME + + def run(self): + conf = self.conf + bld = Build.BuildContext(top_dir=conf.srcnode.abspath(), out_dir=conf.bldnode.abspath()) + bld.env = conf.env + bld.init_dirs() + bld.in_msg = 1 # suppress top-level start_msg + bld.logger = self.logger + try: + bld.check(**self.args) + except: + return 1 + +@conf +def multicheck(self, *k, **kw): + """ + Use tuples to perform parallel configuration tests + """ + self.start_msg(kw.get('msg', 'Executing %d configuration tests' % len(k))) + + class par(object): + def __init__(self): + self.keep = False + self.cache_global = Options.cache_global + self.nocache = Options.options.nocache + self.returned_tasks = [] + def total(self): + return len(tasks) + def to_log(self, *k, **kw): + return + + bld = par() + tasks = [] + for dct in k: + x = cfgtask(bld=bld) + tasks.append(x) + x.args = dct + x.bld = bld + x.conf = self + x.args = dct + + # bind a logger that will keep the info in memory + x.logger = Logs.make_mem_logger(str(id(x)), self.logger) + + def it(): + yield tasks + while 1: + yield [] + p = Runner.Parallel(bld, Options.options.jobs) + p.biter = it() + p.start() + + # flush the logs in order into the config.log + for x in tasks: + x.logger.memhandler.flush() + + for x in tasks: + if x.hasrun != Task.SUCCESS: + self.end_msg(kw.get('errmsg', 'no'), color='YELLOW') + self.fatal(kw.get('fatalmsg', None) or 'One of the tests has failed, see the config.log for more information') + + self.end_msg('ok') + diff --git a/waflib/Tools/c_osx.py b/waflib/Tools/c_osx.py new file mode 100644 index 00000000..ada04e9d --- /dev/null +++ b/waflib/Tools/c_osx.py @@ -0,0 +1,188 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy 2008-2010 + +""" +MacOSX related tools +""" + +import os, shutil, sys, platform +from waflib import TaskGen, Task, Build, Options, Utils, Errors +from waflib.TaskGen import taskgen_method, feature, after_method, before_method + +app_info = ''' + + + + + CFBundlePackageType + APPL + CFBundleGetInfoString + Created by Waf + CFBundleSignature + ???? + NOTE + THIS IS A GENERATED FILE, DO NOT MODIFY + CFBundleExecutable + %s + + +''' +""" +plist template +""" + +@feature('c', 'cxx') +def set_macosx_deployment_target(self): + """ + see WAF issue 285 and also and also http://trac.macports.org/ticket/17059 + """ + if self.env['MACOSX_DEPLOYMENT_TARGET']: + os.environ['MACOSX_DEPLOYMENT_TARGET'] = self.env['MACOSX_DEPLOYMENT_TARGET'] + elif 'MACOSX_DEPLOYMENT_TARGET' not in os.environ: + if sys.platform == 'darwin': + os.environ['MACOSX_DEPLOYMENT_TARGET'] = '.'.join(platform.mac_ver()[0].split('.')[:2]) + +@taskgen_method +def create_bundle_dirs(self, name, out): + """ + Create bundle folders, used by :py:func:`create_task_macplist` and :py:func:`create_task_macapp` + """ + bld = self.bld + dir = out.parent.find_or_declare(name) + dir.mkdir() + macos = dir.find_or_declare(['Contents', 'MacOS']) + macos.mkdir() + return dir + +def bundle_name_for_output(out): + name = out.name + k = name.rfind('.') + if k >= 0: + name = name[:k] + '.app' + else: + name = name + '.app' + return name + +@feature('cprogram', 'cxxprogram') +@after_method('apply_link') +def create_task_macapp(self): + """ + To compile an executable into a Mac application (a .app), set its *mac_app* attribute:: + + def build(bld): + bld.shlib(source='a.c', target='foo', mac_app = True) + + To force *all* executables to be transformed into Mac applications:: + + def build(bld): + bld.env.MACAPP = True + bld.shlib(source='a.c', target='foo') + """ + if self.env['MACAPP'] or getattr(self, 'mac_app', False): + out = self.link_task.outputs[0] + + name = bundle_name_for_output(out) + dir = self.create_bundle_dirs(name, out) + + n1 = dir.find_or_declare(['Contents', 'MacOS', out.name]) + + self.apptask = self.create_task('macapp', self.link_task.outputs, n1) + inst_to = getattr(self, 'install_path', '/Applications') + '/%s/Contents/MacOS/' % name + self.bld.install_files(inst_to, n1, chmod=Utils.O755) + + if getattr(self, 'mac_resources', None): + res_dir = n1.parent.parent.make_node('Resources') + inst_to = getattr(self, 'install_path', '/Applications') + '/%s/Resources' % name + for x in self.to_list(self.mac_resources): + node = self.path.find_node(x) + if not node: + raise Errors.WafError('Missing mac_resource %r in %r' % (x, self)) + + parent = node.parent + if os.path.isdir(node.abspath()): + nodes = node.ant_glob('**') + else: + nodes = [node] + for node in nodes: + rel = node.path_from(parent) + tsk = self.create_task('macapp', node, res_dir.make_node(rel)) + self.bld.install_as(inst_to + '/%s' % rel, node) + + if getattr(self.bld, 'is_install', None): + # disable the normal binary installation + self.install_task.hasrun = Task.SKIP_ME + +@feature('cprogram', 'cxxprogram') +@after_method('apply_link') +def create_task_macplist(self): + """ + Create a :py:class:`waflib.Tools.c_osx.macplist` instance. + """ + if self.env['MACAPP'] or getattr(self, 'mac_app', False): + out = self.link_task.outputs[0] + + name = bundle_name_for_output(out) + + dir = self.create_bundle_dirs(name, out) + n1 = dir.find_or_declare(['Contents', 'Info.plist']) + self.plisttask = plisttask = self.create_task('macplist', [], n1) + + if getattr(self, 'mac_plist', False): + node = self.path.find_resource(self.mac_plist) + if node: + plisttask.inputs.append(node) + else: + plisttask.code = self.mac_plist + else: + plisttask.code = app_info % self.link_task.outputs[0].name + + inst_to = getattr(self, 'install_path', '/Applications') + '/%s/Contents/' % name + self.bld.install_files(inst_to, n1) + +@feature('cshlib', 'cxxshlib') +@before_method('apply_link', 'propagate_uselib_vars') +def apply_bundle(self): + """ + To make a bundled shared library (a ``.bundle``), set the *mac_bundle* attribute:: + + def build(bld): + bld.shlib(source='a.c', target='foo', mac_bundle = True) + + To force *all* executables to be transformed into bundles:: + + def build(bld): + bld.env.MACBUNDLE = True + bld.shlib(source='a.c', target='foo') + """ + if self.env['MACBUNDLE'] or getattr(self, 'mac_bundle', False): + self.env['LINKFLAGS_cshlib'] = self.env['LINKFLAGS_cxxshlib'] = [] # disable the '-dynamiclib' flag + self.env['cshlib_PATTERN'] = self.env['cxxshlib_PATTERN'] = self.env['macbundle_PATTERN'] + use = self.use = self.to_list(getattr(self, 'use', [])) + if not 'MACBUNDLE' in use: + use.append('MACBUNDLE') + +app_dirs = ['Contents', 'Contents/MacOS', 'Contents/Resources'] + +class macapp(Task.Task): + """ + Create mac applications + """ + color = 'PINK' + def run(self): + self.outputs[0].parent.mkdir() + shutil.copy2(self.inputs[0].srcpath(), self.outputs[0].abspath()) + +class macplist(Task.Task): + """ + Create plist files + """ + color = 'PINK' + ext_in = ['.bin'] + def run(self): + if getattr(self, 'code', None): + txt = self.code + else: + txt = self.inputs[0].read() + self.outputs[0].write(txt) + diff --git a/waflib/Tools/c_preproc.py b/waflib/Tools/c_preproc.py new file mode 100644 index 00000000..30170221 --- /dev/null +++ b/waflib/Tools/c_preproc.py @@ -0,0 +1,1030 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +""" +C/C++ preprocessor for finding dependencies + +Reasons for using the Waf preprocessor by default + +#. Some c/c++ extensions (Qt) require a custom preprocessor for obtaining the dependencies (.moc files) +#. Not all compilers provide .d files for obtaining the dependencies (portability) +#. A naive file scanner will not catch the constructs such as "#include foo()" +#. A naive file scanner will catch unnecessary dependencies (change an unused header -> recompile everything) + +Regarding the speed concerns: + +* the preprocessing is performed only when files must be compiled +* the macros are evaluated only for #if/#elif/#include +* system headers are not scanned by default + +Now if you do not want the Waf preprocessor, the tool +gccdeps* uses the .d files produced +during the compilation to track the dependencies (useful when used with the boost libraries). +It only works with gcc >= 4.4 though. + +A dumb preprocessor is also available in the tool *c_dumbpreproc* +""" +# TODO: more varargs, pragma once + +import re, sys, os, string, traceback +from waflib import Logs, Build, Utils, Errors +from waflib.Logs import debug, error + +class PreprocError(Errors.WafError): + pass + +POPFILE = '-' +"Constant representing a special token used in :py:meth:`waflib.Tools.c_preproc.c_parser.start` iteration to switch to a header read previously" + +recursion_limit = 150 +"Limit on the amount of files to read in the dependency scanner" + +go_absolute = False +"Set to 1 to track headers on files in /usr/include - else absolute paths are ignored" + +standard_includes = ['/usr/include'] +if Utils.is_win32: + standard_includes = [] + +use_trigraphs = 0 +"""Apply trigraph rules (False by default)""" + +strict_quotes = 0 +"""Reserve the "#include <>" quotes for system includes (do not search for those includes). False by default.""" + +g_optrans = { +'not':'!', +'and':'&&', +'bitand':'&', +'and_eq':'&=', +'or':'||', +'bitor':'|', +'or_eq':'|=', +'xor':'^', +'xor_eq':'^=', +'compl':'~', +} +"""Operators such as and/or/xor for c++. Set an empty dict to disable.""" + +# ignore #warning and #error +re_lines = re.compile( + '^[ \t]*(#|%:)[ \t]*(ifdef|ifndef|if|else|elif|endif|include|import|define|undef|pragma)[ \t]*(.*)\r*$', + re.IGNORECASE | re.MULTILINE) +"""Match #include lines""" + +re_mac = re.compile("^[a-zA-Z_]\w*") +"""Match macro definitions""" + +re_fun = re.compile('^[a-zA-Z_][a-zA-Z0-9_]*[(]') +"""Match macro functions""" + +re_pragma_once = re.compile('^\s*once\s*', re.IGNORECASE) +"""Match #pragma once statements""" + +re_nl = re.compile('\\\\\r*\n', re.MULTILINE) +"""Match newlines""" + +re_cpp = re.compile( + r"""(/\*[^*]*\*+([^/*][^*]*\*+)*/)|//[^\n]*|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|.[^/"'\\]*)""", + re.MULTILINE) +"""Filter C/C++ comments""" + +trig_def = [('??'+a, b) for a, b in zip("=-/!'()<>", r'#~\|^[]{}')] +"""Trigraph definitions""" + +chr_esc = {'0':0, 'a':7, 'b':8, 't':9, 'n':10, 'f':11, 'v':12, 'r':13, '\\':92, "'":39} +"""Escape characters""" + +NUM = 'i' +"""Number token""" + +OP = 'O' +"""Operator token""" + +IDENT = 'T' +"""Identifier token""" + +STR = 's' +"""String token""" + +CHAR = 'c' +"""Character token""" + +tok_types = [NUM, STR, IDENT, OP] +"""Token types""" + +exp_types = [ + r"""0[xX](?P[a-fA-F0-9]+)(?P[uUlL]*)|L*?'(?P(\\.|[^\\'])+)'|(?P\d+)[Ee](?P[+-]*?\d+)(?P[fFlL]*)|(?P\d*\.\d+)([Ee](?P[+-]*?\d+))?(?P[fFlL]*)|(?P\d+\.\d*)([Ee](?P[+-]*?\d+))?(?P[fFlL]*)|(?P0*)(?P\d+)(?P[uUlL]*)""", + r'L?"([^"\\]|\\.)*"', + r'[a-zA-Z_]\w*', + r'%:%:|<<=|>>=|\.\.\.|<<|<%|<:|<=|>>|>=|\+\+|\+=|--|->|-=|\*=|/=|%:|%=|%>|==|&&|&=|\|\||\|=|\^=|:>|!=|##|[\(\)\{\}\[\]<>\?\|\^\*\+&=:!#;,%/\-\?\~\.]', +] +"""Expression types""" + +re_clexer = re.compile('|'.join(["(?P<%s>%s)" % (name, part) for name, part in zip(tok_types, exp_types)]), re.M) +"""Match expressions into tokens""" + +accepted = 'a' +"""Parser state is *accepted*""" + +ignored = 'i' +"""Parser state is *ignored*, for example preprocessor lines in an #if 0 block""" + +undefined = 'u' +"""Parser state is *undefined* at the moment""" + +skipped = 's' +"""Parser state is *skipped*, for example preprocessor lines in a #elif 0 block""" + +def repl(m): + """Replace function used with :py:attr:`waflib.Tools.c_preproc.re_cpp`""" + s = m.group(1) + if s: + return ' ' + return m.group(3) or '' + +def filter_comments(filename): + """ + Filter the comments from a c/h file, and return the preprocessor lines. + The regexps :py:attr:`waflib.Tools.c_preproc.re_cpp`, :py:attr:`waflib.Tools.c_preproc.re_nl` and :py:attr:`waflib.Tools.c_preproc.re_lines` are used internally. + + :return: the preprocessor directives as a list of (keyword, line) + :rtype: a list of string pairs + """ + # return a list of tuples : keyword, line + code = Utils.readf(filename) + if use_trigraphs: + for (a, b) in trig_def: code = code.split(a).join(b) + code = re_nl.sub('', code) + code = re_cpp.sub(repl, code) + return [(m.group(2), m.group(3)) for m in re.finditer(re_lines, code)] + +prec = {} +""" +Operator precendence rules required for parsing expressions of the form:: + + #if 1 && 2 != 0 +""" +ops = ['* / %', '+ -', '<< >>', '< <= >= >', '== !=', '& | ^', '&& ||', ','] +for x in range(len(ops)): + syms = ops[x] + for u in syms.split(): + prec[u] = x + +def trimquotes(s): + """ + Remove the single quotes around an expression:: + + trimquotes("'test'") == "test" + + :param s: expression to transform + :type s: string + :rtype: string + """ + if not s: return '' + s = s.rstrip() + if s[0] == "'" and s[-1] == "'": return s[1:-1] + return s + +def reduce_nums(val_1, val_2, val_op): + """ + Apply arithmetic rules to compute a result + + :param val1: input parameter + :type val1: int or string + :param val2: input parameter + :type val2: int or string + :param val_op: C operator in *+*, */*, *-*, etc + :type val_op: string + :rtype: int + """ + #print val_1, val_2, val_op + + # now perform the operation, make certain a and b are numeric + try: a = 0 + val_1 + except TypeError: a = int(val_1) + try: b = 0 + val_2 + except TypeError: b = int(val_2) + + d = val_op + if d == '%': c = a%b + elif d=='+': c = a+b + elif d=='-': c = a-b + elif d=='*': c = a*b + elif d=='/': c = a/b + elif d=='^': c = a^b + elif d=='|': c = a|b + elif d=='||': c = int(a or b) + elif d=='&': c = a&b + elif d=='&&': c = int(a and b) + elif d=='==': c = int(a == b) + elif d=='!=': c = int(a != b) + elif d=='<=': c = int(a <= b) + elif d=='<': c = int(a < b) + elif d=='>': c = int(a > b) + elif d=='>=': c = int(a >= b) + elif d=='^': c = int(a^b) + elif d=='<<': c = a<>': c = a>>b + else: c = 0 + return c + +def get_num(lst): + """ + Try to obtain a number from a list of tokens. The token types are defined in :py:attr:`waflib.Tools.ccroot.tok_types`. + + :param lst: list of preprocessor tokens + :type lst: list of tuple (tokentype, value) + :return: a pair containing the number and the rest of the list + :rtype: tuple(value, list) + """ + if not lst: raise PreprocError("empty list for get_num") + (p, v) = lst[0] + if p == OP: + if v == '(': + count_par = 1 + i = 1 + while i < len(lst): + (p, v) = lst[i] + + if p == OP: + if v == ')': + count_par -= 1 + if count_par == 0: + break + elif v == '(': + count_par += 1 + i += 1 + else: + raise PreprocError("rparen expected %r" % lst) + + (num, _) = get_term(lst[1:i]) + return (num, lst[i+1:]) + + elif v == '+': + return get_num(lst[1:]) + elif v == '-': + num, lst = get_num(lst[1:]) + return (reduce_nums('-1', num, '*'), lst) + elif v == '!': + num, lst = get_num(lst[1:]) + return (int(not int(num)), lst) + elif v == '~': + return (~ int(num), lst) + else: + raise PreprocError("Invalid op token %r for get_num" % lst) + elif p == NUM: + return v, lst[1:] + elif p == IDENT: + # all macros should have been replaced, remaining identifiers eval to 0 + return 0, lst[1:] + else: + raise PreprocError("Invalid token %r for get_num" % lst) + +def get_term(lst): + """ + Evaluate an expression recursively, for example:: + + 1+1+1 -> 2+1 -> 3 + + :param lst: list of tokens + :type lst: list of tuple(token, value) + :return: the value and the remaining tokens + :rtype: value, list + """ + + if not lst: raise PreprocError("empty list for get_term") + num, lst = get_num(lst) + if not lst: + return (num, []) + (p, v) = lst[0] + if p == OP: + if v == '&&' and not num: + return (num, []) + elif v == '||' and num: + return (num, []) + elif v == ',': + # skip + return get_term(lst[1:]) + elif v == '?': + count_par = 0 + i = 1 + while i < len(lst): + (p, v) = lst[i] + + if p == OP: + if v == ')': + count_par -= 1 + elif v == '(': + count_par += 1 + elif v == ':': + if count_par == 0: + break + i += 1 + else: + raise PreprocError("rparen expected %r" % lst) + + if int(num): + return get_term(lst[1:i]) + else: + return get_term(lst[i+1:]) + + else: + num2, lst = get_num(lst[1:]) + + if not lst: + # no more tokens to process + num2 = reduce_nums(num, num2, v) + return get_term([(NUM, num2)] + lst) + + # operator precedence + p2, v2 = lst[0] + if p2 != OP: + raise PreprocError("op expected %r" % lst) + + if prec[v2] >= prec[v]: + num2 = reduce_nums(num, num2, v) + return get_term([(NUM, num2)] + lst) + else: + num3, lst = get_num(lst[1:]) + num3 = reduce_nums(num2, num3, v2) + return get_term([(NUM, num), (p, v), (NUM, num3)] + lst) + + + raise PreprocError("cannot reduce %r" % lst) + +def reduce_eval(lst): + """ + Take a list of tokens and output true or false for #if/#elif conditions. + + :param lst: a list of tokens + :type lst: list of tuple(token, value) + :return: a token + :rtype: tuple(NUM, int) + """ + num, lst = get_term(lst) + return (NUM, num) + +def stringize(lst): + """ + Merge a list of tokens into a string + + :param lst: a list of tokens + :type lst: list of tuple(token, value) + :rtype: string + """ + lst = [str(v2) for (p2, v2) in lst] + return "".join(lst) + +def paste_tokens(t1, t2): + """ + Token pasting works between identifiers, particular operators, and identifiers and numbers:: + + a ## b -> ab + > ## = -> >= + a ## 2 -> a2 + + :param t1: token + :type t1: tuple(type, value) + :param t2: token + :type t2: tuple(type, value) + """ + p1 = None + if t1[0] == OP and t2[0] == OP: + p1 = OP + elif t1[0] == IDENT and (t2[0] == IDENT or t2[0] == NUM): + p1 = IDENT + elif t1[0] == NUM and t2[0] == NUM: + p1 = NUM + if not p1: + raise PreprocError('tokens do not make a valid paste %r and %r' % (t1, t2)) + return (p1, t1[1] + t2[1]) + +def reduce_tokens(lst, defs, ban=[]): + """ + Replace the tokens in lst, using the macros provided in defs, and a list of macros that cannot be re-applied + + :param lst: list of tokens + :type lst: list of tuple(token, value) + :param defs: macro definitions + :type defs: dict + :param ban: macros that cannot be substituted (recursion is not allowed) + :type ban: list of string + :return: the new list of tokens + :rtype: value, list + """ + i = 0 + + while i < len(lst): + (p, v) = lst[i] + + if p == IDENT and v == "defined": + del lst[i] + if i < len(lst): + (p2, v2) = lst[i] + if p2 == IDENT: + if v2 in defs: + lst[i] = (NUM, 1) + else: + lst[i] = (NUM, 0) + elif p2 == OP and v2 == '(': + del lst[i] + (p2, v2) = lst[i] + del lst[i] # remove the ident, and change the ) for the value + if v2 in defs: + lst[i] = (NUM, 1) + else: + lst[i] = (NUM, 0) + else: + raise PreprocError("Invalid define expression %r" % lst) + + elif p == IDENT and v in defs: + + if isinstance(defs[v], str): + a, b = extract_macro(defs[v]) + defs[v] = b + macro_def = defs[v] + to_add = macro_def[1] + + if isinstance(macro_def[0], list): + # macro without arguments + del lst[i] + for x in range(len(to_add)): + lst.insert(i, to_add[x]) + i += 1 + else: + # collect the arguments for the funcall + + args = [] + del lst[i] + + if i >= len(lst): + raise PreprocError("expected '(' after %r (got nothing)" % v) + + (p2, v2) = lst[i] + if p2 != OP or v2 != '(': + raise PreprocError("expected '(' after %r" % v) + + del lst[i] + + one_param = [] + count_paren = 0 + while i < len(lst): + p2, v2 = lst[i] + + del lst[i] + if p2 == OP and count_paren == 0: + if v2 == '(': + one_param.append((p2, v2)) + count_paren += 1 + elif v2 == ')': + if one_param: args.append(one_param) + break + elif v2 == ',': + if not one_param: raise PreprocError("empty param in funcall %s" % p) + args.append(one_param) + one_param = [] + else: + one_param.append((p2, v2)) + else: + one_param.append((p2, v2)) + if v2 == '(': count_paren += 1 + elif v2 == ')': count_paren -= 1 + else: + raise PreprocError('malformed macro') + + # substitute the arguments within the define expression + accu = [] + arg_table = macro_def[0] + j = 0 + while j < len(to_add): + (p2, v2) = to_add[j] + + if p2 == OP and v2 == '#': + # stringize is for arguments only + if j+1 < len(to_add) and to_add[j+1][0] == IDENT and to_add[j+1][1] in arg_table: + toks = args[arg_table[to_add[j+1][1]]] + accu.append((STR, stringize(toks))) + j += 1 + else: + accu.append((p2, v2)) + elif p2 == OP and v2 == '##': + # token pasting, how can man invent such a complicated system? + if accu and j+1 < len(to_add): + # we have at least two tokens + + t1 = accu[-1] + + if to_add[j+1][0] == IDENT and to_add[j+1][1] in arg_table: + toks = args[arg_table[to_add[j+1][1]]] + + if toks: + accu[-1] = paste_tokens(t1, toks[0]) #(IDENT, accu[-1][1] + toks[0][1]) + accu.extend(toks[1:]) + else: + # error, case "a##" + accu.append((p2, v2)) + accu.extend(toks) + elif to_add[j+1][0] == IDENT and to_add[j+1][1] == '__VA_ARGS__': + # TODO not sure + # first collect the tokens + va_toks = [] + st = len(macro_def[0]) + pt = len(args) + for x in args[pt-st+1:]: + va_toks.extend(x) + va_toks.append((OP, ',')) + if va_toks: va_toks.pop() # extra comma + if len(accu)>1: + (p3, v3) = accu[-1] + (p4, v4) = accu[-2] + if v3 == '##': + # remove the token paste + accu.pop() + if v4 == ',' and pt < st: + # remove the comma + accu.pop() + accu += va_toks + else: + accu[-1] = paste_tokens(t1, to_add[j+1]) + + j += 1 + else: + # Invalid paste, case "##a" or "b##" + accu.append((p2, v2)) + + elif p2 == IDENT and v2 in arg_table: + toks = args[arg_table[v2]] + reduce_tokens(toks, defs, ban+[v]) + accu.extend(toks) + else: + accu.append((p2, v2)) + + j += 1 + + + reduce_tokens(accu, defs, ban+[v]) + + for x in range(len(accu)-1, -1, -1): + lst.insert(i, accu[x]) + + i += 1 + + +def eval_macro(lst, defs): + """ + Reduce the tokens by :py:func:`waflib.Tools.c_preproc.reduce_tokens` and try to return a 0/1 result by :py:func:`waflib.Tools.c_preproc.reduce_eval`. + + :param lst: list of tokens + :type lst: list of tuple(token, value) + :param defs: macro definitions + :type defs: dict + :rtype: int + """ + reduce_tokens(lst, defs, []) + if not lst: raise PreprocError("missing tokens to evaluate") + (p, v) = reduce_eval(lst) + return int(v) != 0 + +def extract_macro(txt): + """ + Process a macro definition of the form:: + #define f(x, y) x * y + + into a function or a simple macro without arguments + + :param txt: expression to exact a macro definition from + :type txt: string + :return: a tuple containing the name, the list of arguments and the replacement + :rtype: tuple(string, [list, list]) + """ + t = tokenize(txt) + if re_fun.search(txt): + p, name = t[0] + + p, v = t[1] + if p != OP: raise PreprocError("expected open parenthesis") + + i = 1 + pindex = 0 + params = {} + prev = '(' + + while 1: + i += 1 + p, v = t[i] + + if prev == '(': + if p == IDENT: + params[v] = pindex + pindex += 1 + prev = p + elif p == OP and v == ')': + break + else: + raise PreprocError("unexpected token (3)") + elif prev == IDENT: + if p == OP and v == ',': + prev = v + elif p == OP and v == ')': + break + else: + raise PreprocError("comma or ... expected") + elif prev == ',': + if p == IDENT: + params[v] = pindex + pindex += 1 + prev = p + elif p == OP and v == '...': + raise PreprocError("not implemented (1)") + else: + raise PreprocError("comma or ... expected (2)") + elif prev == '...': + raise PreprocError("not implemented (2)") + else: + raise PreprocError("unexpected else") + + #~ print (name, [params, t[i+1:]]) + return (name, [params, t[i+1:]]) + else: + (p, v) = t[0] + return (v, [[], t[1:]]) + +re_include = re.compile('^\s*(<(?P.*)>|"(?P.*)")') +def extract_include(txt, defs): + """ + Process a line in the form:: + + #include foo + + :param txt: include line to process + :type txt: string + :param defs: macro definitions + :type defs: dict + :return: the file name + :rtype: string + """ + m = re_include.search(txt) + if m: + if m.group('a'): return '<', m.group('a') + if m.group('b'): return '"', m.group('b') + + # perform preprocessing and look at the result, it must match an include + toks = tokenize(txt) + reduce_tokens(toks, defs, ['waf_include']) + + if not toks: + raise PreprocError("could not parse include %s" % txt) + + if len(toks) == 1: + if toks[0][0] == STR: + return '"', toks[0][1] + else: + if toks[0][1] == '<' and toks[-1][1] == '>': + return stringize(toks).lstrip('<').rstrip('>') + + raise PreprocError("could not parse include %s." % txt) + +def parse_char(txt): + """ + Parse a c character + + :param txt: character to parse + :type txt: string + :return: a character literal + :rtype: string + """ + + if not txt: raise PreprocError("attempted to parse a null char") + if txt[0] != '\\': + return ord(txt) + c = txt[1] + if c == 'x': + if len(txt) == 4 and txt[3] in string.hexdigits: return int(txt[2:], 16) + return int(txt[2:], 16) + elif c.isdigit(): + if c == '0' and len(txt)==2: return 0 + for i in 3, 2, 1: + if len(txt) > i and txt[1:1+i].isdigit(): + return (1+i, int(txt[1:1+i], 8)) + else: + try: return chr_esc[c] + except KeyError: raise PreprocError("could not parse char literal '%s'" % txt) + +@Utils.run_once +def tokenize(s): + """ + Convert a string into a list of tokens (shlex.split does not apply to c/c++/d) + + :param s: input to tokenize + :type s: string + :return: a list of tokens + :rtype: list of tuple(token, value) + """ + # the same headers are read again and again - 10% improvement on preprocessing the samba headers + ret = [] + for match in re_clexer.finditer(s): + m = match.group + for name in tok_types: + v = m(name) + if v: + if name == IDENT: + try: v = g_optrans[v]; name = OP + except KeyError: + # c++ specific + if v.lower() == "true": + v = 1 + name = NUM + elif v.lower() == "false": + v = 0 + name = NUM + elif name == NUM: + if m('oct'): v = int(v, 8) + elif m('hex'): v = int(m('hex'), 16) + elif m('n0'): v = m('n0') + else: + v = m('char') + if v: v = parse_char(v) + else: v = m('n2') or m('n4') + elif name == OP: + if v == '%:': v = '#' + elif v == '%:%:': v = '##' + elif name == STR: + # remove the quotes around the string + v = v[1:-1] + ret.append((name, v)) + break + return ret + +@Utils.run_once +def define_name(line): + """ + :param line: define line + :type line: string + :rtype: string + :return: the define name + """ + return re_mac.match(line).group(0) + +class c_parser(object): + """ + Used by :py:func:`waflib.Tools.c_preproc.scan` to parse c/h files. Note that by default, + only project headers are parsed. + """ + def __init__(self, nodepaths=None, defines=None): + self.lines = [] + """list of lines read""" + + if defines is None: + self.defs = {} + else: + self.defs = dict(defines) # make a copy + self.state = [] + + self.count_files = 0 + self.currentnode_stack = [] + + self.nodepaths = nodepaths or [] + """Include paths""" + + self.nodes = [] + """List of :py:class:`waflib.Node.Node` found so far""" + + self.names = [] + """List of file names that could not be matched by any file""" + + self.curfile = '' + """Current file""" + + self.ban_includes = set([]) + """Includes that must not be read (#pragma once)""" + + def cached_find_resource(self, node, filename): + """ + Find a file from the input directory + + :param node: directory + :type node: :py:class:`waflib.Node.Node` + :param filename: header to find + :type filename: string + :return: the node if found, or None + :rtype: :py:class:`waflib.Node.Node` + """ + try: + nd = node.ctx.cache_nd + except: + nd = node.ctx.cache_nd = {} + + tup = (node, filename) + try: + return nd[tup] + except KeyError: + ret = node.find_resource(filename) + if ret: + if getattr(ret, 'children', None): + ret = None + elif ret.is_child_of(node.ctx.bldnode): + tmp = node.ctx.srcnode.search(ret.path_from(node.ctx.bldnode)) + if tmp and getattr(tmp, 'children', None): + ret = None + nd[tup] = ret + return ret + + def tryfind(self, filename): + """ + Try to obtain a node from the filename based from the include paths. Will add + the node found to :py:attr:`waflib.Tools.c_preproc.c_parser.nodes` or the file name to + :py:attr:`waflib.Tools.c_preproc.c_parser.names` if no corresponding file is found. Called by + :py:attr:`waflib.Tools.c_preproc.c_parser.start`. + + :param filename: header to find + :type filename: string + :return: the node if found + :rtype: :py:class:`waflib.Node.Node` + """ + self.curfile = filename + + # for msvc it should be a for loop on the whole stack + found = self.cached_find_resource(self.currentnode_stack[-1], filename) + + for n in self.nodepaths: + if found: + break + found = self.cached_find_resource(n, filename) + + if found: + # TODO the duplicates do not increase the no-op build times too much, but they may be worth removing + self.nodes.append(found) + if filename[-4:] != '.moc': + self.addlines(found) + else: + if not filename in self.names: + self.names.append(filename) + return found + + def addlines(self, node): + """ + Add the lines from a header in the list of preprocessor lines to parse + + :param node: header + :type node: :py:class:`waflib.Node.Node` + """ + + self.currentnode_stack.append(node.parent) + filepath = node.abspath() + + self.count_files += 1 + if self.count_files > recursion_limit: + # issue #812 + raise PreprocError("recursion limit exceeded") + pc = self.parse_cache + debug('preproc: reading file %r', filepath) + try: + lns = pc[filepath] + except KeyError: + pass + else: + self.lines.extend(lns) + return + + try: + lines = filter_comments(filepath) + lines.append((POPFILE, '')) + lines.reverse() + pc[filepath] = lines # cache the lines filtered + self.lines.extend(lines) + except IOError: + raise PreprocError("could not read the file %s" % filepath) + except Exception: + if Logs.verbose > 0: + error("parsing %s failed" % filepath) + traceback.print_exc() + + def start(self, node, env): + """ + Preprocess a source file to obtain the dependencies, which are accumulated to :py:attr:`waflib.Tools.c_preproc.c_parser.nodes` + and :py:attr:`waflib.Tools.c_preproc.c_parser.names`. + + :param node: source file + :type node: :py:class:`waflib.Node.Node` + :param env: config set containing additional defines to take into account + :type env: :py:class:`waflib.ConfigSet.ConfigSet` + """ + + debug('preproc: scanning %s (in %s)', node.name, node.parent.name) + + bld = node.ctx + try: + self.parse_cache = bld.parse_cache + except AttributeError: + bld.parse_cache = {} + self.parse_cache = bld.parse_cache + + self.addlines(node) + + # macros may be defined on the command-line, so they must be parsed as if they were part of the file + if env['DEFINES']: + try: + lst = ['%s %s' % (x[0], trimquotes('='.join(x[1:]))) for x in [y.split('=') for y in env['DEFINES']]] + lst.reverse() + self.lines.extend([('define', x) for x in lst]) + except AttributeError: + # if the defines are invalid the compiler will tell the user + pass + + while self.lines: + (token, line) = self.lines.pop() + if token == POPFILE: + self.count_files -= 1 + self.currentnode_stack.pop() + continue + + try: + ve = Logs.verbose + if ve: debug('preproc: line is %s - %s state is %s', token, line, self.state) + state = self.state + + # make certain we define the state if we are about to enter in an if block + if token[:2] == 'if': + state.append(undefined) + elif token == 'endif': + state.pop() + + # skip lines when in a dead 'if' branch, wait for the endif + if token[0] != 'e': + if skipped in self.state or ignored in self.state: + continue + + if token == 'if': + ret = eval_macro(tokenize(line), self.defs) + if ret: state[-1] = accepted + else: state[-1] = ignored + elif token == 'ifdef': + m = re_mac.match(line) + if m and m.group(0) in self.defs: state[-1] = accepted + else: state[-1] = ignored + elif token == 'ifndef': + m = re_mac.match(line) + if m and m.group(0) in self.defs: state[-1] = ignored + else: state[-1] = accepted + elif token == 'include' or token == 'import': + (kind, inc) = extract_include(line, self.defs) + if inc in self.ban_includes: + continue + if token == 'import': self.ban_includes.add(inc) + if ve: debug('preproc: include found %s (%s) ', inc, kind) + if kind == '"' or not strict_quotes: + self.tryfind(inc) + elif token == 'elif': + if state[-1] == accepted: + state[-1] = skipped + elif state[-1] == ignored: + if eval_macro(tokenize(line), self.defs): + state[-1] = accepted + elif token == 'else': + if state[-1] == accepted: state[-1] = skipped + elif state[-1] == ignored: state[-1] = accepted + elif token == 'define': + try: + self.defs[define_name(line)] = line + except: + raise PreprocError("Invalid define line %s" % line) + elif token == 'undef': + m = re_mac.match(line) + if m and m.group(0) in self.defs: + self.defs.__delitem__(m.group(0)) + #print "undef %s" % name + elif token == 'pragma': + if re_pragma_once.match(line.lower()): + self.ban_includes.add(self.curfile) + except Exception as e: + if Logs.verbose: + debug('preproc: line parsing failed (%s): %s %s', e, line, Utils.ex_stack()) + +def scan(task): + """ + Get the dependencies using a c/c++ preprocessor, this is required for finding dependencies of the kind:: + + #include some_macro() + + This function is bound as a task method on :py:class:`waflib.Tools.c.c` and :py:class:`waflib.Tools.cxx.cxx` for example + """ + + global go_absolute + + try: + incn = task.generator.includes_nodes + except AttributeError: + raise Errors.WafError('%r is missing a feature such as "c", "cxx" or "includes": ' % task.generator) + + if go_absolute: + nodepaths = incn + else: + nodepaths = [x for x in incn if x.is_child_of(x.ctx.srcnode) or x.is_child_of(x.ctx.bldnode)] + + tmp = c_parser(nodepaths) + tmp.start(task.inputs[0], task.env) + if Logs.verbose: + debug('deps: deps for %r: %r; unresolved %r' % (task.inputs, tmp.nodes, tmp.names)) + return (tmp.nodes, tmp.names) + diff --git a/waflib/Tools/c_tests.py b/waflib/Tools/c_tests.py new file mode 100644 index 00000000..e10dd440 --- /dev/null +++ b/waflib/Tools/c_tests.py @@ -0,0 +1,175 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +""" +Various configuration tests. +""" + +from waflib.Configure import conf +from waflib.TaskGen import feature, before_method +import sys + +LIB_CODE = ''' +#ifdef _MSC_VER +#define testEXPORT __declspec(dllexport) +#else +#define testEXPORT +#endif +testEXPORT int lib_func(void) { return 9; } +''' + +MAIN_CODE = ''' +#ifdef _MSC_VER +#define testEXPORT __declspec(dllimport) +#else +#define testEXPORT +#endif +testEXPORT int lib_func(void); +int main(void) {return !(lib_func() == 9);} +''' + +@feature('link_lib_test') +@before_method('process_source') +def link_lib_test_fun(self): + """ + The configuration test :py:func:`waflib.Tools.ccroot.run_c_code` declares a unique task generator, + so we need to create other task generators from here to check if the linker is able to link libraries. + """ + def write_test_file(task): + task.outputs[0].write(task.generator.code) + + rpath = [] + if getattr(self, 'add_rpath', False): + rpath = [self.bld.path.get_bld().abspath()] + + mode = self.mode + m = '%s %s' % (mode, mode) + bld = self.bld + bld(rule=write_test_file, target='test.' + mode, code=LIB_CODE) + bld(rule=write_test_file, target='main.' + mode, code=MAIN_CODE) + bld(features= m + 'shlib', source='test.' + mode, target='test') + bld(features= m + 'program test_exec', source='main.' + mode, target='app', use='test', rpath=rpath) + +@conf +def check_library(self, mode=None): + """ + Check if libraries can be linked with the current linker. Uses :py:func:`waflib.Tools.c_tests.link_lib_test_fun`. + + :param mode: c or cxx or d + :type mode: string + """ + if not mode: + mode = 'c' + if self.env.CXX: + mode = 'cxx' + self.check( + compile_filename = [], + features = 'link_lib_test', + msg = 'Checking for libraries', + mode = mode + ) + +######################################################################################## + +INLINE_CODE = ''' +typedef int foo_t; +static %s foo_t static_foo () {return 0; } +%s foo_t foo () { + return 0; +} +''' +INLINE_VALUES = ['inline', '__inline__', '__inline'] + +@conf +def check_inline(self, **kw): + """ + Check for the right value for inline macro. + Define INLINE_MACRO to 1 if the define is found. + If the inline macro is not 'inline', add a define to the ``config.h`` (#define inline __inline__) + + :param define_name: define INLINE_MACRO by default to 1 if the macro is defined + :type define_name: string + :param features: by default *c* or *cxx* depending on the compiler present + :type features: list of string + """ + + self.start_msg('Checking for inline') + + if not 'define_name' in kw: + kw['define_name'] = 'INLINE_MACRO' + if not 'features' in kw: + if self.env.CXX: + kw['features'] = ['cxx'] + else: + kw['features'] = ['c'] + + for x in INLINE_VALUES: + kw['fragment'] = INLINE_CODE % (x, x) + + try: + self.check(**kw) + except self.errors.ConfigurationError: + continue + else: + self.end_msg(x) + if x != 'inline': + self.define('inline', x, quote=False) + return x + self.fatal('could not use inline functions') + +######################################################################################## + +LARGE_FRAGMENT = '#include \nint main() { return !(sizeof(off_t) >= 8); }\n' + +@conf +def check_large_file(self, **kw): + """ + Check for large file support and define the macro HAVE_LARGEFILE + The test is skipped on win32 systems (DEST_BINFMT == pe). + + :param define_name: define to set, by default *HAVE_LARGEFILE* + :type define_name: string + :param execute: execute the test (yes by default) + :type execute: bool + """ + + if not 'define_name' in kw: + kw['define_name'] = 'HAVE_LARGEFILE' + if not 'execute' in kw: + kw['execute'] = True + + if not 'features' in kw: + if self.env.CXX: + kw['features'] = ['cxx', 'cxxprogram'] + else: + kw['features'] = ['c', 'cprogram'] + + kw['fragment'] = LARGE_FRAGMENT + + kw['msg'] = 'Checking for large file support' + ret = True + try: + if self.env.DEST_BINFMT != 'pe': + ret = self.check(**kw) + except self.errors.ConfigurationError: + pass + else: + if ret: + return True + + kw['msg'] = 'Checking for -D_FILE_OFFSET_BITS=64' + kw['defines'] = ['_FILE_OFFSET_BITS=64'] + try: + ret = self.check(**kw) + except self.errors.ConfigurationError: + pass + else: + self.define('_FILE_OFFSET_BITS', 64) + return ret + + self.fatal('There is no support for large files') + +######################################################################################## + + diff --git a/waflib/Tools/ccroot.py b/waflib/Tools/ccroot.py new file mode 100644 index 00000000..66a5cd45 --- /dev/null +++ b/waflib/Tools/ccroot.py @@ -0,0 +1,603 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 (ita) + +""" +Classes and methods shared by tools providing support for C-like language such +as C/C++/D/Assembly/Go (this support module is almost never used alone). +""" + +import os, sys, re +from waflib import TaskGen, Task, Utils, Logs, Build, Options, Node, Errors +from waflib.Logs import error, debug, warn +from waflib.TaskGen import after_method, before_method, feature, taskgen_method, extension +from waflib.Tools import c_aliases, c_preproc, c_config, c_osx, c_tests +from waflib.Configure import conf + +USELIB_VARS = Utils.defaultdict(set) +""" +Mapping for features to :py:class:`waflib.ConfigSet.ConfigSet` variables. See :py:func:`waflib.Tools.ccroot.propagate_uselib_vars`. +""" + +USELIB_VARS['c'] = set(['INCLUDES', 'FRAMEWORKPATH', 'DEFINES', 'CPPFLAGS', 'CCDEPS', 'CFLAGS', 'ARCH']) +USELIB_VARS['cxx'] = set(['INCLUDES', 'FRAMEWORKPATH', 'DEFINES', 'CPPFLAGS', 'CXXDEPS', 'CXXFLAGS', 'ARCH']) +USELIB_VARS['d'] = set(['INCLUDES', 'DFLAGS']) + +USELIB_VARS['cprogram'] = USELIB_VARS['cxxprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS', 'FRAMEWORK', 'FRAMEWORKPATH', 'ARCH']) +USELIB_VARS['cshlib'] = USELIB_VARS['cxxshlib'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS', 'FRAMEWORK', 'FRAMEWORKPATH', 'ARCH']) +USELIB_VARS['cstlib'] = USELIB_VARS['cxxstlib'] = set(['ARFLAGS', 'LINKDEPS']) + +USELIB_VARS['dprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS']) +USELIB_VARS['dshlib'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS']) +USELIB_VARS['dstlib'] = set(['ARFLAGS', 'LINKDEPS']) + +USELIB_VARS['go'] = set(['GOCFLAGS']) +USELIB_VARS['goprogram'] = set(['GOLFLAGS']) + +USELIB_VARS['asm'] = set(['ASFLAGS']) + +# ================================================================================================= + +@taskgen_method +def create_compiled_task(self, name, node): + """ + Create the compilation task: c, cxx, asm, etc. The output node is created automatically (object file with a typical **.o** extension). + The task is appended to the list *compiled_tasks* which is then used by :py:func:`waflib.Tools.ccroot.apply_link` + + :param name: name of the task class + :type name: string + :param node: the file to compile + :type node: :py:class:`waflib.Node.Node` + :return: The task created + :rtype: :py:class:`waflib.Task.Task` + """ + out = '%s.%d.o' % (node.name, self.idx) + task = self.create_task(name, node, node.parent.find_or_declare(out)) + try: + self.compiled_tasks.append(task) + except AttributeError: + self.compiled_tasks = [task] + return task + +@taskgen_method +def to_incnodes(self, inlst): + """ + Task generator method provided to convert a list of string/nodes into a list of includes folders. + + The paths are assumed to be relative to the task generator path, except if they begin by **#** + in which case they are searched from the top-level directory (``bld.srcnode``). + The folders are simply assumed to be existing. + + The node objects in the list are returned in the output list. The strings are converted + into node objects if possible. The node is searched from the source directory, and if a match is found, + the equivalent build directory is created and added to the returned list too. When a folder cannot be found, it is ignored. + + :param inlst: list of folders + :type inlst: space-delimited string or a list of string/nodes + :rtype: list of :py:class:`waflib.Node.Node` + :return: list of include folders as nodes + """ + lst = [] + seen = set([]) + for x in self.to_list(inlst): + if x in seen or not x: + continue + seen.add(x) + + if isinstance(x, Node.Node): + lst.append(x) + else: + if os.path.isabs(x): + lst.append(self.bld.root.make_node(x) or x) + else: + if x[0] == '#': + p = self.bld.bldnode.make_node(x[1:]) + v = self.bld.srcnode.make_node(x[1:]) + else: + p = self.path.get_bld().make_node(x) + v = self.path.make_node(x) + p.mkdir() + lst.append(p) + lst.append(v) + return lst + +@feature('c', 'cxx', 'd', 'go', 'asm', 'fc', 'includes') +@after_method('propagate_uselib_vars', 'process_source') +def apply_incpaths(self): + """ + Task generator method that processes the attribute *includes*:: + + tg = bld(features='includes', includes='.') + + The folders only need to be relative to the current directory, the equivalent build directory is + added automatically (for headers created in the build directory). This enable using a build directory + or not (``top == out``). + + This method will add a list of nodes read by :py:func:`waflib.Tools.ccroot.to_incnodes` in ``tg.env.INCPATHS``, + and the list of include paths in ``tg.env.INCLUDES``. + """ + + lst = self.to_incnodes(self.to_list(getattr(self, 'includes', [])) + self.env['INCLUDES']) + self.includes_nodes = lst + self.env['INCPATHS'] = [x.abspath() for x in lst] + +class link_task(Task.Task): + """ + Base class for all link tasks. A task generator is supposed to have at most one link task bound in the attribute *link_task*. See :py:func:`waflib.Tools.ccroot.apply_link`. + + .. inheritance-diagram:: waflib.Tools.ccroot.stlink_task waflib.Tools.c.cprogram waflib.Tools.c.cshlib waflib.Tools.cxx.cxxstlib waflib.Tools.cxx.cxxprogram waflib.Tools.cxx.cxxshlib waflib.Tools.d.dprogram waflib.Tools.d.dshlib waflib.Tools.d.dstlib waflib.Tools.ccroot.fake_shlib waflib.Tools.ccroot.fake_stlib waflib.Tools.asm.asmprogram waflib.Tools.asm.asmshlib waflib.Tools.asm.asmstlib + """ + color = 'YELLOW' + + inst_to = None + """Default installation path for the link task outputs, or None to disable""" + + chmod = Utils.O644 + """Default installation mode for the link task outputs""" + + def add_target(self, target): + """ + Process the *target* attribute to add the platform-specific prefix/suffix such as *.so* or *.exe*. + The settings are retrieved from ``env.clsname_PATTERN`` + """ + if isinstance(target, str): + pattern = self.env[self.__class__.__name__ + '_PATTERN'] + if not pattern: + pattern = '%s' + folder, name = os.path.split(target) + + if self.__class__.__name__.find('shlib') > 0: + if self.env.DEST_BINFMT == 'pe' and getattr(self.generator, 'vnum', None): + # include the version in the dll file name, + # the import lib file name stays unversionned. + name = name + '-' + self.generator.vnum.split('.')[0] + + tmp = folder + os.sep + pattern % name + target = self.generator.path.find_or_declare(tmp) + self.set_outputs(target) + +class stlink_task(link_task): + """ + Base for static link tasks, which use *ar* most of the time. + The target is always removed before being written. + """ + run_str = '${AR} ${ARFLAGS} ${AR_TGT_F}${TGT} ${AR_SRC_F}${SRC}' + +def rm_tgt(cls): + old = cls.run + def wrap(self): + try: os.remove(self.outputs[0].abspath()) + except OSError: pass + return old(self) + setattr(cls, 'run', wrap) +rm_tgt(stlink_task) + +@feature('c', 'cxx', 'd', 'go', 'fc', 'asm') +@after_method('process_source') +def apply_link(self): + """ + Collect the tasks stored in ``compiled_tasks`` (created by :py:func:`waflib.Tools.ccroot.create_compiled_task`), and + use the outputs for a new instance of :py:class:`waflib.Tools.ccroot.link_task`. The class to use is the first link task + matching a name from the attribute *features*, for example:: + + def build(bld): + tg = bld(features='cxx cxxprogram cprogram', source='main.c', target='app') + + will create the task ``tg.link_task`` as a new instance of :py:class:`waflib.Tools.cxx.cxxprogram` + """ + + for x in self.features: + if x == 'cprogram' and 'cxx' in self.features: # limited compat + x = 'cxxprogram' + elif x == 'cshlib' and 'cxx' in self.features: + x = 'cxxshlib' + + if x in Task.classes: + if issubclass(Task.classes[x], link_task): + link = x + break + else: + return + + objs = [t.outputs[0] for t in getattr(self, 'compiled_tasks', [])] + self.link_task = self.create_task(link, objs) + self.link_task.add_target(self.target) + + if getattr(self.bld, 'is_install', None): + # remember that the install paths are given by the task generators + try: + inst_to = self.install_path + except AttributeError: + inst_to = self.link_task.__class__.inst_to + if inst_to: + # install a copy of the node list we have at this moment (implib not added) + self.install_task = self.bld.install_files(inst_to, self.link_task.outputs[:], env=self.env, chmod=self.link_task.chmod) + +@taskgen_method +def use_rec(self, name, **kw): + """ + Processes the ``use`` keyword recursively. This method is kind of private and only meant to be used from ``process_use`` + """ + + if name in self.tmp_use_not or name in self.tmp_use_seen: + return + + try: + y = self.bld.get_tgen_by_name(name) + except Errors.WafError: + self.uselib.append(name) + self.tmp_use_not.add(name) + return + + self.tmp_use_seen.append(name) + y.post() + + # bind temporary attributes on the task generator + y.tmp_use_objects = objects = kw.get('objects', True) + y.tmp_use_stlib = stlib = kw.get('stlib', True) + try: + link_task = y.link_task + except AttributeError: + y.tmp_use_var = '' + else: + objects = False + if not isinstance(y.link_task, stlink_task): + stlib = False + y.tmp_use_var = 'LIB' + else: + y.tmp_use_var = 'STLIB' + + p = self.tmp_use_prec + for x in self.to_list(getattr(y, 'use', [])): + try: + p[x].append(name) + except: + p[x] = [name] + self.use_rec(x, objects=objects, stlib=stlib) + +@feature('c', 'cxx', 'd', 'use', 'fc') +@before_method('apply_incpaths', 'propagate_uselib_vars') +@after_method('apply_link', 'process_source') +def process_use(self): + """ + Process the ``use`` attribute which contains a list of task generator names:: + + def build(bld): + bld.shlib(source='a.c', target='lib1') + bld.program(source='main.c', target='app', use='lib1') + + See :py:func:`waflib.Tools.ccroot.use_rec`. + """ + + use_not = self.tmp_use_not = set([]) + use_seen = self.tmp_use_seen = [] # we would like an ordered set + use_prec = self.tmp_use_prec = {} + self.uselib = self.to_list(getattr(self, 'uselib', [])) + self.includes = self.to_list(getattr(self, 'includes', [])) + names = self.to_list(getattr(self, 'use', [])) + + for x in names: + self.use_rec(x) + + for x in use_not: + if x in use_prec: + del use_prec[x] + + # topological sort + out = [] + tmp = [] + for x in self.tmp_use_seen: + for k in use_prec.values(): + if x in k: + break + else: + tmp.append(x) + + while tmp: + e = tmp.pop() + out.append(e) + try: + nlst = use_prec[e] + except KeyError: + pass + else: + del use_prec[e] + for x in nlst: + for y in use_prec: + if x in use_prec[y]: + break + else: + tmp.append(x) + if use_prec: + raise Errors.WafError('Cycle detected in the use processing %r' % use_prec) + out.reverse() + + link_task = getattr(self, 'link_task', None) + for x in out: + y = self.bld.get_tgen_by_name(x) + var = y.tmp_use_var + if var and link_task: + if var == 'LIB' or y.tmp_use_stlib: + self.env.append_value(var, [y.target[y.target.rfind(os.sep) + 1:]]) + self.link_task.dep_nodes.extend(y.link_task.outputs) + tmp_path = y.link_task.outputs[0].parent.path_from(self.bld.bldnode) + self.env.append_value(var + 'PATH', [tmp_path]) + else: + if y.tmp_use_objects: + self.add_objects_from_tgen(y) + + if getattr(y, 'export_includes', None): + self.includes.extend(y.to_incnodes(y.export_includes)) + + # and finally, add the uselib variables (no recursion needed) + for x in names: + try: + y = self.bld.get_tgen_by_name(x) + except: + if not self.env['STLIB_' + x] and not x in self.uselib: + self.uselib.append(x) + else: + for k in self.to_list(getattr(y, 'uselib', [])): + if not self.env['STLIB_' + k] and not k in self.uselib: + self.uselib.append(k) + +@taskgen_method +def add_objects_from_tgen(self, tg): + # Not public yet, wait for waf 1.6.7 at least - the purpose of this is to add pdb files to the compiled + # tasks but not to the link tasks (to avoid errors) + try: + link_task = self.link_task + except AttributeError: + pass + else: + for tsk in getattr(tg, 'compiled_tasks', []): + for x in tsk.outputs: + if x.name.endswith('.o') or x.name.endswith('.obj'): + link_task.inputs.append(x) + +@taskgen_method +def get_uselib_vars(self): + """ + :return: the *uselib* variables associated to the *features* attribute (see :py:attr:`waflib.Tools.ccroot.USELIB_VARS`) + :rtype: list of string + """ + _vars = set([]) + for x in self.features: + if x in USELIB_VARS: + _vars |= USELIB_VARS[x] + return _vars + +@feature('c', 'cxx', 'd', 'fc', 'javac', 'cs', 'uselib') +@after_method('process_use') +def propagate_uselib_vars(self): + """ + Process uselib variables for adding flags. For example, the following target:: + + def build(bld): + bld.env.AFLAGS_aaa = ['bar'] + from waflib.Tools.ccroot import USELIB_VARS + USELIB_VARS['aaa'] = set('AFLAGS') + + tg = bld(features='aaa', aflags='test') + + The *aflags* attribute will be processed and this method will set:: + + tg.env.AFLAGS = ['bar', 'test'] + """ + _vars = self.get_uselib_vars() + env = self.env + + for x in _vars: + y = x.lower() + env.append_unique(x, self.to_list(getattr(self, y, []))) + + for x in self.features: + for var in _vars: + compvar = '%s_%s' % (var, x) + env.append_value(var, env[compvar]) + + for x in self.to_list(getattr(self, 'uselib', [])): + for v in _vars: + env.append_value(v, env[v + '_' + x]) + +# ============ the code above must not know anything about import libs ========== + +@feature('cshlib', 'cxxshlib', 'fcshlib') +@after_method('apply_link') +def apply_implib(self): + """ + Handle dlls and their import libs on Windows-like systems. + + A ``.dll.a`` file called *import library* is generated. + It must be installed as it is required for linking the library. + """ + if not self.env.DEST_BINFMT == 'pe': + return + + dll = self.link_task.outputs[0] + if isinstance(self.target, Node.Node): + name = self.target.name + else: + name = os.path.split(self.target)[1] + implib = self.env['implib_PATTERN'] % name + implib = dll.parent.find_or_declare(implib) + self.env.append_value('LINKFLAGS', self.env['IMPLIB_ST'] % implib.bldpath()) + self.link_task.outputs.append(implib) + + if getattr(self, 'defs', None) and self.env.DEST_BINFMT == 'pe': + node = self.path.find_resource(self.defs) + if not node: + raise Errors.WafError('invalid def file %r' % self.defs) + if 'msvc' in (self.env.CC_NAME, self.env.CXX_NAME): + self.env.append_value('LINKFLAGS', '/def:%s' % node.path_from(self.bld.bldnode)) + self.link_task.dep_nodes.append(node) + else: + #gcc for windows takes *.def file a an input without any special flag + self.link_task.inputs.append(node) + + try: + inst_to = self.install_path + except AttributeError: + inst_to = self.link_task.__class__.inst_to + if not inst_to: + return + + self.implib_install_task = self.bld.install_as('${PREFIX}/lib/%s' % implib.name, implib, self.env) + +# ============ the code above must not know anything about vnum processing on unix platforms ========= + +@feature('cshlib', 'cxxshlib', 'dshlib', 'fcshlib', 'vnum') +@after_method('apply_link') +def apply_vnum(self): + """ + Enforce version numbering on shared libraries. The valid version numbers must have at most two dots:: + + def build(bld): + bld.shlib(source='a.c', target='foo', vnum='14.15.16') + + In this example, ``libfoo.so`` is installed as ``libfoo.so.1.2.3``, and the following symbolic links are created: + + * ``libfoo.so → libfoo.so.1.2.3`` + * ``libfoo.so.1 → libfoo.so.1.2.3`` + """ + if not getattr(self, 'vnum', '') or os.name != 'posix' or self.env.DEST_BINFMT not in ('elf', 'mac-o'): + return + + link = self.link_task + nums = self.vnum.split('.') + node = link.outputs[0] + + libname = node.name + if libname.endswith('.dylib'): + name3 = libname.replace('.dylib', '.%s.dylib' % self.vnum) + name2 = libname.replace('.dylib', '.%s.dylib' % nums[0]) + else: + name3 = libname + '.' + self.vnum + name2 = libname + '.' + nums[0] + + # add the so name for the ld linker - to disable, just unset env.SONAME_ST + if self.env.SONAME_ST: + v = self.env.SONAME_ST % name2 + self.env.append_value('LINKFLAGS', v.split()) + + # the following task is just to enable execution from the build dir :-/ + tsk = self.create_task('vnum', node, [node.parent.find_or_declare(name2), node.parent.find_or_declare(name3)]) + + if getattr(self.bld, 'is_install', None): + self.install_task.hasrun = Task.SKIP_ME + bld = self.bld + path = self.install_task.dest + t1 = bld.install_as(path + os.sep + name3, node, env=self.env) + t2 = bld.symlink_as(path + os.sep + name2, name3) + t3 = bld.symlink_as(path + os.sep + libname, name3) + self.vnum_install_task = (t1, t2, t3) + +class vnum(Task.Task): + """ + Create the symbolic links for a versioned shared library. Instances are created by :py:func:`waflib.Tools.ccroot.apply_vnum` + """ + color = 'CYAN' + quient = True + ext_in = ['.bin'] + def run(self): + for x in self.outputs: + path = x.abspath() + try: + os.remove(path) + except OSError: + pass + + try: + os.symlink(self.inputs[0].name, path) + except OSError: + return 1 + +class fake_shlib(link_task): + """ + Task used for reading a system library and adding the dependency on it + """ + def runnable_status(self): + for t in self.run_after: + if not t.hasrun: + return Task.ASK_LATER + + for x in self.outputs: + x.sig = Utils.h_file(x.abspath()) + return Task.SKIP_ME + +class fake_stlib(stlink_task): + """ + Task used for reading a system library and adding the dependency on it + """ + def runnable_status(self): + for t in self.run_after: + if not t.hasrun: + return Task.ASK_LATER + + for x in self.outputs: + x.sig = Utils.h_file(x.abspath()) + return Task.SKIP_ME + +@conf +def read_shlib(self, name, paths=[]): + """ + Read a system shared library, enabling its use as a local library. Will trigger a rebuild if the file changes:: + + def build(bld): + bld.read_shlib('m') + bld.program(source='main.c', use='m') + """ + return self(name=name, features='fake_lib', lib_paths=paths, lib_type='shlib') + +@conf +def read_stlib(self, name, paths=[]): + """ + Read a system static library, enabling a use as a local library. Will trigger a rebuild if the file changes. + """ + return self(name=name, features='fake_lib', lib_paths=paths, lib_type='stlib') + +lib_patterns = { + 'shlib' : ['lib%s.so', '%s.so', 'lib%s.dll', '%s.dll'], + 'stlib' : ['lib%s.a', '%s.a', 'lib%s.dll', '%s.dll', 'lib%s.lib', '%s.lib'], +} + +@feature('fake_lib') +def process_lib(self): + """ + Find the location of a foreign library. Used by :py:class:`waflib.Tools.ccroot.read_shlib` and :py:class:`waflib.Tools.ccroot.read_stlib`. + """ + node = None + + names = [x % self.name for x in lib_patterns[self.lib_type]] + for x in self.lib_paths + [self.path, '/usr/lib64', '/usr/lib', '/usr/local/lib64', '/usr/local/lib']: + if not isinstance(x, Node.Node): + x = self.bld.root.find_node(x) or self.path.find_node(x) + if not x: + continue + + for y in names: + node = x.find_node(y) + if node: + node.sig = Utils.h_file(node.abspath()) + break + else: + continue + break + else: + raise Errors.WafError('could not find library %r' % self.name) + self.link_task = self.create_task('fake_%s' % self.lib_type, [], [node]) + self.target = self.name + + +class fake_o(Task.Task): + def runnable_status(self): + return Task.SKIP_ME + +@extension('.o', '.obj') +def add_those_o_files(self, node): + tsk = self.create_task('fake_o', [], node) + try: + self.compiled_tasks.append(tsk) + except AttributeError: + self.compiled_tasks = [tsk] + diff --git a/waflib/Tools/compiler_c.py b/waflib/Tools/compiler_c.py new file mode 100644 index 00000000..7e9ed82d --- /dev/null +++ b/waflib/Tools/compiler_c.py @@ -0,0 +1,98 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Matthias Jahn jahn dôt matthias ât freenet dôt de, 2007 (pmarat) + +""" +Try to detect a C compiler from the list of supported compilers (gcc, msvc, etc):: + + def options(opt): + opt.load('compiler_c') + def configure(cnf): + cnf.load('compiler_c') + def build(bld): + bld.program(source='main.c', target='app') + +The compilers are associated to platforms in :py:attr:`waflib.Tools.compiler_c.c_compiler`. To register +a new C compiler named *cfoo* (assuming the tool ``waflib/extras/cfoo.py`` exists), use:: + + def options(opt): + opt.load('compiler_c') + def configure(cnf): + from waflib.Tools.compiler_c import c_compiler + c_compiler['win32'] = ['cfoo', 'msvc', 'gcc'] + cnf.load('compiler_c') + def build(bld): + bld.program(source='main.c', target='app') + +Not all compilers need to have a specific tool. For example, the clang compilers can be detected by the gcc tools when using:: + + $ CC=clang waf configure +""" + +import os, sys, imp, types +from waflib.Tools import ccroot +from waflib import Utils, Configure +from waflib.Logs import debug + +c_compiler = { +'win32': ['msvc', 'gcc'], +'cygwin': ['gcc'], +'darwin': ['gcc'], +'aix': ['xlc', 'gcc'], +'linux': ['gcc', 'icc'], +'sunos': ['suncc', 'gcc'], +'irix': ['gcc', 'irixcc'], +'hpux': ['gcc'], +'gnu': ['gcc'], +'java': ['gcc', 'msvc', 'icc'], +'default':['gcc'], +} +""" +Dict mapping the platform names to waf tools finding specific compilers:: + + from waflib.Tools.compiler_c import c_compiler + c_compiler['linux'] = ['gcc', 'icc', 'suncc'] +""" + +def configure(conf): + """ + Try to find a suitable C compiler or raise a :py:class:`waflib.Errors.ConfigurationError`. + """ + try: test_for_compiler = conf.options.check_c_compiler + except AttributeError: conf.fatal("Add options(opt): opt.load('compiler_c')") + for compiler in test_for_compiler.split(): + conf.env.stash() + conf.start_msg('Checking for %r (c compiler)' % compiler) + try: + conf.load(compiler) + except conf.errors.ConfigurationError as e: + conf.env.revert() + conf.end_msg(False) + debug('compiler_c: %r' % e) + else: + if conf.env['CC']: + conf.end_msg(conf.env.get_flat('CC')) + conf.env['COMPILER_CC'] = compiler + break + conf.end_msg(False) + else: + conf.fatal('could not configure a c compiler!') + +def options(opt): + """ + Restrict the compiler detection from the command-line:: + + $ waf configure --check-c-compiler=gcc + """ + opt.load_special_tools('c_*.py', ban=['c_dumbpreproc.py']) + global c_compiler + build_platform = Utils.unversioned_sys_platform() + possible_compiler_list = c_compiler[build_platform in c_compiler and build_platform or 'default'] + test_for_compiler = ' '.join(possible_compiler_list) + cc_compiler_opts = opt.add_option_group("C Compiler Options") + cc_compiler_opts.add_option('--check-c-compiler', default="%s" % test_for_compiler, + help='On this platform (%s) the following C-Compiler will be checked by default: "%s"' % (build_platform, test_for_compiler), + dest="check_c_compiler") + for x in test_for_compiler.split(): + opt.load('%s' % x) + diff --git a/waflib/Tools/compiler_cxx.py b/waflib/Tools/compiler_cxx.py new file mode 100644 index 00000000..3f3173f9 --- /dev/null +++ b/waflib/Tools/compiler_cxx.py @@ -0,0 +1,102 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Matthias Jahn jahn dôt matthias ât freenet dôt de 2007 (pmarat) + +""" +Try to detect a C++ compiler from the list of supported compilers (g++, msvc, etc):: + + def options(opt): + opt.load('compiler_cxx') + def configure(cnf): + cnf.load('compiler_cxx') + def build(bld): + bld.program(source='main.cpp', target='app') + +The compilers are associated to platforms in :py:attr:`waflib.Tools.compiler_cxx.cxx_compiler`. To register +a new C++ compiler named *cfoo* (assuming the tool ``waflib/extras/cfoo.py`` exists), use:: + + def options(opt): + opt.load('compiler_cxx') + def configure(cnf): + from waflib.Tools.compiler_cxx import cxx_compiler + cxx_compiler['win32'] = ['cfoo', 'msvc', 'gcc'] + cnf.load('compiler_cxx') + def build(bld): + bld.program(source='main.c', target='app') + +Not all compilers need to have a specific tool. For example, the clang compilers can be detected by the gcc tools when using:: + + $ CXX=clang waf configure +""" + + +import os, sys, imp, types +from waflib.Tools import ccroot +from waflib import Utils, Configure +from waflib.Logs import debug + +cxx_compiler = { +'win32': ['msvc', 'g++'], +'cygwin': ['g++'], +'darwin': ['g++'], +'aix': ['xlc++', 'g++'], +'linux': ['g++', 'icpc'], +'sunos': ['sunc++', 'g++'], +'irix': ['g++'], +'hpux': ['g++'], +'gnu': ['g++'], +'java': ['g++', 'msvc', 'icpc'], +'default': ['g++'] +} +""" +Dict mapping the platform names to waf tools finding specific compilers:: + + from waflib.Tools.compiler_cxx import cxx_compiler + cxx_compiler['linux'] = ['gxx', 'icpc', 'suncxx'] +""" + + +def configure(conf): + """ + Try to find a suitable C++ compiler or raise a :py:class:`waflib.Errors.ConfigurationError`. + """ + try: test_for_compiler = conf.options.check_cxx_compiler + except AttributeError: conf.fatal("Add options(opt): opt.load('compiler_cxx')") + + for compiler in test_for_compiler.split(): + conf.env.stash() + conf.start_msg('Checking for %r (c++ compiler)' % compiler) + try: + conf.load(compiler) + except conf.errors.ConfigurationError as e: + conf.env.revert() + conf.end_msg(False) + debug('compiler_cxx: %r' % e) + else: + if conf.env['CXX']: + conf.end_msg(conf.env.get_flat('CXX')) + conf.env['COMPILER_CXX'] = compiler + break + conf.end_msg(False) + else: + conf.fatal('could not configure a c++ compiler!') + +def options(opt): + """ + Restrict the compiler detection from the command-line:: + + $ waf configure --check-cxx-compiler=gxx + """ + opt.load_special_tools('cxx_*.py') + global cxx_compiler + build_platform = Utils.unversioned_sys_platform() + possible_compiler_list = cxx_compiler[build_platform in cxx_compiler and build_platform or 'default'] + test_for_compiler = ' '.join(possible_compiler_list) + cxx_compiler_opts = opt.add_option_group('C++ Compiler Options') + cxx_compiler_opts.add_option('--check-cxx-compiler', default="%s" % test_for_compiler, + help='On this platform (%s) the following C++ Compiler will be checked by default: "%s"' % (build_platform, test_for_compiler), + dest="check_cxx_compiler") + + for x in test_for_compiler.split(): + opt.load('%s' % x) + diff --git a/waflib/Tools/compiler_d.py b/waflib/Tools/compiler_d.py new file mode 100644 index 00000000..e7d4f2a7 --- /dev/null +++ b/waflib/Tools/compiler_d.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Carlos Rafael Giani, 2007 (dv) +# Thomas Nagy, 2010 (ita) + +""" +Try to detect a D compiler from the list of supported compilers:: + + def options(opt): + opt.load('compiler_d') + def configure(cnf): + cnf.load('compiler_d') + def build(bld): + bld.program(source='main.d', target='app') + +Only two D compilers are really present at the moment: + +* gdc, the ldc compiler having a very similar command-line interface +* dmd +""" + +import os, sys, imp, types +from waflib import Utils, Configure, Options, Logs + +def configure(conf): + """ + Try to find a suitable D compiler or raise a :py:class:`waflib.Errors.ConfigurationError`. + """ + for compiler in conf.options.dcheck.split(','): + conf.env.stash() + conf.start_msg('Checking for %r (d compiler)' % compiler) + try: + conf.load(compiler) + except conf.errors.ConfigurationError as e: + conf.env.revert() + conf.end_msg(False) + Logs.debug('compiler_cxx: %r' % e) + else: + if conf.env.D: + conf.end_msg(conf.env.get_flat('D')) + conf.env['COMPILER_D'] = compiler + conf.env.D_COMPILER = conf.env.D # TODO remove this, left for compatibility + break + conf.end_msg(False) + else: + conf.fatal('no suitable d compiler was found') + +def options(opt): + """ + Restrict the compiler detection from the command-line:: + + $ waf configure --check-d-compiler=dmd + """ + d_compiler_opts = opt.add_option_group('D Compiler Options') + d_compiler_opts.add_option('--check-d-compiler', default='gdc,dmd', action='store', + help='check for the compiler [Default:gdc,dmd]', dest='dcheck') + for d_compiler in ['gdc', 'dmd']: + opt.load('%s' % d_compiler) + diff --git a/waflib/Tools/compiler_fc.py b/waflib/Tools/compiler_fc.py new file mode 100644 index 00000000..f2730f8e --- /dev/null +++ b/waflib/Tools/compiler_fc.py @@ -0,0 +1,68 @@ +#!/usr/bin/env python +# encoding: utf-8 + +import os, sys, imp, types +from waflib import Utils, Configure, Options, Logs, Errors +from waflib.Tools import fc + +fc_compiler = { + 'win32' : ['gfortran','ifort'], + 'darwin' : ['gfortran', 'g95', 'ifort'], + 'linux' : ['gfortran', 'g95', 'ifort'], + 'java' : ['gfortran', 'g95', 'ifort'], + 'default': ['gfortran'], + 'aix' : ['gfortran'] +} + +def __list_possible_compiler(platform): + try: + return fc_compiler[platform] + except KeyError: + return fc_compiler["default"] + +def configure(conf): + """ + Try to find a suitable Fortran compiler or raise a :py:class:`waflib.Errors.ConfigurationError`. + """ + try: test_for_compiler = conf.options.check_fc + except AttributeError: conf.fatal("Add options(opt): opt.load('compiler_fc')") + orig = conf.env + for compiler in test_for_compiler.split(): + try: + conf.start_msg('Checking for %r (fortran compiler)' % compiler) + conf.env = orig.derive() + conf.load(compiler) + except conf.errors.ConfigurationError as e: + conf.end_msg(False) + Logs.debug('compiler_fortran: %r' % e) + else: + if conf.env['FC']: + orig.table = conf.env.get_merged_dict() + conf.env = orig + conf.end_msg(conf.env.get_flat('FC')) + conf.env.COMPILER_FORTRAN = compiler + break + conf.end_msg(False) + else: + conf.fatal('could not configure a fortran compiler!') + +def options(opt): + """ + Restrict the compiler detection from the command-line:: + + $ waf configure --check-fortran-compiler=ifort + """ + opt.load_special_tools('fc_*.py') + build_platform = Utils.unversioned_sys_platform() + detected_platform = Options.platform + possible_compiler_list = __list_possible_compiler(detected_platform) + test_for_compiler = ' '.join(possible_compiler_list) + fortran_compiler_opts = opt.add_option_group("Fortran Compiler Options") + fortran_compiler_opts.add_option('--check-fortran-compiler', + default="%s" % test_for_compiler, + help='On this platform (%s) the following Fortran Compiler will be checked by default: "%s"' % (detected_platform, test_for_compiler), + dest="check_fc") + + for compiler in test_for_compiler.split(): + opt.load('%s' % compiler) + diff --git a/waflib/Tools/cs.py b/waflib/Tools/cs.py new file mode 100644 index 00000000..4a615d80 --- /dev/null +++ b/waflib/Tools/cs.py @@ -0,0 +1,178 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +""" +C# support. A simple example:: + + def configure(conf): + conf.load('cs') + def build(bld): + bld(features='cs', source='main.cs', gen='foo') + +Note that the configuration may compile C# snippets:: + + FRAG = ''' + namespace Moo { + public class Test { public static int Main(string[] args) { return 0; } } + }''' + def configure(conf): + conf.check(features='cs', fragment=FRAG, compile_filename='test.cs', gen='test.exe', + type='exe', csflags=['-pkg:gtk-sharp-2.0'], msg='Checking for Gtksharp support') +""" + +from waflib import Utils, Task, Options, Logs, Errors +from waflib.TaskGen import before_method, after_method, feature +from waflib.Tools import ccroot +from waflib.Configure import conf + +ccroot.USELIB_VARS['cs'] = set(['CSFLAGS', 'ASSEMBLIES', 'RESOURCES']) +ccroot.lib_patterns['csshlib'] = ['%s'] + +@feature('cs') +@before_method('process_source') +def apply_cs(self): + """ + Create a C# task bound to the attribute *cs_task*. There can be only one C# task by task generator. + """ + cs_nodes = [] + no_nodes = [] + for x in self.to_nodes(self.source): + if x.name.endswith('.cs'): + cs_nodes.append(x) + else: + no_nodes.append(x) + self.source = no_nodes + + bintype = getattr(self, 'type', self.gen.endswith('.dll') and 'library' or 'exe') + self.cs_task = tsk = self.create_task('mcs', cs_nodes, self.path.find_or_declare(self.gen)) + tsk.env.CSTYPE = '/target:%s' % bintype + tsk.env.OUT = '/out:%s' % tsk.outputs[0].abspath() + + inst_to = getattr(self, 'install_path', bintype=='exe' and '${BINDIR}' or '${LIBDIR}') + if inst_to: + # note: we are making a copy, so the files added to cs_task.outputs won't be installed automatically + mod = getattr(self, 'chmod', bintype=='exe' and Utils.O755 or Utils.O644) + self.install_task = self.bld.install_files(inst_to, self.cs_task.outputs[:], env=self.env, chmod=mod) + +@feature('cs') +@after_method('apply_cs') +def use_cs(self): + """ + C# applications honor the **use** keyword:: + + def build(bld): + bld(features='cs', source='My.cs', type='library', gen='my.dll', name='mylib') + bld(features='cs', source='Hi.cs', includes='.', type='exe', gen='hi.exe', use='mylib', name='hi') + """ + names = self.to_list(getattr(self, 'use', [])) + get = self.bld.get_tgen_by_name + for x in names: + try: + y = get(x) + except Errors.WafError: + self.cs_task.env.append_value('CSFLAGS', '/reference:%s' % x) + continue + y.post() + + tsk = getattr(y, 'cs_task', None) or getattr(y, 'link_task', None) + if not tsk: + self.bld.fatal('cs task has no link task for use %r' % self) + self.cs_task.dep_nodes.extend(tsk.outputs) # dependency + self.cs_task.set_run_after(tsk) # order (redundant, the order is infered from the nodes inputs/outputs) + self.cs_task.env.append_value('CSFLAGS', '/reference:%s' % tsk.outputs[0].abspath()) + +@feature('cs') +@after_method('apply_cs', 'use_cs') +def debug_cs(self): + """ + The C# targets may create .mdb or .pdb files:: + + def build(bld): + bld(features='cs', source='My.cs', type='library', gen='my.dll', csdebug='full') + # csdebug is a value in [True, 'full', 'pdbonly'] + """ + csdebug = getattr(self, 'csdebug', self.env.CSDEBUG) + if not csdebug: + return + + node = self.cs_task.outputs[0] + if self.env.CS_NAME == 'mono': + out = node.parent.find_or_declare(node.name + '.mdb') + else: + out = node.change_ext('.pdb') + self.cs_task.outputs.append(out) + try: + self.install_task.source.append(out) + except AttributeError: + pass + + if csdebug == 'pdbonly': + val = ['/debug+', '/debug:pdbonly'] + elif csdebug == 'full': + val = ['/debug+', '/debug:full'] + else: + val = ['/debug-'] + self.cs_task.env.append_value('CSFLAGS', val) + + +class mcs(Task.Task): + """ + Compile C# files + """ + color = 'YELLOW' + run_str = '${MCS} ${CSTYPE} ${CSFLAGS} ${ASS_ST:ASSEMBLIES} ${RES_ST:RESOURCES} ${OUT} ${SRC}' + +def configure(conf): + """ + Find a C# compiler, set the variable MCS for the compiler and CS_NAME (mono or csc) + """ + csc = getattr(Options.options, 'cscbinary', None) + if csc: + conf.env.MCS = csc + conf.find_program(['csc', 'mcs', 'gmcs'], var='MCS') + conf.env.ASS_ST = '/r:%s' + conf.env.RES_ST = '/resource:%s' + + conf.env.CS_NAME = 'csc' + if str(conf.env.MCS).lower().find('mcs') > -1: + conf.env.CS_NAME = 'mono' + +def options(opt): + """ + Add a command-line option for the configuration:: + + $ waf configure --with-csc-binary=/foo/bar/mcs + """ + opt.add_option('--with-csc-binary', type='string', dest='cscbinary') + +class fake_csshlib(Task.Task): + """ + Task used for reading a foreign .net assembly and adding the dependency on it + """ + color = 'YELLOW' + inst_to = None + + def runnable_status(self): + for x in self.outputs: + x.sig = Utils.h_file(x.abspath()) + return Task.SKIP_ME + +@conf +def read_csshlib(self, name, paths=[]): + """ + Read a foreign .net assembly for the *use* system:: + + def build(bld): + bld.read_csshlib('ManagedLibrary.dll', paths=[bld.env.mylibrarypath]) + bld(features='cs', source='Hi.cs', type='exe', gen='hi.exe', use='ManagedLibrary.dll') + + :param name: Name of the library + :type name: string + :param paths: Folders in which the library may be found + :type paths: list of string + :return: A task generator having the feature *fake_lib* which will call :py:func:`waflib.Tools.ccroot.process_lib` + :rtype: :py:class:`waflib.TaskGen.task_gen` + """ + return self(name=name, features='fake_lib', lib_paths=paths, lib_type='csshlib') + diff --git a/waflib/Tools/cxx.py b/waflib/Tools/cxx.py new file mode 100644 index 00000000..6ec992e2 --- /dev/null +++ b/waflib/Tools/cxx.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 (ita) + +"Base for c++ programs and libraries" + +from waflib import TaskGen, Task, Utils +from waflib.Tools import c_preproc +from waflib.Tools.ccroot import link_task, stlink_task + +def cxx_hook(self, node): + "Bind the c++ file extensions to the creation of a :py:class:`waflib.Tools.cxx.cxx` instance" + return self.create_compiled_task('cxx', node) +TaskGen.extension('.cpp','.cc','.cxx','.C','.c++')(cxx_hook) # leave like this for python 2.3 + +if not '.c' in TaskGen.task_gen.mappings: + TaskGen.task_gen.mappings['.c'] = TaskGen.task_gen.mappings['.cpp'] + +class cxx(Task.Task): + "Compile C++ files into object files" + run_str = '${CXX} ${ARCH_ST:ARCH} ${CXXFLAGS} ${CPPFLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${CPPPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} ${CXX_SRC_F}${SRC} ${CXX_TGT_F}${TGT}' + vars = ['CXXDEPS'] # unused variable to depend on, just in case + ext_in = ['.h'] # set the build order easily by using ext_out=['.h'] + scan = c_preproc.scan + +class cxxprogram(link_task): + "Link object files into a c++ program" + run_str = '${LINK_CXX} ${CXXLNK_SRC_F}${SRC} ${CXXLNK_TGT_F}${TGT[0].abspath()} ${LINKFLAGS} ${RPATH_ST:RPATH} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${FRAMEWORK_ST:FRAMEWORK} ${ARCH_ST:ARCH} ${STLIB_MARKER} ${STLIBPATH_ST:STLIBPATH} ${STLIB_ST:STLIB} ${SHLIB_MARKER} ${LIBPATH_ST:LIBPATH} ${LIB_ST:LIB}' + vars = ['LINKDEPS'] + ext_out = ['.bin'] + inst_to = '${BINDIR}' + chmod = Utils.O755 + +class cxxshlib(cxxprogram): + "Link object files into a c++ shared library" + inst_to = '${LIBDIR}' + +class cxxstlib(stlink_task): + "Link object files into a c++ static library" + pass # do not remove + diff --git a/waflib/Tools/d.py b/waflib/Tools/d.py new file mode 100644 index 00000000..b11bcecd --- /dev/null +++ b/waflib/Tools/d.py @@ -0,0 +1,93 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Carlos Rafael Giani, 2007 (dv) +# Thomas Nagy, 2007-2010 (ita) + +import os, sys +from waflib import Utils, Task, Errors +from waflib.TaskGen import taskgen_method, feature, after_method, before_method, extension +from waflib.Configure import conf +from waflib.Tools.ccroot import link_task +from waflib.Tools import d_scan, d_config +from waflib.Tools.ccroot import link_task, stlink_task + +class d(Task.Task): + "Compile a d file into an object file" + color = 'GREEN' + run_str = '${D} ${DFLAGS} ${DINC_ST:INCPATHS} ${D_SRC_F:SRC} ${D_TGT_F:TGT}' + scan = d_scan.scan + +class d_with_header(d): + "Compile a d file and generate a header" + run_str = '${D} ${DFLAGS} ${DINC_ST:INCPATHS} ${D_HDR_F:tgt.outputs[1].bldpath()} ${D_SRC_F:SRC} ${D_TGT_F:tgt.outputs[0].bldpath()}' + +class d_header(Task.Task): + "Compile d headers" + color = 'BLUE' + run_str = '${D} ${D_HEADER} ${SRC}' + +class dprogram(link_task): + "Link object files into a d program" + run_str = '${D_LINKER} ${DLNK_SRC_F}${SRC} ${DLNK_TGT_F:TGT} ${LINKFLAGS} ${RPATH_ST:RPATH} ${DSTLIB_MARKER} ${DSTLIBPATH_ST:STLIBPATH} ${DSTLIB_ST:STLIB} ${DSHLIB_MARKER} ${LIBPATH_ST:LIBPATH} ${DSHLIB_ST:LIB}' + inst_to = '${BINDIR}' + chmod = Utils.O755 + +class dshlib(dprogram): + "Link object files into a d shared library" + inst_to = '${LIBDIR}' + +class dstlib(stlink_task): + "Link object files into a d static library" + pass # do not remove + +@extension('.d', '.di', '.D') +def d_hook(self, node): + """ + Compile *D* files. To get .di files as well as .o files, set the following:: + + def build(bld): + bld.program(source='foo.d', target='app', generate_headers=True) + + """ + if getattr(self, 'generate_headers', None): + task = self.create_compiled_task('d_with_header', node) + header_node = node.change_ext(self.env['DHEADER_ext']) + task.outputs.append(header_node) + else: + task = self.create_compiled_task('d', node) + return task + +@taskgen_method +def generate_header(self, filename, install_path=None): + """ + See feature request #104:: + + def build(bld): + tg = bld.program(source='foo.d', target='app') + tg.generate_header('blah.d') + # is equivalent to: + #tg = bld.program(source='foo.d', target='app', header_lst='blah.d') + + :param filename: header to create + :type filename: string + :param install_path: unused (TODO) + """ + try: + self.header_lst.append([filename, install_path]) + except AttributeError: + self.header_lst = [[filename, install_path]] + +@feature('d') +def process_header(self): + """ + Process the attribute 'header_lst' to create the d header compilation tasks:: + + def build(bld): + bld.program(source='foo.d', target='app', header_lst='blah.d') + """ + for i in getattr(self, 'header_lst', []): + node = self.path.find_resource(i[0]) + if not node: + raise Errors.WafError('file %r not found on d obj' % i[0]) + self.create_task('d_header', node, node.change_ext('.di')) + diff --git a/waflib/Tools/d_config.py b/waflib/Tools/d_config.py new file mode 100644 index 00000000..a2f8e4e0 --- /dev/null +++ b/waflib/Tools/d_config.py @@ -0,0 +1,57 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +from waflib import Utils +from waflib.Configure import conf + +@conf +def d_platform_flags(self): + """ + Set the extensions dll/so for d programs and libraries + """ + v = self.env + if not v.DEST_OS: + v.DEST_OS = Utils.unversioned_sys_platform() + if Utils.destos_to_binfmt(self.env.DEST_OS) == 'pe': + v['dprogram_PATTERN'] = '%s.exe' + v['dshlib_PATTERN'] = 'lib%s.dll' + v['dstlib_PATTERN'] = 'lib%s.a' + else: + v['dprogram_PATTERN'] = '%s' + v['dshlib_PATTERN'] = 'lib%s.so' + v['dstlib_PATTERN'] = 'lib%s.a' + +DLIB = ''' +version(D_Version2) { + import std.stdio; + int main() { + writefln("phobos2"); + return 0; + } +} else { + version(Tango) { + import tango.stdc.stdio; + int main() { + printf("tango"); + return 0; + } + } else { + import std.stdio; + int main() { + writefln("phobos1"); + return 0; + } + } +} +''' +"""Detection string for the D standard library""" + +@conf +def check_dlibrary(self): + """ + Detect the kind of standard library that comes with the compiler, will set conf.env.DLIBRARY to tango, phobos1 or phobos2. + """ + ret = self.check_cc(features='d dprogram', fragment=DLIB, compile_filename='test.d', execute=True, define_ret=True) + self.env.DLIBRARY = ret.strip() + diff --git a/waflib/Tools/d_scan.py b/waflib/Tools/d_scan.py new file mode 100644 index 00000000..dc4049e8 --- /dev/null +++ b/waflib/Tools/d_scan.py @@ -0,0 +1,209 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +""" +Provide a scanner for finding dependencies on d files +""" + +import re +from waflib import Utils, Logs + +def filter_comments(filename): + """ + :param filename: d file name + :type filename: string + :rtype: list + :return: a list of characters + """ + txt = Utils.readf(filename) + i = 0 + buf = [] + max = len(txt) + begin = 0 + while i < max: + c = txt[i] + if c == '"' or c == "'": # skip a string or character literal + buf.append(txt[begin:i]) + delim = c + i += 1 + while i < max: + c = txt[i] + if c == delim: break + elif c == '\\': # skip the character following backslash + i += 1 + i += 1 + i += 1 + begin = i + elif c == '/': # try to replace a comment with whitespace + buf.append(txt[begin:i]) + i += 1 + if i == max: break + c = txt[i] + if c == '+': # eat nesting /+ +/ comment + i += 1 + nesting = 1 + c = None + while i < max: + prev = c + c = txt[i] + if prev == '/' and c == '+': + nesting += 1 + c = None + elif prev == '+' and c == '/': + nesting -= 1 + if nesting == 0: break + c = None + i += 1 + elif c == '*': # eat /* */ comment + i += 1 + c = None + while i < max: + prev = c + c = txt[i] + if prev == '*' and c == '/': break + i += 1 + elif c == '/': # eat // comment + i += 1 + while i < max and txt[i] != '\n': + i += 1 + else: # no comment + begin = i - 1 + continue + i += 1 + begin = i + buf.append(' ') + else: + i += 1 + buf.append(txt[begin:]) + return buf + +class d_parser(object): + """ + Parser for d files + """ + def __init__(self, env, incpaths): + #self.code = '' + #self.module = '' + #self.imports = [] + + self.allnames = [] + + self.re_module = re.compile("module\s+([^;]+)") + self.re_import = re.compile("import\s+([^;]+)") + self.re_import_bindings = re.compile("([^:]+):(.*)") + self.re_import_alias = re.compile("[^=]+=(.+)") + + self.env = env + + self.nodes = [] + self.names = [] + + self.incpaths = incpaths + + def tryfind(self, filename): + """ + Search file a file matching an module/import directive + + :param filename: file to read + :type filename: string + """ + found = 0 + for n in self.incpaths: + found = n.find_resource(filename.replace('.', '/') + '.d') + if found: + self.nodes.append(found) + self.waiting.append(found) + break + if not found: + if not filename in self.names: + self.names.append(filename) + + def get_strings(self, code): + """ + :param code: d code to parse + :type code: string + :return: the modules that the code uses + :rtype: a list of match objects + """ + #self.imports = [] + self.module = '' + lst = [] + + # get the module name (if present) + + mod_name = self.re_module.search(code) + if mod_name: + self.module = re.sub('\s+', '', mod_name.group(1)) # strip all whitespaces + + # go through the code, have a look at all import occurrences + + # first, lets look at anything beginning with "import" and ending with ";" + import_iterator = self.re_import.finditer(code) + if import_iterator: + for import_match in import_iterator: + import_match_str = re.sub('\s+', '', import_match.group(1)) # strip all whitespaces + + # does this end with an import bindings declaration? + # (import bindings always terminate the list of imports) + bindings_match = self.re_import_bindings.match(import_match_str) + if bindings_match: + import_match_str = bindings_match.group(1) + # if so, extract the part before the ":" (since the module declaration(s) is/are located there) + + # split the matching string into a bunch of strings, separated by a comma + matches = import_match_str.split(',') + + for match in matches: + alias_match = self.re_import_alias.match(match) + if alias_match: + # is this an alias declaration? (alias = module name) if so, extract the module name + match = alias_match.group(1) + + lst.append(match) + return lst + + def start(self, node): + """ + The parsing starts here + + :param node: input file + :type node: :py:class:`waflib.Node.Node` + """ + self.waiting = [node] + # while the stack is not empty, add the dependencies + while self.waiting: + nd = self.waiting.pop(0) + self.iter(nd) + + def iter(self, node): + """ + Find all the modules that a file depends on, uses :py:meth:`waflib.Tools.d_scan.d_parser.tryfind` to process dependent files + + :param node: input file + :type node: :py:class:`waflib.Node.Node` + """ + path = node.abspath() # obtain the absolute path + code = "".join(filter_comments(path)) # read the file and filter the comments + names = self.get_strings(code) # obtain the import strings + for x in names: + # optimization + if x in self.allnames: continue + self.allnames.append(x) + + # for each name, see if it is like a node or not + self.tryfind(x) + +def scan(self): + "look for .d/.di used by a d file" + env = self.env + gruik = d_parser(env, self.generator.includes_nodes) + node = self.inputs[0] + gruik.start(node) + nodes = gruik.nodes + names = gruik.names + + if Logs.verbose: + Logs.debug('deps: deps for %s: %r; unresolved %r' % (str(node), nodes, names)) + return (nodes, names) + diff --git a/waflib/Tools/dbus.py b/waflib/Tools/dbus.py new file mode 100644 index 00000000..b4606330 --- /dev/null +++ b/waflib/Tools/dbus.py @@ -0,0 +1,70 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Ali Sabil, 2007 + +""" +Compile dbus files with **dbus-binding-tool** + +Typical usage:: + + def options(opt): + opt.load('compiler_c dbus') + def configure(conf): + conf.load('compiler_c dbus') + def build(bld): + tg = bld.program( + includes = '.', + source = bld.path.ant_glob('*.c'), + target = 'gnome-hello') + tg.add_dbus_file('test.xml', 'test_prefix', 'glib-server') +""" + +from waflib import Task, Errors +from waflib.TaskGen import taskgen_method, before_method + +@taskgen_method +def add_dbus_file(self, filename, prefix, mode): + """ + Add a dbus file to the list of dbus files to process. Store them in the attribute *dbus_lst*. + + :param filename: xml file to compile + :type filename: string + :param prefix: dbus binding tool prefix (--prefix=prefix) + :type prefix: string + :param mode: dbus binding tool mode (--mode=mode) + :type mode: string + """ + if not hasattr(self, 'dbus_lst'): + self.dbus_lst = [] + if not 'process_dbus' in self.meths: + self.meths.append('process_dbus') + self.dbus_lst.append([filename, prefix, mode]) + +@before_method('apply_core') +def process_dbus(self): + """ + Process the dbus files stored in the attribute *dbus_lst* to create :py:class:`waflib.Tools.dbus.dbus_binding_tool` instances. + """ + for filename, prefix, mode in getattr(self, 'dbus_lst', []): + node = self.path.find_resource(filename) + if not node: + raise Errors.WafError('file not found ' + filename) + tsk = self.create_task('dbus_binding_tool', node, node.change_ext('.h')) + tsk.env.DBUS_BINDING_TOOL_PREFIX = prefix + tsk.env.DBUS_BINDING_TOOL_MODE = mode + +class dbus_binding_tool(Task.Task): + """ + Compile a dbus file + """ + color = 'BLUE' + ext_out = ['.h'] + run_str = '${DBUS_BINDING_TOOL} --prefix=${DBUS_BINDING_TOOL_PREFIX} --mode=${DBUS_BINDING_TOOL_MODE} --output=${TGT} ${SRC}' + shell = True # temporary workaround for #795 + +def configure(conf): + """ + Detect the program dbus-binding-tool and set the *conf.env.DBUS_BINDING_TOOL* + """ + dbus_binding_tool = conf.find_program('dbus-binding-tool', var='DBUS_BINDING_TOOL') + diff --git a/waflib/Tools/dmd.py b/waflib/Tools/dmd.py new file mode 100644 index 00000000..4556d9ac --- /dev/null +++ b/waflib/Tools/dmd.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Carlos Rafael Giani, 2007 (dv) +# Thomas Nagy, 2008-2010 (ita) + +import sys +from waflib.Tools import ar, d +from waflib.Configure import conf + +@conf +def find_dmd(conf): + """ + Find the program *dmd* or *ldc* and set the variable *D* + """ + conf.find_program(['dmd', 'ldc'], var='D') + +@conf +def common_flags_ldc(conf): + """ + Set the D flags required by *ldc* + """ + v = conf.env + v['DFLAGS'] = ['-d-version=Posix'] + v['LINKFLAGS'] = [] + v['DFLAGS_dshlib'] = ['-relocation-model=pic'] + +@conf +def common_flags_dmd(conf): + """ + Set the flags required by *dmd* + """ + + v = conf.env + + # _DFLAGS _DIMPORTFLAGS + + # Compiler is dmd so 'gdc' part will be ignored, just + # ensure key is there, so wscript can append flags to it + #v['DFLAGS'] = ['-version=Posix'] + + v['D_SRC_F'] = ['-c'] + v['D_TGT_F'] = '-of%s' + + # linker + v['D_LINKER'] = v['D'] + v['DLNK_SRC_F'] = '' + v['DLNK_TGT_F'] = '-of%s' + v['DINC_ST'] = '-I%s' + + v['DSHLIB_MARKER'] = v['DSTLIB_MARKER'] = '' + v['DSTLIB_ST'] = v['DSHLIB_ST'] = '-L-l%s' + v['DSTLIBPATH_ST'] = v['DLIBPATH_ST'] = '-L-L%s' + + v['LINKFLAGS_dprogram']= ['-quiet'] + + v['DFLAGS_dshlib'] = ['-fPIC'] + v['LINKFLAGS_dshlib'] = ['-L-shared'] + + v['DHEADER_ext'] = '.di' + v.DFLAGS_d_with_header = ['-H', '-Hf'] + v['D_HDR_F'] = '%s' + +def configure(conf): + """ + Configuration for dmd/ldc + """ + conf.find_dmd() + conf.load('ar') + conf.load('d') + conf.common_flags_dmd() + conf.d_platform_flags() + + if str(conf.env.D).find('ldc') > -1: + conf.common_flags_ldc() + diff --git a/waflib/Tools/errcheck.py b/waflib/Tools/errcheck.py new file mode 100644 index 00000000..e19eed4f --- /dev/null +++ b/waflib/Tools/errcheck.py @@ -0,0 +1,209 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2011 (ita) + +""" +errheck: Search for common mistakes + +There is a performance hit, so this tool is only loaded when running "waf -v" +""" + +typos = { +'feature':'features', +'sources':'source', +'targets':'target', +'include':'includes', +'export_include':'export_includes', +'define':'defines', +'importpath':'includes', +'installpath':'install_path', +} + +meths_typos = ['__call__', 'program', 'shlib', 'stlib', 'objects'] + +from waflib import Logs, Build, Node, Task, TaskGen, ConfigSet, Errors, Utils +import waflib.Tools.ccroot + +def check_same_targets(self): + mp = Utils.defaultdict(list) + uids = {} + + def check_task(tsk): + if not isinstance(tsk, Task.Task): + return + + for node in tsk.outputs: + mp[node].append(tsk) + try: + uids[tsk.uid()].append(tsk) + except: + uids[tsk.uid()] = [tsk] + + for g in self.groups: + for tg in g: + try: + for tsk in tg.tasks: + check_task(tsk) + except AttributeError: + # raised if not a task generator, which should be uncommon + check_task(tg) + + dupe = False + for (k, v) in mp.items(): + if len(v) > 1: + dupe = True + msg = '* Node %r is created by more than once%s. The task generators are:' % (k, Logs.verbose == 1 and " (full message on 'waf -v -v')" or "") + Logs.error(msg) + for x in v: + if Logs.verbose > 1: + Logs.error(' %d. %r' % (1 + v.index(x), x.generator)) + else: + Logs.error(' %d. %r in %r' % (1 + v.index(x), x.generator.name, getattr(x.generator, 'path', None))) + + if not dupe: + for (k, v) in uids.items(): + if len(v) > 1: + Logs.error('* Several tasks use the same identifier. Please check the information on\n http://waf.googlecode.com/svn/docs/apidocs/Task.html#waflib.Task.Task.uid') + for tsk in v: + Logs.error(' - object %r (%r) defined in %r' % (tsk.__class__.__name__, tsk, tsk.generator)) + +def check_invalid_constraints(self): + feat = set([]) + for x in list(TaskGen.feats.values()): + feat.union(set(x)) + for (x, y) in TaskGen.task_gen.prec.items(): + feat.add(x) + feat.union(set(y)) + ext = set([]) + for x in TaskGen.task_gen.mappings.values(): + ext.add(x.__name__) + invalid = ext & feat + if invalid: + Logs.error('The methods %r have invalid annotations: @extension <-> @feature/@before_method/@after_method' % list(invalid)) + + # the build scripts have been read, so we can check for invalid after/before attributes on task classes + for cls in list(Task.classes.values()): + for x in ('before', 'after'): + for y in Utils.to_list(getattr(cls, x, [])): + if not Task.classes.get(y, None): + Logs.error('Erroneous order constraint %r=%r on task class %r' % (x, y, cls.__name__)) + +def replace(m): + """ + We could add properties, but they would not work in some cases: + bld.program(...) requires 'source' in the attributes + """ + oldcall = getattr(Build.BuildContext, m) + def call(self, *k, **kw): + ret = oldcall(self, *k, **kw) + for x in typos: + if x in kw: + err = True + Logs.error('Fix the typo %r -> %r on %r' % (x, typos[x], ret)) + return ret + setattr(Build.BuildContext, m, call) + +def enhance_lib(): + """ + modify existing classes and methods + """ + for m in meths_typos: + replace(m) + + # catch '..' in ant_glob patterns + old_ant_glob = Node.Node.ant_glob + def ant_glob(self, *k, **kw): + if k: + lst=Utils.to_list(k[0]) + for pat in lst: + if '..' in pat.split('/'): + Logs.error("In ant_glob pattern %r: '..' means 'two dots', not 'parent directory'" % k[0]) + return old_ant_glob(self, *k, **kw) + Node.Node.ant_glob = ant_glob + + # catch conflicting ext_in/ext_out/before/after declarations + old = Task.is_before + def is_before(t1, t2): + ret = old(t1, t2) + if ret and old(t2, t1): + Logs.error('Contradictory order constraints in classes %r %r' % (t1, t2)) + return ret + Task.is_before = is_before + + # check for bld(feature='cshlib') where no 'c' is given - this can be either a mistake or on purpose + # so we only issue a warning + def check_err_features(self): + lst = self.to_list(self.features) + if 'shlib' in lst: + Logs.error('feature shlib -> cshlib, dshlib or cxxshlib') + for x in ('c', 'cxx', 'd', 'fc'): + if not x in lst and lst and lst[0] in [x+y for y in ('program', 'shlib', 'stlib')]: + Logs.error('%r features is probably missing %r' % (self, x)) + TaskGen.feature('*')(check_err_features) + + # check for erroneous order constraints + def check_err_order(self): + if not hasattr(self, 'rule'): + for x in ('before', 'after', 'ext_in', 'ext_out'): + if hasattr(self, x): + Logs.warn('Erroneous order constraint %r on non-rule based task generator %r' % (x, self)) + else: + for x in ('before', 'after'): + for y in self.to_list(getattr(self, x, [])): + if not Task.classes.get(y, None): + Logs.error('Erroneous order constraint %s=%r on %r' % (x, y, self)) + TaskGen.feature('*')(check_err_order) + + # check for @extension used with @feature/@before_method/@after_method + def check_compile(self): + check_invalid_constraints(self) + try: + ret = self.orig_compile() + finally: + check_same_targets(self) + return ret + Build.BuildContext.orig_compile = Build.BuildContext.compile + Build.BuildContext.compile = check_compile + + # check for invalid build groups #914 + def use_rec(self, name, **kw): + try: + y = self.bld.get_tgen_by_name(name) + except Errors.WafError: + pass + else: + idx = self.bld.get_group_idx(self) + odx = self.bld.get_group_idx(y) + if odx > idx: + msg = "Invalid 'use' across build groups:" + if Logs.verbose > 1: + msg += '\n target %r\n uses:\n %r' % (self, y) + else: + msg += " %r uses %r (try 'waf -v -v' for the full error)" % (self.name, name) + raise Errors.WafError(msg) + self.orig_use_rec(name, **kw) + TaskGen.task_gen.orig_use_rec = TaskGen.task_gen.use_rec + TaskGen.task_gen.use_rec = use_rec + + # check for env.append + def getattri(self, name, default=None): + if name == 'append' or name == 'add': + raise Errors.WafError('env.append and env.add do not exist: use env.append_value/env.append_unique') + elif name == 'prepend': + raise Errors.WafError('env.prepend does not exist: use env.prepend_value') + if name in self.__slots__: + return object.__getattr__(self, name, default) + else: + return self[name] + ConfigSet.ConfigSet.__getattr__ = getattri + + +def options(opt): + """ + Add a few methods + """ + enhance_lib() + +def configure(conf): + pass + diff --git a/waflib/Tools/fc.py b/waflib/Tools/fc.py new file mode 100644 index 00000000..8fb349bf --- /dev/null +++ b/waflib/Tools/fc.py @@ -0,0 +1,205 @@ +#! /usr/bin/env python +# encoding: utf-8 +# DC 2008 +# Thomas Nagy 2010 (ita) + +""" +fortran support +""" + +import re + +from waflib import Utils, Task, TaskGen, Logs +from waflib.Tools import ccroot, fc_config, fc_scan +from waflib.TaskGen import feature, before_method, after_method, extension +from waflib.Configure import conf + +ccroot.USELIB_VARS['fc'] = set(['FCFLAGS', 'DEFINES', 'INCLUDES']) +ccroot.USELIB_VARS['fcprogram_test'] = ccroot.USELIB_VARS['fcprogram'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS']) +ccroot.USELIB_VARS['fcshlib'] = set(['LIB', 'STLIB', 'LIBPATH', 'STLIBPATH', 'LINKFLAGS', 'RPATH', 'LINKDEPS']) +ccroot.USELIB_VARS['fcstlib'] = set(['ARFLAGS', 'LINKDEPS']) + +@feature('fcprogram', 'fcshlib', 'fcstlib', 'fcprogram_test') +def dummy(self): + pass + +@extension('.f', '.f90', '.F', '.F90', '.for', '.FOR') +def fc_hook(self, node): + "Bind the typical Fortran file extensions to the creation of a :py:class:`waflib.Tools.fc.fc` instance" + return self.create_compiled_task('fc', node) + +@conf +def modfile(conf, name): + """ + Turn a module name into the right module file name. + Defaults to all lower case. + """ + return {'lower' :name.lower() + '.mod', + 'lower.MOD' :name.upper() + '.MOD', + 'UPPER.mod' :name.upper() + '.mod', + 'UPPER' :name.upper() + '.MOD'}[conf.env.FC_MOD_CAPITALIZATION or 'lower'] + +def get_fortran_tasks(tsk): + """ + Obtain all other fortran tasks from the same build group. Those tasks must not have + the attribute 'nomod' or 'mod_fortran_done' + """ + bld = tsk.generator.bld + tasks = bld.get_tasks_group(bld.get_group_idx(tsk.generator)) + return [x for x in tasks if isinstance(x, fc) and not getattr(x, 'nomod', None) and not getattr(x, 'mod_fortran_done', None)] + +class fc(Task.Task): + """ + The fortran tasks can only run when all fortran tasks in the current group are ready to be executed + This may cause a deadlock if another fortran task is waiting for something that cannot happen (circular dependency) + in this case, set the 'nomod=True' on those tasks instances to break the loop + """ + + color = 'GREEN' + run_str = '${FC} ${FCFLAGS} ${FCINCPATH_ST:INCPATHS} ${FCDEFINES_ST:DEFINES} ${_FCMODOUTFLAGS} ${FC_TGT_F}${TGT[0].abspath()} ${FC_SRC_F}${SRC[0].abspath()}' + vars = ["FORTRANMODPATHFLAG"] + + def scan(self): + """scanner for fortran dependencies""" + tmp = fc_scan.fortran_parser(self.generator.includes_nodes) + tmp.task = self + tmp.start(self.inputs[0]) + if Logs.verbose: + Logs.debug('deps: deps for %r: %r; unresolved %r' % (self.inputs, tmp.nodes, tmp.names)) + return (tmp.nodes, tmp.names) + + def runnable_status(self): + """ + Set the mod file outputs and the dependencies on the mod files over all the fortran tasks + executed by the main thread so there are no concurrency issues + """ + if getattr(self, 'mod_fortran_done', None): + return super(fc, self).runnable_status() + + # now, if we reach this part it is because this fortran task is the first in the list + bld = self.generator.bld + + # obtain the fortran tasks + lst = get_fortran_tasks(self) + + # disable this method for other tasks + for tsk in lst: + tsk.mod_fortran_done = True + + # wait for all the .f tasks to be ready for execution + # and ensure that the scanners are called at least once + for tsk in lst: + ret = tsk.runnable_status() + if ret == Task.ASK_LATER: + # we have to wait for one of the other fortran tasks to be ready + # this may deadlock if there are dependencies between the fortran tasks + # but this should not happen (we are setting them here!) + for x in lst: + x.mod_fortran_done = None + + # TODO sort the list of tasks in bld.producer.outstanding to put all fortran tasks at the end + return Task.ASK_LATER + + ins = Utils.defaultdict(set) + outs = Utils.defaultdict(set) + + # the .mod files to create + for tsk in lst: + key = tsk.uid() + for x in bld.raw_deps[key]: + if x.startswith('MOD@'): + name = bld.modfile(x.replace('MOD@', '')) + node = bld.srcnode.find_or_declare(name) + tsk.set_outputs(node) + outs[id(node)].add(tsk) + + # the .mod files to use + for tsk in lst: + key = tsk.uid() + for x in bld.raw_deps[key]: + if x.startswith('USE@'): + name = bld.modfile(x.replace('USE@', '')) + node = bld.srcnode.find_resource(name) + if node and node not in tsk.outputs: + if not node in bld.node_deps[key]: + bld.node_deps[key].append(node) + ins[id(node)].add(tsk) + + # if the intersection matches, set the order + for k in ins.keys(): + for a in ins[k]: + a.run_after.update(outs[k]) + + # the scanner cannot output nodes, so we have to set them + # ourselves as task.dep_nodes (additional input nodes) + tmp = [] + for t in outs[k]: + tmp.extend(t.outputs) + a.dep_nodes.extend(tmp) + + # old python versions + try: + a.dep_nodes.sort(key=lambda x: x.abspath()) + except: + a.dep_nodes.sort(lambda x, y: cmp(x.abspath(), y.abspath())) + + # the task objects have changed: clear the signature cache + for tsk in lst: + try: + delattr(tsk, 'cache_sig') + except AttributeError: + pass + + return super(fc, self).runnable_status() + +class fcprogram(ccroot.link_task): + """Link fortran programs""" + color = 'YELLOW' + run_str = '${FC} ${FCLNK_SRC_F}${SRC} ${FCLNK_TGT_F}${TGT[0].abspath()} ${LINKFLAGS} ${RPATH_ST:RPATH} ${FCSTLIB_MARKER} ${FCSTLIBPATH_ST:STLIBPATH} ${FCSTLIB_ST:STLIB} ${FCSHLIB_MARKER} ${FCLIBPATH_ST:LIBPATH} ${FCLIB_ST:LIB}' + inst_to = '${BINDIR}' + chmod = Utils.O755 + +class fcshlib(fcprogram): + """Link fortran libraries""" + inst_to = '${LIBDIR}' + +class fcprogram_test(fcprogram): + """Custom link task to obtain the compiler outputs for fortran configuration tests""" + + def can_retrieve_cache(self): + """This task is always executed""" + return False + + def runnable_status(self): + """This task is always executed""" + ret = super(fcprogram_test, self).runnable_status() + if ret == Task.SKIP_ME: + ret = Task.RUN_ME + return ret + + def exec_command(self, cmd, **kw): + """Store the compiler std our/err onto the build context, to bld.out + bld.err""" + bld = self.generator.bld + + kw['shell'] = isinstance(cmd, str) + kw['stdout'] = kw['stderr'] = Utils.subprocess.PIPE + kw['cwd'] = bld.variant_dir + bld.out = bld.err = '' + + bld.to_log('command: %s\n' % cmd) + + kw['output'] = 0 + try: + (bld.out, bld.err) = bld.cmd_and_log(cmd, **kw) + except Exception as e: + return -1 + + if bld.out: + bld.to_log("out: %s\n" % bld.out) + if bld.err: + bld.to_log("err: %s\n" % bld.err) + +class fcstlib(ccroot.stlink_task): + """Link fortran static libraries (uses ar by default)""" + pass # do not remove the pass statement + diff --git a/waflib/Tools/fc_config.py b/waflib/Tools/fc_config.py new file mode 100644 index 00000000..0fd06bc8 --- /dev/null +++ b/waflib/Tools/fc_config.py @@ -0,0 +1,443 @@ +#! /usr/bin/env python +# encoding: utf-8 +# DC 2008 +# Thomas Nagy 2010 (ita) + +""" +Fortran configuration helpers +""" + +import re, shutil, os, sys, string, shlex +from waflib.Configure import conf +from waflib.TaskGen import feature, after_method, before_method +from waflib import Build, Utils + +FC_FRAGMENT = ' program main\n end program main\n' +FC_FRAGMENT2 = ' PROGRAM MAIN\n END\n' # what's the actual difference between these? + +@conf +def fc_flags(conf): + """ + Define common fortran configuration flags and file extensions + """ + v = conf.env + + v['FC_SRC_F'] = [] + v['FC_TGT_F'] = ['-c', '-o'] + v['FCINCPATH_ST'] = '-I%s' + v['FCDEFINES_ST'] = '-D%s' + + if not v['LINK_FC']: v['LINK_FC'] = v['FC'] + v['FCLNK_SRC_F'] = [] + v['FCLNK_TGT_F'] = ['-o'] + + v['FCFLAGS_fcshlib'] = ['-fpic'] + v['LINKFLAGS_fcshlib'] = ['-shared'] + v['fcshlib_PATTERN'] = 'lib%s.so' + + v['fcstlib_PATTERN'] = 'lib%s.a' + + v['FCLIB_ST'] = '-l%s' + v['FCLIBPATH_ST'] = '-L%s' + v['FCSTLIB_ST'] = '-l%s' + v['FCSTLIBPATH_ST'] = '-L%s' + v['FCSTLIB_MARKER'] = '-Wl,-Bstatic' + v['FCSHLIB_MARKER'] = '-Wl,-Bdynamic' + + v['SONAME_ST'] = '-Wl,-h,%s' + + +@conf +def check_fortran(self, *k, **kw): + """See if the fortran compiler works by compiling a simple fortran program""" + self.check_cc( + fragment = FC_FRAGMENT, + compile_filename = 'test.f', + features = 'fc fcprogram', + msg = 'Compiling a simple fortran app') + +@conf +def check_fc(self, *k, **kw): + """ + Same as :py:func:`waflib.Tools.c_config.check` but default to the *Fortran* programming language + (Overriding the C defaults in :py:func:`waflib.Tools.c_config.validate_c` here) + """ + kw['compiler'] = 'fc' + if not 'compile_mode' in kw: + kw['compile_mode'] = 'fc' + if not 'type' in kw: + kw['type'] = 'fcprogram' + if not 'compile_filename' in kw: + kw['compile_filename'] = 'test.f90' + if not 'code' in kw: + kw['code'] = FC_FRAGMENT + return self.check(*k, **kw) + +# ------------------------------------------------------------------------ +# --- These are the default platform modifiers, refactored here for +# convenience. gfortran and g95 have much overlap. +# ------------------------------------------------------------------------ + +@conf +def fortran_modifier_darwin(conf): + """ + Define fortran flags and extensions for the OSX systems + """ + v = conf.env + v['FCFLAGS_fcshlib'] = ['-fPIC', '-compatibility_version', '1', '-current_version', '1'] + v['LINKFLAGS_fcshlib'] = ['-dynamiclib'] + v['fcshlib_PATTERN'] = 'lib%s.dylib' + v['FRAMEWORKPATH_ST'] = '-F%s' + v['FRAMEWORK_ST'] = '-framework %s' + + v['LINKFLAGS_fcstlib'] = [] + + v['FCSHLIB_MARKER'] = '' + v['FCSTLIB_MARKER'] = '' + v['SONAME_ST'] = '' + + +@conf +def fortran_modifier_win32(conf): + """Define fortran flags for the windows platforms""" + v = conf.env + v['fcprogram_PATTERN'] = v['fcprogram_test_PATTERN'] = '%s.exe' + + v['fcshlib_PATTERN'] = '%s.dll' + v['implib_PATTERN'] = 'lib%s.dll.a' + v['IMPLIB_ST'] = '-Wl,--out-implib,%s' + + v['FCFLAGS_fcshlib'] = [] + + v.append_value('FCFLAGS_fcshlib', ['-DDLL_EXPORT']) # TODO adding nonstandard defines like this DLL_EXPORT is not a good idea + + # Auto-import is enabled by default even without this option, + # but enabling it explicitly has the nice effect of suppressing the rather boring, debug-level messages + # that the linker emits otherwise. + v.append_value('LINKFLAGS', ['-Wl,--enable-auto-import']) + +@conf +def fortran_modifier_cygwin(conf): + """Define fortran flags for use on cygwin""" + fortran_modifier_win32(conf) + v = conf.env + v['fcshlib_PATTERN'] = 'cyg%s.dll' + v.append_value('LINKFLAGS_fcshlib', ['-Wl,--enable-auto-image-base']) + v['FCFLAGS_fcshlib'] = [] +# ------------------------------------------------------------------------ + +@conf +def check_fortran_dummy_main(self, *k, **kw): + """ + Guess if a main function is needed by compiling a code snippet with + the C compiler and link with the Fortran compiler + + TODO: (DC) + - handling dialects (F77, F90, etc... -> needs core support first) + - fix dummy main check (AC_FC_DUMMY_MAIN vs AC_FC_MAIN) + + TODO: what does the above mean? (ita) + """ + + if not self.env.CC: + self.fatal('A c compiler is required for check_fortran_dummy_main') + + lst = ['MAIN__', '__MAIN', '_MAIN', 'MAIN_', 'MAIN'] + lst.extend([m.lower() for m in lst]) + lst.append('') + + self.start_msg('Detecting whether we need a dummy main') + for main in lst: + kw['fortran_main'] = main + try: + self.check_cc( + fragment = 'int %s() { return 0; }\n' % (main or 'test'), + features = 'c fcprogram', + mandatory = True + ) + if not main: + self.env.FC_MAIN = -1 + self.end_msg('no') + else: + self.env.FC_MAIN = main + self.end_msg('yes %s' % main) + break + except self.errors.ConfigurationError: + pass + else: + self.end_msg('not found') + self.fatal('could not detect whether fortran requires a dummy main, see the config.log') + +# ------------------------------------------------------------------------ + +GCC_DRIVER_LINE = re.compile('^Driving:') +POSIX_STATIC_EXT = re.compile('\S+\.a') +POSIX_LIB_FLAGS = re.compile('-l\S+') + +@conf +def is_link_verbose(self, txt): + """Return True if 'useful' link options can be found in txt""" + assert isinstance(txt, str) + for line in txt.splitlines(): + if not GCC_DRIVER_LINE.search(line): + if POSIX_STATIC_EXT.search(line) or POSIX_LIB_FLAGS.search(line): + return True + return False + +@conf +def check_fortran_verbose_flag(self, *k, **kw): + """ + Check what kind of verbose (-v) flag works, then set it to env.FC_VERBOSE_FLAG + """ + self.start_msg('fortran link verbose flag') + for x in ['-v', '--verbose', '-verbose', '-V']: + try: + self.check_cc( + features = 'fc fcprogram_test', + fragment = FC_FRAGMENT2, + compile_filename = 'test.f', + linkflags = [x], + mandatory=True + ) + except self.errors.ConfigurationError: + pass + else: + # output is on stderr or stdout (for xlf) + if self.is_link_verbose(self.test_bld.err) or self.is_link_verbose(self.test_bld.out): + self.end_msg(x) + break + else: + self.end_msg('failure') + self.fatal('Could not obtain the fortran link verbose flag (see config.log)') + + self.env.FC_VERBOSE_FLAG = x + return x + +# ------------------------------------------------------------------------ + +# linkflags which match those are ignored +LINKFLAGS_IGNORED = [r'-lang*', r'-lcrt[a-zA-Z0-9\.]*\.o', r'-lc$', r'-lSystem', r'-libmil', r'-LIST:*', r'-LNO:*'] +if os.name == 'nt': + LINKFLAGS_IGNORED.extend([r'-lfrt*', r'-luser32', r'-lkernel32', r'-ladvapi32', r'-lmsvcrt', r'-lshell32', r'-lmingw', r'-lmoldname']) +else: + LINKFLAGS_IGNORED.append(r'-lgcc*') +RLINKFLAGS_IGNORED = [re.compile(f) for f in LINKFLAGS_IGNORED] + +def _match_ignore(line): + """Returns True if the line should be ignored (fortran test for verbosity).""" + for i in RLINKFLAGS_IGNORED: + if i.match(line): + return True + return False + +def parse_fortran_link(lines): + """Given the output of verbose link of Fortran compiler, this returns a + list of flags necessary for linking using the standard linker.""" + # TODO: On windows ? + final_flags = [] + for line in lines: + if not GCC_DRIVER_LINE.match(line): + _parse_flink_line(line, final_flags) + return final_flags + +SPACE_OPTS = re.compile('^-[LRuYz]$') +NOSPACE_OPTS = re.compile('^-[RL]') + +def _parse_flink_line(line, final_flags): + """private""" + lexer = shlex.shlex(line, posix = True) + lexer.whitespace_split = True + + t = lexer.get_token() + tmp_flags = [] + while t: + def parse(token): + # Here we go (convention for wildcard is shell, not regex !) + # 1 TODO: we first get some root .a libraries + # 2 TODO: take everything starting by -bI:* + # 3 Ignore the following flags: -lang* | -lcrt*.o | -lc | + # -lgcc* | -lSystem | -libmil | -LANG:=* | -LIST:* | -LNO:*) + # 4 take into account -lkernel32 + # 5 For options of the kind -[[LRuYz]], as they take one argument + # after, the actual option is the next token + # 6 For -YP,*: take and replace by -Larg where arg is the old + # argument + # 7 For -[lLR]*: take + + # step 3 + if _match_ignore(token): + pass + # step 4 + elif token.startswith('-lkernel32') and sys.platform == 'cygwin': + tmp_flags.append(token) + # step 5 + elif SPACE_OPTS.match(token): + t = lexer.get_token() + if t.startswith('P,'): + t = t[2:] + for opt in t.split(os.pathsep): + tmp_flags.append('-L%s' % opt) + # step 6 + elif NOSPACE_OPTS.match(token): + tmp_flags.append(token) + # step 7 + elif POSIX_LIB_FLAGS.match(token): + tmp_flags.append(token) + else: + # ignore anything not explicitely taken into account + pass + + t = lexer.get_token() + return t + t = parse(t) + + final_flags.extend(tmp_flags) + return final_flags + +@conf +def check_fortran_clib(self, autoadd=True, *k, **kw): + """ + Obtain the flags for linking with the C library + if this check works, add uselib='CLIB' to your task generators + """ + if not self.env.FC_VERBOSE_FLAG: + self.fatal('env.FC_VERBOSE_FLAG is not set: execute check_fortran_verbose_flag?') + + self.start_msg('Getting fortran runtime link flags') + try: + self.check_cc( + fragment = FC_FRAGMENT2, + compile_filename = 'test.f', + features = 'fc fcprogram_test', + linkflags = [self.env.FC_VERBOSE_FLAG] + ) + except: + self.end_msg(False) + if kw.get('mandatory', True): + conf.fatal('Could not find the c library flags') + else: + out = self.test_bld.err + flags = parse_fortran_link(out.splitlines()) + self.end_msg('ok (%s)' % ' '.join(flags)) + self.env.LINKFLAGS_CLIB = flags + return flags + return [] + +def getoutput(conf, cmd, stdin=False): + """ + TODO a bit redundant, can be removed anytime + """ + try: + if stdin: + stdin = Utils.subprocess.PIPE + else: + stdin = None + p = Utils.subprocess.Popen(cmd, stdin=stdin, stdout=Utils.subprocess.PIPE, stderr=Utils.subprocess.PIPE) + if stdin: + p.stdin.write('\n'.encode()) + stdout, stderr = p.communicate() + except: + conf.fatal('could not determine the compiler version %r' % cmd) + else: + if not isinstance(stdout, str): + stdout = stdout.decode(sys.stdout.encoding) + if not isinstance(stderr, str): + stderr = stderr.decode(sys.stdout.encoding) + return stdout, stderr + +# ------------------------------------------------------------------------ + +ROUTINES_CODE = """\ + subroutine foobar() + return + end + subroutine foo_bar() + return + end +""" + +MAIN_CODE = """ +void %(dummy_func_nounder)s(void); +void %(dummy_func_under)s(void); +int %(main_func_name)s() { + %(dummy_func_nounder)s(); + %(dummy_func_under)s(); + return 0; +} +""" + +@feature('link_main_routines_func') +@before_method('process_source') +def link_main_routines_tg_method(self): + """ + The configuration test declares a unique task generator, + so we create other task generators from there for fortran link tests + """ + def write_test_file(task): + task.outputs[0].write(task.generator.code) + bld = self.bld + bld(rule=write_test_file, target='main.c', code=MAIN_CODE % self.__dict__) + bld(rule=write_test_file, target='test.f', code=ROUTINES_CODE) + bld(features='fc fcstlib', source='test.f', target='test') + bld(features='c fcprogram', source='main.c', target='app', use='test') + +def mangling_schemes(): + """ + Generate triplets for use with mangle_name + (used in check_fortran_mangling) + the order is tuned for gfortan + """ + for u in ['_', '']: + for du in ['', '_']: + for c in ["lower", "upper"]: + yield (u, du, c) + +def mangle_name(u, du, c, name): + """Mangle a name from a triplet (used in check_fortran_mangling)""" + return getattr(name, c)() + u + (name.find('_') != -1 and du or '') + +@conf +def check_fortran_mangling(self, *k, **kw): + """ + Detect the mangling scheme, sets FORTRAN_MANGLING to the triplet found + + This test will compile a fortran static library, then link a c app against it + """ + if not self.env.CC: + self.fatal('A c compiler is required for link_main_routines') + if not self.env.FC: + self.fatal('A fortran compiler is required for link_main_routines') + if not self.env.FC_MAIN: + self.fatal('Checking for mangling requires self.env.FC_MAIN (execute "check_fortran_dummy_main" first?)') + + self.start_msg('Getting fortran mangling scheme') + for (u, du, c) in mangling_schemes(): + try: + self.check_cc( + compile_filename = [], + features = 'link_main_routines_func', + msg = 'nomsg', + errmsg = 'nomsg', + mandatory=True, + dummy_func_nounder = mangle_name(u, du, c, "foobar"), + dummy_func_under = mangle_name(u, du, c, "foo_bar"), + main_func_name = self.env.FC_MAIN + ) + except self.errors.ConfigurationError: + pass + else: + self.end_msg("ok ('%s', '%s', '%s-case')" % (u, du, c)) + self.env.FORTRAN_MANGLING = (u, du, c) + break + else: + self.end_msg(False) + self.fatal('mangler not found') + + return (u, du, c) + +@feature('pyext') +@before_method('propagate_uselib_vars', 'apply_link') +def set_lib_pat(self): + """Set the fortran flags for linking with the python library""" + self.env['fcshlib_PATTERN'] = self.env['pyext_PATTERN'] + diff --git a/waflib/Tools/fc_scan.py b/waflib/Tools/fc_scan.py new file mode 100644 index 00000000..cbe77d36 --- /dev/null +++ b/waflib/Tools/fc_scan.py @@ -0,0 +1,126 @@ +#! /usr/bin/env python +# encoding: utf-8 +# DC 2008 +# Thomas Nagy 2010 (ita) + +import re + +from waflib import Utils, Task, TaskGen, Logs +from waflib.TaskGen import feature, before_method, after_method, extension +from waflib.Configure import conf + +INC_REGEX = """(?:^|['">]\s*;)\s*INCLUDE\s+(?:\w+_)?[<"'](.+?)(?=["'>])""" +USE_REGEX = """(?:^|;)\s*USE(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)""" +MOD_REGEX = """(?:^|;)\s*MODULE(?!\s*PROCEDURE)(?:\s+|(?:(?:\s*,\s*(?:NON_)?INTRINSIC)?\s*::))\s*(\w+)""" + +# TODO (DC) +# - handle pre-processed files (FORTRANPPCOM in scons) +# - handle multiple dialects +# TODO (ita) understand what the above is supposed to mean ^^ + +re_inc = re.compile(INC_REGEX, re.I) +re_use = re.compile(USE_REGEX, re.I) +re_mod = re.compile(MOD_REGEX, re.I) + +class fortran_parser(object): + """ + This parser will return: + + * the nodes corresponding to the module names that will be produced + * the nodes corresponding to the include files used + * the module names used by the fortran file + """ + + def __init__(self, incpaths): + self.seen = [] + """Files already parsed""" + + self.nodes = [] + """List of :py:class:`waflib.Node.Node` representing the dependencies to return""" + + self.names = [] + """List of module names to return""" + + self.incpaths = incpaths + """List of :py:class:`waflib.Node.Node` representing the include paths""" + + def find_deps(self, node): + """ + Parse a fortran file to read the dependencies used and provided + + :param node: fortran file to read + :type node: :py:class:`waflib.Node.Node` + :return: lists representing the includes, the modules used, and the modules created by a fortran file + :rtype: tuple of list of strings + """ + txt = node.read() + incs = [] + uses = [] + mods = [] + for line in txt.splitlines(): + # line by line regexp search? optimize? + m = re_inc.search(line) + if m: + incs.append(m.group(1)) + m = re_use.search(line) + if m: + uses.append(m.group(1)) + m = re_mod.search(line) + if m: + mods.append(m.group(1)) + return (incs, uses, mods) + + def start(self, node): + """ + Start the parsing. Use the stack self.waiting to hold the nodes to iterate on + + :param node: fortran file + :type node: :py:class:`waflib.Node.Node` + """ + self.waiting = [node] + while self.waiting: + nd = self.waiting.pop(0) + self.iter(nd) + + def iter(self, node): + """ + Process a single file in the search for dependencies, extract the files used + the modules used, and the modules provided. + """ + path = node.abspath() + incs, uses, mods = self.find_deps(node) + for x in incs: + if x in self.seen: + continue + self.seen.append(x) + self.tryfind_header(x) + + for x in uses: + name = "USE@%s" % x + if not name in self.names: + self.names.append(name) + + for x in mods: + name = "MOD@%s" % x + if not name in self.names: + self.names.append(name) + + def tryfind_header(self, filename): + """ + Try to find an include and add it the nodes to process + + :param filename: file name + :type filename: string + """ + found = None + for n in self.incpaths: + found = n.find_resource(filename) + if found: + self.nodes.append(found) + self.waiting.append(found) + break + if not found: + if not filename in self.names: + self.names.append(filename) + + diff --git a/waflib/Tools/flex.py b/waflib/Tools/flex.py new file mode 100644 index 00000000..8fbe2c8b --- /dev/null +++ b/waflib/Tools/flex.py @@ -0,0 +1,46 @@ +#!/usr/bin/env python +# encoding: utf-8 +# John O'Meara, 2006 +# Thomas Nagy, 2006-2010 (ita) + +""" +The **flex** program is a code generator which creates C or C++ files. +The generated files are compiled into object files. +""" + +import waflib.TaskGen + +def decide_ext(self, node): + if 'cxx' in self.features: + return ['.lex.cc'] + return ['.lex.c'] + +def flexfun(tsk): + env = tsk.env + bld = tsk.generator.bld + wd = bld.variant_dir + def to_list(xx): + if isinstance(xx, str): return [xx] + return xx + tsk.last_cmd = lst = [] + lst.extend(to_list(env['FLEX'])) + lst.extend(to_list(env['FLEXFLAGS'])) + lst.extend([a.path_from(bld.bldnode) for a in tsk.inputs]) + lst = [x for x in lst if x] + txt = bld.cmd_and_log(lst, cwd=wd, env=env.env or None, quiet=0) + tsk.outputs[0].write(txt) + +waflib.TaskGen.declare_chain( + name = 'flex', + rule = flexfun, # issue #854 + ext_in = '.l', + decider = decide_ext, +) + +def configure(conf): + """ + Detect the *flex* program + """ + conf.find_program('flex', var='FLEX') + conf.env.FLEXFLAGS = ['-t'] + diff --git a/waflib/Tools/g95.py b/waflib/Tools/g95.py new file mode 100644 index 00000000..46c9d10b --- /dev/null +++ b/waflib/Tools/g95.py @@ -0,0 +1,65 @@ +#! /usr/bin/env python +# encoding: utf-8 +# KWS 2010 +# Thomas Nagy 2010 (ita) + +import re +from waflib import Utils +from waflib.Tools import fc, fc_config, fc_scan +from waflib.Configure import conf + +@conf +def find_g95(conf): + fc = conf.find_program('g95', var='FC') + fc = conf.cmd_to_list(fc) + conf.get_g95_version(fc) + conf.env.FC_NAME = 'G95' + +@conf +def g95_flags(conf): + v = conf.env + v['FCFLAGS_fcshlib'] = ['-fPIC'] + v['FORTRANMODFLAG'] = ['-fmod=', ''] # template for module path + v['FCFLAGS_DEBUG'] = ['-Werror'] # why not + +@conf +def g95_modifier_win32(conf): + fc_config.fortran_modifier_win32(conf) + +@conf +def g95_modifier_cygwin(conf): + fc_config.fortran_modifier_cygwin(conf) + +@conf +def g95_modifier_darwin(conf): + fc_config.fortran_modifier_darwin(conf) + +@conf +def g95_modifier_platform(conf): + dest_os = conf.env['DEST_OS'] or Utils.unversioned_sys_platform() + g95_modifier_func = getattr(conf, 'g95_modifier_' + dest_os, None) + if g95_modifier_func: + g95_modifier_func() + +@conf +def get_g95_version(conf, fc): + """get the compiler version""" + + version_re = re.compile(r"g95\s*(?P\d*)\.(?P\d*)").search + cmd = fc + ['--version'] + out, err = fc_config.getoutput(conf, cmd, stdin=False) + if out: + match = version_re(out) + else: + match = version_re(err) + if not match: + conf.fatal('cannot determine g95 version') + k = match.groupdict() + conf.env['FC_VERSION'] = (k['major'], k['minor']) + +def configure(conf): + conf.find_g95() + conf.find_ar() + conf.fc_flags() + conf.g95_flags() + conf.g95_modifier_platform() diff --git a/waflib/Tools/gas.py b/waflib/Tools/gas.py new file mode 100644 index 00000000..8444c7e8 --- /dev/null +++ b/waflib/Tools/gas.py @@ -0,0 +1,16 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2008-2010 (ita) + +"Detect as/gas/gcc for compiling assembly files" + +import waflib.Tools.asm # - leave this +from waflib.Tools import ar + +def configure(conf): + """ + Find the programs gas/as/gcc and set the variable *AS* + """ + conf.find_program(['gas', 'as', 'gcc'], var='AS') + conf.env.AS_TGT_F = '-o' + conf.find_ar() diff --git a/waflib/Tools/gcc.py b/waflib/Tools/gcc.py new file mode 100644 index 00000000..1b95d87c --- /dev/null +++ b/waflib/Tools/gcc.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) +# Ralf Habacker, 2006 (rh) +# Yinon Ehrlich, 2009 + +""" +gcc/llvm detection. +""" + +import os, sys +from waflib import Configure, Options, Utils +from waflib.Tools import ccroot, ar +from waflib.Configure import conf + +@conf +def find_gcc(conf): + """ + Find the program gcc, and if present, try to detect its version number + """ + cc = conf.find_program(['gcc', 'cc'], var='CC') + cc = conf.cmd_to_list(cc) + conf.get_cc_version(cc, gcc=True) + conf.env.CC_NAME = 'gcc' + conf.env.CC = cc + +@conf +def gcc_common_flags(conf): + """ + Common flags for gcc on nearly all platforms + """ + v = conf.env + + v['CC_SRC_F'] = [] + v['CC_TGT_F'] = ['-c', '-o'] + + # linker + if not v['LINK_CC']: v['LINK_CC'] = v['CC'] + v['CCLNK_SRC_F'] = [] + v['CCLNK_TGT_F'] = ['-o'] + v['CPPPATH_ST'] = '-I%s' + v['DEFINES_ST'] = '-D%s' + + v['LIB_ST'] = '-l%s' # template for adding libs + v['LIBPATH_ST'] = '-L%s' # template for adding libpaths + v['STLIB_ST'] = '-l%s' + v['STLIBPATH_ST'] = '-L%s' + v['RPATH_ST'] = '-Wl,-rpath,%s' + + v['SONAME_ST'] = '-Wl,-h,%s' + v['SHLIB_MARKER'] = '-Wl,-Bdynamic' + v['STLIB_MARKER'] = '-Wl,-Bstatic' + + # program + v['cprogram_PATTERN'] = '%s' + + # shared librar + v['CFLAGS_cshlib'] = ['-fPIC'] + v['LINKFLAGS_cshlib'] = ['-shared'] + v['cshlib_PATTERN'] = 'lib%s.so' + + # static lib + v['LINKFLAGS_cstlib'] = ['-Wl,-Bstatic'] + v['cstlib_PATTERN'] = 'lib%s.a' + + # osx stuff + v['LINKFLAGS_MACBUNDLE'] = ['-bundle', '-undefined', 'dynamic_lookup'] + v['CFLAGS_MACBUNDLE'] = ['-fPIC'] + v['macbundle_PATTERN'] = '%s.bundle' + +@conf +def gcc_modifier_win32(conf): + """Configuration flags for executing gcc on Windows""" + v = conf.env + v['cprogram_PATTERN'] = '%s.exe' + + v['cshlib_PATTERN'] = '%s.dll' + v['implib_PATTERN'] = 'lib%s.dll.a' + v['IMPLIB_ST'] = '-Wl,--out-implib,%s' + + v['CFLAGS_cshlib'] = [] + + v.append_value('CFLAGS_cshlib', ['-DDLL_EXPORT']) # TODO adding nonstandard defines like this DLL_EXPORT is not a good idea + + # Auto-import is enabled by default even without this option, + # but enabling it explicitly has the nice effect of suppressing the rather boring, debug-level messages + # that the linker emits otherwise. + v.append_value('LINKFLAGS', ['-Wl,--enable-auto-import']) + +@conf +def gcc_modifier_cygwin(conf): + """Configuration flags for executing gcc on Cygwin""" + gcc_modifier_win32(conf) + v = conf.env + v['cshlib_PATTERN'] = 'cyg%s.dll' + v.append_value('LINKFLAGS_cshlib', ['-Wl,--enable-auto-image-base']) + v['CFLAGS_cshlib'] = [] + +@conf +def gcc_modifier_darwin(conf): + """Configuration flags for executing gcc on MacOS""" + v = conf.env + v['CFLAGS_cshlib'] = ['-fPIC', '-compatibility_version', '1', '-current_version', '1'] + v['LINKFLAGS_cshlib'] = ['-dynamiclib'] + v['cshlib_PATTERN'] = 'lib%s.dylib' + v['FRAMEWORKPATH_ST'] = '-F%s' + v['FRAMEWORK_ST'] = ['-framework'] + v['ARCH_ST'] = ['-arch'] + + v['LINKFLAGS_cstlib'] = [] + + v['SHLIB_MARKER'] = [] + v['STLIB_MARKER'] = [] + v['SONAME_ST'] = [] + +@conf +def gcc_modifier_aix(conf): + """Configuration flags for executing gcc on AIX""" + v = conf.env + v['LINKFLAGS_cprogram'] = ['-Wl,-brtl'] + v['LINKFLAGS_cshlib'] = ['-shared','-Wl,-brtl,-bexpfull'] + v['SHLIB_MARKER'] = [] + +@conf +def gcc_modifier_hpux(conf): + v = conf.env + v['SHLIB_MARKER'] = [] + v['CFLAGS_cshlib'] = ['-fPIC','-DPIC'] + v['cshlib_PATTERN'] = 'lib%s.sl' + +@conf +def gcc_modifier_platform(conf): + """Execute platform-specific functions based on *gcc_modifier_+NAME*""" + # * set configurations specific for a platform. + # * the destination platform is detected automatically by looking at the macros the compiler predefines, + # and if it's not recognised, it fallbacks to sys.platform. + gcc_modifier_func = getattr(conf, 'gcc_modifier_' + conf.env.DEST_OS, None) + if gcc_modifier_func: + gcc_modifier_func() + +def configure(conf): + """ + Configuration for gcc + """ + conf.find_gcc() + conf.find_ar() + conf.gcc_common_flags() + conf.gcc_modifier_platform() + conf.cc_load_tools() + conf.cc_add_flags() + conf.link_add_flags() + + diff --git a/waflib/Tools/gdc.py b/waflib/Tools/gdc.py new file mode 100644 index 00000000..6bb23a60 --- /dev/null +++ b/waflib/Tools/gdc.py @@ -0,0 +1,56 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Carlos Rafael Giani, 2007 (dv) + +import sys +from waflib.Tools import ar, d +from waflib.Configure import conf + +@conf +def find_gdc(conf): + """ + Find the program gdc and set the variable *D* + """ + conf.find_program('gdc', var='D') + +@conf +def common_flags_gdc(conf): + """ + Set the flags required by *gdc* + """ + v = conf.env + + # _DFLAGS _DIMPORTFLAGS + + # for mory info about the meaning of this dict see dmd.py + v['DFLAGS'] = [] + + v['D_SRC_F'] = ['-c'] + v['D_TGT_F'] = '-o%s' + + # linker + v['D_LINKER'] = v['D'] + v['DLNK_SRC_F'] = '' + v['DLNK_TGT_F'] = '-o%s' + v['DINC_ST'] = '-I%s' + + v['DSHLIB_MARKER'] = v['DSTLIB_MARKER'] = '' + v['DSTLIB_ST'] = v['DSHLIB_ST'] = '-l%s' + v['DSTLIBPATH_ST'] = v['DLIBPATH_ST'] = '-L%s' + + v['LINKFLAGS_dshlib'] = ['-shared'] + + v['DHEADER_ext'] = '.di' + v.DFLAGS_d_with_header = '-fintfc' + v['D_HDR_F'] = '-fintfc-file=%s' + +def configure(conf): + """ + Configuration for gdc + """ + conf.find_gdc() + conf.load('ar') + conf.load('d') + conf.common_flags_gdc() + conf.d_platform_flags() + diff --git a/waflib/Tools/gfortran.py b/waflib/Tools/gfortran.py new file mode 100644 index 00000000..081b4309 --- /dev/null +++ b/waflib/Tools/gfortran.py @@ -0,0 +1,90 @@ +#! /usr/bin/env python +# encoding: utf-8 +# DC 2008 +# Thomas Nagy 2010 (ita) + +import re +from waflib import Utils +from waflib.Tools import fc, fc_config, fc_scan +from waflib.Configure import conf + +@conf +def find_gfortran(conf): + """Find the gfortran program (will look in the environment variable 'FC')""" + fc = conf.find_program(['gfortran','g77'], var='FC') + # (fallback to g77 for systems, where no gfortran is available) + fc = conf.cmd_to_list(fc) + conf.get_gfortran_version(fc) + conf.env.FC_NAME = 'GFORTRAN' + +@conf +def gfortran_flags(conf): + v = conf.env + v['FCFLAGS_fcshlib'] = ['-fPIC'] + v['FORTRANMODFLAG'] = ['-J', ''] # template for module path + v['FCFLAGS_DEBUG'] = ['-Werror'] # why not + +@conf +def gfortran_modifier_win32(conf): + fc_config.fortran_modifier_win32(conf) + +@conf +def gfortran_modifier_cygwin(conf): + fc_config.fortran_modifier_cygwin(conf) + +@conf +def gfortran_modifier_darwin(conf): + fc_config.fortran_modifier_darwin(conf) + +@conf +def gfortran_modifier_platform(conf): + dest_os = conf.env['DEST_OS'] or Utils.unversioned_sys_platform() + gfortran_modifier_func = getattr(conf, 'gfortran_modifier_' + dest_os, None) + if gfortran_modifier_func: + gfortran_modifier_func() + +@conf +def get_gfortran_version(conf, fc): + """Get the compiler version""" + + # ensure this is actually gfortran, not an imposter. + version_re = re.compile(r"GNU\s*Fortran", re.I).search + cmd = fc + ['--version'] + out, err = fc_config.getoutput(conf, cmd, stdin=False) + if out: match = version_re(out) + else: match = version_re(err) + if not match: + conf.fatal('Could not determine the compiler type') + + # --- now get more detailed info -- see c_config.get_cc_version + cmd = fc + ['-dM', '-E', '-'] + out, err = fc_config.getoutput(conf, cmd, stdin=True) + + if out.find('__GNUC__') < 0: + conf.fatal('Could not determine the compiler type') + + k = {} + out = out.split('\n') + import shlex + + for line in out: + lst = shlex.split(line) + if len(lst)>2: + key = lst[1] + val = lst[2] + k[key] = val + + def isD(var): + return var in k + + def isT(var): + return var in k and k[var] != '0' + + conf.env['FC_VERSION'] = (k['__GNUC__'], k['__GNUC_MINOR__'], k['__GNUC_PATCHLEVEL__']) + +def configure(conf): + conf.find_gfortran() + conf.find_ar() + conf.fc_flags() + conf.gfortran_flags() + conf.gfortran_modifier_platform() diff --git a/waflib/Tools/glib2.py b/waflib/Tools/glib2.py new file mode 100644 index 00000000..c309f024 --- /dev/null +++ b/waflib/Tools/glib2.py @@ -0,0 +1,378 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +""" +Support for GLib2 tools: + +* marshal +* enums +* gsettings +""" + +import os +from waflib import Task, Utils, Options, Errors, Logs +from waflib.TaskGen import taskgen_method, before_method, after_method, feature + +################## marshal files + +@taskgen_method +def add_marshal_file(self, filename, prefix): + """ + Add a file to the list of marshal files to process. Store them in the attribute *marshal_list*. + + :param filename: xml file to compile + :type filename: string + :param prefix: marshal prefix (--prefix=prefix) + :type prefix: string + """ + if not hasattr(self, 'marshal_list'): + self.marshal_list = [] + self.meths.append('process_marshal') + self.marshal_list.append((filename, prefix)) + +@before_method('process_source') +def process_marshal(self): + """ + Process the marshal files stored in the attribute *marshal_list* to create :py:class:`waflib.Tools.glib2.glib_genmarshal` instances. + Add the c file created to the list of source to process. + """ + for f, prefix in getattr(self, 'marshal_list', []): + node = self.path.find_resource(f) + + if not node: + raise Errors.WafError('file not found %r' % f) + + h_node = node.change_ext('.h') + c_node = node.change_ext('.c') + + task = self.create_task('glib_genmarshal', node, [h_node, c_node]) + task.env.GLIB_GENMARSHAL_PREFIX = prefix + self.source = self.to_nodes(getattr(self, 'source', [])) + self.source.append(c_node) + +class glib_genmarshal(Task.Task): + + def run(self): + + bld = self.inputs[0].__class__.ctx + + get = self.env.get_flat + cmd1 = "%s %s --prefix=%s --header > %s" % ( + get('GLIB_GENMARSHAL'), + self.inputs[0].srcpath(), + get('GLIB_GENMARSHAL_PREFIX'), + self.outputs[0].abspath() + ) + + ret = bld.exec_command(cmd1) + if ret: return ret + + #print self.outputs[1].abspath() + c = '''#include "%s"\n''' % self.outputs[0].name + self.outputs[1].write(c) + + cmd2 = "%s %s --prefix=%s --body >> %s" % ( + get('GLIB_GENMARSHAL'), + self.inputs[0].srcpath(), + get('GLIB_GENMARSHAL_PREFIX'), + self.outputs[1].abspath() + ) + return bld.exec_command(cmd2) + + vars = ['GLIB_GENMARSHAL_PREFIX', 'GLIB_GENMARSHAL'] + color = 'BLUE' + ext_out = ['.h'] + +########################## glib-mkenums + +@taskgen_method +def add_enums_from_template(self, source='', target='', template='', comments=''): + """ + Add a file to the list of enum files to process. Store them in the attribute *enums_list*. + + :param source: enum file to process + :type source: string + :param target: target file + :type target: string + :param template: template file + :type template: string + :param comments: comments + :type comments: string + """ + if not hasattr(self, 'enums_list'): + self.enums_list = [] + self.meths.append('process_enums') + self.enums_list.append({'source': source, + 'target': target, + 'template': template, + 'file-head': '', + 'file-prod': '', + 'file-tail': '', + 'enum-prod': '', + 'value-head': '', + 'value-prod': '', + 'value-tail': '', + 'comments': comments}) + +@taskgen_method +def add_enums(self, source='', target='', + file_head='', file_prod='', file_tail='', enum_prod='', + value_head='', value_prod='', value_tail='', comments=''): + """ + Add a file to the list of enum files to process. Store them in the attribute *enums_list*. + + :param source: enum file to process + :type source: string + :param target: target file + :type target: string + :param file_head: unused + :param file_prod: unused + :param file_tail: unused + :param enum_prod: unused + :param value_head: unused + :param value_prod: unused + :param value_tail: unused + :param comments: comments + :type comments: string + """ + if not hasattr(self, 'enums_list'): + self.enums_list = [] + self.meths.append('process_enums') + self.enums_list.append({'source': source, + 'template': '', + 'target': target, + 'file-head': file_head, + 'file-prod': file_prod, + 'file-tail': file_tail, + 'enum-prod': enum_prod, + 'value-head': value_head, + 'value-prod': value_prod, + 'value-tail': value_tail, + 'comments': comments}) + +@before_method('process_source') +def process_enums(self): + """ + Process the enum files stored in the attribute *enum_list* to create :py:class:`waflib.Tools.glib2.glib_mkenums` instances. + """ + for enum in getattr(self, 'enums_list', []): + task = self.create_task('glib_mkenums') + env = task.env + + inputs = [] + + # process the source + source_list = self.to_list(enum['source']) + if not source_list: + raise Errors.WafError('missing source ' + str(enum)) + source_list = [self.path.find_resource(k) for k in source_list] + inputs += source_list + env['GLIB_MKENUMS_SOURCE'] = [k.abspath() for k in source_list] + + # find the target + if not enum['target']: + raise Errors.WafError('missing target ' + str(enum)) + tgt_node = self.path.find_or_declare(enum['target']) + if tgt_node.name.endswith('.c'): + self.source.append(tgt_node) + env['GLIB_MKENUMS_TARGET'] = tgt_node.abspath() + + + options = [] + + if enum['template']: # template, if provided + template_node = self.path.find_resource(enum['template']) + options.append('--template %s' % (template_node.abspath())) + inputs.append(template_node) + params = {'file-head' : '--fhead', + 'file-prod' : '--fprod', + 'file-tail' : '--ftail', + 'enum-prod' : '--eprod', + 'value-head' : '--vhead', + 'value-prod' : '--vprod', + 'value-tail' : '--vtail', + 'comments': '--comments'} + for param, option in params.items(): + if enum[param]: + options.append('%s %r' % (option, enum[param])) + + env['GLIB_MKENUMS_OPTIONS'] = ' '.join(options) + + # update the task instance + task.set_inputs(inputs) + task.set_outputs(tgt_node) + +class glib_mkenums(Task.Task): + """ + Process enum files + """ + run_str = '${GLIB_MKENUMS} ${GLIB_MKENUMS_OPTIONS} ${GLIB_MKENUMS_SOURCE} > ${GLIB_MKENUMS_TARGET}' + color = 'PINK' + ext_out = ['.h'] + +######################################### gsettings + +@taskgen_method +def add_settings_schemas(self, filename_list): + """ + Add settings files to process, add them to *settings_schema_files* + + :param filename_list: files + :type filename_list: list of string + """ + if not hasattr(self, 'settings_schema_files'): + self.settings_schema_files = [] + + if not isinstance(filename_list, list): + filename_list = [filename_list] + + self.settings_schema_files.extend(filename_list) + +@taskgen_method +def add_settings_enums(self, namespace, filename_list): + """ + This function may be called only once by task generator to set the enums namespace. + + :param namespace: namespace + :type namespace: string + :param filename_list: enum files to process + :type filename_list: file list + """ + if hasattr(self, 'settings_enum_namespace'): + raise Errors.WafError("Tried to add gsettings enums to '%s' more than once" % self.name) + self.settings_enum_namespace = namespace + + if type(filename_list) != 'list': + filename_list = [filename_list] + self.settings_enum_files = filename_list + + +def r_change_ext(self, ext): + """ + Change the extension from the *last* dot in the filename. The gsettings schemas + often have names of the form org.gsettings.test.gschema.xml + """ + name = self.name + k = name.rfind('.') + if k >= 0: + name = name[:k] + ext + else: + name = name + ext + return self.parent.find_or_declare([name]) + +@feature('glib2') +def process_settings(self): + """ + Process the schema files in *settings_schema_files* to create :py:class:`waflib.Tools.glib2.glib_mkenums` instances. The + same files are validated through :py:class:`waflib.Tools.glib2.glib_validate_schema` tasks. + + """ + enums_tgt_node = [] + install_files = [] + + settings_schema_files = getattr(self, 'settings_schema_files', []) + if settings_schema_files and not self.env['GLIB_COMPILE_SCHEMAS']: + raise Errors.WafError ("Unable to process GSettings schemas - glib-compile-schemas was not found during configure") + + # 1. process gsettings_enum_files (generate .enums.xml) + # + if hasattr(self, 'settings_enum_files'): + enums_task = self.create_task('glib_mkenums') + + source_list = self.settings_enum_files + source_list = [self.path.find_resource(k) for k in source_list] + enums_task.set_inputs(source_list) + enums_task.env['GLIB_MKENUMS_SOURCE'] = [k.abspath() for k in source_list] + + target = self.settings_enum_namespace + '.enums.xml' + tgt_node = self.path.find_or_declare(target) + enums_task.set_outputs(tgt_node) + enums_task.env['GLIB_MKENUMS_TARGET'] = tgt_node.abspath() + enums_tgt_node = [tgt_node] + + install_files.append (tgt_node) + + options = '--comments "" --fhead "" --vhead " <@type@ id=\\"%s.@EnumName@\\">" --vprod " " --vtail " " --ftail "" ' % (self.settings_enum_namespace) + enums_task.env['GLIB_MKENUMS_OPTIONS'] = options + + # 2. process gsettings_schema_files (validate .gschema.xml files) + # + for schema in settings_schema_files: + schema_task = self.create_task ('glib_validate_schema') + + schema_node = self.path.find_resource(schema) + if not schema_node: + raise Errors.WafError("Cannot find the schema file '%s'" % schema) + install_files.append(schema_node) + source_list = enums_tgt_node + [schema_node] + + schema_task.set_inputs (source_list) + schema_task.env['GLIB_COMPILE_SCHEMAS_OPTIONS'] = [("--schema-file=" + k.abspath()) for k in source_list] + + target_node = r_change_ext (schema_node, '.xml.valid') + schema_task.set_outputs (target_node) + schema_task.env['GLIB_VALIDATE_SCHEMA_OUTPUT'] = target_node.abspath() + + # 3. schemas install task + def compile_schemas_callback(bld): + if not bld.is_install: return + Logs.pprint ('YELLOW','Updating GSettings schema cache') + command = Utils.subst_vars("${GLIB_COMPILE_SCHEMAS} ${GSETTINGSSCHEMADIR}", bld.env) + ret = self.bld.exec_command(command) + + if self.bld.is_install: + if not self.env['GSETTINGSSCHEMADIR']: + raise Errors.WafError ('GSETTINGSSCHEMADIR not defined (should have been set up automatically during configure)') + + if install_files: + self.bld.install_files (self.env['GSETTINGSSCHEMADIR'], install_files) + + if not hasattr(self.bld, '_compile_schemas_registered'): + self.bld.add_post_fun (compile_schemas_callback) + self.bld._compile_schemas_registered = True + +class glib_validate_schema(Task.Task): + """ + Validate schema files + """ + run_str = 'rm -f ${GLIB_VALIDATE_SCHEMA_OUTPUT} && ${GLIB_COMPILE_SCHEMAS} --dry-run ${GLIB_COMPILE_SCHEMAS_OPTIONS} && touch ${GLIB_VALIDATE_SCHEMA_OUTPUT}' + color = 'PINK' + +def configure(conf): + """ + Find the following programs: + + * *glib-genmarshal* and set *GLIB_GENMARSHAL* + * *glib-mkenums* and set *GLIB_MKENUMS* + * *glib-compile-schemas* and set *GLIB_COMPILE_SCHEMAS* (not mandatory) + + And set the variable *GSETTINGSSCHEMADIR* + """ + conf.find_program('glib-genmarshal', var='GLIB_GENMARSHAL') + conf.find_perl_program('glib-mkenums', var='GLIB_MKENUMS') + + # when cross-compiling, gsettings.m4 locates the program with the following: + # pkg-config --variable glib_compile_schemas gio-2.0 + conf.find_program('glib-compile-schemas', var='GLIB_COMPILE_SCHEMAS', mandatory=False) + + def getstr(varname): + return getattr(Options.options, varname, getattr(conf.env,varname, '')) + + # TODO make this dependent on the gnu_dirs tool? + gsettingsschemadir = getstr('GSETTINGSSCHEMADIR') + if not gsettingsschemadir: + datadir = getstr('DATADIR') + if not datadir: + prefix = conf.env['PREFIX'] + datadir = os.path.join(prefix, 'share') + gsettingsschemadir = os.path.join(datadir, 'glib-2.0', 'schemas') + + conf.env['GSETTINGSSCHEMADIR'] = gsettingsschemadir + +def options(opt): + """ + Add the ``--gsettingsschemadir`` command-line option + """ + opt.add_option('--gsettingsschemadir', help='GSettings schema location [Default: ${datadir}/glib-2.0/schemas]',default='',dest='GSETTINGSSCHEMADIR') + diff --git a/waflib/Tools/gnu_dirs.py b/waflib/Tools/gnu_dirs.py new file mode 100644 index 00000000..c9fbe5cf --- /dev/null +++ b/waflib/Tools/gnu_dirs.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Ali Sabil, 2007 + +""" +Sets various standard variables such as INCLUDEDIR. SBINDIR and others. To use this module just call:: + + opt.load('gnu_dirs') + +and:: + + conf.load('gnu_dirs') + +Add options for the standard GNU directories, this tool will add the options +found in autotools, and will update the environment with the following +installation variables: + +============== ========================================= ======================= +Variable Description Value +============== ========================================= ======================= +PREFIX architecture-independent files /usr/local +EXEC_PREFIX architecture-dependent files PREFIX +BINDIR user executables EXEC_PREFIX/bin +SBINDIR user executables EXEC_PREFIX/sbin +LIBEXECDIR program executables EXEC_PREFIX/libexec +SYSCONFDIR read-only single-machine data PREFIX/etc +SHAREDSTATEDIR modifiable architecture-independent data PREFIX/com +LOCALSTATEDIR modifiable single-machine data PREFIX/var +LIBDIR object code libraries EXEC_PREFIX/lib +INCLUDEDIR C header files PREFIX/include +OLDINCLUDEDIR C header files for non-gcc /usr/include +DATAROOTDIR read-only arch.-independent data root PREFIX/share +DATADIR read-only architecture-independent data DATAROOTDIR +INFODIR info documentation DATAROOTDIR/info +LOCALEDIR locale-dependent data DATAROOTDIR/locale +MANDIR man documentation DATAROOTDIR/man +DOCDIR documentation root DATAROOTDIR/doc/APPNAME +HTMLDIR html documentation DOCDIR +DVIDIR dvi documentation DOCDIR +PDFDIR pdf documentation DOCDIR +PSDIR ps documentation DOCDIR +============== ========================================= ======================= +""" + +import os +from waflib import Utils, Options, Context + +_options = [x.split(', ') for x in ''' +bindir, user executables, ${EXEC_PREFIX}/bin +sbindir, system admin executables, ${EXEC_PREFIX}/sbin +libexecdir, program executables, ${EXEC_PREFIX}/libexec +sysconfdir, read-only single-machine data, ${PREFIX}/etc +sharedstatedir, modifiable architecture-independent data, ${PREFIX}/com +localstatedir, modifiable single-machine data, ${PREFIX}/var +libdir, object code libraries, ${EXEC_PREFIX}/lib +includedir, C header files, ${PREFIX}/include +oldincludedir, C header files for non-gcc, /usr/include +datarootdir, read-only arch.-independent data root, ${PREFIX}/share +datadir, read-only architecture-independent data, ${DATAROOTDIR} +infodir, info documentation, ${DATAROOTDIR}/info +localedir, locale-dependent data, ${DATAROOTDIR}/locale +mandir, man documentation, ${DATAROOTDIR}/man +docdir, documentation root, ${DATAROOTDIR}/doc/${PACKAGE} +htmldir, html documentation, ${DOCDIR} +dvidir, dvi documentation, ${DOCDIR} +pdfdir, pdf documentation, ${DOCDIR} +psdir, ps documentation, ${DOCDIR} +'''.split('\n') if x] + +def configure(conf): + """ + Read the command-line options to set lots of variables in *conf.env*. The variables + BINDIR and LIBDIR will be overwritten. + """ + def get_param(varname, default): + return getattr(Options.options, varname, '') or default + + env = conf.env + conf.env.LIBDIR = conf.env.BINDIR = [] + env['EXEC_PREFIX'] = get_param('EXEC_PREFIX', env['PREFIX']) + env['PACKAGE'] = getattr(Context.g_module, 'APPNAME', None) or env['PACKAGE'] + + complete = False + iter = 0 + while not complete and iter < len(_options) + 1: + iter += 1 + complete = True + for name, help, default in _options: + name = name.upper() + if not env[name]: + try: + env[name] = Utils.subst_vars(get_param(name, default).replace('/', os.sep), env) + except TypeError: + complete = False + if not complete: + lst = [name for name, _, _ in _options if not env[name.upper()]] + raise conf.errors.WafError('Variable substitution failure %r' % lst) + +def options(opt): + """ + Add lots of command-line options, for example:: + + --exec-prefix: EXEC_PREFIX + """ + inst_dir = opt.add_option_group('Installation directories', +'By default, "waf install" will put the files in\ + "/usr/local/bin", "/usr/local/lib" etc. An installation prefix other\ + than "/usr/local" can be given using "--prefix", for example "--prefix=$HOME"') + + for k in ('--prefix', '--destdir'): + option = opt.parser.get_option(k) + if option: + opt.parser.remove_option(k) + inst_dir.add_option(option) + + inst_dir.add_option('--exec-prefix', + help = 'installation prefix [Default: ${PREFIX}]', + default = '', + dest = 'EXEC_PREFIX') + + dirs_options = opt.add_option_group('Pre-defined installation directories', '') + + for name, help, default in _options: + option_name = '--' + name + str_default = default + str_help = '%s [Default: %s]' % (help, str_default) + dirs_options.add_option(option_name, help=str_help, default='', dest=name.upper()) + diff --git a/waflib/Tools/gxx.py b/waflib/Tools/gxx.py new file mode 100644 index 00000000..4ab486b0 --- /dev/null +++ b/waflib/Tools/gxx.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) +# Ralf Habacker, 2006 (rh) +# Yinon Ehrlich, 2009 + +""" +g++/llvm detection. +""" + +import os, sys +from waflib import Configure, Options, Utils +from waflib.Tools import ccroot, ar +from waflib.Configure import conf + +@conf +def find_gxx(conf): + """ + Find the program g++, and if present, try to detect its version number + """ + cxx = conf.find_program(['g++', 'c++'], var='CXX') + cxx = conf.cmd_to_list(cxx) + conf.get_cc_version(cxx, gcc=True) + conf.env.CXX_NAME = 'gcc' + conf.env.CXX = cxx + +@conf +def gxx_common_flags(conf): + """ + Common flags for g++ on nearly all platforms + """ + v = conf.env + + v['CXX_SRC_F'] = [] + v['CXX_TGT_F'] = ['-c', '-o'] + + # linker + if not v['LINK_CXX']: v['LINK_CXX'] = v['CXX'] + v['CXXLNK_SRC_F'] = [] + v['CXXLNK_TGT_F'] = ['-o'] + v['CPPPATH_ST'] = '-I%s' + v['DEFINES_ST'] = '-D%s' + + v['LIB_ST'] = '-l%s' # template for adding libs + v['LIBPATH_ST'] = '-L%s' # template for adding libpaths + v['STLIB_ST'] = '-l%s' + v['STLIBPATH_ST'] = '-L%s' + v['RPATH_ST'] = '-Wl,-rpath,%s' + + v['SONAME_ST'] = '-Wl,-h,%s' + v['SHLIB_MARKER'] = '-Wl,-Bdynamic' + v['STLIB_MARKER'] = '-Wl,-Bstatic' + + # program + v['cxxprogram_PATTERN'] = '%s' + + # shared library + v['CXXFLAGS_cxxshlib'] = ['-fPIC'] + v['LINKFLAGS_cxxshlib'] = ['-shared'] + v['cxxshlib_PATTERN'] = 'lib%s.so' + + # static lib + v['LINKFLAGS_cxxstlib'] = ['-Wl,-Bstatic'] + v['cxxstlib_PATTERN'] = 'lib%s.a' + + # osx stuff + v['LINKFLAGS_MACBUNDLE'] = ['-bundle', '-undefined', 'dynamic_lookup'] + v['CXXFLAGS_MACBUNDLE'] = ['-fPIC'] + v['macbundle_PATTERN'] = '%s.bundle' + +@conf +def gxx_modifier_win32(conf): + """Configuration flags for executing gcc on Windows""" + v = conf.env + v['cxxprogram_PATTERN'] = '%s.exe' + + v['cxxshlib_PATTERN'] = '%s.dll' + v['implib_PATTERN'] = 'lib%s.dll.a' + v['IMPLIB_ST'] = '-Wl,--out-implib,%s' + + v['CXXFLAGS_cxxshlib'] = [] + + v.append_value('CXXFLAGS_cxxshlib', ['-DDLL_EXPORT']) # TODO adding nonstandard defines like this DLL_EXPORT is not a good idea + + # Auto-import is enabled by default even without this option, + # but enabling it explicitly has the nice effect of suppressing the rather boring, debug-level messages + # that the linker emits otherwise. + v.append_value('LINKFLAGS', ['-Wl,--enable-auto-import']) + +@conf +def gxx_modifier_cygwin(conf): + """Configuration flags for executing g++ on Cygwin""" + gxx_modifier_win32(conf) + v = conf.env + v['cxxshlib_PATTERN'] = 'cyg%s.dll' + v.append_value('LINKFLAGS_cxxshlib', ['-Wl,--enable-auto-image-base']) + v['CXXFLAGS_cxxshlib'] = [] + +@conf +def gxx_modifier_darwin(conf): + """Configuration flags for executing g++ on MacOS""" + v = conf.env + v['CXXFLAGS_cxxshlib'] = ['-fPIC', '-compatibility_version', '1', '-current_version', '1'] + v['LINKFLAGS_cxxshlib'] = ['-dynamiclib'] + v['cxxshlib_PATTERN'] = 'lib%s.dylib' + v['FRAMEWORKPATH_ST'] = '-F%s' + v['FRAMEWORK_ST'] = ['-framework'] + v['ARCH_ST'] = ['-arch'] + + v['LINKFLAGS_cxxstlib'] = [] + + v['SHLIB_MARKER'] = [] + v['STLIB_MARKER'] = [] + v['SONAME_ST'] = [] + +@conf +def gxx_modifier_aix(conf): + """Configuration flags for executing g++ on AIX""" + v = conf.env + v['LINKFLAGS_cxxprogram']= ['-Wl,-brtl'] + + v['LINKFLAGS_cxxshlib'] = ['-shared', '-Wl,-brtl,-bexpfull'] + v['SHLIB_MARKER'] = [] + +@conf +def gxx_modifier_hpux(conf): + v = conf.env + v['SHLIB_MARKER'] = [] + v['CFLAGS_cxxshlib'] = ['-fPIC','-DPIC'] + v['cxxshlib_PATTERN'] = 'lib%s.sl' + +@conf +def gxx_modifier_platform(conf): + """Execute platform-specific functions based on *gxx_modifier_+NAME*""" + # * set configurations specific for a platform. + # * the destination platform is detected automatically by looking at the macros the compiler predefines, + # and if it's not recognised, it fallbacks to sys.platform. + gxx_modifier_func = getattr(conf, 'gxx_modifier_' + conf.env.DEST_OS, None) + if gxx_modifier_func: + gxx_modifier_func() + +def configure(conf): + """ + Configuration for g++ + """ + conf.find_gxx() + conf.find_ar() + conf.gxx_common_flags() + conf.gxx_modifier_platform() + conf.cxx_load_tools() + conf.cxx_add_flags() + conf.link_add_flags() + diff --git a/waflib/Tools/icc.py b/waflib/Tools/icc.py new file mode 100644 index 00000000..7f506f8a --- /dev/null +++ b/waflib/Tools/icc.py @@ -0,0 +1,42 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Stian Selnes, 2008 +# Thomas Nagy 2009-2010 (ita) + +""" +Detect the Intel C compiler +""" + +import os, sys +from waflib.Tools import ccroot, ar, gcc +from waflib.Configure import conf + +@conf +def find_icc(conf): + """ + Find the program icc and execute it to ensure it really is icc + """ + if sys.platform == 'cygwin': + conf.fatal('The Intel compiler does not work on Cygwin') + + v = conf.env + cc = None + if v['CC']: cc = v['CC'] + elif 'CC' in conf.environ: cc = conf.environ['CC'] + if not cc: cc = conf.find_program('icc', var='CC') + if not cc: cc = conf.find_program('ICL', var='CC') + if not cc: conf.fatal('Intel C Compiler (icc) was not found') + cc = conf.cmd_to_list(cc) + + conf.get_cc_version(cc, icc=True) + v['CC'] = cc + v['CC_NAME'] = 'icc' + +def configure(conf): + conf.find_icc() + conf.find_ar() + conf.gcc_common_flags() + conf.gcc_modifier_platform() + conf.cc_load_tools() + conf.cc_add_flags() + conf.link_add_flags() diff --git a/waflib/Tools/icpc.py b/waflib/Tools/icpc.py new file mode 100644 index 00000000..138f882d --- /dev/null +++ b/waflib/Tools/icpc.py @@ -0,0 +1,41 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy 2009-2010 (ita) + +""" +Detect the Intel C++ compiler +""" + +import os, sys +from waflib.Tools import ccroot, ar, gxx +from waflib.Configure import conf + +@conf +def find_icpc(conf): + """ + Find the program icpc, and execute it to ensure it really is icpc + """ + if sys.platform == 'cygwin': + conf.fatal('The Intel compiler does not work on Cygwin') + + v = conf.env + cxx = None + if v['CXX']: cxx = v['CXX'] + elif 'CXX' in conf.environ: cxx = conf.environ['CXX'] + if not cxx: cxx = conf.find_program('icpc', var='CXX') + if not cxx: conf.fatal('Intel C++ Compiler (icpc) was not found') + cxx = conf.cmd_to_list(cxx) + + conf.get_cc_version(cxx, icc=True) + v['CXX'] = cxx + v['CXX_NAME'] = 'icc' + +def configure(conf): + conf.find_icpc() + conf.find_ar() + conf.gxx_common_flags() + conf.gxx_modifier_platform() + conf.cxx_load_tools() + conf.cxx_add_flags() + conf.link_add_flags() + diff --git a/waflib/Tools/ifort.py b/waflib/Tools/ifort.py new file mode 100644 index 00000000..b999efb2 --- /dev/null +++ b/waflib/Tools/ifort.py @@ -0,0 +1,49 @@ +#! /usr/bin/env python +# encoding: utf-8 +# DC 2008 +# Thomas Nagy 2010 (ita) + +import re +from waflib import Utils +from waflib.Tools import fc, fc_config, fc_scan +from waflib.Configure import conf + +@conf +def find_ifort(conf): + fc = conf.find_program('ifort', var='FC') + fc = conf.cmd_to_list(fc) + conf.get_ifort_version(fc) + conf.env.FC_NAME = 'IFORT' + +@conf +def ifort_modifier_cygwin(conf): + raise NotImplementedError("Ifort on cygwin not yet implemented") + +@conf +def ifort_modifier_platform(conf): + dest_os = conf.env['DEST_OS'] or Utils.unversioned_sys_platform() + ifort_modifier_func = getattr(conf, 'ifort_modifier_' + dest_os, None) + if ifort_modifier_func: + ifort_modifier_func() + +@conf +def get_ifort_version(conf, fc): + """get the compiler version""" + + version_re = re.compile(r"ifort\s*\(IFORT\)\s*(?P\d*)\.(?P\d*)", re.I).search + cmd = fc + ['--version'] + out, err = fc_config.getoutput(conf, cmd, stdin=False) + if out: + match = version_re(out) + else: + match = version_re(err) + if not match: + conf.fatal('cannot determine ifort version.') + k = match.groupdict() + conf.env['FC_VERSION'] = (k['major'], k['minor']) + +def configure(conf): + conf.find_ifort() + conf.find_ar() + conf.fc_flags() + conf.ifort_modifier_platform() diff --git a/waflib/Tools/intltool.py b/waflib/Tools/intltool.py new file mode 100644 index 00000000..207508dc --- /dev/null +++ b/waflib/Tools/intltool.py @@ -0,0 +1,176 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +""" +Support for translation tools such as msgfmt and intltool + +Usage:: + + def configure(conf): + conf.load('gnu_dirs intltool') + + def build(bld): + # process the .po files into .gmo files, and install them in LOCALEDIR + bld(features='intltool_po', appname='myapp', podir='po', install_path="${LOCALEDIR}") + + # process an input file, substituting the translations from the po dir + bld( + features = "intltool_in", + podir = "../po", + flags = ["-d", "-q", "-u", "-c"], + source = 'kupfer.desktop.in', + install_path = "${DATADIR}/applications", + ) + +Usage of the :py:mod:`waflib.Tools.gnu_dirs` is recommended, but not obligatory. +""" + +import os, re +from waflib import Configure, TaskGen, Task, Utils, Runner, Options, Build, Logs +import waflib.Tools.ccroot +from waflib.TaskGen import feature, before_method +from waflib.Logs import error + +@before_method('process_source') +@feature('intltool_in') +def apply_intltool_in_f(self): + """ + Create tasks to translate files by intltool-merge:: + + def build(bld): + bld( + features = "intltool_in", + podir = "../po", + flags = ["-d", "-q", "-u", "-c"], + source = 'kupfer.desktop.in', + install_path = "${DATADIR}/applications", + ) + + :param podir: location of the .po files + :type podir: string + :param source: source files to process + :type source: list of string + :param flags: compilation flags ("-quc" by default) + :type flags: list of string + :param install_path: installation path + :type install_path: string + """ + try: self.meths.remove('process_source') + except ValueError: pass + + if not self.env.LOCALEDIR: + self.env.LOCALEDIR = self.env.PREFIX + '/share/locale' + + for i in self.to_list(self.source): + node = self.path.find_resource(i) + + podir = getattr(self, 'podir', 'po') + podirnode = self.path.find_dir(podir) + if not podirnode: + error("could not find the podir %r" % podir) + continue + + cache = getattr(self, 'intlcache', '.intlcache') + self.env['INTLCACHE'] = os.path.join(self.path.bldpath(), podir, cache) + self.env['INTLPODIR'] = podirnode.bldpath() + self.env['INTLFLAGS'] = getattr(self, 'flags', ['-q', '-u', '-c']) + + task = self.create_task('intltool', node, node.change_ext('')) + inst = getattr(self, 'install_path', '${LOCALEDIR}') + if inst: + self.bld.install_files(inst, task.outputs) + +@feature('intltool_po') +def apply_intltool_po(self): + """ + Create tasks to process po files:: + + def build(bld): + bld(features='intltool_po', appname='myapp', podir='po', install_path="${LOCALEDIR}") + + The relevant task generator arguments are: + + :param podir: directory of the .po files + :type podir: string + :param appname: name of the application + :type appname: string + :param install_path: installation directory + :type install_path: string + + The file LINGUAS must be present in the directory pointed by *podir* and list the translation files to process. + """ + try: self.meths.remove('process_source') + except ValueError: pass + + if not self.env.LOCALEDIR: + self.env.LOCALEDIR = self.env.PREFIX + '/share/locale' + + appname = getattr(self, 'appname', 'set_your_app_name') + podir = getattr(self, 'podir', '') + inst = getattr(self, 'install_path', '${LOCALEDIR}') + + linguas = self.path.find_node(os.path.join(podir, 'LINGUAS')) + if linguas: + # scan LINGUAS file for locales to process + file = open(linguas.abspath()) + langs = [] + for line in file.readlines(): + # ignore lines containing comments + if not line.startswith('#'): + langs += line.split() + file.close() + re_linguas = re.compile('[-a-zA-Z_@.]+') + for lang in langs: + # Make sure that we only process lines which contain locales + if re_linguas.match(lang): + node = self.path.find_resource(os.path.join(podir, re_linguas.match(lang).group() + '.po')) + task = self.create_task('po', node, node.change_ext('.mo')) + + if inst: + filename = task.outputs[0].name + (langname, ext) = os.path.splitext(filename) + inst_file = inst + os.sep + langname + os.sep + 'LC_MESSAGES' + os.sep + appname + '.mo' + self.bld.install_as(inst_file, task.outputs[0], chmod=getattr(self, 'chmod', Utils.O644), env=task.env) + + else: + Logs.pprint('RED', "Error no LINGUAS file found in po directory") + +class po(Task.Task): + """ + Compile .po files into .gmo files + """ + run_str = '${MSGFMT} -o ${TGT} ${SRC}' + color = 'BLUE' + +class intltool(Task.Task): + """ + Let intltool-merge translate an input file + """ + run_str = '${INTLTOOL} ${INTLFLAGS} ${INTLCACHE} ${INTLPODIR} ${SRC} ${TGT}' + color = 'BLUE' + +def configure(conf): + """ + Detect the program *msgfmt* and set *conf.env.MSGFMT*. + Detect the program *intltool-merge* and set *conf.env.INTLTOOL*. + It is possible to set INTLTOOL in the environment, but it must not have spaces in it:: + + $ INTLTOOL="/path/to/the program/intltool" waf configure + + If a C/C++ compiler is present, execute a compilation test to find the header *locale.h*. + """ + conf.find_program('msgfmt', var='MSGFMT') + conf.find_perl_program('intltool-merge', var='INTLTOOL') + + prefix = conf.env.PREFIX + datadir = conf.env.DATADIR + if not datadir: + datadir = os.path.join(prefix,'share') + + conf.define('LOCALEDIR', os.path.join(datadir, 'locale').replace('\\', '\\\\')) + conf.define('DATADIR', datadir.replace('\\', '\\\\')) + + if conf.env.CC or conf.env.CXX: + conf.check(header_name='locale.h') + diff --git a/waflib/Tools/irixcc.py b/waflib/Tools/irixcc.py new file mode 100644 index 00000000..f609359e --- /dev/null +++ b/waflib/Tools/irixcc.py @@ -0,0 +1,63 @@ +#! /usr/bin/env python +# imported from samba + +""" +compiler definition for irix/MIPSpro cc compiler +based on suncc.py from waf +""" + +import os +from waflib import Utils +from waflib.Tools import ccroot, ar +from waflib.Configure import conf + +@conf +def find_irixcc(conf): + v = conf.env + cc = None + if v['CC']: cc = v['CC'] + elif 'CC' in conf.environ: cc = conf.environ['CC'] + if not cc: cc = conf.find_program('cc', var='CC') + if not cc: conf.fatal('irixcc was not found') + cc = conf.cmd_to_list(cc) + + try: + conf.cmd_and_log(cc + ['-version']) + except: + conf.fatal('%r -version could not be executed' % cc) + + v['CC'] = cc + v['CC_NAME'] = 'irix' + +@conf +def irixcc_common_flags(conf): + v = conf.env + + v['CC_SRC_F'] = '' + v['CC_TGT_F'] = ['-c', '-o'] + v['CPPPATH_ST'] = '-I%s' + v['DEFINES_ST'] = '-D%s' + + # linker + if not v['LINK_CC']: v['LINK_CC'] = v['CC'] + v['CCLNK_SRC_F'] = '' + v['CCLNK_TGT_F'] = ['-o'] + + v['LIB_ST'] = '-l%s' # template for adding libs + v['LIBPATH_ST'] = '-L%s' # template for adding libpaths + v['STLIB_ST'] = '-l%s' + v['STLIBPATH_ST'] = '-L%s' + + v['cprogram_PATTERN'] = '%s' + v['cshlib_PATTERN'] = 'lib%s.so' + v['cstlib_PATTERN'] = 'lib%s.a' + +def configure(conf): + conf.find_irixcc() + conf.find_cpp() + conf.find_ar() + conf.irixcc_common_flags() + conf.cc_load_tools() + conf.cc_add_flags() + conf.link_add_flags() + diff --git a/waflib/Tools/javaw.py b/waflib/Tools/javaw.py new file mode 100644 index 00000000..61d4b7a5 --- /dev/null +++ b/waflib/Tools/javaw.py @@ -0,0 +1,427 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +""" +Java support + +Javac is one of the few compilers that behaves very badly: + +#. it outputs files where it wants to (-d is only for the package root) + +#. it recompiles files silently behind your back + +#. it outputs an undefined amount of files (inner classes) + +This tool uses the -verbose flag to track the java classes created. +Remember that the compilation can be performed using Jython[1] rather than regular Python. Instead of +running one of the following commands:: + + ./waf configure + python waf configure + +You would have to run:: + + java -jar /path/to/jython.jar waf configure + +[1] http://www.jython.org/ +""" + +import os, re +from waflib.Configure import conf +from waflib import TaskGen, Task, Utils, Options, Build, Errors, Node +from waflib.TaskGen import feature, before_method, after_method + +from waflib.Tools import ccroot +ccroot.USELIB_VARS['javac'] = set(['CLASSPATH', 'JAVACFLAGS']) + + +SOURCE_RE = '**/*.java' +JAR_RE = '**/*' +re_verbose = re.compile(r'^\[.*?\]\n*', re.M) +re_classes = re.compile(r'\[wrote (?:RegularFileObject\[)*(.*?\.class)\]') + +class_check_source = ''' +public class Test { + public static void main(String[] argv) { + Class lib; + if (argv.length < 1) { + System.err.println("Missing argument"); + System.exit(77); + } + try { + lib = Class.forName(argv[0]); + } catch (ClassNotFoundException e) { + System.err.println("ClassNotFoundException"); + System.exit(1); + } + lib = null; + System.exit(0); + } +} +''' + +@feature('javac') +@before_method('process_source') +def apply_java(self): + """ + Create a javac task for compiling *.java files*. There can be + only one javac task by task generator. + """ + Utils.def_attrs(self, jarname='', classpath='', + sourcepath='.', srcdir='.', + jar_mf_attributes={}, jar_mf_classpath=[]) + + nodes_lst = [] + + outdir = getattr(self, 'outdir', None) + if outdir: + if not isinstance(outdir, Node.Node): + outdir = self.path.get_bld().make_node(self.outdir) + else: + outdir = self.path.get_bld() + outdir.mkdir() + self.env['OUTDIR'] = outdir.abspath() + + self.javac_task = tsk = self.create_task('javac') + tmp = [] + + srcdir = getattr(self, 'srcdir', '') + if isinstance(srcdir, Node.Node): + srcdir = [srcdir] + for x in Utils.to_list(srcdir): + if isinstance(x, Node.Node): + y = x + else: + y = self.path.find_dir(x) + if not y: + self.bld.fatal('Could not find the folder %s from %s' % (x, self.path)) + tmp.append(y) + tsk.srcdir = tmp + + if getattr(self, 'compat', None): + tsk.env.append_value('JAVACFLAGS', ['-source', self.compat]) + + if hasattr(self, 'sourcepath'): + fold = [isinstance(x, Node.Node) and x or self.path.find_dir(x) for x in self.to_list(self.sourcepath)] + names = os.pathsep.join([x.srcpath() for x in fold]) + else: + names = [x.srcpath() for x in tsk.srcdir] + + if names: + tsk.env.append_value('JAVACFLAGS', ['-sourcepath', names]) + +@feature('javac') +@after_method('apply_java') +def use_javac_files(self): + """ + Process the *use* attribute referring to other java compilations + """ + lst = [] + self.uselib = self.to_list(getattr(self, 'uselib', [])) + names = self.to_list(getattr(self, 'use', [])) + get = self.bld.get_tgen_by_name + for x in names: + try: + y = get(x) + except: + self.uselib.append(x) + else: + y.post() + lst.append(y.jar_task.outputs[0].abspath()) + self.javac_task.set_run_after(y.jar_task) + + if lst: + self.env.append_value('CLASSPATH', lst) + +@feature('javac') +@after_method('apply_java', 'propagate_uselib_vars', 'use_javac_files') +def set_classpath(self): + """ + Set the CLASSPATH value on the *javac* task previously created. + """ + self.env.append_value('CLASSPATH', getattr(self, 'classpath', [])) + for x in self.tasks: + x.env.CLASSPATH = os.pathsep.join(self.env.CLASSPATH) + os.pathsep + +@feature('jar') +@after_method('apply_java', 'use_javac_files') +@before_method('process_source') +def jar_files(self): + """ + Create a jar task. There can be only one jar task by task generator. + """ + destfile = getattr(self, 'destfile', 'test.jar') + jaropts = getattr(self, 'jaropts', []) + manifest = getattr(self, 'manifest', None) + + basedir = getattr(self, 'basedir', None) + if basedir: + if not isinstance(self.basedir, Node.Node): + basedir = self.path.get_bld().make_node(basedir) + else: + basedir = self.path.get_bld() + if not basedir: + self.bld.fatal('Could not find the basedir %r for %r' % (self.basedir, self)) + + self.jar_task = tsk = self.create_task('jar_create') + if manifest: + jarcreate = getattr(self, 'jarcreate', 'cfm') + node = self.path.find_node(manifest) + tsk.dep_nodes.append(node) + jaropts.insert(0, node.abspath()) + else: + jarcreate = getattr(self, 'jarcreate', 'cf') + if not isinstance(destfile, Node.Node): + destfile = self.path.find_or_declare(destfile) + if not destfile: + self.bld.fatal('invalid destfile %r for %r' % (destfile, self)) + tsk.set_outputs(destfile) + tsk.basedir = basedir + + jaropts.append('-C') + jaropts.append(basedir.bldpath()) + jaropts.append('.') + + tsk.env['JAROPTS'] = jaropts + tsk.env['JARCREATE'] = jarcreate + + if getattr(self, 'javac_task', None): + tsk.set_run_after(self.javac_task) + +@feature('jar') +@after_method('jar_files') +def use_jar_files(self): + """ + Process the *use* attribute to set the build order on the + tasks created by another task generator. + """ + lst = [] + self.uselib = self.to_list(getattr(self, 'uselib', [])) + names = self.to_list(getattr(self, 'use', [])) + get = self.bld.get_tgen_by_name + for x in names: + try: + y = get(x) + except: + self.uselib.append(x) + else: + y.post() + self.jar_task.run_after.update(y.tasks) + +class jar_create(Task.Task): + """ + Create a jar file + """ + color = 'GREEN' + run_str = '${JAR} ${JARCREATE} ${TGT} ${JAROPTS}' + + def runnable_status(self): + """ + Wait for dependent tasks to be executed, then read the + files to update the list of inputs. + """ + for t in self.run_after: + if not t.hasrun: + return Task.ASK_LATER + if not self.inputs: + global JAR_RE + try: + self.inputs = [x for x in self.basedir.ant_glob(JAR_RE, remove=False) if id(x) != id(self.outputs[0])] + except: + raise Errors.WafError('Could not find the basedir %r for %r' % (self.basedir, self)) + return super(jar_create, self).runnable_status() + +class javac(Task.Task): + """ + Compile java files + """ + color = 'BLUE' + + nocache = True + """ + The .class files cannot be put into a cache at the moment + """ + + vars = ['CLASSPATH', 'JAVACFLAGS', 'JAVAC', 'OUTDIR'] + """ + The javac task will be executed again if the variables CLASSPATH, JAVACFLAGS, JAVAC or OUTDIR change. + """ + + def runnable_status(self): + """ + Wait for dependent tasks to be complete, then read the file system to find the input nodes. + """ + for t in self.run_after: + if not t.hasrun: + return Task.ASK_LATER + + if not self.inputs: + global SOURCE_RE + self.inputs = [] + for x in self.srcdir: + self.inputs.extend(x.ant_glob(SOURCE_RE, remove=False)) + return super(javac, self).runnable_status() + + def run(self): + """ + Execute the javac compiler + """ + env = self.env + gen = self.generator + bld = gen.bld + wd = bld.bldnode.abspath() + def to_list(xx): + if isinstance(xx, str): return [xx] + return xx + self.last_cmd = lst = [] + lst.extend(to_list(env['JAVAC'])) + lst.extend(['-classpath']) + lst.extend(to_list(env['CLASSPATH'])) + lst.extend(['-d']) + lst.extend(to_list(env['OUTDIR'])) + lst.extend(to_list(env['JAVACFLAGS'])) + lst.extend([a.path_from(bld.bldnode) for a in self.inputs]) + lst = [x for x in lst if x] + try: + self.out = self.generator.bld.cmd_and_log(lst, cwd=wd, env=env.env or None, output=0, quiet=0)[1] + except: + self.generator.bld.cmd_and_log(lst, cwd=wd, env=env.env or None) + + def post_run(self): + """ + The -verbose flags gives us the files created, so we have to parse the outputs + to update the signatures of the nodes created. + """ + for x in re_classes.findall(self.out): + if os.path.isabs(x): + n = self.generator.bld.root.find_node(x) + else: + n = self.generator.bld.bldnode.find_node(x) + if not n: + raise ValueError('cannot find %r in %r' % (x, self.generator.bld.bldnode.abspath())) + n.sig = Utils.h_file(n.abspath()) + self.generator.bld.task_sigs[self.uid()] = self.cache_sig + out = re_verbose.sub('', self.out).strip() + if out: + self.generator.bld.to_log(out + '\n') + +def configure(self): + """ + Detect the javac, java and jar programs + """ + # If JAVA_PATH is set, we prepend it to the path list + java_path = self.environ['PATH'].split(os.pathsep) + v = self.env + + if 'JAVA_HOME' in self.environ: + java_path = [os.path.join(self.environ['JAVA_HOME'], 'bin')] + java_path + self.env['JAVA_HOME'] = [self.environ['JAVA_HOME']] + + for x in 'javac java jar'.split(): + self.find_program(x, var=x.upper(), path_list=java_path) + self.env[x.upper()] = self.cmd_to_list(self.env[x.upper()]) + + if 'CLASSPATH' in self.environ: + v['CLASSPATH'] = self.environ['CLASSPATH'] + + if not v['JAR']: self.fatal('jar is required for making java packages') + if not v['JAVAC']: self.fatal('javac is required for compiling java classes') + v['JARCREATE'] = 'cf' # can use cvf + v['JAVACFLAGS'] = ['-verbose'] # required + +@conf +def check_java_class(self, classname, with_classpath=None): + """ + Check if the specified java class exists + + :param classname: class to check, like java.util.HashMap + :type classname: string + :param with_classpath: additional classpath to give + :type with_classpath: string + """ + + import shutil + + javatestdir = '.waf-javatest' + + classpath = javatestdir + if self.env['CLASSPATH']: + classpath += os.pathsep + self.env['CLASSPATH'] + if isinstance(with_classpath, str): + classpath += os.pathsep + with_classpath + + shutil.rmtree(javatestdir, True) + os.mkdir(javatestdir) + + java_file = open(os.path.join(javatestdir, 'Test.java'), 'w') + java_file.write(class_check_source) + java_file.close() + + # Compile the source + self.exec_command(self.env['JAVAC'] + [os.path.join(javatestdir, 'Test.java')], shell=False) + + # Try to run the app + cmd = self.env['JAVA'] + ['-cp', classpath, 'Test', classname] + self.to_log("%s\n" % str(cmd)) + found = self.exec_command(cmd, shell=False) + + self.msg('Checking for java class %s' % classname, not found) + + shutil.rmtree(javatestdir, True) + + return found + +@conf +def check_jni_headers(conf): + """ + Check for jni headers and libraries. On success the conf.env variables xxx_JAVA are added for use in C/C++ targets:: + + def options(opt): + opt.load('compiler_c') + + def configure(conf): + conf.load('compiler_c java') + conf.check_jni_headers() + + def build(bld): + bld.shlib(source='a.c', target='app', use='JAVA') + """ + + if not conf.env.CC_NAME and not conf.env.CXX_NAME: + conf.fatal('load a compiler first (gcc, g++, ..)') + + if not conf.env.JAVA_HOME: + conf.fatal('set JAVA_HOME in the system environment') + + # jni requires the jvm + javaHome = conf.env['JAVA_HOME'][0] + + dir = conf.root.find_dir(conf.env.JAVA_HOME[0] + '/include') + if dir is None: + conf.fatal('JAVA_HOME does not seem to be set properly') + + f = dir.ant_glob('**/(jni|jni_md).h') + incDirs = [x.parent.abspath() for x in f] + + dir = conf.root.find_dir(conf.env.JAVA_HOME[0]) + f = dir.ant_glob('**/*jvm.(so|dll|dylib)') + libDirs = [x.parent.abspath() for x in f] or [javaHome] + + # On windows, we need both the .dll and .lib to link. On my JDK, they are + # in different directories... + f = dir.ant_glob('**/*jvm.(lib)') + if f: + libDirs = [[x, y.parent.abspath()] for x in libDirs for y in f] + + for d in libDirs: + try: + conf.check(header_name='jni.h', define_name='HAVE_JNI_H', lib='jvm', + libpath=d, includes=incDirs, uselib_store='JAVA', uselib='JAVA') + except: + pass + else: + break + else: + conf.fatal('could not find lib jvm in %r (see config.log)' % libDirs) + diff --git a/waflib/Tools/kde4.py b/waflib/Tools/kde4.py new file mode 100644 index 00000000..700b14d5 --- /dev/null +++ b/waflib/Tools/kde4.py @@ -0,0 +1,90 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +""" +Support for the KDE4 libraries and msgfmt +""" + +import os, sys, re +from waflib import Options, TaskGen, Task, Utils +from waflib.TaskGen import feature, after_method + +@feature('msgfmt') +def apply_msgfmt(self): + """ + Process all languages to create .mo files and to install them:: + + def build(bld): + bld(features='msgfmt', langs='es de fr', appname='myapp', install_path='${KDE4_LOCALE_INSTALL_DIR}') + """ + for lang in self.to_list(self.langs): + node = self.path.find_resource(lang+'.po') + task = self.create_task('msgfmt', node, node.change_ext('.mo')) + + langname = lang.split('/') + langname = langname[-1] + + inst = getattr(self, 'install_path', '${KDE4_LOCALE_INSTALL_DIR}') + + self.bld.install_as( + inst + os.sep + langname + os.sep + 'LC_MESSAGES' + os.sep + getattr(self, 'appname', 'set_your_appname') + '.mo', + task.outputs[0], + chmod = getattr(self, 'chmod', Utils.O644)) + +class msgfmt(Task.Task): + """ + Transform .po files into .mo files + """ + color = 'BLUE' + run_str = '${MSGFMT} ${SRC} -o ${TGT}' + +def configure(self): + """ + Detect kde4-config and set various variables for the *use* system:: + + def options(opt): + opt.load('compiler_cxx kde4') + def configure(conf): + conf.load('compiler_cxx kde4') + def build(bld): + bld.program(source='main.c', target='app', use='KDECORE KIO KHTML') + """ + kdeconfig = self.find_program('kde4-config') + prefix = self.cmd_and_log('%s --prefix' % kdeconfig).strip() + fname = '%s/share/apps/cmake/modules/KDELibsDependencies.cmake' % prefix + try: os.stat(fname) + except OSError: + fname = '%s/share/kde4/apps/cmake/modules/KDELibsDependencies.cmake' % prefix + try: os.stat(fname) + except OSError: self.fatal('could not open %s' % fname) + + try: + txt = Utils.readf(fname) + except (OSError, IOError): + self.fatal('could not read %s' % fname) + + txt = txt.replace('\\\n', '\n') + fu = re.compile('#(.*)\n') + txt = fu.sub('', txt) + + setregexp = re.compile('([sS][eE][tT]\s*\()\s*([^\s]+)\s+\"([^"]+)\"\)') + found = setregexp.findall(txt) + + for (_, key, val) in found: + #print key, val + self.env[key] = val + + # well well, i could just write an interpreter for cmake files + self.env['LIB_KDECORE']= ['kdecore'] + self.env['LIB_KDEUI'] = ['kdeui'] + self.env['LIB_KIO'] = ['kio'] + self.env['LIB_KHTML'] = ['khtml'] + self.env['LIB_KPARTS'] = ['kparts'] + + self.env['LIBPATH_KDECORE'] = [self.env['KDE4_LIB_INSTALL_DIR']] + self.env['INCLUDES_KDECORE'] = [self.env['KDE4_INCLUDE_INSTALL_DIR']] + self.env.append_value('INCLUDES_KDECORE', [self.env['KDE4_INCLUDE_INSTALL_DIR']+ os.sep + 'KDE']) + + self.find_program('msgfmt', var='MSGFMT') + diff --git a/waflib/Tools/lua.py b/waflib/Tools/lua.py new file mode 100644 index 00000000..814f77d7 --- /dev/null +++ b/waflib/Tools/lua.py @@ -0,0 +1,38 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Sebastian Schlingmann, 2008 +# Thomas Nagy, 2008-2010 (ita) + +""" +Lua support. + +Compile *.lua* files into *.luac*:: + + def configure(conf): + conf.load('lua') + conf.env.LUADIR = '/usr/local/share/myapp/scripts/' + def build(bld): + bld(source='foo.lua') +""" + +from waflib.TaskGen import extension +from waflib import Task, Utils + +@extension('.lua') +def add_lua(self, node): + tsk = self.create_task('luac', node, node.change_ext('.luac')) + inst_to = getattr(self, 'install_path', self.env.LUADIR and '${LUADIR}' or None) + if inst_to: + self.bld.install_files(inst_to, tsk.outputs) + return tsk + +class luac(Task.Task): + run_str = '${LUAC} -s -o ${TGT} ${SRC}' + color = 'PINK' + +def configure(conf): + """ + Detect the luac compiler and set *conf.env.LUAC* + """ + conf.find_program('luac', var='LUAC') + diff --git a/waflib/Tools/msvc.py b/waflib/Tools/msvc.py new file mode 100644 index 00000000..04c72df9 --- /dev/null +++ b/waflib/Tools/msvc.py @@ -0,0 +1,934 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Carlos Rafael Giani, 2006 (dv) +# Tamas Pal, 2007 (folti) +# Nicolas Mercier, 2009 + +""" +Microsoft Visual C++/Intel C++ compiler support + +Usage:: + + $ waf configure --msvc_version="msvc 10.0,msvc 9.0" --msvc_target="x64" + +or:: + + def configure(conf): + conf.env['MSVC_VERSIONS'] = ['msvc 10.0', 'msvc 9.0', 'msvc 8.0', 'msvc 7.1', 'msvc 7.0', 'msvc 6.0', 'wsdk 7.0', 'intel 11', 'PocketPC 9.0', 'Smartphone 8.0'] + conf.env['MSVC_TARGETS'] = ['x64'] + conf.load('msvc') + +or:: + + def configure(conf): + conf.load('msvc', funs='no_autodetect') + conf.check_lib_msvc('gdi32') + conf.check_libs_msvc('kernel32 user32') + def build(bld): + tg = bld.program(source='main.c', target='app', use='KERNEL32 USER32 GDI32') + +Platforms and targets will be tested in the order they appear; +the first good configuration will be used. +Supported platforms: ia64, x64, x86, x86_amd64, x86_ia64 + +Compilers supported: + +* msvc => Visual Studio, versions 6.0 (VC 98, VC .NET 2002) to 10.0 (Visual Studio 2010) +* wsdk => Windows SDK, versions 6.0, 6.1, 7.0 +* icl => Intel compiler, versions 9,10,11 +* Smartphone => Compiler/SDK for Smartphone devices (armv4/v4i) +* PocketPC => Compiler/SDK for PocketPC devices (armv4/v4i) + +To use WAF in a VS2008 Make file project (see http://code.google.com/p/waf/issues/detail?id=894) +You may consider to set the environment variable "VS_UNICODE_OUTPUT" to nothing before calling waf. +So in your project settings use something like 'cmd.exe /C "set VS_UNICODE_OUTPUT=& set PYTHONUNBUFFERED=true & waf build"'. +cmd.exe /C "chcp 1252 & set PYTHONUNBUFFERED=true && set && waf configure" +Setting PYTHONUNBUFFERED gives the unbuffered output. +""" + +import os, sys, re, tempfile +try: + import _winreg +except: + try: + import winreg as _winreg + except: + _winreg = None + +from waflib import Utils, TaskGen, Runner, Configure, Task, Options +from waflib.Logs import debug, info, warn, error +from waflib.TaskGen import after_method, before_method, feature + +from waflib.Configure import conf +from waflib.Tools import ccroot, c, cxx, ar, winres + + +g_msvc_systemlibs = ''' +aclui activeds ad1 adptif adsiid advapi32 asycfilt authz bhsupp bits bufferoverflowu cabinet +cap certadm certidl ciuuid clusapi comctl32 comdlg32 comsupp comsuppd comsuppw comsuppwd comsvcs +credui crypt32 cryptnet cryptui d3d8thk daouuid dbgeng dbghelp dciman32 ddao35 ddao35d +ddao35u ddao35ud delayimp dhcpcsvc dhcpsapi dlcapi dnsapi dsprop dsuiext dtchelp +faultrep fcachdll fci fdi framedyd framedyn gdi32 gdiplus glauxglu32 gpedit gpmuuid +gtrts32w gtrtst32hlink htmlhelp httpapi icm32 icmui imagehlp imm32 iphlpapi iprop +kernel32 ksguid ksproxy ksuser libcmt libcmtd libcpmt libcpmtd loadperf lz32 mapi +mapi32 mgmtapi minidump mmc mobsync mpr mprapi mqoa mqrt msacm32 mscms mscoree +msdasc msimg32 msrating mstask msvcmrt msvcurt msvcurtd mswsock msxml2 mtx mtxdm +netapi32 nmapinmsupp npptools ntdsapi ntdsbcli ntmsapi ntquery odbc32 odbcbcp +odbccp32 oldnames ole32 oleacc oleaut32 oledb oledlgolepro32 opends60 opengl32 +osptk parser pdh penter pgobootrun pgort powrprof psapi ptrustm ptrustmd ptrustu +ptrustud qosname rasapi32 rasdlg rassapi resutils riched20 rpcndr rpcns4 rpcrt4 rtm +rtutils runtmchk scarddlg scrnsave scrnsavw secur32 sensapi setupapi sfc shell32 +shfolder shlwapi sisbkup snmpapi sporder srclient sti strsafe svcguid tapi32 thunk32 +traffic unicows url urlmon user32 userenv usp10 uuid uxtheme vcomp vcompd vdmdbg +version vfw32 wbemuuid webpost wiaguid wininet winmm winscard winspool winstrm +wintrust wldap32 wmiutils wow32 ws2_32 wsnmp32 wsock32 wst wtsapi32 xaswitch xolehlp +'''.split() +"""importlibs provided by MSVC/Platform SDK. Do NOT search them""" + +all_msvc_platforms = [ ('x64', 'amd64'), ('x86', 'x86'), ('ia64', 'ia64'), ('x86_amd64', 'amd64'), ('x86_ia64', 'ia64') ] +"""List of msvc platforms""" + +all_wince_platforms = [ ('armv4', 'arm'), ('armv4i', 'arm'), ('mipsii', 'mips'), ('mipsii_fp', 'mips'), ('mipsiv', 'mips'), ('mipsiv_fp', 'mips'), ('sh4', 'sh'), ('x86', 'cex86') ] +"""List of wince platforms""" + +all_icl_platforms = [ ('intel64', 'amd64'), ('em64t', 'amd64'), ('ia32', 'x86'), ('Itanium', 'ia64')] +"""List of icl platforms""" + +def options(opt): + opt.add_option('--msvc_version', type='string', help = 'msvc version, eg: "msvc 10.0,msvc 9.0"', default='') + opt.add_option('--msvc_targets', type='string', help = 'msvc targets, eg: "x64,arm"', default='') + +def setup_msvc(conf, versions): + platforms = getattr(Options.options, 'msvc_targets', '').split(',') + if platforms == ['']: + platforms=Utils.to_list(conf.env['MSVC_TARGETS']) or [i for i,j in all_msvc_platforms+all_icl_platforms+all_wince_platforms] + desired_versions = getattr(Options.options, 'msvc_version', '').split(',') + if desired_versions == ['']: + desired_versions = conf.env['MSVC_VERSIONS'] or [v for v,_ in versions][::-1] + versiondict = dict(versions) + + for version in desired_versions: + try: + targets = dict(versiondict [version]) + for target in platforms: + try: + arch,(p1,p2,p3) = targets[target] + compiler,revision = version.split() + return compiler,revision,p1,p2,p3 + except KeyError: continue + except KeyError: continue + conf.fatal('msvc: Impossible to find a valid architecture for building (in setup_msvc)') + +@conf +def get_msvc_version(conf, compiler, version, target, vcvars): + """ + Create a bat file to obtain the location of the libraries + + :param compiler: ? + :param version: ? + :target: ? + :vcvars: ? + :return: the location of msvc, the location of include dirs, and the library paths + :rtype: tuple of strings + """ + debug('msvc: get_msvc_version: %r %r %r', compiler, version, target) + batfile = conf.bldnode.make_node('waf-print-msvc.bat') + batfile.write("""@echo off +set INCLUDE= +set LIB= +call "%s" %s +echo PATH=%%PATH%% +echo INCLUDE=%%INCLUDE%% +echo LIB=%%LIB%% +""" % (vcvars,target)) + sout = conf.cmd_and_log(['cmd', '/E:on', '/V:on', '/C', batfile.abspath()]) + lines = sout.splitlines() + + if not lines[0]: lines=lines[1:] + for x in ('Setting environment', 'Setting SDK environment', 'Intel(R) C++ Compiler', 'Intel Parallel Studio'): + if lines[0].find(x) != -1: + break + else: + debug('msvc: get_msvc_version: %r %r %r -> not found', compiler, version, target) + conf.fatal('msvc: Impossible to find a valid architecture for building (in get_msvc_version)') + + for line in lines[1:]: + if line.startswith('PATH='): + path = line[5:] + MSVC_PATH = path.split(';') + elif line.startswith('INCLUDE='): + MSVC_INCDIR = [i for i in line[8:].split(';') if i] + elif line.startswith('LIB='): + MSVC_LIBDIR = [i for i in line[4:].split(';') if i] + + # Check if the compiler is usable at all. + # The detection may return 64-bit versions even on 32-bit systems, and these would fail to run. + env = {} + env.update(os.environ) + env.update(PATH = path) + compiler_name, linker_name, lib_name = _get_prog_names(conf, compiler) + cxx = conf.find_program(compiler_name, path_list=MSVC_PATH) + cxx = conf.cmd_to_list(cxx) + + # delete CL if exists. because it could contain parameters wich can change cl's behaviour rather catastrophically. + if 'CL' in env: + del(env['CL']) + + try: + try: + conf.cmd_and_log(cxx + ['/help'], env=env) + except Exception as e: + debug('msvc: get_msvc_version: %r %r %r -> failure' % (compiler, version, target)) + debug(str(e)) + conf.fatal('msvc: cannot run the compiler (in get_msvc_version)') + else: + debug('msvc: get_msvc_version: %r %r %r -> OK', compiler, version, target) + finally: + conf.env[compiler_name] = '' + + return (MSVC_PATH, MSVC_INCDIR, MSVC_LIBDIR) + +@conf +def gather_wsdk_versions(conf, versions): + """ + Use winreg to add the msvc versions to the input list + + :param versions: list to modify + :type versions: list + """ + version_pattern = re.compile('^v..?.?\...?.?') + try: + all_versions = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Microsoft\\Microsoft SDKs\\Windows') + except WindowsError: + try: + all_versions = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Microsoft SDKs\\Windows') + except WindowsError: + return + index = 0 + while 1: + try: + version = _winreg.EnumKey(all_versions, index) + except WindowsError: + break + index = index + 1 + if not version_pattern.match(version): + continue + try: + msvc_version = _winreg.OpenKey(all_versions, version) + path,type = _winreg.QueryValueEx(msvc_version,'InstallationFolder') + except WindowsError: + continue + if os.path.isfile(os.path.join(path, 'bin', 'SetEnv.cmd')): + targets = [] + for target,arch in all_msvc_platforms: + try: + targets.append((target, (arch, conf.get_msvc_version('wsdk', version, '/'+target, os.path.join(path, 'bin', 'SetEnv.cmd'))))) + except conf.errors.ConfigurationError: + pass + versions.append(('wsdk ' + version[1:], targets)) + +def gather_wince_supported_platforms(): + """ + Checks SmartPhones SDKs + + :param versions: list to modify + :type versions: list + """ + supported_wince_platforms = [] + try: + ce_sdk = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Microsoft\\Windows CE Tools\\SDKs') + except WindowsError: + try: + ce_sdk = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Microsoft\\Windows CE Tools\\SDKs') + except WindowsError: + ce_sdk = '' + if not ce_sdk: + return supported_wince_platforms + + ce_index = 0 + while 1: + try: + sdk_device = _winreg.EnumKey(ce_sdk, ce_index) + except WindowsError: + break + ce_index = ce_index + 1 + sdk = _winreg.OpenKey(ce_sdk, sdk_device) + try: + path,type = _winreg.QueryValueEx(sdk, 'SDKRootDir') + except WindowsError: + continue + path=str(path) + path,device = os.path.split(path) + if not device: + path,device = os.path.split(path) + for arch,compiler in all_wince_platforms: + platforms = [] + if os.path.isdir(os.path.join(path, device, 'Lib', arch)): + platforms.append((arch, compiler, os.path.join(path, device, 'Include', arch), os.path.join(path, device, 'Lib', arch))) + if platforms: + supported_wince_platforms.append((device, platforms)) + return supported_wince_platforms + +def gather_msvc_detected_versions(): + #Detected MSVC versions! + version_pattern = re.compile('^(\d\d?\.\d\d?)(Exp)?$') + detected_versions = [] + for vcver,vcvar in [('VCExpress','Exp'), ('VisualStudio','')]: + try: + prefix = 'SOFTWARE\\Wow6432node\\Microsoft\\'+vcver + all_versions = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, prefix) + except WindowsError: + try: + prefix = 'SOFTWARE\\Microsoft\\'+vcver + all_versions = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, prefix) + except WindowsError: + continue + + index = 0 + while 1: + try: + version = _winreg.EnumKey(all_versions, index) + except WindowsError: + break + index = index + 1 + match = version_pattern.match(version) + if not match: + continue + else: + versionnumber = float(match.group(1)) + detected_versions.append((versionnumber, version+vcvar, prefix+"\\"+version)) + def fun(tup): + return tup[0] + + try: + detected_versions.sort(key = fun) + except: + # old python sort + detected_versions.sort(lambda x,y: cmp(x[0], y[0])) + return detected_versions + +@conf +def gather_msvc_targets(conf, versions, version, vc_path): + #Looking for normal MSVC compilers! + targets = [] + if os.path.isfile(os.path.join(vc_path, 'vcvarsall.bat')): + for target,realtarget in all_msvc_platforms[::-1]: + try: + targets.append((target, (realtarget, conf.get_msvc_version('msvc', version, target, os.path.join(vc_path, 'vcvarsall.bat'))))) + except conf.errors.ConfigurationError: + pass + elif os.path.isfile(os.path.join(vc_path, 'Common7', 'Tools', 'vsvars32.bat')): + try: + targets.append(('x86', ('x86', conf.get_msvc_version('msvc', version, 'x86', os.path.join(vc_path, 'Common7', 'Tools', 'vsvars32.bat'))))) + except conf.errors.ConfigurationError: + pass + elif os.path.isfile(os.path.join(vc_path, 'Bin', 'vcvars32.bat')): + try: + targets.append(('x86', ('x86', conf.get_msvc_version('msvc', version, '', os.path.join(vc_path, 'Bin', 'vcvars32.bat'))))) + except conf.errors.ConfigurationError: + pass + versions.append(('msvc '+ version, targets)) + +@conf +def gather_wince_targets(conf, versions, version, vc_path, vsvars, supported_platforms): + #Looking for Win CE compilers! + for device,platforms in supported_platforms: + cetargets = [] + for platform,compiler,include,lib in platforms: + winCEpath = os.path.join(vc_path, 'ce') + if not os.path.isdir(winCEpath): + continue + try: + common_bindirs,_1,_2 = conf.get_msvc_version('msvc', version, 'x86', vsvars) + except conf.errors.ConfigurationError: + continue + if os.path.isdir(os.path.join(winCEpath, 'lib', platform)): + bindirs = [os.path.join(winCEpath, 'bin', compiler), os.path.join(winCEpath, 'bin', 'x86_'+compiler)] + common_bindirs + incdirs = [include, os.path.join(winCEpath, 'include'), os.path.join(winCEpath, 'atlmfc', 'include')] + libdirs = [lib, os.path.join(winCEpath, 'lib', platform), os.path.join(winCEpath, 'atlmfc', 'lib', platform)] + cetargets.append((platform, (platform, (bindirs,incdirs,libdirs)))) + if cetargets: + versions.append((device + ' ' + version, cetargets)) + +@conf +def gather_msvc_versions(conf, versions): + vc_paths = [] + for (v,version,reg) in gather_msvc_detected_versions(): + try: + try: + msvc_version = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, reg + "\\Setup\\VC") + except WindowsError: + msvc_version = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, reg + "\\Setup\\Microsoft Visual C++") + path,type = _winreg.QueryValueEx(msvc_version, 'ProductDir') + vc_paths.append((version, os.path.abspath(str(path)))) + except WindowsError: + continue + + wince_supported_platforms = gather_wince_supported_platforms() + + for version,vc_path in vc_paths: + vs_path = os.path.dirname(vc_path) + vsvars = os.path.join(vs_path, 'Common7', 'Tools', 'vsvars32.bat') + if wince_supported_platforms and os.path.isfile(vsvars): + conf.gather_wince_targets(versions, version, vc_path, vsvars, wince_supported_platforms) + + for version,vc_path in vc_paths: + vs_path = os.path.dirname(vc_path) + conf.gather_msvc_targets(versions, version, vc_path) + +@conf +def gather_icl_versions(conf, versions): + """ + Checks ICL compilers + + :param versions: list to modify + :type versions: list + """ + version_pattern = re.compile('^...?.?\....?.?') + try: + all_versions = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Wow6432node\\Intel\\Compilers\\C++') + except WindowsError: + try: + all_versions = _winreg.OpenKey(_winreg.HKEY_LOCAL_MACHINE, 'SOFTWARE\\Intel\\Compilers\\C++') + except WindowsError: + return + index = 0 + while 1: + try: + version = _winreg.EnumKey(all_versions, index) + except WindowsError: + break + index = index + 1 + if not version_pattern.match(version): + continue + targets = [] + for target,arch in all_icl_platforms: + try: + if target=='intel64': targetDir='EM64T_NATIVE' + else: targetDir=target + _winreg.OpenKey(all_versions,version+'\\'+targetDir) + icl_version=_winreg.OpenKey(all_versions,version) + path,type=_winreg.QueryValueEx(icl_version,'ProductDir') + if os.path.isfile(os.path.join(path,'bin','iclvars.bat')): + try: + targets.append((target,(arch,conf.get_msvc_version('intel',version,target,os.path.join(path,'bin','iclvars.bat'))))) + except conf.errors.ConfigurationError: + pass + except WindowsError: + pass + for target,arch in all_icl_platforms: + try: + icl_version = _winreg.OpenKey(all_versions, version+'\\'+target) + path,type = _winreg.QueryValueEx(icl_version,'ProductDir') + if os.path.isfile(os.path.join(path, 'bin', 'iclvars.bat')): + try: + targets.append((target, (arch, conf.get_msvc_version('intel', version, target, os.path.join(path, 'bin', 'iclvars.bat'))))) + except conf.errors.ConfigurationError: + pass + except WindowsError: + continue + major = version[0:2] + versions.append(('intel ' + major, targets)) + +@conf +def get_msvc_versions(conf): + """ + :return: list of compilers installed + :rtype: list of string + """ + if not conf.env['MSVC_INSTALLED_VERSIONS']: + lst = [] + conf.gather_icl_versions(lst) + conf.gather_wsdk_versions(lst) + conf.gather_msvc_versions(lst) + conf.env['MSVC_INSTALLED_VERSIONS'] = lst + return conf.env['MSVC_INSTALLED_VERSIONS'] + +@conf +def print_all_msvc_detected(conf): + """ + Print the contents of *conf.env.MSVC_INSTALLED_VERSIONS* + """ + for version,targets in conf.env['MSVC_INSTALLED_VERSIONS']: + info(version) + for target,l in targets: + info("\t"+target) + +@conf +def detect_msvc(conf): + versions = get_msvc_versions(conf) + return setup_msvc(conf, versions) + +@conf +def find_lt_names_msvc(self, libname, is_static=False): + """ + Win32/MSVC specific code to glean out information from libtool la files. + this function is not attached to the task_gen class + """ + lt_names=[ + 'lib%s.la' % libname, + '%s.la' % libname, + ] + + for path in self.env['LIBPATH']: + for la in lt_names: + laf=os.path.join(path,la) + dll=None + if os.path.exists(laf): + ltdict = Utils.read_la_file(laf) + lt_libdir=None + if ltdict.get('libdir', ''): + lt_libdir = ltdict['libdir'] + if not is_static and ltdict.get('library_names', ''): + dllnames=ltdict['library_names'].split() + dll=dllnames[0].lower() + dll=re.sub('\.dll$', '', dll) + return (lt_libdir, dll, False) + elif ltdict.get('old_library', ''): + olib=ltdict['old_library'] + if os.path.exists(os.path.join(path,olib)): + return (path, olib, True) + elif lt_libdir != '' and os.path.exists(os.path.join(lt_libdir,olib)): + return (lt_libdir, olib, True) + else: + return (None, olib, True) + else: + raise self.errors.WafError('invalid libtool object file: %s' % laf) + return (None, None, None) + +@conf +def libname_msvc(self, libname, is_static=False): + lib = libname.lower() + lib = re.sub('\.lib$','',lib) + + if lib in g_msvc_systemlibs: + return lib + + lib=re.sub('^lib','',lib) + + if lib == 'm': + return None + + (lt_path, lt_libname, lt_static) = self.find_lt_names_msvc(lib, is_static) + + if lt_path != None and lt_libname != None: + if lt_static == True: + # file existance check has been made by find_lt_names + return os.path.join(lt_path,lt_libname) + + if lt_path != None: + _libpaths=[lt_path] + self.env['LIBPATH'] + else: + _libpaths=self.env['LIBPATH'] + + static_libs=[ + 'lib%ss.lib' % lib, + 'lib%s.lib' % lib, + '%ss.lib' % lib, + '%s.lib' %lib, + ] + + dynamic_libs=[ + 'lib%s.dll.lib' % lib, + 'lib%s.dll.a' % lib, + '%s.dll.lib' % lib, + '%s.dll.a' % lib, + 'lib%s_d.lib' % lib, + '%s_d.lib' % lib, + '%s.lib' %lib, + ] + + libnames=static_libs + if not is_static: + libnames=dynamic_libs + static_libs + + for path in _libpaths: + for libn in libnames: + if os.path.exists(os.path.join(path, libn)): + debug('msvc: lib found: %s' % os.path.join(path,libn)) + return re.sub('\.lib$', '',libn) + + #if no lib can be found, just return the libname as msvc expects it + self.fatal("The library %r could not be found" % libname) + return re.sub('\.lib$', '', libname) + +@conf +def check_lib_msvc(self, libname, is_static=False, uselib_store=None): + """ + Ideally we should be able to place the lib in the right env var, either STLIB or LIB, + but we don't distinguish static libs from shared libs. + This is ok since msvc doesn't have any special linker flag to select static libs (no env['STLIB_MARKER']) + """ + libn = self.libname_msvc(libname, is_static) + + if not uselib_store: + uselib_store = libname.upper() + + if False and is_static: # disabled + self.env['STLIB_' + uselib_store] = [libn] + else: + self.env['LIB_' + uselib_store] = [libn] + +@conf +def check_libs_msvc(self, libnames, is_static=False): + for libname in Utils.to_list(libnames): + self.check_lib_msvc(libname, is_static) + +def configure(conf): + """ + Configuration methods to call for detecting msvc + """ + conf.autodetect() + conf.find_msvc() + conf.msvc_common_flags() + conf.cc_load_tools() + conf.cxx_load_tools() + conf.cc_add_flags() + conf.cxx_add_flags() + conf.link_add_flags() + conf.visual_studio_add_flags() + +@conf +def no_autodetect(conf): + conf.env.NO_MSVC_DETECT = 1 + configure(conf) + +@conf +def autodetect(conf): + v = conf.env + if v.NO_MSVC_DETECT: + return + compiler, version, path, includes, libdirs = conf.detect_msvc() + v['PATH'] = path + v['INCLUDES'] = includes + v['LIBPATH'] = libdirs + v['MSVC_COMPILER'] = compiler + try: + v['MSVC_VERSION'] = float(version) + except: + v['MSVC_VERSION'] = float(version[:-3]) + +def _get_prog_names(conf, compiler): + if compiler=='intel': + compiler_name = 'ICL' + linker_name = 'XILINK' + lib_name = 'XILIB' + else: + # assumes CL.exe + compiler_name = 'CL' + linker_name = 'LINK' + lib_name = 'LIB' + return compiler_name, linker_name, lib_name + +@conf +def find_msvc(conf): + """Due to path format limitations, limit operation only to native Win32. Yeah it sucks.""" + if sys.platform == 'cygwin': + conf.fatal('MSVC module does not work under cygwin Python!') + + # the autodetection is supposed to be performed before entering in this method + v = conf.env + path = v['PATH'] + compiler = v['MSVC_COMPILER'] + version = v['MSVC_VERSION'] + + compiler_name, linker_name, lib_name = _get_prog_names(conf, compiler) + v.MSVC_MANIFEST = (compiler == 'msvc' and version >= 8) or (compiler == 'wsdk' and version >= 6) or (compiler == 'intel' and version >= 11) + + # compiler + cxx = None + if v['CXX']: cxx = v['CXX'] + elif 'CXX' in conf.environ: cxx = conf.environ['CXX'] + cxx = conf.find_program(compiler_name, var='CXX', path_list=path) + cxx = conf.cmd_to_list(cxx) + + # before setting anything, check if the compiler is really msvc + env = dict(conf.environ) + if path: env.update(PATH = ';'.join(path)) + if not conf.cmd_and_log(cxx + ['/nologo', '/help'], env=env): + conf.fatal('the msvc compiler could not be identified') + + # c/c++ compiler + v['CC'] = v['CXX'] = cxx + v['CC_NAME'] = v['CXX_NAME'] = 'msvc' + + # linker + if not v['LINK_CXX']: + link = conf.find_program(linker_name, path_list=path) + if link: v['LINK_CXX'] = link + else: conf.fatal('%s was not found (linker)' % linker_name) + v['LINK'] = link + + if not v['LINK_CC']: + v['LINK_CC'] = v['LINK_CXX'] + + # staticlib linker + if not v['AR']: + stliblink = conf.find_program(lib_name, path_list=path, var='AR') + if not stliblink: return + v['ARFLAGS'] = ['/NOLOGO'] + + # manifest tool. Not required for VS 2003 and below. Must have for VS 2005 and later + if v.MSVC_MANIFEST: + mt = conf.find_program('MT', path_list=path, var='MT') + v['MTFLAGS'] = ['/NOLOGO'] + + conf.load('winres') + + if not conf.env['WINRC']: + warn('Resource compiler not found. Compiling resource file is disabled') + +@conf +def visual_studio_add_flags(self): + """visual studio flags found in the system environment""" + v = self.env + try: v.prepend_value('INCLUDES', self.environ['INCLUDE'].split(';')) # notice the 'S' + except: pass + try: v.prepend_value('LIBPATH', self.environ['LIB'].split(';')) + except: pass + +@conf +def msvc_common_flags(conf): + """ + Setup the flags required for executing the msvc compiler + + The default is to allow a static and a shared library having the same name in the same directory, the static one being prefixed by 'lib'. If you feel that this + is incorrect, just change the extension (issue #824):: + + bld.env.STLIB_ST = bld.env.SHLIB_ST = '%s.lib' + bld.stlib(..., name='libfoo') + bld.shlib(..., name='foo') + """ + v = conf.env + + v['DEST_BINFMT'] = 'pe' + v.append_value('CFLAGS', ['/nologo']) + v.append_value('CXXFLAGS', ['/nologo']) + v['DEFINES_ST'] = '/D%s' + + v['CC_SRC_F'] = '' + v['CC_TGT_F'] = ['/c', '/Fo'] + if v['MSVC_VERSION'] >= 8: + v['CC_TGT_F']= ['/FC'] + v['CC_TGT_F'] + v['CXX_SRC_F'] = '' + v['CXX_TGT_F'] = ['/c', '/Fo'] + if v['MSVC_VERSION'] >= 8: + v['CXX_TGT_F']= ['/FC'] + v['CXX_TGT_F'] + + v['CPPPATH_ST'] = '/I%s' # template for adding include paths + + v['AR_TGT_F'] = v['CCLNK_TGT_F'] = v['CXXLNK_TGT_F'] = '/OUT:' + + # Subsystem specific flags + v['CFLAGS_CONSOLE'] = v['CXXFLAGS_CONSOLE'] = ['/SUBSYSTEM:CONSOLE'] + v['CFLAGS_NATIVE'] = v['CXXFLAGS_NATIVE'] = ['/SUBSYSTEM:NATIVE'] + v['CFLAGS_POSIX'] = v['CXXFLAGS_POSIX'] = ['/SUBSYSTEM:POSIX'] + v['CFLAGS_WINDOWS'] = v['CXXFLAGS_WINDOWS'] = ['/SUBSYSTEM:WINDOWS'] + v['CFLAGS_WINDOWSCE'] = v['CXXFLAGS_WINDOWSCE'] = ['/SUBSYSTEM:WINDOWSCE'] + + # CRT specific flags + v['CFLAGS_CRT_MULTITHREADED'] = v['CXXFLAGS_CRT_MULTITHREADED'] = ['/MT'] + v['CFLAGS_CRT_MULTITHREADED_DLL'] = v['CXXFLAGS_CRT_MULTITHREADED_DLL'] = ['/MD'] + + v['CFLAGS_CRT_MULTITHREADED_DBG'] = v['CXXFLAGS_CRT_MULTITHREADED_DBG'] = ['/MTd'] + v['CFLAGS_CRT_MULTITHREADED_DLL_DBG'] = v['CXXFLAGS_CRT_MULTITHREADED_DLL_DBG'] = ['/MDd'] + + # linker + v['LIB_ST'] = '%s.lib' # template for adding shared libs + v['LIBPATH_ST'] = '/LIBPATH:%s' # template for adding libpaths + v['STLIB_ST'] = 'lib%s.lib' + v['STLIBPATH_ST'] = '/LIBPATH:%s' + + v.append_value('LINKFLAGS', ['/NOLOGO']) + if v['MSVC_MANIFEST']: + v.append_value('LINKFLAGS', ['/MANIFEST']) + + # shared library + v['CFLAGS_cshlib'] = [] + v['CXXFLAGS_cxxshlib'] = [] + v['LINKFLAGS_cshlib'] = v['LINKFLAGS_cxxshlib'] = ['/DLL'] + v['cshlib_PATTERN'] = v['cxxshlib_PATTERN'] = '%s.dll' + v['implib_PATTERN'] = '%s.lib' + v['IMPLIB_ST'] = '/IMPLIB:%s' + + # static library + v['LINKFLAGS_cstlib'] = [] + v['cstlib_PATTERN'] = v['cxxstlib_PATTERN'] = 'lib%s.lib' + + # program + v['cprogram_PATTERN'] = v['cxxprogram_PATTERN'] = '%s.exe' + + +####################################################################################################### +##### conf above, build below + +@after_method('apply_link') +@feature('c', 'cxx') +def apply_flags_msvc(self): + """ + Add additional flags implied by msvc, such as subsystems and pdb files:: + + def build(bld): + bld.stlib(source='main.c', target='bar', subsystem='gruik') + """ + if self.env.CC_NAME != 'msvc' or not getattr(self, 'link_task', None): + return + + is_static = isinstance(self.link_task, ccroot.stlink_task) + + subsystem = getattr(self, 'subsystem', '') + if subsystem: + subsystem = '/subsystem:%s' % subsystem + flags = is_static and 'ARFLAGS' or 'LINKFLAGS' + self.env.append_value(flags, subsystem) + + if not is_static: + for f in self.env.LINKFLAGS: + d = f.lower() + if d[1:] == 'debug': + pdbnode = self.link_task.outputs[0].change_ext('.pdb') + self.link_task.outputs.append(pdbnode) + + try: + self.install_task.source.append(pdbnode) + except AttributeError: + pass + + break + +# split the manifest file processing from the link task, like for the rc processing + +@feature('cprogram', 'cshlib', 'cxxprogram', 'cxxshlib') +@after_method('apply_link') +def apply_manifest(self): + """ + Special linker for MSVC with support for embedding manifests into DLL's + and executables compiled by Visual Studio 2005 or probably later. Without + the manifest file, the binaries are unusable. + See: http://msdn2.microsoft.com/en-us/library/ms235542(VS.80).aspx + """ + + if self.env.CC_NAME == 'msvc' and self.env.MSVC_MANIFEST and getattr(self, 'link_task', None): + out_node = self.link_task.outputs[0] + man_node = out_node.parent.find_or_declare(out_node.name + '.manifest') + self.link_task.outputs.append(man_node) + self.link_task.do_manifest = True + +def exec_mf(self): + """ + Create the manifest file + """ + env = self.env + mtool = env['MT'] + if not mtool: + return 0 + + self.do_manifest = False + + outfile = self.outputs[0].abspath() + + manifest = None + for out_node in self.outputs: + if out_node.name.endswith('.manifest'): + manifest = out_node.abspath() + break + if manifest is None: + # Should never get here. If we do, it means the manifest file was + # never added to the outputs list, thus we don't have a manifest file + # to embed, so we just return. + return 0 + + # embedding mode. Different for EXE's and DLL's. + # see: http://msdn2.microsoft.com/en-us/library/ms235591(VS.80).aspx + mode = '' + if 'cprogram' in self.generator.features or 'cxxprogram' in self.generator.features: + mode = '1' + elif 'cshlib' in self.generator.features or 'cxxshlib' in self.generator.features: + mode = '2' + + debug('msvc: embedding manifest in mode %r' % mode) + + lst = [] + lst.append(env['MT']) + lst.extend(Utils.to_list(env['MTFLAGS'])) + lst.extend(['-manifest', manifest]) + lst.append('-outputresource:%s;%s' % (outfile, mode)) + + lst = [lst] + return self.exec_command(*lst) + +def quote_response_command(self, flag): + if flag.find(' ') > -1: + for x in ('/LIBPATH:', '/IMPLIB:', '/OUT:', '/I'): + if flag.startswith(x): + flag = '%s"%s"' % (x, flag[len(x):]) + break + else: + flag = '"%s"' % flag + return flag + +def exec_response_command(self, cmd, **kw): + # not public yet + try: + tmp = None + if sys.platform.startswith('win') and isinstance(cmd, list) and len(' '.join(cmd)) >= 8192: + program = cmd[0] #unquoted program name, otherwise exec_command will fail + cmd = [self.quote_response_command(x) for x in cmd] + (fd, tmp) = tempfile.mkstemp() + os.write(fd, ' '.join(i.replace('\\', '\\\\') for i in cmd[1:]).encode()) + os.close(fd) + cmd = [program, '@' + tmp] + # no return here, that's on purpose + ret = self.generator.bld.exec_command(cmd, **kw) + finally: + if tmp: + try: + os.remove(tmp) + except: + pass # anti-virus and indexers can keep the files open -_- + return ret + +########## stupid evil command modification: concatenate the tokens /Fx, /doc, and /x: with the next token + +def exec_command_msvc(self, *k, **kw): + """ + Change the command-line execution for msvc programs. + Instead of quoting all the paths and keep using the shell, we can just join the options msvc is interested in + """ + if self.env['CC_NAME'] == 'msvc': + if isinstance(k[0], list): + lst = [] + carry = '' + for a in k[0]: + if a == '/Fo' or a == '/doc' or a[-1] == ':': + carry = a + else: + lst.append(carry + a) + carry = '' + k = [lst] + + if self.env['PATH']: + env = dict(os.environ) + env.update(PATH = ';'.join(self.env['PATH'])) + kw['env'] = env + + bld = self.generator.bld + try: + if not kw.get('cwd', None): + kw['cwd'] = bld.cwd + except AttributeError: + bld.cwd = kw['cwd'] = bld.variant_dir + + ret = self.exec_response_command(k[0], **kw) + if not ret and getattr(self, 'do_manifest', None): + ret = self.exec_mf() + return ret + +for k in 'c cxx cprogram cxxprogram cshlib cxxshlib cstlib cxxstlib'.split(): + cls = Task.classes.get(k, None) + if cls: + cls.exec_command = exec_command_msvc + cls.exec_response_command = exec_response_command + cls.quote_response_command = quote_response_command + cls.exec_mf = exec_mf + diff --git a/waflib/Tools/nasm.py b/waflib/Tools/nasm.py new file mode 100644 index 00000000..4ab775ab --- /dev/null +++ b/waflib/Tools/nasm.py @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2008-2010 (ita) + +""" +Nasm tool (asm processing) +""" + +import waflib.Tools.asm # leave this +from waflib.TaskGen import feature + +@feature('asm') +def apply_nasm_vars(self): + """provided for compatibility""" + self.env.append_value('ASFLAGS', self.to_list(getattr(self, 'nasm_flags', []))) + +def configure(conf): + """ + Detect nasm/yasm and set the variable *AS* + """ + nasm = conf.find_program(['nasm', 'yasm'], var='AS') + conf.env.AS_TGT_F = '-o' diff --git a/waflib/Tools/perl.py b/waflib/Tools/perl.py new file mode 100644 index 00000000..dfa00396 --- /dev/null +++ b/waflib/Tools/perl.py @@ -0,0 +1,153 @@ +#!/usr/bin/env python +# encoding: utf-8 +# andersg at 0x63.nu 2007 +# Thomas Nagy 2010 (ita) + +""" +Support for Perl extensions. A C/C++ compiler is required:: + + def options(opt): + opt.load('compiler_c perl') + def configure(conf): + conf.load('compiler_c perl') + conf.check_perl_version((5,6,0)) + conf.check_perl_ext_devel() + conf.check_perl_module('Cairo') + conf.check_perl_module('Devel::PPPort 4.89') + def build(bld): + bld( + features = 'c cshlib perlext', + source = 'Mytest.xs', + target = 'Mytest', + install_path = '${ARCHDIR_PERL}/auto') + bld.install_files('${ARCHDIR_PERL}', 'Mytest.pm') +""" + +import os +from waflib import Task, Options, Utils +from waflib.Configure import conf +from waflib.TaskGen import extension, feature, before_method + +@before_method('apply_incpaths', 'apply_link', 'propagate_uselib_vars') +@feature('perlext') +def init_perlext(self): + """ + Change the values of *cshlib_PATTERN* and *cxxshlib_PATTERN* to remove the + *lib* prefix from library names. + """ + self.uselib = self.to_list(getattr(self, 'uselib', [])) + if not 'PERLEXT' in self.uselib: self.uselib.append('PERLEXT') + self.env['cshlib_PATTERN'] = self.env['cxxshlib_PATTERN'] = self.env['perlext_PATTERN'] + +@extension('.xs') +def xsubpp_file(self, node): + """ + Create :py:class:`waflib.Tools.perl.xsubpp` tasks to process *.xs* files + """ + outnode = node.change_ext('.c') + self.create_task('xsubpp', node, outnode) + self.source.append(outnode) + +class xsubpp(Task.Task): + """ + Process *.xs* files + """ + run_str = '${PERL} ${XSUBPP} -noprototypes -typemap ${EXTUTILS_TYPEMAP} ${SRC} > ${TGT}' + color = 'BLUE' + ext_out = ['.h'] + +@conf +def check_perl_version(self, minver=None): + """ + Check if Perl is installed, and set the variable PERL. + """ + res = True + + cver = "" if minver is None else ".".join(map(str,minver)) + + self.start_msg('Checking for minimum perl version %s' % cver) + + perl = getattr(Options.options, 'perlbinary', None) + + if not perl: + perl = self.find_program('perl', var='PERL') + + if not perl: + self.end_msg("Perl not found", color="YELLOW") + return False + + self.env['PERL'] = perl + + version = self.cmd_and_log([perl, "-e", 'printf \"%vd\", $^V']) + if not version: + res = False + version = "Unknown" + elif not minver is None: + ver = tuple(map(int, version.split("."))) + if ver < minver: + res = False + + self.end_msg(version, color="GREEN" if res else "YELLOW") + return res + +@conf +def check_perl_module(self, module): + """ + Check if specified perlmodule is installed. + + The minimum version can be specified by specifying it after modulename + like this:: + + def configure(conf): + conf.check_perl_module("Some::Module 2.92") + """ + cmd = [self.env['PERL'], '-e', 'use %s' % module] + self.start_msg('perl module %s' % module) + try: + r = self.cmd_and_log(cmd) + except: + self.end_msg(False) + return None + self.end_msg(r or True) + return r + +@conf +def check_perl_ext_devel(self): + """ + Check for configuration needed to build perl extensions. + + Sets different xxx_PERLEXT variables in the environment. + + Also sets the ARCHDIR_PERL variable useful as installation path, + which can be overridden by ``--with-perl-archdir`` option. + """ + + env = self.env + perl = env.PERL + if not perl: + self.fatal('find perl first') + + def read_out(cmd): + return Utils.to_list(self.cmd_and_log(perl + cmd)) + + env['LINKFLAGS_PERLEXT'] = read_out(" -MConfig -e'print $Config{lddlflags}'") + env['INCLUDES_PERLEXT'] = read_out(" -MConfig -e'print \"$Config{archlib}/CORE\"'") + env['CFLAGS_PERLEXT'] = read_out(" -MConfig -e'print \"$Config{ccflags} $Config{cccdlflags}\"'") + + env['XSUBPP'] = read_out(" -MConfig -e'print \"$Config{privlib}/ExtUtils/xsubpp$Config{exe_ext}\"'") + env['EXTUTILS_TYPEMAP'] = read_out(" -MConfig -e'print \"$Config{privlib}/ExtUtils/typemap\"'") + + if not getattr(Options.options, 'perlarchdir', None): + env['ARCHDIR_PERL'] = self.cmd_and_log(perl + " -MConfig -e'print $Config{sitearch}'") + else: + env['ARCHDIR_PERL'] = getattr(Options.options, 'perlarchdir') + + env['perlext_PATTERN'] = '%s.' + self.cmd_and_log(perl + " -MConfig -e'print $Config{dlext}'") + +def options(opt): + """ + Add the ``--with-perl-archdir`` and ``--with-perl-binary`` command-line options. + """ + opt.add_option('--with-perl-binary', type='string', dest='perlbinary', help = 'Specify alternate perl binary', default=None) + opt.add_option('--with-perl-archdir', type='string', dest='perlarchdir', help = 'Specify directory where to install arch specific files', default=None) + diff --git a/waflib/Tools/python.py b/waflib/Tools/python.py new file mode 100644 index 00000000..3da9c894 --- /dev/null +++ b/waflib/Tools/python.py @@ -0,0 +1,490 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2007-2010 (ita) +# Gustavo Carneiro (gjc), 2007 + +""" +Support for Python, detect the headers and libraries and provide +*use* variables to link C/C++ programs against them:: + + def options(opt): + opt.load('compiler_c python') + def configure(conf): + conf.load('compiler_c python') + conf.check_python_version((2,4,2)) + conf.check_python_headers() + def build(bld): + bld.program(features='pyembed', source='a.c', target='myprog') + bld.shlib(features='pyext', source='b.c', target='mylib') +""" + +import os, sys +from waflib import Utils, Options, Errors +from waflib.Logs import debug, warn, info, error +from waflib.TaskGen import extension, before_method, after_method, feature +from waflib.Configure import conf + +FRAG = ''' +#include +#ifdef __cplusplus +extern "C" { +#endif + void Py_Initialize(void); + void Py_Finalize(void); +#ifdef __cplusplus +} +#endif +int main() +{ + Py_Initialize(); + Py_Finalize(); + return 0; +} +''' +""" +Piece of C/C++ code used in :py:func:`waflib.Tools.python.check_python_headers` +""" + +INST = ''' +import sys, py_compile +py_compile.compile(sys.argv[1], sys.argv[2], sys.argv[3]) +''' +""" +Piece of Python code used in :py:func:`waflib.Tools.python.install_pyfile` for installing python files +""" + +@extension('.py') +def process_py(self, node): + """ + Add a callback using :py:func:`waflib.Tools.python.install_pyfile` to install a python file + """ + try: + if not self.bld.is_install: + return + except: + return + + if not getattr(self, 'install_path', None): + self.install_path = '${PYTHONDIR}' + + # i wonder now why we wanted to do this after the build is over + # issue #901: people want to preserve the structure of installed files + def inst_py(ctx): + install_from = getattr(self, 'install_from', None) + if install_from: + install_from = self.path.find_dir(install_from) + install_pyfile(self, node, install_from) + self.bld.add_post_fun(inst_py) + +def install_pyfile(self, node, install_from=None): + """ + Execute the installation of a python file + + :param node: python file + :type node: :py:class:`waflib.Node.Node` + """ + + from_node = install_from or node.parent + tsk = self.bld.install_as(self.install_path + '/' + node.path_from(from_node), node, postpone=False) + path = tsk.get_install_path() + + if self.bld.is_install < 0: + info("+ removing byte compiled python files") + for x in 'co': + try: + os.remove(path + x) + except OSError: + pass + + if self.bld.is_install > 0: + try: + st1 = os.stat(path) + except: + error('The python file is missing, this should not happen') + + for x in ['c', 'o']: + do_inst = self.env['PY' + x.upper()] + try: + st2 = os.stat(path + x) + except OSError: + pass + else: + if st1.st_mtime <= st2.st_mtime: + do_inst = False + + if do_inst: + lst = (x == 'o') and [self.env['PYFLAGS_OPT']] or [] + (a, b, c) = (path, path + x, tsk.get_install_path(destdir=False) + x) + argv = self.env['PYTHON'] + lst + ['-c', INST, a, b, c] + info('+ byte compiling %r' % (path + x)) + ret = Utils.subprocess.Popen(argv).wait() + if ret: + raise Errors.WafError('py%s compilation failed %r' % (x, path)) + +@feature('py') +def feature_py(self): + """ + Dummy feature which does nothing + """ + pass + +@feature('pyext') +@before_method('propagate_uselib_vars', 'apply_link') +@after_method('apply_bundle') +def init_pyext(self): + """ + Change the values of *cshlib_PATTERN* and *cxxshlib_PATTERN* to remove the + *lib* prefix from library names. + """ + if not getattr(self, 'install_path', None): + self.install_path = '${PYTHONARCHDIR}' + self.uselib = self.to_list(getattr(self, 'uselib', [])) + if not 'PYEXT' in self.uselib: + self.uselib.append('PYEXT') + # override shlib_PATTERN set by the osx module + self.env['cshlib_PATTERN'] = self.env['cxxshlib_PATTERN'] = self.env['macbundle_PATTERN'] = self.env['pyext_PATTERN'] + +@feature('pyext') +@before_method('apply_link', 'apply_bundle') +def set_bundle(self): + if sys.platform.startswith('darwin'): + self.mac_bundle = True + +@before_method('propagate_uselib_vars') +@feature('pyembed') +def init_pyembed(self): + """ + Add the PYEMBED variable. + """ + self.uselib = self.to_list(getattr(self, 'uselib', [])) + if not 'PYEMBED' in self.uselib: + self.uselib.append('PYEMBED') + +@conf +def get_python_variables(conf, variables, imports=['import sys']): + """ + Execute a python interpreter to dump configuration variables + + :param variables: variables to print + :type variables: list of string + :param imports: one import by element + :type imports: list of string + :return: the variable values + :rtype: list of string + """ + program = list(imports) + program.append('') + for v in variables: + program.append("print(repr(%s))" % v) + os_env = dict(os.environ) + try: + del os_env['MACOSX_DEPLOYMENT_TARGET'] # see comments in the OSX tool + except KeyError: + pass + + try: + out = conf.cmd_and_log(conf.env.PYTHON + ['-c', '\n'.join(program)], env=os_env) + except Errors.WafError: + conf.fatal('The distutils module is unusable: install "python-devel"?') + return_values = [] + for s in out.split('\n'): + s = s.strip() + if not s: + continue + if s == 'None': + return_values.append(None) + elif s[0] == "'" and s[-1] == "'": + return_values.append(s[1:-1]) + elif s[0].isdigit(): + return_values.append(int(s)) + else: break + return return_values + +@conf +def check_python_headers(conf): + """ + Check for headers and libraries necessary to extend or embed python by using the module *distutils*. + On success the environment variables xxx_PYEXT and xxx_PYEMBED are added: + + * PYEXT: for compiling python extensions + * PYEMBED: for embedding a python interpreter + """ + + # FIXME rewrite + + if not conf.env['CC_NAME'] and not conf.env['CXX_NAME']: + conf.fatal('load a compiler first (gcc, g++, ..)') + + if not conf.env['PYTHON_VERSION']: + conf.check_python_version() + + env = conf.env + pybin = conf.env.PYTHON + if not pybin: + conf.fatal('could not find the python executable') + + v = 'prefix SO LDFLAGS LIBDIR LIBPL INCLUDEPY Py_ENABLE_SHARED MACOSX_DEPLOYMENT_TARGET LDSHARED CFLAGS'.split() + try: + lst = conf.get_python_variables(["get_config_var('%s') or ''" % x for x in v], + ['from distutils.sysconfig import get_config_var']) + except RuntimeError: + conf.fatal("Python development headers not found (-v for details).") + + vals = ['%s = %r' % (x, y) for (x, y) in zip(v, lst)] + conf.to_log("Configuration returned from %r:\n%r\n" % (pybin, '\n'.join(vals))) + + dct = dict(zip(v, lst)) + x = 'MACOSX_DEPLOYMENT_TARGET' + if dct[x]: + conf.env[x] = conf.environ[x] = dct[x] + + env['pyext_PATTERN'] = '%s' + dct['SO'] # not a mistake + + # Check for python libraries for embedding + + all_flags = dct['LDFLAGS'] + ' ' + dct['CFLAGS'] + conf.parse_flags(all_flags, 'PYEMBED') + + all_flags = dct['LDFLAGS'] + ' ' + dct['LDSHARED'] + ' ' + dct['CFLAGS'] + conf.parse_flags(all_flags, 'PYEXT') + + result = None + #name = 'python' + env['PYTHON_VERSION'] + + # TODO simplify this + for name in ('python' + env['PYTHON_VERSION'], 'python' + env['PYTHON_VERSION'].replace('.', '')): + + # LIBPATH_PYEMBED is already set; see if it works. + if not result and env['LIBPATH_PYEMBED']: + path = env['LIBPATH_PYEMBED'] + conf.to_log("\n\n# Trying default LIBPATH_PYEMBED: %r\n" % path) + result = conf.check(lib=name, uselib='PYEMBED', libpath=path, mandatory=False, msg='Checking for library %s in LIBPATH_PYEMBED' % name) + + if not result and dct['LIBDIR']: + path = [dct['LIBDIR']] + conf.to_log("\n\n# try again with -L$python_LIBDIR: %r\n" % path) + result = conf.check(lib=name, uselib='PYEMBED', libpath=path, mandatory=False, msg='Checking for library %s in LIBDIR' % name) + + if not result and dct['LIBPL']: + path = [dct['LIBPL']] + conf.to_log("\n\n# try again with -L$python_LIBPL (some systems don't install the python library in $prefix/lib)\n") + result = conf.check(lib=name, uselib='PYEMBED', libpath=path, mandatory=False, msg='Checking for library %s in python_LIBPL' % name) + + if not result: + path = [os.path.join(dct['prefix'], "libs")] + conf.to_log("\n\n# try again with -L$prefix/libs, and pythonXY name rather than pythonX.Y (win32)\n") + result = conf.check(lib=name, uselib='PYEMBED', libpath=path, mandatory=False, msg='Checking for library %s in $prefix/libs' % name) + + if result: + break # do not forget to set LIBPATH_PYEMBED + + if result: + env['LIBPATH_PYEMBED'] = path + env.append_value('LIB_PYEMBED', [name]) + else: + conf.to_log("\n\n### LIB NOT FOUND\n") + + # under certain conditions, python extensions must link to + # python libraries, not just python embedding programs. + if (Utils.is_win32 or sys.platform.startswith('os2') + or dct['Py_ENABLE_SHARED']): + env['LIBPATH_PYEXT'] = env['LIBPATH_PYEMBED'] + env['LIB_PYEXT'] = env['LIB_PYEMBED'] + + # We check that pythonX.Y-config exists, and if it exists we + # use it to get only the includes, else fall back to distutils. + num = '.'.join(env['PYTHON_VERSION'].split('.')[:2]) + conf.find_program(['python%s-config' % num, 'python-config-%s' % num, 'python%sm-config' % num], var='PYTHON_CONFIG', mandatory=False) + + includes = [] + if conf.env.PYTHON_CONFIG: + for incstr in conf.cmd_and_log([ conf.env.PYTHON_CONFIG, '--includes']).strip().split(): + # strip the -I or /I + if (incstr.startswith('-I') or incstr.startswith('/I')): + incstr = incstr[2:] + # append include path, unless already given + if incstr not in includes: + includes.append(incstr) + conf.to_log("Include path for Python extensions " + "(found via python-config --includes): %r\n" % (includes,)) + env['INCLUDES_PYEXT'] = includes + env['INCLUDES_PYEMBED'] = includes + else: + conf.to_log("Include path for Python extensions " + "(found via distutils module): %r\n" % (dct['INCLUDEPY'],)) + env['INCLUDES_PYEXT'] = [dct['INCLUDEPY']] + env['INCLUDES_PYEMBED'] = [dct['INCLUDEPY']] + + # Code using the Python API needs to be compiled with -fno-strict-aliasing + if env['CC_NAME'] == 'gcc': + env.append_value('CFLAGS_PYEMBED', ['-fno-strict-aliasing']) + env.append_value('CFLAGS_PYEXT', ['-fno-strict-aliasing']) + if env['CXX_NAME'] == 'gcc': + env.append_value('CXXFLAGS_PYEMBED', ['-fno-strict-aliasing']) + env.append_value('CXXFLAGS_PYEXT', ['-fno-strict-aliasing']) + + if env.CC_NAME == "msvc": + from distutils.msvccompiler import MSVCCompiler + dist_compiler = MSVCCompiler() + dist_compiler.initialize() + env.append_value('CFLAGS_PYEXT', dist_compiler.compile_options) + env.append_value('CXXFLAGS_PYEXT', dist_compiler.compile_options) + env.append_value('LINKFLAGS_PYEXT', dist_compiler.ldflags_shared) + + # See if it compiles + try: + conf.check(header_name='Python.h', define_name='HAVE_PYTHON_H', + uselib='PYEMBED', fragment=FRAG, + errmsg='Could not find the python development headers') + except conf.errors.ConfigurationError: + # python3.2, oh yeah + conf.check_cfg(path=conf.env.PYTHON_CONFIG, package='', uselib_store='PYEMBED', args=['--cflags', '--libs']) + conf.check(header_name='Python.h', define_name='HAVE_PYTHON_H', msg='Getting the python flags from python-config', + uselib='PYEMBED', fragment=FRAG, + errmsg='Could not find the python development headers elsewhere') + +@conf +def check_python_version(conf, minver=None): + """ + Check if the python interpreter is found matching a given minimum version. + minver should be a tuple, eg. to check for python >= 2.4.2 pass (2,4,2) as minver. + + If successful, PYTHON_VERSION is defined as 'MAJOR.MINOR' + (eg. '2.4') of the actual python version found, and PYTHONDIR is + defined, pointing to the site-packages directory appropriate for + this python version, where modules/packages/extensions should be + installed. + + :param minver: minimum version + :type minver: tuple of int + """ + assert minver is None or isinstance(minver, tuple) + pybin = conf.env['PYTHON'] + if not pybin: + conf.fatal('could not find the python executable') + + # Get python version string + cmd = pybin + ['-c', 'import sys\nfor x in sys.version_info: print(str(x))'] + debug('python: Running python command %r' % cmd) + lines = conf.cmd_and_log(cmd).split() + assert len(lines) == 5, "found %i lines, expected 5: %r" % (len(lines), lines) + pyver_tuple = (int(lines[0]), int(lines[1]), int(lines[2]), lines[3], int(lines[4])) + + # compare python version with the minimum required + result = (minver is None) or (pyver_tuple >= minver) + + if result: + # define useful environment variables + pyver = '.'.join([str(x) for x in pyver_tuple[:2]]) + conf.env['PYTHON_VERSION'] = pyver + + if 'PYTHONDIR' in conf.environ: + pydir = conf.environ['PYTHONDIR'] + else: + if Utils.is_win32: + (python_LIBDEST, pydir) = \ + conf.get_python_variables( + ["get_config_var('LIBDEST') or ''", + "get_python_lib(standard_lib=0, prefix=%r) or ''" % conf.env['PREFIX']], + ['from distutils.sysconfig import get_config_var, get_python_lib']) + else: + python_LIBDEST = None + (pydir,) = \ + conf.get_python_variables( + ["get_python_lib(standard_lib=0, prefix=%r) or ''" % conf.env['PREFIX']], + ['from distutils.sysconfig import get_python_lib']) + if python_LIBDEST is None: + if conf.env['LIBDIR']: + python_LIBDEST = os.path.join(conf.env['LIBDIR'], "python" + pyver) + else: + python_LIBDEST = os.path.join(conf.env['PREFIX'], "lib", "python" + pyver) + + + if 'PYTHONARCHDIR' in conf.environ: + pyarchdir = conf.environ['PYTHONARCHDIR'] + else: + (pyarchdir, ) = conf.get_python_variables( + ["get_python_lib(plat_specific=1, standard_lib=0, prefix=%r) or ''" % conf.env['PREFIX']], + ['from distutils.sysconfig import get_python_lib']) + if not pyarchdir: + pyarchdir = pydir + + if hasattr(conf, 'define'): # conf.define is added by the C tool, so may not exist + conf.define('PYTHONDIR', pydir) + conf.define('PYTHONARCHDIR', pyarchdir) + + conf.env['PYTHONDIR'] = pydir + conf.env['PYTHONARCHDIR'] = pyarchdir + + # Feedback + pyver_full = '.'.join(map(str, pyver_tuple[:3])) + if minver is None: + conf.msg('Checking for python version', pyver_full) + else: + minver_str = '.'.join(map(str, minver)) + conf.msg('Checking for python version', pyver_tuple, ">= %s" % (minver_str,) and 'GREEN' or 'YELLOW') + + if not result: + conf.fatal('The python version is too old, expecting %r' % (minver,)) + +PYTHON_MODULE_TEMPLATE = ''' +import %s +print(1) +''' + +@conf +def check_python_module(conf, module_name): + """ + Check if the selected python interpreter can import the given python module:: + + def configure(conf): + conf.check_python_module('pygccxml') + + :param module_name: module + :type module_name: string + """ + conf.start_msg('Python module %s' % module_name) + try: + conf.cmd_and_log(conf.env['PYTHON'] + ['-c', PYTHON_MODULE_TEMPLATE % module_name]) + except: + conf.end_msg(False) + conf.fatal('Could not find the python module %r' % module_name) + conf.end_msg(True) + +def configure(conf): + """ + Detect the python interpreter + """ + try: + conf.find_program('python', var='PYTHON') + except conf.errors.ConfigurationError: + warn("could not find a python executable, setting to sys.executable '%s'" % sys.executable) + conf.env.PYTHON = sys.executable + + if conf.env.PYTHON != sys.executable: + warn("python executable '%s' different from sys.executable '%s'" % (conf.env.PYTHON, sys.executable)) + conf.env.PYTHON = conf.cmd_to_list(conf.env.PYTHON) + + v = conf.env + v['PYCMD'] = '"import sys, py_compile;py_compile.compile(sys.argv[1], sys.argv[2])"' + v['PYFLAGS'] = '' + v['PYFLAGS_OPT'] = '-O' + + v['PYC'] = getattr(Options.options, 'pyc', 1) + v['PYO'] = getattr(Options.options, 'pyo', 1) + +def options(opt): + """ + Add the options ``--nopyc`` and ``--nopyo`` + """ + opt.add_option('--nopyc', + action='store_false', + default=1, + help = 'Do not install bytecode compiled .pyc files (configuration) [Default:install]', + dest = 'pyc') + opt.add_option('--nopyo', + action='store_false', + default=1, + help='Do not install optimised compiled .pyo files (configuration) [Default:install]', + dest='pyo') + diff --git a/waflib/Tools/qt4.py b/waflib/Tools/qt4.py new file mode 100644 index 00000000..4083e7a4 --- /dev/null +++ b/waflib/Tools/qt4.py @@ -0,0 +1,642 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +""" +Support for the Qt4 libraries and tools:: + + def options(opt): + opt.load('compiler_cxx qt4') + def configure(conf): + conf.load('compiler_cxx qt4') + conf.env.append_value('CXXFLAGS', ['-g']) # test + def build(bld): + bld( + features = 'qt4 cxx cxxprogram', + uselib = 'QTCORE QTGUI QTOPENGL QTSVG', + source = 'main.cpp textures.qrc aboutDialog.ui', + target = 'window', + ) + +The C++ files must include the .moc files, which is regarded as the +best practice (much faster compilations). This also implies that the +include paths have to be set properly. To have the include paths added +automatically, use the following:: + + from waflib.TaskGen import feature, before_method, after_method + @feature('cxx') + @after_method('process_source') + @before_method('apply_incpaths') + def add_includes_paths(self): + incs = set(self.to_list(getattr(self, 'includes', ''))) + for x in self.compiled_tasks: + incs.add(x.inputs[0].parent.path_from(self.path)) + self.includes = list(incs) + +Another tool provides a Qt processing that does not require the moc +includes. See http://code.google.com/p/waf/source/browse/trunk/playground/slow_qt/ +""" + +try: + from xml.sax import make_parser + from xml.sax.handler import ContentHandler +except ImportError: + has_xml = False + ContentHandler = object +else: + has_xml = True + +import os, sys +from waflib.Tools import c_preproc, cxx +from waflib import Task, Utils, Options, Errors +from waflib.TaskGen import feature, after_method, extension +from waflib.Configure import conf +from waflib.Logs import error + +MOC_H = ['.h', '.hpp', '.hxx', '.hh'] +""" +File extensions associated to the .moc files +""" + +EXT_RCC = ['.qrc'] +""" +File extension for the resource (.qrc) files +""" + +EXT_UI = ['.ui'] +""" +File extension for the user interface (.ui) files +""" + +EXT_QT4 = ['.cpp', '.cc', '.cxx', '.C'] +""" +File extensions of C++ files that may require a .moc processing +""" + +QT4_LIBS = "QtCore QtGui QtUiTools QtNetwork QtOpenGL QtSql QtSvg QtTest QtXml QtXmlPatterns QtWebKit Qt3Support QtHelp QtScript QtDeclarative" + +class qxx(cxx.cxx): + """ + Each C++ file can have zero or several .moc files to create. + They are known only when the files are scanned (preprocessor) + To avoid scanning the c++ files each time (parsing C/C++), the results + are retrieved from the task cache (bld.node_deps/bld.raw_deps). + The moc tasks are also created *dynamically* during the build. + """ + + def __init__(self, *k, **kw): + Task.Task.__init__(self, *k, **kw) + self.moc_done = 0 + + def scan(self): + """Re-use the C/C++ scanner, but remove the moc files from the dependencies""" + (nodes, names) = c_preproc.scan(self) + # for some reasons (variants) the moc node may end in the list of node deps + for x in nodes: + if x.name.endswith('.moc'): + nodes.remove(x) + names.append(x.path_from(self.inputs[0].parent.get_bld())) + return (nodes, names) + + def runnable_status(self): + """ + Compute the task signature to make sure the scanner was executed. Create the + moc tasks by using :py:meth:`waflib.Tools.qt4.qxx.add_moc_tasks` (if necessary), + then postpone the task execution (there is no need to recompute the task signature). + """ + if self.moc_done: + return Task.Task.runnable_status(self) + else: + for t in self.run_after: + if not t.hasrun: + return Task.ASK_LATER + self.add_moc_tasks() + return Task.Task.runnable_status(self) + + def add_moc_tasks(self): + """ + Create the moc tasks by looking in ``bld.raw_deps[self.uid()]`` + """ + node = self.inputs[0] + bld = self.generator.bld + + try: + # compute the signature once to know if there is a moc file to create + self.signature() + except KeyError: + # the moc file may be referenced somewhere else + pass + else: + # remove the signature, it must be recomputed with the moc task + delattr(self, 'cache_sig') + + moctasks=[] + mocfiles=[] + try: + tmp_lst = bld.raw_deps[self.uid()] + bld.raw_deps[self.uid()] = [] + except KeyError: + tmp_lst = [] + for d in tmp_lst: + if not d.endswith('.moc'): + continue + # paranoid check + if d in mocfiles: + error("paranoia owns") + continue + # process that base.moc only once + mocfiles.append(d) + + # find the extension - this search is done only once + + h_node = None + try: ext = Options.options.qt_header_ext.split() + except AttributeError: pass + if not ext: ext = MOC_H + + base2 = d[:-4] + for x in [node.parent] + self.generator.includes_nodes: + for e in ext: + h_node = x.find_node(base2 + e) + if h_node: + break + else: + continue + break + else: + raise Errors.WafError('no header found for %r which is a moc file' % d) + + # next time we will not search for the extension (look at the 'for' loop below) + m_node = h_node.change_ext('.moc') + bld.node_deps[(self.inputs[0].parent.abspath(), m_node.name)] = h_node + + # create the task + task = Task.classes['moc'](env=self.env, generator=self.generator) + task.set_inputs(h_node) + task.set_outputs(m_node) + + # direct injection in the build phase (safe because called from the main thread) + gen = bld.producer + gen.outstanding.insert(0, task) + gen.total += 1 + + moctasks.append(task) + + # remove raw deps except the moc files to save space (optimization) + tmp_lst = bld.raw_deps[self.uid()] = mocfiles + + # look at the file inputs, it is set right above + lst = bld.node_deps.get(self.uid(), ()) + for d in lst: + name = d.name + if name.endswith('.moc'): + task = Task.classes['moc'](env=self.env, generator=self.generator) + task.set_inputs(bld.node_deps[(self.inputs[0].parent.abspath(), name)]) # 1st element in a tuple + task.set_outputs(d) + + gen = bld.producer + gen.outstanding.insert(0, task) + gen.total += 1 + + moctasks.append(task) + + # simple scheduler dependency: run the moc task before others + self.run_after.update(set(moctasks)) + self.moc_done = 1 + + run = Task.classes['cxx'].__dict__['run'] + +class trans_update(Task.Task): + """Update a .ts files from a list of C++ files""" + run_str = '${QT_LUPDATE} ${SRC} -ts ${TGT}' + color = 'BLUE' +Task.update_outputs(trans_update) + +class XMLHandler(ContentHandler): + """ + Parser for *.qrc* files + """ + def __init__(self): + self.buf = [] + self.files = [] + def startElement(self, name, attrs): + if name == 'file': + self.buf = [] + def endElement(self, name): + if name == 'file': + self.files.append(str(''.join(self.buf))) + def characters(self, cars): + self.buf.append(cars) + +@extension(*EXT_RCC) +def create_rcc_task(self, node): + "Create rcc and cxx tasks for *.qrc* files" + rcnode = node.change_ext('_rc.cpp') + rcctask = self.create_task('rcc', node, rcnode) + cpptask = self.create_task('cxx', rcnode, rcnode.change_ext('.o')) + try: + self.compiled_tasks.append(cpptask) + except AttributeError: + self.compiled_tasks = [cpptask] + return cpptask + +@extension(*EXT_UI) +def create_uic_task(self, node): + "hook for uic tasks" + uictask = self.create_task('ui4', node) + uictask.outputs = [self.path.find_or_declare(self.env['ui_PATTERN'] % node.name[:-3])] + +@extension('.ts') +def add_lang(self, node): + """add all the .ts file into self.lang""" + self.lang = self.to_list(getattr(self, 'lang', [])) + [node] + +@feature('qt4') +@after_method('apply_link') +def apply_qt4(self): + """ + Add MOC_FLAGS which may be necessary for moc:: + + def build(bld): + bld.program(features='qt4', source='main.cpp', target='app', use='QTCORE') + + The additional parameters are: + + :param lang: list of translation files (\*.ts) to process + :type lang: list of :py:class:`waflib.Node.Node` or string without the .ts extension + :param update: whether to process the C++ files to update the \*.ts files (use **waf --translate**) + :type update: bool + :param langname: if given, transform the \*.ts files into a .qrc files to include in the binary file + :type langname: :py:class:`waflib.Node.Node` or string without the .qrc extension + """ + if getattr(self, 'lang', None): + qmtasks = [] + for x in self.to_list(self.lang): + if isinstance(x, str): + x = self.path.find_resource(x + '.ts') + qmtasks.append(self.create_task('ts2qm', x, x.change_ext('.qm'))) + + if getattr(self, 'update', None) and Options.options.trans_qt4: + cxxnodes = [a.inputs[0] for a in self.compiled_tasks] + [ + a.inputs[0] for a in self.tasks if getattr(a, 'inputs', None) and a.inputs[0].name.endswith('.ui')] + for x in qmtasks: + self.create_task('trans_update', cxxnodes, x.inputs) + + if getattr(self, 'langname', None): + qmnodes = [x.outputs[0] for x in qmtasks] + rcnode = self.langname + if isinstance(rcnode, str): + rcnode = self.path.find_or_declare(rcnode + '.qrc') + t = self.create_task('qm2rcc', qmnodes, rcnode) + k = create_rcc_task(self, t.outputs[0]) + self.link_task.inputs.append(k.outputs[0]) + + lst = [] + for flag in self.to_list(self.env['CXXFLAGS']): + if len(flag) < 2: continue + f = flag[0:2] + if f in ['-D', '-I', '/D', '/I']: + if (f[0] == '/'): + lst.append('-' + flag[1:]) + else: + lst.append(flag) + self.env['MOC_FLAGS'] = lst + +@extension(*EXT_QT4) +def cxx_hook(self, node): + """ + Re-map C++ file extensions to the :py:class:`waflib.Tools.qt4.qxx` task. + """ + return self.create_compiled_task('qxx', node) + +class rcc(Task.Task): + """ + Process *.qrc* files + """ + color = 'BLUE' + run_str = '${QT_RCC} -name ${SRC[0].name} ${SRC[0].abspath()} ${RCC_ST} -o ${TGT}' + ext_out = ['.h'] + + def scan(self): + """Parse the *.qrc* files""" + node = self.inputs[0] + parser = make_parser() + curHandler = XMLHandler() + parser.setContentHandler(curHandler) + fi = open(self.inputs[0].abspath()) + parser.parse(fi) + fi.close() + + nodes = [] + names = [] + root = self.inputs[0].parent + for x in curHandler.files: + nd = root.find_resource(x) + if nd: nodes.append(nd) + else: names.append(x) + return (nodes, names) + +class moc(Task.Task): + """ + Create *.moc* files + """ + color = 'BLUE' + run_str = '${QT_MOC} ${MOC_FLAGS} ${MOCCPPPATH_ST:INCPATHS} ${MOCDEFINES_ST:DEFINES} ${SRC} ${MOC_ST} ${TGT}' + +class ui4(Task.Task): + """ + Process *.ui* files + """ + color = 'BLUE' + run_str = '${QT_UIC} ${SRC} -o ${TGT}' + ext_out = ['.h'] + +class ts2qm(Task.Task): + """ + Create *.qm* files from *.ts* files + """ + color = 'BLUE' + run_str = '${QT_LRELEASE} ${QT_LRELEASE_FLAGS} ${SRC} -qm ${TGT}' + +class qm2rcc(Task.Task): + """ + Transform *.qm* files into *.rc* files + """ + color = 'BLUE' + after = 'ts2qm' + + def run(self): + """Create a qrc file including the inputs""" + txt = '\n'.join(['%s' % k.path_from(self.outputs[0].parent) for k in self.inputs]) + code = '\n\n%s\n\n' % txt + self.outputs[0].write(code) + +def configure(self): + """ + Besides the configuration options, the environment variable QT4_ROOT may be used + to give the location of the qt4 libraries (absolute path). + + The detection will use the program *pkg-config* through :py:func:`waflib.Tools.config_c.check_cfg` + """ + self.find_qt4_binaries() + self.set_qt4_libs_to_check() + self.find_qt4_libraries() + self.add_qt4_rpath() + self.simplify_qt4_libs() + +@conf +def find_qt4_binaries(self): + env = self.env + opt = Options.options + + qtdir = getattr(opt, 'qtdir', '') + qtbin = getattr(opt, 'qtbin', '') + + paths = [] + + if qtdir: + qtbin = os.path.join(qtdir, 'bin') + + # the qt directory has been given from QT4_ROOT - deduce the qt binary path + if not qtdir: + qtdir = self.environ.get('QT4_ROOT', '') + qtbin = os.path.join(qtdir, 'bin') + + if qtbin: + paths = [qtbin] + + # no qtdir, look in the path and in /usr/local/Trolltech + if not qtdir: + paths = os.environ.get('PATH', '').split(os.pathsep) + paths.append('/usr/share/qt4/bin/') + try: + lst = Utils.listdir('/usr/local/Trolltech/') + except OSError: + pass + else: + if lst: + lst.sort() + lst.reverse() + + # keep the highest version + qtdir = '/usr/local/Trolltech/%s/' % lst[0] + qtbin = os.path.join(qtdir, 'bin') + paths.append(qtbin) + + # at the end, try to find qmake in the paths given + # keep the one with the highest version + cand = None + prev_ver = ['4', '0', '0'] + for qmk in ['qmake-qt4', 'qmake4', 'qmake']: + try: + qmake = self.find_program(qmk, path_list=paths) + except self.errors.ConfigurationError: + pass + else: + try: + version = self.cmd_and_log([qmake, '-query', 'QT_VERSION']).strip() + except self.errors.ConfigurationError: + pass + else: + if version: + new_ver = version.split('.') + if new_ver > prev_ver: + cand = qmake + prev_ver = new_ver + if cand: + self.env.QMAKE = cand + else: + self.fatal('Could not find qmake for qt4') + + qtbin = self.cmd_and_log([self.env.QMAKE, '-query', 'QT_INSTALL_BINS']).strip() + os.sep + + def find_bin(lst, var): + for f in lst: + try: + ret = self.find_program(f, path_list=paths) + except self.errors.ConfigurationError: + pass + else: + env[var]=ret + break + + find_bin(['uic-qt3', 'uic3'], 'QT_UIC3') + find_bin(['uic-qt4', 'uic'], 'QT_UIC') + if not env['QT_UIC']: + self.fatal('cannot find the uic compiler for qt4') + + try: + uicver = self.cmd_and_log(env['QT_UIC'] + " -version 2>&1").strip() + except self.errors.ConfigurationError: + self.fatal('this uic compiler is for qt3, add uic for qt4 to your path') + uicver = uicver.replace('Qt User Interface Compiler ','').replace('User Interface Compiler for Qt', '') + self.msg('Checking for uic version', '%s' % uicver) + if uicver.find(' 3.') != -1: + self.fatal('this uic compiler is for qt3, add uic for qt4 to your path') + + find_bin(['moc-qt4', 'moc'], 'QT_MOC') + find_bin(['rcc'], 'QT_RCC') + find_bin(['lrelease-qt4', 'lrelease'], 'QT_LRELEASE') + find_bin(['lupdate-qt4', 'lupdate'], 'QT_LUPDATE') + + env['UIC3_ST']= '%s -o %s' + env['UIC_ST'] = '%s -o %s' + env['MOC_ST'] = '-o' + env['ui_PATTERN'] = 'ui_%s.h' + env['QT_LRELEASE_FLAGS'] = ['-silent'] + env.MOCCPPPATH_ST = '-I%s' + env.MOCDEFINES_ST = '-D%s' + +@conf +def find_qt4_libraries(self): + qtlibs = getattr(Options.options, 'qtlibs', '') + if not qtlibs: + try: + qtlibs = self.cmd_and_log([self.env.QMAKE, '-query', 'QT_INSTALL_LIBS']).strip() + except Errors.WafError: + qtdir = self.cmd_and_log([self.env.QMAKE, '-query', 'QT_INSTALL_PREFIX']).strip() + os.sep + qtlibs = os.path.join(qtdir, 'lib') + self.msg('Found the Qt4 libraries in', qtlibs) + + qtincludes = self.cmd_and_log([self.env.QMAKE, '-query', 'QT_INSTALL_HEADERS']).strip() + env = self.env + if not 'PKG_CONFIG_PATH' in os.environ: + os.environ['PKG_CONFIG_PATH'] = '%s:%s/pkgconfig:/usr/lib/qt4/lib/pkgconfig:/opt/qt4/lib/pkgconfig:/usr/lib/qt4/lib:/opt/qt4/lib' % (qtlibs, qtlibs) + + try: + self.check_cfg(atleast_pkgconfig_version='0.1') + except self.errors.ConfigurationError: + for i in self.qt4_vars: + uselib = i.upper() + if sys.platform == "darwin": + # Since at least qt 4.7.3 each library locates in separate directory + frameworkName = i + ".framework" + qtDynamicLib = os.path.join(qtlibs, frameworkName, i) + if os.path.exists(qtDynamicLib): + env.append_unique('FRAMEWORK_' + uselib, i) + self.msg('Checking for %s' % i, qtDynamicLib, 'GREEN') + else: + self.msg('Checking for %s' % i, False, 'YELLOW') + env.append_unique('INCLUDES_' + uselib, os.path.join(qtlibs, frameworkName, 'Headers')) + elif sys.platform != "win32": + qtDynamicLib = os.path.join(qtlibs, "lib" + i + ".so") + qtStaticLib = os.path.join(qtlibs, "lib" + i + ".a") + if os.path.exists(qtDynamicLib): + env.append_unique('LIB_' + uselib, i) + self.msg('Checking for %s' % i, qtDynamicLib, 'GREEN') + elif os.path.exists(qtStaticLib): + env.append_unique('LIB_' + uselib, i) + self.msg('Checking for %s' % i, qtStaticLib, 'GREEN') + else: + self.msg('Checking for %s' % i, False, 'YELLOW') + + env.append_unique('LIBPATH_' + uselib, qtlibs) + env.append_unique('INCLUDES_' + uselib, qtincludes) + env.append_unique('INCLUDES_' + uselib, os.path.join(qtincludes, i)) + else: + # Release library names are like QtCore4 + for k in ("lib%s.a", "lib%s4.a", "%s.lib", "%s4.lib"): + lib = os.path.join(qtlibs, k % i) + if os.path.exists(lib): + env.append_unique('LIB_' + uselib, i + k[k.find("%s") + 2 : k.find('.')]) + self.msg('Checking for %s' % i, lib, 'GREEN') + break + else: + self.msg('Checking for %s' % i, False, 'YELLOW') + + env.append_unique('LIBPATH_' + uselib, qtlibs) + env.append_unique('INCLUDES_' + uselib, qtincludes) + env.append_unique('INCLUDES_' + uselib, os.path.join(qtincludes, i)) + + # Debug library names are like QtCore4d + uselib = i.upper() + "_debug" + for k in ("lib%sd.a", "lib%sd4.a", "%sd.lib", "%sd4.lib"): + lib = os.path.join(qtlibs, k % i) + if os.path.exists(lib): + env.append_unique('LIB_' + uselib, i + k[k.find("%s") + 2 : k.find('.')]) + self.msg('Checking for %s' % i, lib, 'GREEN') + break + else: + self.msg('Checking for %s' % i, False, 'YELLOW') + + env.append_unique('LIBPATH_' + uselib, qtlibs) + env.append_unique('INCLUDES_' + uselib, qtincludes) + env.append_unique('INCLUDES_' + uselib, os.path.join(qtincludes, i)) + else: + for i in self.qt4_vars_debug + self.qt4_vars: + self.check_cfg(package=i, args='--cflags --libs', mandatory=False) + +@conf +def simplify_qt4_libs(self): + # the libpaths make really long command-lines + # remove the qtcore ones from qtgui, etc + env = self.env + def process_lib(vars_, coreval): + for d in vars_: + var = d.upper() + if var == 'QTCORE': + continue + + value = env['LIBPATH_'+var] + if value: + core = env[coreval] + accu = [] + for lib in value: + if lib in core: + continue + accu.append(lib) + env['LIBPATH_'+var] = accu + + process_lib(self.qt4_vars, 'LIBPATH_QTCORE') + process_lib(self.qt4_vars_debug, 'LIBPATH_QTCORE_DEBUG') + +@conf +def add_qt4_rpath(self): + # rpath if wanted + env = self.env + if Options.options.want_rpath: + def process_rpath(vars_, coreval): + for d in vars_: + var = d.upper() + value = env['LIBPATH_'+var] + if value: + core = env[coreval] + accu = [] + for lib in value: + if var != 'QTCORE': + if lib in core: + continue + accu.append('-Wl,--rpath='+lib) + env['RPATH_'+var] = accu + process_rpath(self.qt4_vars, 'LIBPATH_QTCORE') + process_rpath(self.qt4_vars_debug, 'LIBPATH_QTCORE_DEBUG') + +@conf +def set_qt4_libs_to_check(self): + if not hasattr(self, 'qt4_vars'): + self.qt4_vars = QT4_LIBS + self.qt4_vars = Utils.to_list(self.qt4_vars) + if not hasattr(self, 'qt4_vars_debug'): + self.qt4_vars_debug = [a + '_debug' for a in self.qt4_vars] + self.qt4_vars_debug = Utils.to_list(self.qt4_vars_debug) + +def options(opt): + """ + Command-line options + """ + opt.add_option('--want-rpath', action='store_true', default=False, dest='want_rpath', help='enable the rpath for qt libraries') + + opt.add_option('--header-ext', + type='string', + default='', + help='header extension for moc files', + dest='qt_header_ext') + + for i in 'qtdir qtbin qtlibs'.split(): + opt.add_option('--'+i, type='string', default='', dest=i) + + if sys.platform == "darwin": + opt.add_option('--no-qt4-framework', action="store_false", help='do not use the framework version of Qt4 in OS X', dest='use_qt4_osxframework',default=True) + + opt.add_option('--translate', action="store_true", help="collect translation strings", dest="trans_qt4", default=False) + diff --git a/waflib/Tools/ruby.py b/waflib/Tools/ruby.py new file mode 100644 index 00000000..4965b018 --- /dev/null +++ b/waflib/Tools/ruby.py @@ -0,0 +1,193 @@ +#!/usr/bin/env python +# encoding: utf-8 +# daniel.svensson at purplescout.se 2008 +# Thomas Nagy 2010 (ita) + +""" +Support for Ruby extensions. A C/C++ compiler is required:: + + def options(opt): + opt.load('compiler_c ruby') + def configure(conf): + conf.load('compiler_c ruby') + conf.check_ruby_version((1,8,0)) + conf.check_ruby_ext_devel() + conf.check_ruby_module('libxml') + def build(bld): + bld( + features = 'c cshlib rubyext', + source = 'rb_mytest.c', + target = 'mytest_ext', + install_path = '${ARCHDIR_RUBY}') + bld.install_files('${LIBDIR_RUBY}', 'Mytest.rb') +""" + +import os +from waflib import Task, Options, Utils +from waflib.TaskGen import before_method, feature, after_method, Task, extension +from waflib.Configure import conf + +@feature('rubyext') +@before_method('apply_incpaths', 'apply_lib_vars', 'apply_bundle', 'apply_link') +def init_rubyext(self): + """ + Add required variables for ruby extensions + """ + self.install_path = '${ARCHDIR_RUBY}' + self.uselib = self.to_list(getattr(self, 'uselib', '')) + if not 'RUBY' in self.uselib: + self.uselib.append('RUBY') + if not 'RUBYEXT' in self.uselib: + self.uselib.append('RUBYEXT') + +@feature('rubyext') +@before_method('apply_link', 'propagate_uselib') +def apply_ruby_so_name(self): + """ + Strip the *lib* prefix from ruby extensions + """ + self.env['cshlib_PATTERN'] = self.env['cxxshlib_PATTERN'] = self.env['rubyext_PATTERN'] + +@conf +def check_ruby_version(self, minver=()): + """ + Checks if ruby is installed. + If installed the variable RUBY will be set in environment. + The ruby binary can be overridden by ``--with-ruby-binary`` command-line option. + """ + + if Options.options.rubybinary: + self.env.RUBY = Options.options.rubybinary + else: + self.find_program('ruby', var='RUBY') + + ruby = self.env.RUBY + + try: + version = self.cmd_and_log([ruby, '-e', 'puts defined?(VERSION) ? VERSION : RUBY_VERSION']).strip() + except: + self.fatal('could not determine ruby version') + self.env.RUBY_VERSION = version + + try: + ver = tuple(map(int, version.split("."))) + except: + self.fatal('unsupported ruby version %r' % version) + + cver = '' + if minver: + if ver < minver: + self.fatal('ruby is too old %r' % ver) + cver = '.'.join([str(x) for x in minver]) + else: + cver = ver + + self.msg('Checking for ruby version %s' % str(minver or ''), cver) + +@conf +def check_ruby_ext_devel(self): + """ + Check if a ruby extension can be created + """ + if not self.env.RUBY: + self.fatal('ruby detection is required first') + + if not self.env.CC_NAME and not self.env.CXX_NAME: + self.fatal('load a c/c++ compiler first') + + version = tuple(map(int, self.env.RUBY_VERSION.split("."))) + + def read_out(cmd): + return Utils.to_list(self.cmd_and_log([self.env.RUBY, '-rrbconfig', '-e', cmd])) + + def read_config(key): + return read_out('puts Config::CONFIG[%r]' % key) + + ruby = self.env['RUBY'] + archdir = read_config('archdir') + cpppath = archdir + + if version >= (1, 9, 0): + ruby_hdrdir = read_config('rubyhdrdir') + cpppath += ruby_hdrdir + cpppath += [os.path.join(ruby_hdrdir[0], read_config('arch')[0])] + + self.check(header_name='ruby.h', includes=cpppath, errmsg='could not find ruby header file') + + self.env.LIBPATH_RUBYEXT = read_config('libdir') + self.env.LIBPATH_RUBYEXT += archdir + self.env.INCLUDES_RUBYEXT = cpppath + self.env.CFLAGS_RUBYEXT = read_config('CCDLFLAGS') + self.env.rubyext_PATTERN = '%s.' + read_config('DLEXT')[0] + + # ok this is really stupid, but the command and flags are combined. + # so we try to find the first argument... + flags = read_config('LDSHARED') + while flags and flags[0][0] != '-': + flags = flags[1:] + + # we also want to strip out the deprecated ppc flags + if len(flags) > 1 and flags[1] == "ppc": + flags = flags[2:] + + self.env.LINKFLAGS_RUBYEXT = flags + self.env.LINKFLAGS_RUBYEXT += read_config('LIBS') + self.env.LINKFLAGS_RUBYEXT += read_config('LIBRUBYARG_SHARED') + + if Options.options.rubyarchdir: + self.env.ARCHDIR_RUBY = Options.options.rubyarchdir + else: + self.env.ARCHDIR_RUBY = read_config('sitearchdir')[0] + + if Options.options.rubylibdir: + self.env.LIBDIR_RUBY = Options.options.rubylibdir + else: + self.env.LIBDIR_RUBY = read_config('sitelibdir')[0] + +@conf +def check_ruby_module(self, module_name): + """ + Check if the selected ruby interpreter can require the given ruby module:: + + def configure(conf): + conf.check_ruby_module('libxml') + + :param module_name: module + :type module_name: string + """ + self.start_msg('Ruby module %s' % module_name) + try: + self.cmd_and_log([self.env['RUBY'], '-e', 'require \'%s\';puts 1' % module_name]) + except: + self.end_msg(False) + self.fatal('Could not find the ruby module %r' % module_name) + self.end_msg(True) + +@extension('.rb') +def process(self, node): + tsk = self.create_task('run_ruby', node) + +class run_ruby(Task.Task): + """ + Task to run ruby files detected by file extension .rb:: + + def options(opt): + opt.load('ruby') + + def configure(ctx): + ctx.check_ruby_version() + + def build(bld): + bld.env['RBFLAGS'] = '-e puts "hello world"' + bld(source='a_ruby_file.rb') + """ + run_str = '${RUBY} ${RBFLAGS} -I ${SRC[0].parent.abspath()} ${SRC}' + +def options(opt): + """ + Add the ``--with-ruby-archdir``, ``--with-ruby-libdir`` and ``--with-ruby-binary`` options + """ + opt.add_option('--with-ruby-archdir', type='string', dest='rubyarchdir', help='Specify directory where to install arch specific files') + opt.add_option('--with-ruby-libdir', type='string', dest='rubylibdir', help='Specify alternate ruby library path') + opt.add_option('--with-ruby-binary', type='string', dest='rubybinary', help='Specify alternate ruby binary') + diff --git a/waflib/Tools/suncc.py b/waflib/Tools/suncc.py new file mode 100644 index 00000000..be5b1512 --- /dev/null +++ b/waflib/Tools/suncc.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) +# Ralf Habacker, 2006 (rh) + +import os +from waflib import Utils +from waflib.Tools import ccroot, ar +from waflib.Configure import conf + +@conf +def find_scc(conf): + """ + Detect the Sun C compiler + """ + v = conf.env + cc = None + if v['CC']: cc = v['CC'] + elif 'CC' in conf.environ: cc = conf.environ['CC'] + if not cc: cc = conf.find_program('cc', var='CC') + if not cc: conf.fatal('Could not find a Sun C compiler') + cc = conf.cmd_to_list(cc) + + try: + conf.cmd_and_log(cc + ['-flags']) + except: + conf.fatal('%r is not a Sun compiler' % cc) + + v['CC'] = cc + v['CC_NAME'] = 'sun' + +@conf +def scc_common_flags(conf): + """ + Flags required for executing the sun C compiler + """ + v = conf.env + + v['CC_SRC_F'] = [] + v['CC_TGT_F'] = ['-c', '-o'] + + # linker + if not v['LINK_CC']: v['LINK_CC'] = v['CC'] + v['CCLNK_SRC_F'] = '' + v['CCLNK_TGT_F'] = ['-o'] + v['CPPPATH_ST'] = '-I%s' + v['DEFINES_ST'] = '-D%s' + + v['LIB_ST'] = '-l%s' # template for adding libs + v['LIBPATH_ST'] = '-L%s' # template for adding libpaths + v['STLIB_ST'] = '-l%s' + v['STLIBPATH_ST'] = '-L%s' + + v['SONAME_ST'] = '-Wl,-h,%s' + v['SHLIB_MARKER'] = '-Bdynamic' + v['STLIB_MARKER'] = '-Bstatic' + + # program + v['cprogram_PATTERN'] = '%s' + + # shared library + v['CFLAGS_cshlib'] = ['-Kpic', '-DPIC'] + v['LINKFLAGS_cshlib'] = ['-G'] + v['cshlib_PATTERN'] = 'lib%s.so' + + # static lib + v['LINKFLAGS_cstlib'] = ['-Bstatic'] + v['cstlib_PATTERN'] = 'lib%s.a' + +def configure(conf): + conf.find_scc() + conf.find_ar() + conf.scc_common_flags() + conf.cc_load_tools() + conf.cc_add_flags() + conf.link_add_flags() + diff --git a/waflib/Tools/suncxx.py b/waflib/Tools/suncxx.py new file mode 100644 index 00000000..2b967b3a --- /dev/null +++ b/waflib/Tools/suncxx.py @@ -0,0 +1,78 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) +# Ralf Habacker, 2006 (rh) + +import os +from waflib import Utils +from waflib.Tools import ccroot, ar +from waflib.Configure import conf + +@conf +def find_sxx(conf): + """ + Detect the sun C++ compiler + """ + v = conf.env + cc = None + if v['CXX']: cc = v['CXX'] + elif 'CXX' in conf.environ: cc = conf.environ['CXX'] + if not cc: cc = conf.find_program('CC', var='CXX') #studio + if not cc: cc = conf.find_program('c++', var='CXX') + if not cc: conf.fatal('Could not find a Sun C++ compiler') + cc = conf.cmd_to_list(cc) + + try: + conf.cmd_and_log(cc + ['-flags']) + except: + conf.fatal('%r is not a Sun compiler' % cc) + + v['CXX'] = cc + v['CXX_NAME'] = 'sun' + +@conf +def sxx_common_flags(conf): + """ + Flags required for executing the sun C++ compiler + """ + v = conf.env + + v['CXX_SRC_F'] = [] + v['CXX_TGT_F'] = ['-c', '-o'] + + # linker + if not v['LINK_CXX']: v['LINK_CXX'] = v['CXX'] + v['CXXLNK_SRC_F'] = [] + v['CXXLNK_TGT_F'] = ['-o'] + v['CPPPATH_ST'] = '-I%s' + v['DEFINES_ST'] = '-D%s' + + v['LIB_ST'] = '-l%s' # template for adding libs + v['LIBPATH_ST'] = '-L%s' # template for adding libpaths + v['STLIB_ST'] = '-l%s' + v['STLIBPATH_ST'] = '-L%s' + + v['SONAME_ST'] = '-Wl,-h,%s' + v['SHLIB_MARKER'] = '-Bdynamic' + v['STLIB_MARKER'] = '-Bstatic' + + # program + v['cxxprogram_PATTERN'] = '%s' + + # shared library + v['CXXFLAGS_cxxshlib'] = ['-Kpic', '-DPIC'] + v['LINKFLAGS_cxxshlib'] = ['-G'] + v['cxxshlib_PATTERN'] = 'lib%s.so' + + # static lib + v['LINKFLAGS_cxxstlib'] = ['-Bstatic'] + v['cxxstlib_PATTERN'] = 'lib%s.a' + +def configure(conf): + conf.find_sxx() + conf.find_ar() + conf.sxx_common_flags() + conf.cxx_load_tools() + conf.cxx_add_flags() + conf.link_add_flags() + diff --git a/waflib/Tools/tex.py b/waflib/Tools/tex.py new file mode 100644 index 00000000..835ed576 --- /dev/null +++ b/waflib/Tools/tex.py @@ -0,0 +1,377 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +""" +TeX/LaTeX/PDFLaTeX/XeLaTeX support + +Example:: + + def configure(conf): + conf.load('tex') + if not conf.env.LATEX: + conf.fatal('The program LaTex is required') + + def build(bld): + bld( + features = 'tex', + type = 'latex', # pdflatex or xelatex + source = 'document.ltx', # mandatory, the source + outs = 'ps', # 'pdf' or 'ps pdf' + deps = 'crossreferencing.lst', # to give dependencies directly + prompt = 1, # 0 for the batch mode + ) + +To configure with a special program use:: + + $ PDFLATEX=luatex waf configure +""" + +import os, re +from waflib import Utils, Task, Runner, Build, Errors +from waflib.TaskGen import feature, before_method +from waflib.Logs import error, warn, debug + +re_bibunit = re.compile(r'\\(?Pputbib)\[(?P[^\[\]]*)\]',re.M) +def bibunitscan(self): + """ + Parse the inputs and try to find the *bibunit* dependencies + + :return: list of bibunit files + :rtype: list of :py:class:`waflib.Node.Node` + """ + node = self.inputs[0] + env = self.env + + nodes = [] + if not node: return nodes + + code = Utils.readf(node.abspath()) + + for match in re_bibunit.finditer(code): + path = match.group('file') + if path: + for k in ['', '.bib']: + # add another loop for the tex include paths? + debug('tex: trying %s%s' % (path, k)) + fi = node.parent.find_resource(path + k) + if fi: + nodes.append(fi) + # no break, people are crazy + else: + debug('tex: could not find %s' % path) + + debug("tex: found the following bibunit files: %s" % nodes) + return nodes + +exts_deps_tex = ['', '.ltx', '.tex', '.bib', '.pdf', '.png', '.eps', '.ps'] +"""List of typical file extensions included in latex files""" + +re_tex = re.compile(r'\\(?Pinclude|bibliography|putbib|includegraphics|input|import|bringin|lstinputlisting)(\[[^\[\]]*\])?{(?P[^{}]*)}',re.M) +"""Regexp for expressions that may include latex files""" + +g_bibtex_re = re.compile('bibdata', re.M) +"""Regexp for bibtex files""" + +class tex(Task.Task): + """ + Compile a tex/latex file. + + .. inheritance-diagram:: waflib.Tools.tex.latex waflib.Tools.tex.xelatex waflib.Tools.tex.pdflatex + """ + + bibtex_fun, _ = Task.compile_fun('${BIBTEX} ${BIBTEXFLAGS} ${SRCFILE}', shell=False) + bibtex_fun.__doc__ = """ + Execute the program **bibtex** + """ + + makeindex_fun, _ = Task.compile_fun('${MAKEINDEX} ${MAKEINDEXFLAGS} ${SRCFILE}', shell=False) + makeindex_fun.__doc__ = """ + Execute the program **makeindex** + """ + + def scan(self): + """ + A recursive regex-based scanner that finds latex dependencies. It uses :py:attr:`waflib.Tools.tex.re_tex` + + Depending on your needs you might want: + + * to change re_tex:: + + from waflib.Tools import tex + tex.re_tex = myregex + + * or to change the method scan from the latex tasks:: + + from waflib.Task import classes + classes['latex'].scan = myscanfunction + """ + node = self.inputs[0] + env = self.env + + nodes = [] + names = [] + seen = [] + if not node: return (nodes, names) + + def parse_node(node): + if node in seen: + return + seen.append(node) + code = node.read() + global re_tex + for match in re_tex.finditer(code): + for path in match.group('file').split(','): + if path: + add_name = True + found = None + for k in exts_deps_tex: + debug('tex: trying %s%s' % (path, k)) + found = node.parent.find_resource(path + k) + if found and not found in self.outputs: + nodes.append(found) + add_name = False + if found.name.endswith('.tex') or found.name.endswith('.ltx'): + parse_node(found) + # no break, people are crazy + if add_name: + names.append(path) + parse_node(node) + + for x in nodes: + x.parent.get_bld().mkdir() + + debug("tex: found the following : %s and names %s" % (nodes, names)) + return (nodes, names) + + def check_status(self, msg, retcode): + """ + Check an exit status and raise an error with a particular message + + :param msg: message to display if the code is non-zero + :type msg: string + :param retcode: condition + :type retcode: boolean + """ + if retcode != 0: + raise Errors.WafError("%r command exit status %r" % (msg, retcode)) + + def bibfile(self): + """ + Parse the *.aux* file to find a bibfile to process. + If yes, execute :py:meth:`waflib.Tools.tex.tex.bibtex_fun` + """ + try: + ct = self.aux_node.read() + except (OSError, IOError): + error('error bibtex scan') + else: + fo = g_bibtex_re.findall(ct) + + # there is a .aux file to process + if fo: + warn('calling bibtex') + + self.env.env = {} + self.env.env.update(os.environ) + self.env.env.update({'BIBINPUTS': self.TEXINPUTS, 'BSTINPUTS': self.TEXINPUTS}) + self.env.SRCFILE = self.aux_node.name[:-4] + self.check_status('error when calling bibtex', self.bibtex_fun()) + + def bibunits(self): + """ + Parse the *.aux* file to find bibunit files. If there are bibunit files, + execute :py:meth:`waflib.Tools.tex.tex.bibtex_fun`. + """ + try: + bibunits = bibunitscan(self) + except FSError: + error('error bibunitscan') + else: + if bibunits: + fn = ['bu' + str(i) for i in xrange(1, len(bibunits) + 1)] + if fn: + warn('calling bibtex on bibunits') + + for f in fn: + self.env.env = {'BIBINPUTS': self.TEXINPUTS, 'BSTINPUTS': self.TEXINPUTS} + self.env.SRCFILE = f + self.check_status('error when calling bibtex', self.bibtex_fun()) + + def makeindex(self): + """ + Look on the filesystem if there is a *.idx* file to process. If yes, execute + :py:meth:`waflib.Tools.tex.tex.makeindex_fun` + """ + try: + idx_path = self.idx_node.abspath() + os.stat(idx_path) + except OSError: + warn('index file %s absent, not calling makeindex' % idx_path) + else: + warn('calling makeindex') + + self.env.SRCFILE = self.idx_node.name + self.env.env = {} + self.check_status('error when calling makeindex %s' % idx_path, self.makeindex_fun()) + + def run(self): + """ + Runs the TeX build process. + + It may require multiple passes, depending on the + usage of cross-references, bibliographies, content susceptible of + needing such passes. + The appropriate TeX compiler is called until the *.aux* file ceases + changing. + + Makeindex and bibtex are called if necessary. + """ + env = self.env + bld = self.generator.bld + + if not env['PROMPT_LATEX']: + env.append_value('LATEXFLAGS', '-interaction=batchmode') + env.append_value('PDFLATEXFLAGS', '-interaction=batchmode') + env.append_value('XELATEXFLAGS', '-interaction=batchmode') + + fun = self.texfun + + node = self.inputs[0] + srcfile = node.abspath() + + texinputs = self.env.TEXINPUTS or '' + self.TEXINPUTS = node.parent.get_bld().abspath() + os.pathsep + node.parent.get_src().abspath() + os.pathsep + texinputs + os.pathsep + + self.aux_node = node.change_ext('.aux') + self.idx_node = node.change_ext('.idx') + + # important, set the cwd for everybody + self.cwd = self.inputs[0].parent.get_bld().abspath() + + warn('first pass on %s' % self.__class__.__name__) + + self.env.env = {} + self.env.env.update(os.environ) + self.env.env.update({'TEXINPUTS': self.TEXINPUTS}) + self.env.SRCFILE = srcfile + self.check_status('error when calling latex', fun()) + + self.bibfile() + self.bibunits() + self.makeindex() + + hash = '' + for i in range(10): + # prevent against infinite loops - one never knows + + # watch the contents of file.aux and stop if file.aux does not change anymore + prev_hash = hash + try: + hash = Utils.h_file(self.aux_node.abspath()) + except (OSError, IOError): + error('could not read aux.h -> %s' % self.aux_node.abspath()) + pass + if hash and hash == prev_hash: + break + + # run the command + warn('calling %s' % self.__class__.__name__) + + self.env.env = {} + self.env.env.update(os.environ) + self.env.env.update({'TEXINPUTS': self.TEXINPUTS}) + self.env.SRCFILE = srcfile + self.check_status('error when calling %s' % self.__class__.__name__, fun()) + +class latex(tex): + texfun, vars = Task.compile_fun('${LATEX} ${LATEXFLAGS} ${SRCFILE}', shell=False) +class pdflatex(tex): + texfun, vars = Task.compile_fun('${PDFLATEX} ${PDFLATEXFLAGS} ${SRCFILE}', shell=False) +class xelatex(tex): + texfun, vars = Task.compile_fun('${XELATEX} ${XELATEXFLAGS} ${SRCFILE}', shell=False) + +class dvips(Task.Task): + run_str = '${DVIPS} ${DVIPSFLAGS} ${SRC} -o ${TGT}' + color = 'BLUE' + after = ['latex', 'pdflatex', 'xelatex'] + +class dvipdf(Task.Task): + run_str = '${DVIPDF} ${DVIPDFFLAGS} ${SRC} ${TGT}' + color = 'BLUE' + after = ['latex', 'pdflatex', 'xelatex'] + +class pdf2ps(Task.Task): + run_str = '${PDF2PS} ${PDF2PSFLAGS} ${SRC} ${TGT}' + color = 'BLUE' + after = ['latex', 'pdflatex', 'xelatex'] + +@feature('tex') +@before_method('process_source') +def apply_tex(self): + """ + Create :py:class:`waflib.Tools.tex.tex` objects, and dvips/dvipdf/pdf2ps tasks if necessary (outs='ps', etc). + """ + if not getattr(self, 'type', None) in ['latex', 'pdflatex', 'xelatex']: + self.type = 'pdflatex' + + tree = self.bld + outs = Utils.to_list(getattr(self, 'outs', [])) + + # prompt for incomplete files (else the batchmode is used) + self.env['PROMPT_LATEX'] = getattr(self, 'prompt', 1) + + deps_lst = [] + + if getattr(self, 'deps', None): + deps = self.to_list(self.deps) + for filename in deps: + n = self.path.find_resource(filename) + if not n in deps_lst: deps_lst.append(n) + + for node in self.to_nodes(self.source): + + if self.type == 'latex': + task = self.create_task('latex', node, node.change_ext('.dvi')) + elif self.type == 'pdflatex': + task = self.create_task('pdflatex', node, node.change_ext('.pdf')) + elif self.type == 'xelatex': + task = self.create_task('xelatex', node, node.change_ext('.pdf')) + + task.env = self.env + + # add the manual dependencies + if deps_lst: + try: + lst = tree.node_deps[task.uid()] + for n in deps_lst: + if not n in lst: + lst.append(n) + except KeyError: + tree.node_deps[task.uid()] = deps_lst + + if self.type == 'latex': + if 'ps' in outs: + tsk = self.create_task('dvips', task.outputs, node.change_ext('.ps')) + tsk.env.env = {'TEXINPUTS' : node.parent.abspath() + os.pathsep + self.path.abspath() + os.pathsep + self.path.get_bld().abspath()} + if 'pdf' in outs: + tsk = self.create_task('dvipdf', task.outputs, node.change_ext('.pdf')) + tsk.env.env = {'TEXINPUTS' : node.parent.abspath() + os.pathsep + self.path.abspath() + os.pathsep + self.path.get_bld().abspath()} + elif self.type == 'pdflatex': + if 'ps' in outs: + self.create_task('pdf2ps', task.outputs, node.change_ext('.ps')) + self.source = [] + +def configure(self): + """ + Try to find the programs tex, latex and others. Do not raise any error if they + are not found. + """ + v = self.env + for p in 'tex latex pdflatex xelatex bibtex dvips dvipdf ps2pdf makeindex pdf2ps'.split(): + try: + self.find_program(p, var=p.upper()) + except self.errors.ConfigurationError: + pass + v['DVIPSFLAGS'] = '-Ppdf' + diff --git a/waflib/Tools/vala.py b/waflib/Tools/vala.py new file mode 100644 index 00000000..b1cbbccb --- /dev/null +++ b/waflib/Tools/vala.py @@ -0,0 +1,336 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Ali Sabil, 2007 +# Radosław Szkodziński, 2010 + +""" +At this point, vala is still unstable, so do not expect +this tool to be too stable either (apis, etc) +""" + +import os.path, shutil, re +from waflib import Context, Task, Utils, Logs, Options, Errors +from waflib.TaskGen import extension +from waflib.Configure import conf + +class valac(Task.Task): + """ + Task to compile vala files. + """ + vars = ["VALAC", "VALAC_VERSION", "VALAFLAGS"] + ext_out = ['.h'] + + def run(self): + env = self.env + + cmd = [env['VALAC'], '-C', '--quiet'] + cmd.extend(Utils.to_list(env['VALAFLAGS'])) + + if self.threading: + cmd.append('--thread') + + if self.profile: + cmd.append('--profile=%s' % self.profile) + + if self.target_glib: + cmd.append('--target-glib=%s' % self.target_glib) + + if self.is_lib: + cmd.append('--library=' + self.target) + for x in self.outputs: + if x.name.endswith('.h'): + cmd.append('--header=' + x.name) + if self.gir: + cmd.append('--gir=%s.gir' % self.gir) + + for vapi_dir in self.vapi_dirs: + cmd.append('--vapidir=%s' % vapi_dir) + + for package in self.packages: + cmd.append('--pkg=%s' % package) + + for package in self.packages_private: + cmd.append('--pkg=%s' % package) + + for define in self.vala_defines: + cmd.append('--define=%s' % define) + + cmd.extend([a.abspath() for a in self.inputs]) + ret = self.exec_command(cmd, cwd=self.outputs[0].parent.abspath()) + + if ret: + return ret + + for x in self.outputs: + if id(x.parent) != id(self.outputs[0].parent): + shutil.move(self.outputs[0].parent.abspath() + os.sep + x.name, x.abspath()) + + if self.packages and getattr(self, 'deps_node', None): + self.deps_node.write('\n'.join(self.packages)) + + return ret + +@extension('.vala', '.gs') +def vala_file(self, node): + """ + Compile a vala file and bind the task to *self.valatask*. If an existing vala task is already set, add the node + to its inputs. The typical example is:: + + def build(bld): + bld.program( + packages = 'gtk+-2.0', + target = 'vala-gtk-example', + uselib = 'GTK GLIB', + source = 'vala-gtk-example.vala foo.vala', + vala_defines = ['DEBUG'] + # the following arguments are for libraries + #gir = 'hello-1.0', + #gir_path = '/tmp', + #vapi_path = '/tmp', + #pkg_name = 'hello' + ) + + + :param node: vala file + :type node: :py:class:`waflib.Node.Node` + """ + # TODO: the vala task should use self.generator.attribute instead of copying attributes from self to the task + valatask = getattr(self, "valatask", None) + # there is only one vala task and it compiles all vala files .. :-/ + if not valatask: + def _get_api_version(): + api_version = '1.0' + if hasattr(Context.g_module, 'API_VERSION'): + version = Context.g_module.API_VERSION.split(".") + if version[0] == "0": + api_version = "0." + version[1] + else: + api_version = version[0] + ".0" + return api_version + + valatask = self.create_task('valac') + self.valatask = valatask # this assumes one vala task by task generator + self.includes = Utils.to_list(getattr(self, 'includes', [])) + self.uselib = self.to_list(getattr(self, 'uselib', [])) + valatask.packages = [] + valatask.packages_private = Utils.to_list(getattr(self, 'packages_private', [])) + valatask.vapi_dirs = [] + valatask.target = self.target + valatask.threading = False + valatask.install_path = getattr(self, 'install_path', '') + valatask.profile = getattr(self, 'profile', 'gobject') + valatask.vala_defines = getattr(self, 'vala_defines', []) + valatask.target_glib = None + valatask.gir = getattr(self, 'gir', None) + valatask.gir_path = getattr(self, 'gir_path', '${DATAROOTDIR}/gir-1.0') + valatask.vapi_path = getattr(self, 'vapi_path', '${DATAROOTDIR}/vala/vapi') + valatask.pkg_name = getattr(self, 'pkg_name', self.env['PACKAGE']) + valatask.header_path = getattr(self, 'header_path', '${INCLUDEDIR}/%s-%s' % (valatask.pkg_name, _get_api_version())) + + valatask.is_lib = False + if not 'cprogram' in self.features: + valatask.is_lib = True + + packages = Utils.to_list(getattr(self, 'packages', [])) + vapi_dirs = Utils.to_list(getattr(self, 'vapi_dirs', [])) + includes = [] + + if hasattr(self, 'use'): + local_packages = Utils.to_list(self.use)[:] # make sure to have a copy + seen = [] + while len(local_packages) > 0: + package = local_packages.pop() + if package in seen: + continue + seen.append(package) + + # check if the package exists + try: + package_obj = self.bld.get_tgen_by_name(package) + except Errors.WafError: + continue + package_name = package_obj.target + package_node = package_obj.path + package_dir = package_node.path_from(self.path) + + for task in package_obj.tasks: + for output in task.outputs: + if output.name == package_name + ".vapi": + valatask.set_run_after(task) + if package_name not in packages: + packages.append(package_name) + if package_dir not in vapi_dirs: + vapi_dirs.append(package_dir) + if package_dir not in includes: + includes.append(package_dir) + + if hasattr(package_obj, 'use'): + lst = self.to_list(package_obj.use) + lst.reverse() + local_packages = [pkg for pkg in lst if pkg not in seen] + local_packages + + valatask.packages = packages + for vapi_dir in vapi_dirs: + try: + valatask.vapi_dirs.append(self.path.find_dir(vapi_dir).abspath()) + valatask.vapi_dirs.append(self.path.find_dir(vapi_dir).get_bld().abspath()) + except AttributeError: + Logs.warn("Unable to locate Vala API directory: '%s'" % vapi_dir) + + self.includes.append(self.bld.srcnode.abspath()) + self.includes.append(self.bld.bldnode.abspath()) + for include in includes: + try: + self.includes.append(self.path.find_dir(include).abspath()) + self.includes.append(self.path.find_dir(include).get_bld().abspath()) + except AttributeError: + Logs.warn("Unable to locate include directory: '%s'" % include) + + + if valatask.profile == 'gobject': + if hasattr(self, 'target_glib'): + Logs.warn('target_glib on vala tasks is not supported --vala-target-glib=MAJOR.MINOR from the vala tool options') + + if getattr(Options.options, 'vala_target_glib', None): + valatask.target_glib = Options.options.vala_target_glib + + if not 'GOBJECT' in self.uselib: + self.uselib.append('GOBJECT') + + if hasattr(self, 'threading'): + if valatask.profile == 'gobject': + valatask.threading = self.threading + if not 'GTHREAD' in self.uselib: + self.uselib.append('GTHREAD') + else: + #Vala doesn't have threading support for dova nor posix + Logs.warn("Profile %s does not have threading support" % valatask.profile) + + if valatask.is_lib: + valatask.outputs.append(self.path.find_or_declare('%s.h' % self.target)) + valatask.outputs.append(self.path.find_or_declare('%s.vapi' % self.target)) + + if valatask.gir: + valatask.outputs.append(self.path.find_or_declare('%s.gir' % self.gir)) + + if valatask.packages: + d = self.path.find_or_declare('%s.deps' % self.target) + valatask.outputs.append(d) + valatask.deps_node = d + + valatask.inputs.append(node) + c_node = node.change_ext('.c') + + valatask.outputs.append(c_node) + self.source.append(c_node) + + if valatask.is_lib: + headers_list = [o for o in valatask.outputs if o.suffix() == ".h"] + try: + self.install_vheader.source = headers_list + except AttributeError: + self.install_vheader = self.bld.install_files(valatask.header_path, headers_list, self.env) + + vapi_list = [o for o in valatask.outputs if (o.suffix() in (".vapi", ".deps"))] + try: + self.install_vapi.source = vapi_list + except AttributeError: + self.install_vapi = self.bld.install_files(valatask.vapi_path, vapi_list, self.env) + + gir_list = [o for o in valatask.outputs if o.suffix() == ".gir"] + try: + self.install_gir.source = gir_list + except AttributeError: + self.install_gir = self.bld.install_files(valatask.gir_path, gir_list, self.env) + +valac = Task.update_outputs(valac) # no decorators for python2 classes + +@conf +def find_valac(self, valac_name, min_version): + """ + Find the valac program, and execute it to store the version + number in *conf.env.VALAC_VERSION* + + :param valac_name: program name + :type valac_name: string or list of string + :param min_version: minimum version acceptable + :type min_version: tuple of int + """ + valac = self.find_program(valac_name, var='VALAC') + try: + output = self.cmd_and_log(valac + ' --version') + except Exception: + valac_version = None + else: + ver = re.search(r'\d+.\d+.\d+', output).group(0).split('.') + valac_version = tuple([int(x) for x in ver]) + + self.msg('Checking for %s version >= %r' % (valac_name, min_version), + valac_version, valac_version and valac_version >= min_version) + if valac and valac_version < min_version: + self.fatal("%s version %r is too old, need >= %r" % (valac_name, valac_version, min_version)) + + self.env['VALAC_VERSION'] = valac_version + return valac + +@conf +def check_vala(self, min_version=(0,8,0), branch=None): + """ + Check if vala compiler from a given branch exists of at least a given + version. + + :param min_version: minimum version acceptable (0.8.0) + :type min_version: tuple + :param branch: first part of the version number, in case a snapshot is used (0, 8) + :type branch: tuple of int + """ + if not branch: + branch = min_version[:2] + try: + find_valac(self, 'valac-%d.%d' % (branch[0], branch[1]), min_version) + except self.errors.ConfigurationError: + find_valac(self, 'valac', min_version) + +@conf +def check_vala_deps(self): + """ + Load the gobject and gthread packages if they are missing. + """ + if not self.env['HAVE_GOBJECT']: + pkg_args = {'package': 'gobject-2.0', + 'uselib_store': 'GOBJECT', + 'args': '--cflags --libs'} + if getattr(Options.options, 'vala_target_glib', None): + pkg_args['atleast_version'] = Options.options.vala_target_glib + self.check_cfg(**pkg_args) + + if not self.env['HAVE_GTHREAD']: + pkg_args = {'package': 'gthread-2.0', + 'uselib_store': 'GTHREAD', + 'args': '--cflags --libs'} + if getattr(Options.options, 'vala_target_glib', None): + pkg_args['atleast_version'] = Options.options.vala_target_glib + self.check_cfg(**pkg_args) + +def configure(self): + """ + Use the following to enforce minimum vala version:: + + def configure(conf): + conf.load('vala', funs='') + conf.check_vala(min_version=(0,10,0)) + """ + self.load('gnu_dirs') + self.check_vala_deps() + self.check_vala() + +def options(opt): + """ + Load the :py:mod:`waflib.Tools.gnu_dirs` tool and add the ``--vala-target-glib`` command-line option + """ + opt.load('gnu_dirs') + valaopts = opt.add_option_group('Vala Compiler Options') + valaopts.add_option ('--vala-target-glib', default=None, + dest='vala_target_glib', metavar='MAJOR.MINOR', + help='Target version of glib for Vala GObject code generation') + diff --git a/waflib/Tools/waf_unit_test.py b/waflib/Tools/waf_unit_test.py new file mode 100644 index 00000000..9828505a --- /dev/null +++ b/waflib/Tools/waf_unit_test.py @@ -0,0 +1,149 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Carlos Rafael Giani, 2006 +# Thomas Nagy, 2010 + +""" +Unit testing system for C/C++/D providing test execution: + +* in parallel, by using ``waf -j`` +* partial (only the tests that have changed) or full (by using ``waf --alltests``) + +The tests are declared by adding the **test** feature to programs:: + + def options(opt): + opt.load('compiler_cxx waf_unit_test') + def configure(conf): + conf.load('compiler_cxx waf_unit_test') + def build(bld): + bld(features='cxx cxxprogram test', source='main.cpp', target='app') + # or + bld.program(features='test', source='main2.cpp', target='app2') + +When the build is executed, the program 'test' will be built and executed without arguments. +The success/failure is detected by looking at the return code. The status and the standard output/error +are stored on the build context. + +The results can be displayed by registering a callback function. Here is how to call +the predefined callback:: + + def build(bld): + bld(features='cxx cxxprogram test', source='main.c', target='app') + from waflib.Tools import waf_unit_test + bld.add_post_fun(waf_unit_test.summary) +""" + +import os, sys +from waflib.TaskGen import feature, after_method +from waflib import Utils, Task, Logs, Options +testlock = Utils.threading.Lock() + +@feature('test') +@after_method('apply_link') +def make_test(self): + """Create the unit test task. There can be only one unit test task by task generator.""" + if getattr(self, 'link_task', None): + self.create_task('utest', self.link_task.outputs) + +class utest(Task.Task): + """ + Execute a unit test + """ + color = 'PINK' + after = ['vnum', 'inst'] + vars = [] + def runnable_status(self): + """ + Always execute the task if `waf --alltests` was used + """ + ret = super(utest, self).runnable_status() + if ret == Task.SKIP_ME: + if getattr(Options.options, 'all_tests', False): + return Task.RUN_ME + return ret + + def run(self): + """ + Execute the test. The execution is always successful, but the results + are stored on ``self.generator.bld.utest_results`` for postprocessing. + """ + + filename = self.inputs[0].abspath() + self.ut_exec = getattr(self, 'ut_exec', [filename]) + if getattr(self.generator, 'ut_fun', None): + self.generator.ut_fun(self) + + try: + fu = getattr(self.generator.bld, 'all_test_paths') + except AttributeError: + fu = os.environ.copy() + self.generator.bld.all_test_paths = fu + + lst = [] + for g in self.generator.bld.groups: + for tg in g: + if getattr(tg, 'link_task', None): + lst.append(tg.link_task.outputs[0].parent.abspath()) + + def add_path(dct, path, var): + dct[var] = os.pathsep.join(Utils.to_list(path) + [os.environ.get(var, '')]) + + if Utils.is_win32: + add_path(fu, lst, 'PATH') + elif sys.platform == 'darwin': + add_path(fu, lst, 'DYLD_LIBRARY_PATH') + add_path(fu, lst, 'LD_LIBRARY_PATH') + else: + add_path(fu, lst, 'LD_LIBRARY_PATH') + + + cwd = getattr(self.generator, 'ut_cwd', '') or self.inputs[0].parent.abspath() + proc = Utils.subprocess.Popen(self.ut_exec, cwd=cwd, env=fu, stderr=Utils.subprocess.PIPE, stdout=Utils.subprocess.PIPE) + (stdout, stderr) = proc.communicate() + + tup = (filename, proc.returncode, stdout, stderr) + self.generator.utest_result = tup + + testlock.acquire() + try: + bld = self.generator.bld + Logs.debug("ut: %r", tup) + try: + bld.utest_results.append(tup) + except AttributeError: + bld.utest_results = [tup] + finally: + testlock.release() + +def summary(bld): + """ + Display an execution summary:: + + def build(bld): + bld(features='cxx cxxprogram test', source='main.c', target='app') + from waflib.Tools import waf_unit_test + bld.add_post_fun(waf_unit_test.summary) + """ + lst = getattr(bld, 'utest_results', []) + if lst: + Logs.pprint('CYAN', 'execution summary') + + total = len(lst) + tfail = len([x for x in lst if x[1]]) + + Logs.pprint('CYAN', ' tests that pass %d/%d' % (total-tfail, total)) + for (f, code, out, err) in lst: + if not code: + Logs.pprint('CYAN', ' %s' % f) + + Logs.pprint('CYAN', ' tests that fail %d/%d' % (tfail, total)) + for (f, code, out, err) in lst: + if code: + Logs.pprint('CYAN', ' %s' % f) + +def options(opt): + """ + Provide the ``--alltests`` command-line option. + """ + opt.add_option('--alltests', action='store_true', default=False, help='Exec all unit tests', dest='all_tests') + diff --git a/waflib/Tools/winres.py b/waflib/Tools/winres.py new file mode 100644 index 00000000..f54c090a --- /dev/null +++ b/waflib/Tools/winres.py @@ -0,0 +1,51 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Brant Young, 2007 + +"Process *.rc* files for C/C++: X{.rc -> [.res|.rc.o]}" + +from waflib import Task +from waflib.TaskGen import extension + +@extension('.rc') +def rc_file(self, node): + """ + Bind the .rc extension to a winrc task + """ + obj_ext = '.rc.o' + if self.env['WINRC_TGT_F'] == '/fo': + obj_ext = '.res' + rctask = self.create_task('winrc', node, node.change_ext(obj_ext)) + try: + self.compiled_tasks.append(rctask) + except AttributeError: + self.compiled_tasks = [rctask] + +class winrc(Task.Task): + """ + Task for compiling resource files + """ + run_str = '${WINRC} ${WINRCFLAGS} ${CPPPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} ${WINRC_TGT_F} ${TGT} ${WINRC_SRC_F} ${SRC}' + color = 'BLUE' + +def configure(conf): + """ + Detect the programs RC or windres, depending on the C/C++ compiler in use + """ + v = conf.env + v['WINRC_TGT_F'] = '-o' + v['WINRC_SRC_F'] = '-i' + + # find rc.exe + if not conf.env.WINRC: + if v.CC_NAME == 'msvc': + conf.find_program('RC', var='WINRC', path_list = v['PATH']) + v['WINRC_TGT_F'] = '/fo' + v['WINRC_SRC_F'] = '' + else: + conf.find_program('windres', var='WINRC', path_list = v['PATH']) + if not conf.env.WINRC: + conf.fatal('winrc was not found!') + + v['WINRCFLAGS'] = [] + diff --git a/waflib/Tools/xlc.py b/waflib/Tools/xlc.py new file mode 100644 index 00000000..0c232dce --- /dev/null +++ b/waflib/Tools/xlc.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) +# Ralf Habacker, 2006 (rh) +# Yinon Ehrlich, 2009 +# Michael Kuhn, 2009 + +from waflib.Tools import ccroot, ar +from waflib.Configure import conf + +@conf +def find_xlc(conf): + """ + Detect the Aix C compiler + """ + cc = conf.find_program(['xlc_r', 'xlc'], var='CC') + cc = conf.cmd_to_list(cc) + conf.get_xlc_version(cc) + conf.env.CC_NAME = 'xlc' + conf.env.CC = cc + +@conf +def xlc_common_flags(conf): + """ + Flags required for executing the Aix C compiler + """ + v = conf.env + + v['CC_SRC_F'] = [] + v['CC_TGT_F'] = ['-c', '-o'] + + # linker + if not v['LINK_CC']: v['LINK_CC'] = v['CC'] + v['CCLNK_SRC_F'] = [] + v['CCLNK_TGT_F'] = ['-o'] + v['CPPPATH_ST'] = '-I%s' + v['DEFINES_ST'] = '-D%s' + + v['LIB_ST'] = '-l%s' # template for adding libs + v['LIBPATH_ST'] = '-L%s' # template for adding libpaths + v['STLIB_ST'] = '-l%s' + v['STLIBPATH_ST'] = '-L%s' + v['RPATH_ST'] = '-Wl,-rpath,%s' + + v['SONAME_ST'] = [] + v['SHLIB_MARKER'] = [] + v['STLIB_MARKER'] = [] + + # program + v['LINKFLAGS_cprogram'] = ['-Wl,-brtl'] + v['cprogram_PATTERN'] = '%s' + + # shared library + v['CFLAGS_cshlib'] = ['-fPIC'] + v['LINKFLAGS_cshlib'] = ['-G', '-Wl,-brtl,-bexpfull'] + v['cshlib_PATTERN'] = 'lib%s.so' + + # static lib + v['LINKFLAGS_cstlib'] = [] + v['cstlib_PATTERN'] = 'lib%s.a' + +def configure(conf): + conf.find_xlc() + conf.find_ar() + conf.xlc_common_flags() + conf.cc_load_tools() + conf.cc_add_flags() + conf.link_add_flags() + diff --git a/waflib/Tools/xlcxx.py b/waflib/Tools/xlcxx.py new file mode 100644 index 00000000..75b8f9b0 --- /dev/null +++ b/waflib/Tools/xlcxx.py @@ -0,0 +1,69 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) +# Ralf Habacker, 2006 (rh) +# Yinon Ehrlich, 2009 +# Michael Kuhn, 2009 + +from waflib.Tools import ccroot, ar +from waflib.Configure import conf + +@conf +def find_xlcxx(conf): + """ + Detect the Aix C++ compiler + """ + cxx = conf.find_program(['xlc++_r', 'xlc++'], var='CXX') + cxx = conf.cmd_to_list(cxx) + conf.get_xlc_version(cxx) + conf.env.CXX_NAME = 'xlc++' + conf.env.CXX = cxx + +@conf +def xlcxx_common_flags(conf): + """ + Flags required for executing the Aix C++ compiler + """ + v = conf.env + + v['CXX_SRC_F'] = [] + v['CXX_TGT_F'] = ['-c', '-o'] + + # linker + if not v['LINK_CXX']: v['LINK_CXX'] = v['CXX'] + v['CXXLNK_SRC_F'] = [] + v['CXXLNK_TGT_F'] = ['-o'] + v['CPPPATH_ST'] = '-I%s' + v['DEFINES_ST'] = '-D%s' + + v['LIB_ST'] = '-l%s' # template for adding libs + v['LIBPATH_ST'] = '-L%s' # template for adding libpaths + v['STLIB_ST'] = '-l%s' + v['STLIBPATH_ST'] = '-L%s' + v['RPATH_ST'] = '-Wl,-rpath,%s' + + v['SONAME_ST'] = [] + v['SHLIB_MARKER'] = [] + v['STLIB_MARKER'] = [] + + # program + v['LINKFLAGS_cxxprogram']= ['-Wl,-brtl'] + v['cxxprogram_PATTERN'] = '%s' + + # shared library + v['CXXFLAGS_cxxshlib'] = ['-fPIC'] + v['LINKFLAGS_cxxshlib'] = ['-G', '-Wl,-brtl,-bexpfull'] + v['cxxshlib_PATTERN'] = 'lib%s.so' + + # static lib + v['LINKFLAGS_cxxstlib'] = [] + v['cxxstlib_PATTERN'] = 'lib%s.a' + +def configure(conf): + conf.find_xlcxx() + conf.find_ar() + conf.xlcxx_common_flags() + conf.cxx_load_tools() + conf.cxx_add_flags() + conf.link_add_flags() + diff --git a/waflib/Utils.py b/waflib/Utils.py new file mode 100644 index 00000000..930ffcda --- /dev/null +++ b/waflib/Utils.py @@ -0,0 +1,598 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 (ita) + +""" +Utilities and platform-specific fixes + +The portability fixes try to provide a consistent behavior of the Waf API +through Python versions 2.3 to 3.X and across different platforms (win32, linux, etc) +""" + +import os, sys, errno, traceback, inspect, re, shutil, datetime, gc +try: + import subprocess +except: + try: + import waflib.extras.subprocess as subprocess + except: + print("The subprocess module is missing (python2.3?):\n try calling 'waf update --files=subprocess'\n or add a copy of subprocess.py to the python libraries") + +try: + from collections import deque +except ImportError: + class deque(list): + """A deque for Python 2.3 which does not have one""" + def popleft(self): + return self.pop(0) +try: + import _winreg as winreg +except: + try: + import winreg + except: + winreg = None + +from waflib import Errors + +try: + from collections import UserDict +except: + from UserDict import UserDict + +try: + from hashlib import md5 +except: + try: + from md5 import md5 + except: + # never fail to enable fixes from another module + pass + +try: + import threading +except: + class threading(object): + """ + A fake threading class for platforms lacking the threading module. + Use ``waf -j1`` on those platforms + """ + pass + class Lock(object): + """Fake Lock class""" + def acquire(self): + pass + def release(self): + pass + threading.Lock = threading.Thread = Lock +else: + run_old = threading.Thread.run + def run(*args, **kwargs): + try: + run_old(*args, **kwargs) + except (KeyboardInterrupt, SystemExit): + raise + except: + sys.excepthook(*sys.exc_info()) + threading.Thread.run = run + +SIG_NIL = 'iluvcuteoverload'.encode() +"""Arbitrary null value for a md5 hash. This value must be changed when the hash value is replaced (size)""" + +O644 = 420 +"""Constant representing the permissions for regular files (0644 raises a syntax error on python 3)""" + +O755 = 493 +"""Constant representing the permissions for executable files (0755 raises a syntax error on python 3)""" + +rot_chr = ['\\', '|', '/', '-'] +"List of characters to use when displaying the throbber (progress bar)" + +rot_idx = 0 +"Index of the current throbber character (progress bar)" + +try: + from collections import defaultdict +except ImportError: + class defaultdict(dict): + """ + defaultdict was introduced in python 2.5, so we leave it for python 2.4 and 2.3 + """ + def __init__(self, default_factory): + super(defaultdict, self).__init__() + self.default_factory = default_factory + def __getitem__(self, key): + try: + return super(defaultdict, self).__getitem__(key) + except KeyError: + value = self.default_factory() + self[key] = value + return value + +is_win32 = sys.platform in ('win32', 'cli') + +# we should have put this in the Logs.py file instead :-/ +indicator = '\x1b[K%s%s%s\r' +if is_win32 and 'NOCOLOR' in os.environ: + indicator = '%s%s%s\r' + +def readf(fname, m='r'): + """ + Read an entire file into a string, in practice the wrapper + node.read(..) should be used instead of this method:: + + def build(ctx): + from waflib import Utils + txt = Utils.readf(self.path.find_node('wscript').abspath()) + txt = ctx.path.find_node('wscript').read() + + :type fname: string + :param fname: Path to file + :type m: string + :param m: Open mode + :rtype: string + :return: Content of the file + """ + f = open(fname, m) + try: + txt = f.read() + finally: + f.close() + return txt + +def h_file(filename): + """ + Compute a hash value for a file by using md5. This method may be replaced by + a faster version if necessary. The following uses the file size and the timestamp value:: + + import stat + from waflib import Utils + def h_file(filename): + st = os.stat(filename) + if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file') + m = Utils.md5() + m.update(str(st.st_mtime)) + m.update(str(st.st_size)) + m.update(filename) + return m.digest() + Utils.h_file = h_file + + :type filename: string + :param filename: path to the file to hash + :return: hash of the file contents + """ + f = open(filename, 'rb') + m = md5() + try: + while filename: + filename = f.read(100000) + m.update(filename) + finally: + f.close() + return m.digest() + +try: + x = ''.encode('hex') +except: + import binascii + def to_hex(s): + ret = binascii.hexlify(s) + if not isinstance(ret, str): + ret = ret.decode('utf-8') + return ret +else: + def to_hex(s): + return s.encode('hex') + +to_hex.__doc__ = """ +Return the hexadecimal representation of a string + +:param s: string to convert +:type s: string +""" + +listdir = os.listdir +if is_win32: + def listdir_win32(s): + """ + List the contents of a folder in a portable manner. + + :type s: string + :param s: a string, which can be empty on Windows for listing the drive letters + """ + if not s: + try: + import ctypes + except: + # there is nothing much we can do + return [x + ':\\' for x in list('ABCDEFGHIJKLMNOPQRSTUVWXYZ')] + else: + dlen = 4 # length of "?:\\x00" + maxdrives = 26 + buf = ctypes.create_string_buffer(maxdrives * dlen) + ndrives = ctypes.windll.kernel32.GetLogicalDriveStringsA(maxdrives, ctypes.byref(buf)) + return [ buf.raw[4*i:4*i+3].decode('ascii') for i in range(int(ndrives/dlen)) ] + + if len(s) == 2 and s[1] == ":": + s += os.sep + + if not os.path.isdir(s): + e = OSError() + e.errno = errno.ENOENT + raise e + return os.listdir(s) + listdir = listdir_win32 + +def num2ver(ver): + """ + Convert a string, tuple or version number into an integer. The number is supposed to have at most 4 digits:: + + from waflib.Utils import num2ver + num2ver('1.3.2') == num2ver((1,3,2)) == num2ver((1,3,2,0)) + + :type ver: string or tuple of numbers + :param ver: a version number + """ + if isinstance(ver, str): + ver = tuple(ver.split('.')) + if isinstance(ver, tuple): + ret = 0 + for i in range(4): + if i < len(ver): + ret += 256**(3 - i) * int(ver[i]) + return ret + return ver + +def ex_stack(): + """ + Extract the stack to display exceptions + + :return: a string represening the last exception + """ + exc_type, exc_value, tb = sys.exc_info() + exc_lines = traceback.format_exception(exc_type, exc_value, tb) + return ''.join(exc_lines) + +def to_list(sth): + """ + Convert a string argument to a list by splitting on spaces, and pass + through a list argument unchanged:: + + from waflib.Utils import to_list + lst = to_list("a b c d") + + :param sth: List or a string of items separated by spaces + :rtype: list + :return: Argument converted to list + + """ + if isinstance(sth, str): + return sth.split() + else: + return sth + +re_nl = re.compile('\r*\n', re.M) +def str_to_dict(txt): + """ + Parse a string with key = value pairs into a dictionary:: + + from waflib import Utils + x = Utils.str_to_dict(''' + a = 1 + b = test + ''') + + :type s: string + :param s: String to parse + :rtype: dict + :return: Dictionary containing parsed key-value pairs + """ + tbl = {} + + lines = re_nl.split(txt) + for x in lines: + x = x.strip() + if not x or x.startswith('#') or x.find('=') < 0: + continue + tmp = x.split('=') + tbl[tmp[0].strip()] = '='.join(tmp[1:]).strip() + return tbl + +def split_path(path): + return path.split('/') + +def split_path_cygwin(path): + if path.startswith('//'): + ret = path.split('/')[2:] + ret[0] = '/' + ret[0] + return ret + return path.split('/') + +re_sp = re.compile('[/\\\\]') +def split_path_win32(path): + if path.startswith('\\\\'): + ret = re.split(re_sp, path)[2:] + ret[0] = '\\' + ret[0] + return ret + return re.split(re_sp, path) + +if sys.platform == 'cygwin': + split_path = split_path_cygwin +elif is_win32: + split_path = split_path_win32 + +split_path.__doc__ = """ +Split a path by / or \\. This function is not like os.path.split + +:type path: string +:param path: path to split +:return: list of strings +""" + +def check_dir(path): + """ + Ensure that a directory exists (similar to ``mkdir -p``). + + :type dir: string + :param dir: Path to directory + """ + if not os.path.isdir(path): + try: + os.makedirs(path) + except OSError as e: + if not os.path.isdir(path): + raise Errors.WafError('Cannot create the folder %r' % path, ex=e) + +def def_attrs(cls, **kw): + """ + Set default attributes on a class instance + + :type cls: class + :param cls: the class to update the given attributes in. + :type kw: dict + :param kw: dictionary of attributes names and values. + """ + for k, v in kw.items(): + if not hasattr(cls, k): + setattr(cls, k, v) + +def quote_define_name(s): + """ + Convert a string to an identifier suitable for C defines. + + :type s: string + :param s: String to convert + :rtype: string + :return: Identifier suitable for C defines + """ + fu = re.compile("[^a-zA-Z0-9]").sub("_", s) + fu = fu.upper() + return fu + +def h_list(lst): + """ + Hash lists. For tuples, using hash(tup) is much more efficient + + :param lst: list to hash + :type lst: list of strings + :return: hash of the list + """ + m = md5() + m.update(str(lst).encode()) + return m.digest() + +def h_fun(fun): + """ + Hash functions + + :param fun: function to hash + :type fun: function + :return: hash of the function + """ + try: + return fun.code + except AttributeError: + try: + h = inspect.getsource(fun) + except IOError: + h = "nocode" + try: + fun.code = h + except AttributeError: + pass + return h + +reg_subst = re.compile(r"(\\\\)|(\$\$)|\$\{([^}]+)\}") +def subst_vars(expr, params): + """ + Replace ${VAR} with the value of VAR taken from a dict or a config set:: + + from waflib import Utils + s = Utils.subst_vars('${PREFIX}/bin', env) + + :type expr: string + :param expr: String to perform substitution on + :param params: Dictionary or config set to look up variable values. + """ + def repl_var(m): + if m.group(1): + return '\\' + if m.group(2): + return '$' + try: + # ConfigSet instances may contain lists + return params.get_flat(m.group(3)) + except AttributeError: + return params[m.group(3)] + return reg_subst.sub(repl_var, expr) + +def destos_to_binfmt(key): + """ + Return the binary format based on the unversioned platform name. + + :param key: platform name + :type key: string + :return: string representing the binary format + """ + if key == 'darwin': + return 'mac-o' + elif key in ('win32', 'cygwin', 'uwin', 'msys'): + return 'pe' + return 'elf' + +def unversioned_sys_platform(): + """ + Return the unversioned platform name. + Some Python platform names contain versions, that depend on + the build environment, e.g. linux2, freebsd6, etc. + This returns the name without the version number. Exceptions are + os2 and win32, which are returned verbatim. + + :rtype: string + :return: Unversioned platform name + """ + s = sys.platform + if s == 'java': + # The real OS is hidden under the JVM. + from java.lang import System + s = System.getProperty('os.name') + # see http://lopica.sourceforge.net/os.html for a list of possible values + if s == 'Mac OS X': + return 'darwin' + elif s.startswith('Windows '): + return 'win32' + elif s == 'OS/2': + return 'os2' + elif s == 'HP-UX': + return 'hpux' + elif s in ('SunOS', 'Solaris'): + return 'sunos' + else: s = s.lower() + if s == 'win32' or s.endswith('os2') and s != 'sunos2': return s + return re.split('\d+$', s)[0] + +def nada(*k, **kw): + """ + A function that does nothing + + :return: None + """ + pass + +class Timer(object): + """ + Simple object for timing the execution of commands. + Its string representation is the current time:: + + from waflib.Utils import Timer + timer = Timer() + a_few_operations() + s = str(timer) + """ + def __init__(self): + self.start_time = datetime.datetime.utcnow() + + def __str__(self): + delta = datetime.datetime.utcnow() - self.start_time + days = int(delta.days) + hours = delta.seconds // 3600 + minutes = (delta.seconds - hours * 3600) // 60 + seconds = delta.seconds - hours * 3600 - minutes * 60 + float(delta.microseconds) / 1000 / 1000 + result = '' + if days: + result += '%dd' % days + if days or hours: + result += '%dh' % hours + if days or hours or minutes: + result += '%dm' % minutes + return '%s%.3fs' % (result, seconds) + +if is_win32: + old = shutil.copy2 + def copy2(src, dst): + """ + shutil.copy2 does not copy the file attributes on windows, so we + hack into the shutil module to fix the problem + """ + old(src, dst) + shutil.copystat(src, dst) + setattr(shutil, 'copy2', copy2) + +if os.name == 'java': + # Jython cannot disable the gc but they can enable it ... wtf? + try: + gc.disable() + gc.enable() + except NotImplementedError: + gc.disable = gc.enable + +def read_la_file(path): + """ + Read property files, used by msvc.py + + :param path: file to read + :type path: string + """ + sp = re.compile(r'^([^=]+)=\'(.*)\'$') + dc = {} + for line in readf(path).splitlines(): + try: + _, left, right, _ = sp.split(line.strip()) + dc[left] = right + except ValueError: + pass + return dc + +def nogc(fun): + """ + Decorator: let a function disable the garbage collector during its execution. + It is used in the build context when storing/loading the build cache file (pickle) + + :param fun: function to execute + :type fun: function + :return: the return value of the function executed + """ + def f(*k, **kw): + try: + gc.disable() + ret = fun(*k, **kw) + finally: + gc.enable() + return ret + f.__doc__ = fun.__doc__ + return f + +def run_once(fun): + """ + Decorator: let a function cache its results, use like this:: + + @run_once + def foo(k): + return 345*2343 + + :param fun: function to execute + :type fun: function + :return: the return value of the function executed + """ + cache = {} + def wrap(k): + try: + return cache[k] + except KeyError: + ret = fun(k) + cache[k] = ret + return ret + wrap.__cache__ = cache + return wrap + +def get_registry_app_path(key, filename): + if not winreg: + return None + try: + result = winreg.QueryValue(key, "Software\\Microsoft\\Windows\\CurrentVersion\\App Paths\\%s.exe" % filename[0]) + except WindowsError: + pass + else: + if os.path.isfile(result): + return result + diff --git a/waflib/__init__.py b/waflib/__init__.py new file mode 100644 index 00000000..c8a3c349 --- /dev/null +++ b/waflib/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 (ita) diff --git a/waflib/ansiterm.py b/waflib/ansiterm.py new file mode 100644 index 00000000..1663ffbe --- /dev/null +++ b/waflib/ansiterm.py @@ -0,0 +1,241 @@ +import sys, os +try: + if not (sys.stderr.isatty() and sys.stdout.isatty()): + raise ValueError('not a tty') + + from ctypes import * + + class COORD(Structure): + _fields_ = [("X", c_short), ("Y", c_short)] + + class SMALL_RECT(Structure): + _fields_ = [("Left", c_short), ("Top", c_short), ("Right", c_short), ("Bottom", c_short)] + + class CONSOLE_SCREEN_BUFFER_INFO(Structure): + _fields_ = [("Size", COORD), ("CursorPosition", COORD), ("Attributes", c_short), ("Window", SMALL_RECT), ("MaximumWindowSize", COORD)] + + class CONSOLE_CURSOR_INFO(Structure): + _fields_ = [('dwSize',c_ulong), ('bVisible', c_int)] + + sbinfo = CONSOLE_SCREEN_BUFFER_INFO() + csinfo = CONSOLE_CURSOR_INFO() + hconsole = windll.kernel32.GetStdHandle(-11) + windll.kernel32.GetConsoleScreenBufferInfo(hconsole, byref(sbinfo)) + if sbinfo.Size.X < 9 or sbinfo.Size.Y < 9: raise ValueError('small console') + windll.kernel32.GetConsoleCursorInfo(hconsole, byref(csinfo)) +except Exception: + pass +else: + import re, threading + + try: + _type = unicode + except: + _type = str + + to_int = lambda number, default: number and int(number) or default + wlock = threading.Lock() + + STD_OUTPUT_HANDLE = -11 + STD_ERROR_HANDLE = -12 + + class AnsiTerm(object): + """ + emulate a vt100 terminal in cmd.exe + """ + def __init__(self): + self.encoding = sys.stdout.encoding + self.hconsole = windll.kernel32.GetStdHandle(STD_OUTPUT_HANDLE) + self.cursor_history = [] + self.orig_sbinfo = CONSOLE_SCREEN_BUFFER_INFO() + self.orig_csinfo = CONSOLE_CURSOR_INFO() + windll.kernel32.GetConsoleScreenBufferInfo(self.hconsole, byref(self.orig_sbinfo)) + windll.kernel32.GetConsoleCursorInfo(hconsole, byref(self.orig_csinfo)) + + def screen_buffer_info(self): + sbinfo = CONSOLE_SCREEN_BUFFER_INFO() + windll.kernel32.GetConsoleScreenBufferInfo(self.hconsole, byref(sbinfo)) + return sbinfo + + def clear_line(self, param): + mode = param and int(param) or 0 + sbinfo = self.screen_buffer_info() + if mode == 1: # Clear from begining of line to cursor position + line_start = COORD(0, sbinfo.CursorPosition.Y) + line_length = sbinfo.Size.X + elif mode == 2: # Clear entire line + line_start = COORD(sbinfo.CursorPosition.X, sbinfo.CursorPosition.Y) + line_length = sbinfo.Size.X - sbinfo.CursorPosition.X + else: # Clear from cursor position to end of line + line_start = sbinfo.CursorPosition + line_length = sbinfo.Size.X - sbinfo.CursorPosition.X + chars_written = c_int() + windll.kernel32.FillConsoleOutputCharacterA(self.hconsole, c_wchar(' '), line_length, line_start, byref(chars_written)) + windll.kernel32.FillConsoleOutputAttribute(self.hconsole, sbinfo.Attributes, line_length, line_start, byref(chars_written)) + + def clear_screen(self, param): + mode = to_int(param, 0) + sbinfo = self.screen_buffer_info() + if mode == 1: # Clear from begining of screen to cursor position + clear_start = COORD(0, 0) + clear_length = sbinfo.CursorPosition.X * sbinfo.CursorPosition.Y + elif mode == 2: # Clear entire screen and return cursor to home + clear_start = COORD(0, 0) + clear_length = sbinfo.Size.X * sbinfo.Size.Y + windll.kernel32.SetConsoleCursorPosition(self.hconsole, clear_start) + else: # Clear from cursor position to end of screen + clear_start = sbinfo.CursorPosition + clear_length = ((sbinfo.Size.X - sbinfo.CursorPosition.X) + sbinfo.Size.X * (sbinfo.Size.Y - sbinfo.CursorPosition.Y)) + chars_written = c_int() + windll.kernel32.FillConsoleOutputCharacterA(self.hconsole, c_wchar(' '), clear_length, clear_start, byref(chars_written)) + windll.kernel32.FillConsoleOutputAttribute(self.hconsole, sbinfo.Attributes, clear_length, clear_start, byref(chars_written)) + + def push_cursor(self, param): + sbinfo = self.screen_buffer_info() + self.cursor_history.append(sbinfo.CursorPosition) + + def pop_cursor(self, param): + if self.cursor_history: + old_pos = self.cursor_history.pop() + windll.kernel32.SetConsoleCursorPosition(self.hconsole, old_pos) + + def set_cursor(self, param): + y, sep, x = param.partition(';') + x = to_int(x, 1) - 1 + y = to_int(y, 1) - 1 + sbinfo = self.screen_buffer_info() + new_pos = COORD( + min(max(0, x), sbinfo.Size.X), + min(max(0, y), sbinfo.Size.Y) + ) + windll.kernel32.SetConsoleCursorPosition(self.hconsole, new_pos) + + def set_column(self, param): + x = to_int(param, 1) - 1 + sbinfo = self.screen_buffer_info() + new_pos = COORD( + min(max(0, x), sbinfo.Size.X), + sbinfo.CursorPosition.Y + ) + windll.kernel32.SetConsoleCursorPosition(self.hconsole, new_pos) + + def move_cursor(self, x_offset=0, y_offset=0): + sbinfo = self.screen_buffer_info() + new_pos = COORD( + min(max(0, sbinfo.CursorPosition.X + x_offset), sbinfo.Size.X), + min(max(0, sbinfo.CursorPosition.Y + y_offset), sbinfo.Size.Y) + ) + windll.kernel32.SetConsoleCursorPosition(self.hconsole, new_pos) + + def move_up(self, param): + self.move_cursor(y_offset = -to_int(param, 1)) + + def move_down(self, param): + self.move_cursor(y_offset = to_int(param, 1)) + + def move_left(self, param): + self.move_cursor(x_offset = -to_int(param, 1)) + + def move_right(self, param): + self.move_cursor(x_offset = to_int(param, 1)) + + def next_line(self, param): + sbinfo = self.screen_buffer_info() + self.move_cursor( + x_offset = -sbinfo.CursorPosition.X, + y_offset = to_int(param, 1) + ) + + def prev_line(self, param): + sbinfo = self.screen_buffer_info() + self.move_cursor( + x_offset = -sbinfo.CursorPosition.X, + y_offset = -to_int(param, 1) + ) + + def rgb2bgr(self, c): + return ((c&1) << 2) | (c&2) | ((c&4)>>2) + + def set_color(self, param): + cols = param.split(';') + sbinfo = CONSOLE_SCREEN_BUFFER_INFO() + windll.kernel32.GetConsoleScreenBufferInfo(self.hconsole, byref(sbinfo)) + attr = sbinfo.Attributes + for c in cols: + c = to_int(c, 0) + if c in range(30,38): # fgcolor + attr = (attr & 0xfff0) | self.rgb2bgr(c-30) + elif c in range(40,48): # bgcolor + attr = (attr & 0xff0f) | (self.rgb2bgr(c-40) << 4) + elif c == 0: # reset + attr = self.orig_sbinfo.Attributes + elif c == 1: # strong + attr |= 0x08 + elif c == 4: # blink not available -> bg intensity + attr |= 0x80 + elif c == 7: # negative + attr = (attr & 0xff88) | ((attr & 0x70) >> 4) | ((attr & 0x07) << 4) + windll.kernel32.SetConsoleTextAttribute(self.hconsole, attr) + + def show_cursor(self,param): + csinfo.bVisible = 1 + windll.kernel32.SetConsoleCursorInfo(self.hconsole, byref(csinfo)) + + def hide_cursor(self,param): + csinfo.bVisible = 0 + windll.kernel32.SetConsoleCursorInfo(self.hconsole, byref(csinfo)) + + ansi_command_table = { + 'A': move_up, + 'B': move_down, + 'C': move_right, + 'D': move_left, + 'E': next_line, + 'F': prev_line, + 'G': set_column, + 'H': set_cursor, + 'f': set_cursor, + 'J': clear_screen, + 'K': clear_line, + 'h': show_cursor, + 'l': hide_cursor, + 'm': set_color, + 's': push_cursor, + 'u': pop_cursor, + } + # Match either the escape sequence or text not containing escape sequence + ansi_tokens = re.compile('(?:\x1b\[([0-9?;]*)([a-zA-Z])|([^\x1b]+))') + def write(self, text): + try: + wlock.acquire() + for param, cmd, txt in self.ansi_tokens.findall(text): + if cmd: + cmd_func = self.ansi_command_table.get(cmd) + if cmd_func: + cmd_func(self, param) + else: + self.writeconsole(txt) + finally: + wlock.release() + + def writeconsole(self, txt): + chars_written = c_int() + writeconsole = windll.kernel32.WriteConsoleA + if isinstance(txt, _type): + writeconsole = windll.kernel32.WriteConsoleW + + TINY_STEP = 3000 + for x in range(0, len(txt), TINY_STEP): + # According MSDN, size should NOT exceed 64 kb (issue #746) + tiny = txt[x : x + TINY_STEP] + writeconsole(self.hconsole, tiny, len(tiny), byref(chars_written), None) + + def flush(self): + pass + + def isatty(self): + return True + + sys.stderr = sys.stdout = AnsiTerm() + os.environ['TERM'] = 'vt100' + diff --git a/waflib/extras/__init__.py b/waflib/extras/__init__.py new file mode 100644 index 00000000..c8a3c349 --- /dev/null +++ b/waflib/extras/__init__.py @@ -0,0 +1,3 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 (ita) diff --git a/waflib/extras/add_objects.py b/waflib/extras/add_objects.py new file mode 100644 index 00000000..e383a1c8 --- /dev/null +++ b/waflib/extras/add_objects.py @@ -0,0 +1,7 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2011 (ita) + +from waflib import Logs +Logs.warn('This tool has been merged to the main library, remove the references to "add_objects"') + diff --git a/waflib/extras/batched_cc.py b/waflib/extras/batched_cc.py new file mode 100644 index 00000000..39ba6e33 --- /dev/null +++ b/waflib/extras/batched_cc.py @@ -0,0 +1,161 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +""" +Batched builds - compile faster +instead of compiling object files one by one, c/c++ compilers are often able to compile at once: +cc -c ../file1.c ../file2.c ../file3.c + +Files are output on the directory where the compiler is called, and dependencies are more difficult +to track (do not run the command on all source files if only one file changes) + +As such, we do as if the files were compiled one by one, but no command is actually run: +replace each cc/cpp Task by a TaskSlave. A new task called TaskMaster collects the +signatures from each slave and finds out the command-line to run. + +It is only necessary to import this module in the configuration (no other change required) +""" + +import os +from waflib import TaskGen, Task, Build, Logs +from waflib.TaskGen import extension, feature, before_method, after_method + +MAX_BATCH = 50 + +c_str = '${CC} ${CFLAGS} ${CPPFLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${CPPPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} -c ${SRCLST}' +#c_str = '${CC} ${CCFLAGS} ${CPPFLAGS} ${_CCINCFLAGS} ${_CCDEFFLAGS} -c ${SRCLST}' +c_fun, _ = Task.compile_fun_noshell(c_str) + +cxx_str = '${CXX} ${CXXFLAGS} ${CPPFLAGS} ${FRAMEWORKPATH_ST:FRAMEWORKPATH} ${CPPPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} -c ${SRCLST}' +#cxx_str = '${CXX} ${CXXFLAGS} ${CPPFLAGS} ${_CXXINCFLAGS} ${_CXXDEFFLAGS} -c ${SRCLST}' +cxx_fun, _ = Task.compile_fun_noshell(cxx_str) + +count = 70000 +class batch_task(Task.Task): + color = 'RED' + + after = ['c', 'cxx'] + before = ['cprogram', 'cshlib', 'cstlib', 'cxxprogram', 'cxxshlib', 'cxxstlib'] + + def __str__(self): + return '(batch compilation for %d slaves)\n' % len(self.slaves) + + def __init__(self, *k, **kw): + Task.Task.__init__(self, *k, **kw) + self.slaves = [] + self.inputs = [] + self.hasrun = 0 + + global count + count += 1 + self.idx = count + + def add_slave(self, slave): + self.slaves.append(slave) + self.set_run_after(slave) + + def runnable_status(self): + for t in self.run_after: + if not t.hasrun: + return Task.ASK_LATER + + for t in self.slaves: + #if t.executed: + if t.hasrun != Task.SKIPPED: + return Task.RUN_ME + + return Task.SKIP_ME + + def run(self): + outputs = [] + self.outputs = [] + + srclst = [] + slaves = [] + for t in self.slaves: + if t.hasrun != Task.SKIPPED: + slaves.append(t) + srclst.append(t.inputs[0].abspath()) + + self.env.SRCLST = srclst + self.cwd = slaves[0].inputs[0].parent.get_bld().abspath() + + if self.slaves[0].__class__.__name__ == 'c': + ret = c_fun(self) + else: + ret = cxx_fun(self) + + if ret: + return ret + + for t in slaves: + t.old_post_run() + +from waflib.Tools import c, cxx + +def hook(name): + def n_hook(self, node): + task = self.create_task(name, node, node.change_ext('.o')) + try: + self.compiled_tasks.append(task) + except AttributeError: + self.compiled_tasks = [task] + + if not getattr(self, 'masters', None): + self.masters = {} + self.allmasters = [] + + if not node.parent in self.masters: + m = self.masters[node.parent] = self.master = self.create_task('batch') + self.allmasters.append(m) + else: + m = self.masters[node.parent] + if len(m.slaves) > MAX_BATCH: + m = self.masters[node.parent] = self.master = self.create_task('batch') + self.allmasters.append(m) + + m.add_slave(task) + return task + return n_hook + +extension('.c')(hook('c')) +extension('.cpp','.cc','.cxx','.C','.c++')(hook('cxx')) + +@feature('cprogram', 'cshlib', 'cstaticlib', 'cxxprogram', 'cxxshlib', 'cxxstlib') +@after_method('apply_link') +def link_after_masters(self): + if getattr(self, 'allmasters', None): + for m in self.allmasters: + self.link_task.set_run_after(m) + +# Modify the c and cxx task classes - in theory it would be better to +# create subclasses and to re-map the c/c++ extensions +# +for x in ['c', 'cxx']: + t = Task.classes[x] + def run(self): + pass + + def post_run(self): + #self.executed=1 + pass + + def can_retrieve_cache(self): + if self.old_can_retrieve_cache(): + for m in self.generator.allmasters: + try: + m.slaves.remove(self) + except ValueError: + pass #this task wasn't included in that master + return 1 + else: + return None + + setattr(t, 'oldrun', t.__dict__['run']) + setattr(t, 'run', run) + setattr(t, 'old_post_run', t.post_run) + setattr(t, 'post_run', post_run) + setattr(t, 'old_can_retrieve_cache', t.can_retrieve_cache) + setattr(t, 'can_retrieve_cache', can_retrieve_cache) + diff --git a/waflib/extras/biber.py b/waflib/extras/biber.py new file mode 100644 index 00000000..df424160 --- /dev/null +++ b/waflib/extras/biber.py @@ -0,0 +1,59 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2011 (ita) + +""" +Latex processing using "biber" +""" + +import os +from waflib import Task +from waflib.Logs import warn + +from waflib.Tools import tex as texmodule + +class tex(texmodule.tex): + biber_fun, _ = Task.compile_fun('${BIBER} ${BIBERFLAGS} ${SRCFILE}',shell=False) + biber_fun.__doc__ = """ + Execute the program **biber** + """ + + def bibfile(self): + return None + + def bibunits(self): + self.env.env = {} + self.env.env.update(os.environ) + self.env.env.update({'BIBINPUTS': self.TEXINPUTS, 'BSTINPUTS': self.TEXINPUTS}) + self.env.SRCFILE = self.aux_node.name[:-4] + + if not self.env['PROMPT_LATEX']: + self.env.append_unique('BIBERFLAGS', '--quiet') + + path = self.aux_node.abspath()[:-4] + '.bcf' + if os.path.isfile(path): + warn('calling biber') + self.check_status('error when calling biber, check %s.blg for errors' % (self.env.SRCFILE), self.biber_fun()) + else: + super(tex, self).bibfile() + super(tex, self).bibunits() + +class latex(tex): + texfun, vars = Task.compile_fun('${LATEX} ${LATEXFLAGS} ${SRCFILE}', shell=False) +class pdflatex(tex): + texfun, vars = Task.compile_fun('${PDFLATEX} ${PDFLATEXFLAGS} ${SRCFILE}', shell=False) +class xelatex(tex): + texfun, vars = Task.compile_fun('${XELATEX} ${XELATEXFLAGS} ${SRCFILE}', shell=False) + +def configure(self): + """ + Almost the same as in tex.py, but try to detect 'biber' + """ + v = self.env + for p in ' biber tex latex pdflatex xelatex bibtex dvips dvipdf ps2pdf makeindex pdf2ps'.split(): + try: + self.find_program(p, var=p.upper()) + except self.errors.ConfigurationError: + pass + v['DVIPSFLAGS'] = '-Ppdf' + diff --git a/waflib/extras/bjam.py b/waflib/extras/bjam.py new file mode 100644 index 00000000..26662632 --- /dev/null +++ b/waflib/extras/bjam.py @@ -0,0 +1,131 @@ +#! /usr/bin/env python +# per rosengren 2011 + +from waflib.Logs import error,warn,info,debug +from waflib.TaskGen import feature, after_method +from waflib.Task import Task, always_run +from os import sep, readlink +from os.path import abspath + +def options(opt): + grp = opt.add_option_group('Bjam Options') + grp.add_option('--bjam_src', default=None, help='You can find it in /tools/jam/src') + grp.add_option('--bjam_uname', default='linuxx86_64', help='bjam is built in /bin./bjam') + grp.add_option('--bjam_config', default=None) + grp.add_option('--bjam_toolset', default=None) + +def configure(cnf): + if not cnf.env.BJAM_SRC: + cnf.env.BJAM_SRC = cnf.options.bjam_src + if not cnf.env.BJAM_UNAME: + cnf.env.BJAM_UNAME = cnf.options.bjam_uname + try: + cnf.find_program('bjam', path_list=[ + cnf.env.BJAM_SRC + sep + 'bin.' + cnf.env.BJAM_UNAME + ]) + except Exception as e: + cnf.env.BJAM = None + if not cnf.env.BJAM_CONFIG: + cnf.env.BJAM_CONFIG = cnf.options.bjam_config + if not cnf.env.BJAM_TOOLSET: + cnf.env.BJAM_TOOLSET = cnf.options.bjam_toolset + +@feature('bjam') +@after_method('process_rule') +def process_bjam(self): + if not self.bld.env.BJAM: + self.create_task('bjam_creator') + self.create_task('bjam_build') + self.create_task('bjam_installer') + if getattr(self, 'always', False): + always_run(bjam_creator) + always_run(bjam_build) + always_run(bjam_installer) + +class bjam_creator(Task): + ext_out = 'bjam_exe' + vars=['BJAM_SRC', 'BJAM_UNAME'] + def run(self): + env = self.env + gen = self.generator + path = gen.path + bld = gen.bld + bjam = gen.bld.root.find_dir(env.BJAM_SRC) + if not bjam: + error('Can not find bjam source') + return -1 + bjam_exe_relpath = 'bin.' + env.BJAM_UNAME + '/bjam' + bjam_exe = bjam.find_resource(bjam_exe_relpath) + if bjam_exe: + env.BJAM = bjam_exe.srcpath() + return 0 + bjam_cmd = ['./build.sh'] + debug('runner: ' + bjam.srcpath() + '> ' + str(bjam_cmd)) + result = self.exec_command(bjam_cmd, cwd=bjam.srcpath()) + if not result == 0: + error('bjam failed') + return -1 + bjam_exe = bjam.find_resource(bjam_exe_relpath) + if bjam_exe: + env.BJAM = bjam_exe.srcpath() + return 0 + error('bjam failed') + return -1 + +class bjam_build(Task): + ext_in = 'bjam_exe' + ext_out = 'install' + vars = ['BJAM_TOOLSET'] + def run(self): + env = self.env + gen = self.generator + path = gen.path + bld = gen.bld + if hasattr(gen, 'root'): + build_root = path.find_node(gen.root) + else: + build_root = path + jam = bld.srcnode.find_resource(env.BJAM_CONFIG) + if jam: + debug('bjam: Using jam configuration from ' + jam.srcpath()) + jam_rel = jam.relpath_gen(build_root) + else: + warn('No build configuration in build_config/user-config.jam. Using default') + jam_rel = None + bjam_exe = bld.srcnode.find_node(env.BJAM) + if not bjam_exe: + error('env.BJAM is not set') + return -1 + bjam_exe_rel = bjam_exe.relpath_gen(build_root) + cmd = ([bjam_exe_rel] + + (['--user-config=' + jam_rel] if jam_rel else []) + + ['--stagedir=' + path.get_bld().path_from(build_root)] + + ['--debug-configuration'] + + ['--with-' + lib for lib in self.generator.target] + + (['toolset=' + env.BJAM_TOOLSET] if env.BJAM_TOOLSET else []) + + ['link=' + 'shared'] + + ['variant=' + 'release'] + ) + debug('runner: ' + build_root.srcpath() + '> ' + str(cmd)) + ret = self.exec_command(cmd, cwd=build_root.srcpath()) + if ret != 0: + return ret + self.set_outputs(path.get_bld().ant_glob('lib/*') + path.get_bld().ant_glob('bin/*')) + return 0 + +class bjam_installer(Task): + ext_in = 'install' + def run(self): + gen = self.generator + path = gen.path + for idir, pat in [('${LIBDIR}', 'lib/*'), ('${BINDIR}', 'bin/*')]: + files = [] + for n in path.get_bld().ant_glob(pat): + try: + t = readlink(n.srcpath()) + gen.bld.symlink_as(sep.join([idir, n.name]), t, postpone=False) + except OSError: + files.append(n) + gen.bld.install_files(idir, files, postpone=False) + return 0 + diff --git a/waflib/extras/boo.py b/waflib/extras/boo.py new file mode 100644 index 00000000..e37529ea --- /dev/null +++ b/waflib/extras/boo.py @@ -0,0 +1,81 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Yannick LM 2011 + +""" +Support for the boo programming language, for example:: + + bld(features = "boo", # necessary feature + source = "src.boo", # list of boo files + gen = "world.dll", # target + type = "library", # library/exe ("-target:xyz" flag) + name = "world" # necessary if the target is referenced by 'use' + ) +""" + +from waflib import Task +from waflib.Configure import conf +from waflib.TaskGen import feature, after, before, extension + +@extension('.boo') +def boo_hook(self, node): + # Nothing here yet ... + # TODO filter the non-boo source files in 'apply_booc' and remove this method + pass + +@feature('boo') +@before('process_source') +def apply_booc(self): + """Create a booc task """ + src_nodes = self.to_nodes(self.source) + out_node = self.path.find_or_declare(self.gen) + + self.boo_task = self.create_task('booc', src_nodes, [out_node]) + + # Set variables used by the 'booc' task + self.boo_task.env.OUT = '-o:%s' % out_node.abspath() + + # type is "exe" by default + type = getattr(self, "type", "exe") + self.boo_task.env.BOO_TARGET_TYPE = "-target:%s" % type + +@feature('boo') +@after('apply_boo') +def use_boo(self): + """" + boo applications honor the **use** keyword:: + """ + dep_names = self.to_list(getattr(self, 'use', [])) + for dep_name in dep_names: + dep_task_gen = self.bld.get_tgen_by_name(dep_name) + if not dep_task_gen: + continue + dep_task_gen.post() + dep_task = getattr(dep_task_gen, 'boo_task', None) + if not dep_task: + # Try a cs task: + dep_task = getattr(dep_task_gen, 'cs_task', None) + if not dep_task: + # Try a link task: + dep_task = getattr(dep_task, 'link_task', None) + if not dep_task: + # Abort ... + continue + self.boo_task.set_run_after(dep_task) # order + self.boo_task.dep_nodes.extend(dep_task.outputs) # dependency + self.boo_task.env.append_value('BOO_FLAGS', '-reference:%s' % dep_task.outputs[0].abspath()) + +class booc(Task.Task): + """Compiles .boo files """ + color = 'YELLOW' + run_str = '${BOOC} ${BOO_FLAGS} ${BOO_TARGET_TYPE} ${OUT} ${SRC}' + +@conf +def check_booc(self): + self.find_program('booc', 'BOOC') + self.env.BOO_FLAGS = ['-nologo'] + +def configure(self): + """Check that booc is available """ + self.check_booc() + diff --git a/waflib/extras/boost.py b/waflib/extras/boost.py new file mode 100644 index 00000000..ea25d803 --- /dev/null +++ b/waflib/extras/boost.py @@ -0,0 +1,269 @@ +#!/usr/bin/env python +# encoding: utf-8 +# +# partially based on boost.py written by Gernot Vormayr +# written by Ruediger Sonderfeld , 2008 +# modified by Bjoern Michaelsen, 2008 +# modified by Luca Fossati, 2008 +# rewritten for waf 1.5.1, Thomas Nagy, 2008 +# rewritten for waf 1.6.2, Sylvain Rouquette, 2011 + +''' +To add the boost tool to the waf file: +$ ./waf-light --tools=compat15,boost + or, if you have waf >= 1.6.2 +$ ./waf update --files=boost + +The wscript will look like: + +def options(opt): + opt.load('compiler_cxx boost') + +def configure(conf): + conf.load('compiler_cxx boost') + conf.check_boost(lib='system filesystem', mt=True, static=True) + +def build(bld): + bld(source='main.cpp', target='app', use='BOOST') +''' + +import sys +import re +from waflib import Utils, Logs +from waflib.Configure import conf + +BOOST_LIBS = ('/usr/lib', '/usr/local/lib', + '/opt/local/lib', '/sw/lib', '/lib') +BOOST_INCLUDES = ('/usr/include', '/usr/local/include', + '/opt/local/include', '/sw/include') +BOOST_VERSION_FILE = 'boost/version.hpp' +BOOST_VERSION_CODE = ''' +#include +#include +int main() { std::cout << BOOST_LIB_VERSION << std::endl; } +''' + +# toolsets from {boost_dir}/tools/build/v2/tools/common.jam +PLATFORM = Utils.unversioned_sys_platform() +detect_intel = lambda env: (PLATFORM == 'win32') and 'iw' or 'il' +detect_clang = lambda env: (PLATFORM == 'darwin') and 'clang-darwin' or 'clang' +detect_mingw = lambda env: (re.search('MinGW', env.CXX[0])) and 'mgw' or 'gcc' +BOOST_TOOLSETS = { + 'borland': 'bcb', + 'clang': detect_clang, + 'como': 'como', + 'cw': 'cw', + 'darwin': 'xgcc', + 'edg': 'edg', + 'g++': detect_mingw, + 'gcc': detect_mingw, + 'icpc': detect_intel, + 'intel': detect_intel, + 'kcc': 'kcc', + 'kylix': 'bck', + 'mipspro': 'mp', + 'mingw': 'mgw', + 'msvc': 'vc', + 'qcc': 'qcc', + 'sun': 'sw', + 'sunc++': 'sw', + 'tru64cxx': 'tru', + 'vacpp': 'xlc' +} + + +def options(opt): + opt.add_option('--boost-includes', type='string', + default='', dest='boost_includes', + help='''path to the boost directory where the includes are + e.g. /boost_1_45_0/include''') + opt.add_option('--boost-libs', type='string', + default='', dest='boost_libs', + help='''path to the directory where the boost libs are + e.g. /boost_1_45_0/stage/lib''') + opt.add_option('--boost-static', action='store_true', + default=False, dest='boost_static', + help='link static libraries') + opt.add_option('--boost-mt', action='store_true', + default=False, dest='boost_mt', + help='select multi-threaded libraries') + opt.add_option('--boost-abi', type='string', default='', dest='boost_abi', + help='''select libraries with tags (dgsyp, d for debug), + see doc Boost, Getting Started, chapter 6.1''') + opt.add_option('--boost-toolset', type='string', + default='', dest='boost_toolset', + help='force a toolset e.g. msvc, vc90, \ + gcc, mingw, mgw45 (default: auto)') + py_version = '%d%d' % (sys.version_info[0], sys.version_info[1]) + opt.add_option('--boost-python', type='string', + default=py_version, dest='boost_python', + help='select the lib python with this version \ + (default: %s)' % py_version) + + +@conf +def __boost_get_version_file(self, dir): + try: + return self.root.find_dir(dir).find_node(BOOST_VERSION_FILE) + except: + return None + + +@conf +def boost_get_version(self, dir): + """silently retrieve the boost version number""" + re_but = re.compile('^#define\\s+BOOST_LIB_VERSION\\s+"(.*)"$', re.M) + try: + val = re_but.search(self.__boost_get_version_file(dir).read()).group(1) + except: + val = self.check_cxx(fragment=BOOST_VERSION_CODE, includes=[dir], + execute=True, define_ret=True) + return val + + +@conf +def boost_get_includes(self, *k, **kw): + includes = k and k[0] or kw.get('includes', None) + if includes and self.__boost_get_version_file(includes): + return includes + for dir in BOOST_INCLUDES: + if self.__boost_get_version_file(dir): + return dir + if includes: + self.fatal('headers not found in %s' % includes) + else: + self.fatal('headers not found, use --boost-includes=/path/to/boost') + + +@conf +def boost_get_toolset(self, cc): + toolset = cc + if not cc: + build_platform = Utils.unversioned_sys_platform() + if build_platform in BOOST_TOOLSETS: + cc = build_platform + else: + cc = self.env.CXX_NAME + if cc in BOOST_TOOLSETS: + toolset = BOOST_TOOLSETS[cc] + return isinstance(toolset, str) and toolset or toolset(self.env) + + +@conf +def __boost_get_libs_path(self, *k, **kw): + ''' return the lib path and all the files in it ''' + if 'files' in kw: + return self.root.find_dir('.'), Utils.to_list(kw['files']) + libs = k and k[0] or kw.get('libs', None) + if libs: + path = self.root.find_dir(libs) + files = path.ant_glob('*boost_*') + if not libs or not files: + for dir in BOOST_LIBS: + try: + path = self.root.find_dir(dir) + files = path.ant_glob('*boost_*') + if files: + break + path = self.root.find_dir(dir + '64') + files = path.ant_glob('*boost_*') + if files: + break + except: + path = None + if not path: + if libs: + self.fatal('libs not found in %s' % libs) + else: + self.fatal('libs not found, \ + use --boost-includes=/path/to/boost/lib') + return path, files + + +@conf +def boost_get_libs(self, *k, **kw): + ''' + return the lib path and the required libs + according to the parameters + ''' + path, files = self.__boost_get_libs_path(**kw) + t = [] + if kw.get('mt', False): + t.append('mt') + if kw.get('abi', None): + t.append(kw['abi']) + tags = t and '(-%s)+' % '-'.join(t) or '' + toolset = '(-%s[0-9]{0,3})+' % self.boost_get_toolset(kw.get('toolset', '')) + version = '(-%s)+' % self.env.BOOST_VERSION + + def find_lib(re_lib, files): + for file in files: + if re_lib.search(file.name): + return file + return None + + def format_lib_name(name): + if name.startswith('lib'): + name = name[3:] + return name.split('.')[0] + + libs = [] + for lib in Utils.to_list(k and k[0] or kw.get('lib', None)): + py = (lib == 'python') and '(-py%s)+' % kw['python'] or '' + # Trying libraries, from most strict match to least one + for pattern in ['boost_%s%s%s%s%s' % (lib, toolset, tags, py, version), + 'boost_%s%s%s%s' % (lib, tags, py, version), + 'boost_%s%s%s' % (lib, tags, version), + # Give up trying to find the right version + 'boost_%s%s%s%s' % (lib, toolset, tags, py), + 'boost_%s%s%s' % (lib, tags, py), + 'boost_%s%s' % (lib, tags)]: + file = find_lib(re.compile(pattern), files) + if file: + libs.append(format_lib_name(file.name)) + break + else: + self.fatal('lib %s not found in %s' % (lib, path)) + + return path.abspath(), libs + + +@conf +def check_boost(self, *k, **kw): + """ + initialize boost + + You can pass the same parameters as the command line (without "--boost-"), + but the command line has the priority. + """ + if not self.env['CXX']: + self.fatal('load a c++ compiler first, conf.load("compiler_cxx")') + + params = {'lib': k and k[0] or kw.get('lib', None)} + for key, value in self.options.__dict__.items(): + if not key.startswith('boost_'): + continue + key = key[len('boost_'):] + params[key] = value and value or kw.get(key, '') + + var = kw.get('uselib_store', 'BOOST') + + self.start_msg('Checking boost includes') + self.env['INCLUDES_%s' % var] = self.boost_get_includes(**params) + self.env.BOOST_VERSION = self.boost_get_version(self.env['INCLUDES_%s' % var]) + self.end_msg(self.env.BOOST_VERSION) + if Logs.verbose: + Logs.pprint('CYAN', ' path : %s' % self.env['INCLUDES_%s' % var]) + + if not params['lib']: + return + self.start_msg('Checking boost libs') + suffix = params.get('static', 'ST') or '' + path, libs = self.boost_get_libs(**params) + self.env['%sLIBPATH_%s' % (suffix, var)] = [path] + self.env['%sLIB_%s' % (suffix, var)] = libs + self.end_msg('ok') + if Logs.verbose: + Logs.pprint('CYAN', ' path : %s' % path) + Logs.pprint('CYAN', ' libs : %s' % libs) + diff --git a/waflib/extras/c_bgxlc.py b/waflib/extras/c_bgxlc.py new file mode 100644 index 00000000..7998f171 --- /dev/null +++ b/waflib/extras/c_bgxlc.py @@ -0,0 +1,30 @@ +#! /usr/bin/env python +# encoding: utf-8 +# harald at klimachs.de + +import os +from waflib.Tools import ccroot,ar +from waflib.Configure import conf + +from waflib.Tools import xlc # method xlc_common_flags +from waflib.Tools.compiler_c import c_compiler +c_compiler['linux'].insert(0, 'c_bgxlc') + +@conf +def find_bgxlc(conf): + cc = conf.find_program(['bgxlc_r','bgxlc'], var='CC') + cc = conf.cmd_to_list(cc) + conf.get_xlc_version(cc) + conf.env.CC = cc + conf.env.CC_NAME = 'bgxlc' + +def configure(conf): + conf.find_bgxlc() + conf.find_ar() + conf.xlc_common_flags() + conf.env.LINKFLAGS_cshlib = ['-G','-Wl,-bexpfull'] + conf.env.LINKFLAGS_cprogram = [] + conf.cc_load_tools() + conf.cc_add_flags() + conf.link_add_flags() + diff --git a/waflib/extras/c_dumbpreproc.py b/waflib/extras/c_dumbpreproc.py new file mode 100644 index 00000000..62463019 --- /dev/null +++ b/waflib/extras/c_dumbpreproc.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +""" +Dumb C/C++ preprocessor for finding dependencies + +It will look at all include files it can find after removing the comments, so the following +will always add the dependency on both "a.h" and "b.h":: + + #include "a.h" + #ifdef B + #include "b.h" + #endif + int main() { + return 0; + } + +To use:: + + def configure(conf): + conf.load('compiler_c') + conf.load('c_dumbpreproc') +""" + +import re, sys, os, string, traceback +from waflib import Logs, Build, Utils, Errors +from waflib.Logs import debug, error +from waflib.Tools import c_preproc + +re_inc = re.compile( + '^[ \t]*(#|%:)[ \t]*(include)[ \t]*[<"](.*)[>"]\r*$', + re.IGNORECASE | re.MULTILINE) + +def lines_includes(node): + code = node.read() + if c_preproc.use_trigraphs: + for (a, b) in c_preproc.trig_def: code = code.split(a).join(b) + code = c_preproc.re_nl.sub('', code) + code = c_preproc.re_cpp.sub(c_preproc.repl, code) + return [(m.group(2), m.group(3)) for m in re.finditer(re_inc, code)] + +parser = c_preproc.c_parser +class dumb_parser(parser): + def addlines(self, node): + if node in self.nodes[:-1]: + return + self.currentnode_stack.append(node.parent) + self.lines = lines_includes(node) + [(c_preproc.POPFILE, '')] + self.lines + + def start(self, node, env): + self.addlines(node) + while self.lines: + (x, y) = self.lines.pop(0) + if x == c_preproc.POPFILE: + self.currentnode_stack.pop() + continue + self.tryfind(y) + +c_preproc.c_parser = dumb_parser + diff --git a/waflib/extras/compat15.py b/waflib/extras/compat15.py new file mode 100644 index 00000000..6e3f7fe9 --- /dev/null +++ b/waflib/extras/compat15.py @@ -0,0 +1,298 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +""" +This file is provided to enable compatibility with waf 1.5, it will be removed in waf 1.7 +""" + +import sys +from waflib import ConfigSet, Logs, Options, Scripting, Task, Build, Configure, Node, Runner, TaskGen, Utils, Errors, Context + +# the following is to bring some compatibility with waf 1.5 "import waflib.Configure → import Configure" +sys.modules['Environment'] = ConfigSet +ConfigSet.Environment = ConfigSet.ConfigSet + +sys.modules['Logs'] = Logs +sys.modules['Options'] = Options +sys.modules['Scripting'] = Scripting +sys.modules['Task'] = Task +sys.modules['Build'] = Build +sys.modules['Configure'] = Configure +sys.modules['Node'] = Node +sys.modules['Runner'] = Runner +sys.modules['TaskGen'] = TaskGen +sys.modules['Utils'] = Utils + +from waflib.Tools import c_preproc +sys.modules['preproc'] = c_preproc + +from waflib.Tools import c_config +sys.modules['config_c'] = c_config + +ConfigSet.ConfigSet.copy = ConfigSet.ConfigSet.derive +ConfigSet.ConfigSet.set_variant = Utils.nada + +Build.BuildContext.add_subdirs = Build.BuildContext.recurse +Build.BuildContext.new_task_gen = Build.BuildContext.__call__ +Build.BuildContext.is_install = 0 +Node.Node.relpath_gen = Node.Node.path_from + +def name_to_obj(self, s, env=None): + Logs.warn('compat: change "name_to_obj(name, env)" by "get_tgen_by_name(name)"') + return self.get_tgen_by_name(s) +Build.BuildContext.name_to_obj = name_to_obj + +def env_of_name(self, name): + try: + return self.all_envs[name] + except KeyError: + Logs.error('no such environment: '+name) + return None +Build.BuildContext.env_of_name = env_of_name + + +def set_env_name(self, name, env): + self.all_envs[name] = env + return env +Configure.ConfigurationContext.set_env_name = set_env_name + +def retrieve(self, name, fromenv=None): + try: + env = self.all_envs[name] + except KeyError: + env = ConfigSet.ConfigSet() + self.prepare_env(env) + self.all_envs[name] = env + else: + if fromenv: Logs.warn("The environment %s may have been configured already" % name) + return env +Configure.ConfigurationContext.retrieve = retrieve + +Configure.ConfigurationContext.sub_config = Configure.ConfigurationContext.recurse +Configure.ConfigurationContext.check_tool = Configure.ConfigurationContext.load +Configure.conftest = Configure.conf +Configure.ConfigurationError = Errors.ConfigurationError + +Options.OptionsContext.sub_options = Options.OptionsContext.recurse +Options.OptionsContext.tool_options = Context.Context.load +Options.Handler = Options.OptionsContext + +Task.simple_task_type = Task.task_type_from_func = Task.task_factory +Task.TaskBase.classes = Task.classes + +def setitem(self, key, value): + if key.startswith('CCFLAGS'): + key = key[1:] + self.table[key] = value +ConfigSet.ConfigSet.__setitem__ = setitem + +@TaskGen.feature('d') +@TaskGen.before('apply_incpaths') +def old_importpaths(self): + if getattr(self, 'importpaths', []): + self.includes = self.importpaths + +from waflib import Context +eld = Context.load_tool +def load_tool(*k, **kw): + ret = eld(*k, **kw) + if 'set_options' in ret.__dict__: + Logs.warn('compat: rename "set_options" to options') + ret.options = ret.set_options + if 'detect' in ret.__dict__: + Logs.warn('compat: rename "detect" to "configure"') + ret.configure = ret.detect + return ret +Context.load_tool = load_tool + +rev = Context.load_module +def load_module(path): + ret = rev(path) + if 'set_options' in ret.__dict__: + Logs.warn('compat: rename "set_options" to "options" (%r)' % path) + ret.options = ret.set_options + if 'srcdir' in ret.__dict__: + Logs.warn('compat: rename "srcdir" to "top" (%r)' % path) + ret.top = ret.srcdir + if 'blddir' in ret.__dict__: + Logs.warn('compat: rename "blddir" to "out" (%r)' % path) + ret.out = ret.blddir + return ret +Context.load_module = load_module + +old_post = TaskGen.task_gen.post +def post(self): + self.features = self.to_list(self.features) + if 'cc' in self.features: + Logs.warn('compat: the feature cc does not exist anymore (use "c")') + self.features.remove('cc') + self.features.append('c') + if 'cstaticlib' in self.features: + Logs.warn('compat: the feature cstaticlib does not exist anymore (use "cstlib" or "cxxstlib")') + self.features.remove('cstaticlib') + self.features.append(('cxx' in self.features) and 'cxxstlib' or 'cstlib') + if getattr(self, 'ccflags', None): + Logs.warn('compat: "ccflags" was renamed to "cflags"') + self.cflags = self.ccflags + return old_post(self) +TaskGen.task_gen.post = post + +def waf_version(*k, **kw): + Logs.warn('wrong version (waf_version was removed in waf 1.6)') +Utils.waf_version = waf_version + + +import os +@TaskGen.feature('c', 'cxx', 'd') +@TaskGen.before('apply_incpaths', 'propagate_uselib_vars') +@TaskGen.after('apply_link', 'process_source') +def apply_uselib_local(self): + """ + process the uselib_local attribute + execute after apply_link because of the execution order set on 'link_task' + """ + env = self.env + from waflib.Tools.ccroot import stlink_task + + # 1. the case of the libs defined in the project (visit ancestors first) + # the ancestors external libraries (uselib) will be prepended + self.uselib = self.to_list(getattr(self, 'uselib', [])) + self.includes = self.to_list(getattr(self, 'includes', [])) + names = self.to_list(getattr(self, 'uselib_local', [])) + get = self.bld.get_tgen_by_name + seen = set([]) + tmp = Utils.deque(names) # consume a copy of the list of names + if tmp: + Logs.warn('compat: "uselib_local" is deprecated, replace by "use"') + while tmp: + lib_name = tmp.popleft() + # visit dependencies only once + if lib_name in seen: + continue + + y = get(lib_name) + y.post() + seen.add(lib_name) + + # object has ancestors to process (shared libraries): add them to the end of the list + if getattr(y, 'uselib_local', None): + for x in self.to_list(getattr(y, 'uselib_local', [])): + obj = get(x) + obj.post() + if getattr(obj, 'link_task', None): + if not isinstance(obj.link_task, stlink_task): + tmp.append(x) + + # link task and flags + if getattr(y, 'link_task', None): + + link_name = y.target[y.target.rfind(os.sep) + 1:] + if isinstance(y.link_task, stlink_task): + env.append_value('STLIB', [link_name]) + else: + # some linkers can link against programs + env.append_value('LIB', [link_name]) + + # the order + self.link_task.set_run_after(y.link_task) + + # for the recompilation + self.link_task.dep_nodes += y.link_task.outputs + + # add the link path too + tmp_path = y.link_task.outputs[0].parent.bldpath() + if not tmp_path in env['LIBPATH']: + env.prepend_value('LIBPATH', [tmp_path]) + + # add ancestors uselib too - but only propagate those that have no staticlib defined + for v in self.to_list(getattr(y, 'uselib', [])): + if not env['STLIB_' + v]: + if not v in self.uselib: + self.uselib.insert(0, v) + + # if the library task generator provides 'export_includes', add to the include path + # the export_includes must be a list of paths relative to the other library + if getattr(y, 'export_includes', None): + self.includes.extend(y.to_incnodes(y.export_includes)) + +@TaskGen.feature('cprogram', 'cxxprogram', 'cstlib', 'cxxstlib', 'cshlib', 'cxxshlib', 'dprogram', 'dstlib', 'dshlib') +@TaskGen.after('apply_link') +def apply_objdeps(self): + "add the .o files produced by some other object files in the same manner as uselib_local" + names = getattr(self, 'add_objects', []) + if not names: + return + names = self.to_list(names) + + get = self.bld.get_tgen_by_name + seen = [] + while names: + x = names[0] + + # visit dependencies only once + if x in seen: + names = names[1:] + continue + + # object does not exist ? + y = get(x) + + # object has ancestors to process first ? update the list of names + if getattr(y, 'add_objects', None): + added = 0 + lst = y.to_list(y.add_objects) + lst.reverse() + for u in lst: + if u in seen: continue + added = 1 + names = [u]+names + if added: continue # list of names modified, loop + + # safe to process the current object + y.post() + seen.append(x) + + for t in getattr(y, 'compiled_tasks', []): + self.link_task.inputs.extend(t.outputs) + +@TaskGen.after('apply_link') +def process_obj_files(self): + if not hasattr(self, 'obj_files'): + return + for x in self.obj_files: + node = self.path.find_resource(x) + self.link_task.inputs.append(node) + +@TaskGen.taskgen_method +def add_obj_file(self, file): + """Small example on how to link object files as if they were source + obj = bld.create_obj('cc') + obj.add_obj_file('foo.o')""" + if not hasattr(self, 'obj_files'): self.obj_files = [] + if not 'process_obj_files' in self.meths: self.meths.append('process_obj_files') + self.obj_files.append(file) + + +old_define = Configure.ConfigurationContext.__dict__['define'] + +@Configure.conf +def define(self, key, val, quote=True): + old_define(self, key, val, quote) + if key.startswith('HAVE_'): + self.env[key] = 1 + +old_undefine = Configure.ConfigurationContext.__dict__['undefine'] + +@Configure.conf +def undefine(self, key): + old_undefine(self, key) + if key.startswith('HAVE_'): + self.env[key] = 0 + +# some people might want to use export_incdirs, but it was renamed +def set_incdirs(self, val): + Logs.warn('compat: change "export_incdirs" by "export_includes"') + self.export_includes = val +TaskGen.task_gen.export_incdirs = property(None, set_incdirs) + diff --git a/waflib/extras/cython.py b/waflib/extras/cython.py new file mode 100644 index 00000000..6be57ed4 --- /dev/null +++ b/waflib/extras/cython.py @@ -0,0 +1,120 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 + +import re + +import waflib +import waflib.Logs as _msg +from waflib import Task +from waflib.TaskGen import extension, feature, before_method, after_method + +cy_api_pat = re.compile(r'\s*?cdef\s*?(public|api)\w*') +re_cyt = re.compile('import\\s(\\w+)\\s*$', re.M) + +@extension('.pyx') +def add_cython_file(self, node): + """ + Process a *.pyx* file given in the list of source files. No additional + feature is required:: + + def build(bld): + bld(features='c cshlib pyext', source='main.c foo.pyx', target='app') + """ + ext = '.c' + if 'cxx' in self.features: + self.env.append_unique('CYTHONFLAGS', '--cplus') + ext = '.cc' + tsk = self.create_task('cython', node, node.change_ext(ext)) + self.source += tsk.outputs + +class cython(Task.Task): + run_str = '${CYTHON} ${CYTHONFLAGS} -o ${TGT[0].abspath()} ${SRC}' + color = 'GREEN' + + vars = ['INCLUDES'] + """ + Rebuild whenever the INCLUDES change. The variables such as CYTHONFLAGS will be appended + by the metaclass. + """ + + ext_out = ['.h'] + """ + The creation of a .h file is known only after the build has begun, so it is not + possible to compute a build order just by looking at the task inputs/outputs. + """ + + def runnable_status(self): + """ + Perform a double-check to add the headers created by cython + to the output nodes. The scanner is executed only when the cython task + must be executed (optimization). + """ + ret = super(cython, self).runnable_status() + if ret == Task.ASK_LATER: + return ret + for x in self.generator.bld.raw_deps[self.uid()]: + if x.startswith('header:'): + self.outputs.append(self.inputs[0].parent.find_or_declare(x.replace('header:', ''))) + return super(cython, self).runnable_status() + + def scan(self): + """ + Return the dependent files (.pxd) by looking in the include folders. + Put the headers to generate in the custom list "bld.raw_deps". + To inspect the scanne results use:: + + $ waf clean build --zones=deps + """ + txt = self.inputs[0].read() + + mods = [] + for m in re_cyt.finditer(txt): + mods.append(m.group(1)) + + _msg.debug("cython: mods %r" % mods) + incs = getattr(self.generator, 'cython_includes', []) + incs = [self.generator.path.find_dir(x) for x in incs] + incs.append(self.inputs[0].parent) + + found = [] + missing = [] + for x in mods: + for y in incs: + k = y.find_resource(x + '.pxd') + if k: + found.append(k) + break + else: + missing.append(x) + _msg.debug("cython: found %r" % found) + + # Now the .h created - store them in bld.raw_deps for later use + has_api = False + has_public = False + for l in txt.splitlines(): + if cy_api_pat.match(l): + if ' api ' in l: + has_api = True + if ' public ' in l: + has_public = True + name = self.inputs[0].name.replace('.pyx', '') + if has_api: + missing.append('header:%s_api.h' % name) + if has_public: + missing.append('header:%s.h' % name) + + return (found, missing) + +def options(ctx): + ctx.add_option('--cython-flags', action='store', default='', help='space separated list of flags to pass to cython') + +def configure(ctx): + if not ctx.env.CC and not ctx.env.CXX: + ctx.fatal('Load a C/C++ compiler first') + if not ctx.env.PYTHON: + ctx.fatal('Load the python tool first!') + ctx.find_program('cython', var='CYTHON') + if ctx.options.cython_flags: + ctx.env.CYTHONFLAGS = ctx.options.cython_flags + diff --git a/waflib/extras/dcc.py b/waflib/extras/dcc.py new file mode 100644 index 00000000..93fbf0ae --- /dev/null +++ b/waflib/extras/dcc.py @@ -0,0 +1,79 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Jérôme Carretero, 2011 (zougloub) + +from waflib import Configure, Options, Utils +from waflib.Tools import ccroot +from waflib.Configure import conf + +@conf +def find_dcc(conf): + cc = conf.find_program(['dcc'], var='CC', path_list=getattr(Options.options, 'diabbindir', "")) + cc = conf.cmd_to_list(cc) + conf.env.CC_NAME = 'dcc' + conf.env.CC = cc + +@conf +def find_dld(conf): + ld = conf.find_program(['dld'], var='LINK_CC', path_list=getattr(Options.options, 'diabbindir', "")) + ld = conf.cmd_to_list(ld) + conf.env.LINK_CC_NAME = 'dld' + conf.env.LINK_CC = ld + +@conf +def find_dar(conf): + ar = conf.find_program(['dar'], var='DAR', path_list=getattr(Options.options, 'diabbindir', "")) + ar = conf.cmd_to_list(ar) + conf.env.AR = ar + conf.env.AR_NAME = 'dar' + conf.env.ARFLAGS = 'rcs' + +@conf +def find_ddump(conf): + prg = conf.find_program(['ddump'], var='DDUMP', path_list=getattr(Options.options, 'diabbindir', "")) + prg = conf.cmd_to_list(prg) + conf.env.DDUMP = prg + +@conf +def dcc_common_flags(conf): + v = conf.env + v['CC_SRC_F'] = [] + v['CC_TGT_F'] = ['-c', '-o'] + + # linker + if not v['LINK_CC']: v['LINK_CC'] = v['CC'] + v['CCLNK_SRC_F'] = [] + v['CCLNK_TGT_F'] = ['-o'] + v['CPPPATH_ST'] = '-I%s' + v['DEFINES_ST'] = '-D%s' + + v['LIB_ST'] = '-l:%s' # template for adding libs + v['LIBPATH_ST'] = '-L%s' # template for adding libpaths + v['STLIB_ST'] = '-l:%s' + v['STLIBPATH_ST'] = '-L%s' + v['RPATH_ST'] = '-Wl,-rpath,%s' + #v['STLIB_MARKER'] = '-Wl,-Bstatic' + + # program + v['cprogram_PATTERN'] = '%s.elf' + + # static lib + v['LINKFLAGS_cstlib'] = ['-Wl,-Bstatic'] + v['cstlib_PATTERN'] = 'lib%s.a' + +def configure(conf): + conf.find_dcc() + conf.find_dar() + conf.find_dld() + conf.find_ddump() + conf.dcc_common_flags() + conf.cc_load_tools() + conf.cc_add_flags() + conf.link_add_flags() + +def options(opt): + """ + Add the ``--with-diab-bindir`` command-line options. + """ + opt.add_option('--with-diab-bindir', type='string', dest='diabbindir', help = 'Specify alternate diab bin folder', default="") + diff --git a/waflib/extras/doxygen.py b/waflib/extras/doxygen.py new file mode 100644 index 00000000..a62c5a8e --- /dev/null +++ b/waflib/extras/doxygen.py @@ -0,0 +1,158 @@ +#! /usr/bin/env python +# encoding: UTF-8 +# Thomas Nagy 2008-2010 (ita) + +""" + +Doxygen support + +Variables passed to bld(): +* doxyfile -- the Doxyfile to use + +ported from waf 1.5 (incomplete) +""" + +from fnmatch import fnmatchcase +import os, os.path, re, stat +from waflib import Task, Utils, Node, Logs +from waflib.TaskGen import feature + +DOXY_STR = '${DOXYGEN} - ' +DOXY_FMTS = 'html latex man rft xml'.split() +DOXY_FILE_PATTERNS = '*.' + ' *.'.join(''' +c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx hpp h++ idl odl cs php php3 +inc m mm py f90c cc cxx cpp c++ java ii ixx ipp i++ inl h hh hxx +'''.split()) + +re_nl = re.compile('\\\\\r*\n', re.MULTILINE) + +class doxygen(Task.Task): + vars = ['DOXYGEN', 'DOXYFLAGS'] + color = 'BLUE' + + def runnable_status(self): + ''' + self.pars are populated in runnable_status - because this function is being + run *before* both self.pars "consumers" - scan() and run() + + set output_dir (node) for the output + ''' + + for x in self.run_after: + if not x.hasrun: + return Task.ASK_LATER + + if not getattr(self, 'pars', None): + txt = self.inputs[0].read() + txt = re_nl.sub('', txt) + self.pars = Utils.str_to_dict(txt) + if not self.pars.get('OUTPUT_DIRECTORY'): + self.pars['OUTPUT_DIRECTORY'] = self.inputs[0].parent.get_bld().abspath() + if not self.pars.get('INPUT'): + self.pars['INPUT'] = self.inputs[0].parent.abspath() + + if not getattr(self, 'output_dir', None): + self.output_dir = self.generator.bld.root.find_dir(self.pars['OUTPUT_DIRECTORY']) + + self.signature() + return Task.Task.runnable_status(self) + + def scan(self): + if self.pars.get('RECURSIVE') == 'YES': + Logs.warn("Doxygen RECURSIVE dependencies are not supported") + + inputs = self.pars.get('INPUT').split() + exclude_patterns = self.pars.get('EXCLUDE_PATTERNS', '').split() + file_patterns = self.pars.get('FILE_PATTERNS', '').split() + if not file_patterns: + file_patterns = DOXY_FILE_PATTERNS + + nodes = [] + names = [] + for i in inputs: + node = self.generator.bld.root.make_node(i) + if node: + if os.path.isdir(node.abspath()): + for m in node.ant_glob(file_patterns): + nodes.append(self.generator.bld.root.make_node(m.abspath())) + else: + nodes.append(node) + else: + names.append(i) + + return (nodes, names) + + def run(self): + code = '\n'.join(['%s = %s' % (x, self.pars[x]) for x in self.pars]) + code = code.encode() # for python 3 + #fmt = DOXY_STR % (self.inputs[0].parent.abspath()) + cmd = Utils.subst_vars(DOXY_STR, self.env) + proc = Utils.subprocess.Popen(cmd, shell=True, stdin=Utils.subprocess.PIPE) + proc.communicate(code) + return proc.returncode + + def post_run(self): + nodes = self.output_dir.ant_glob('**/*') + for x in nodes: + x.sig = Utils.h_file(x.abspath()) + self.outputs += nodes + return Task.Task.post_run(self) + + #def install(self): + # if getattr(self.generator, 'install_to', None): + # update_build_dir(self.inputs[0].parent, self.env) + # pattern = getattr(self, 'instype', 'html/*') + # self.generator.bld.install_files(self.generator.install_to, self.generator.path.ant_glob(pattern, dir=0, src=0)) + +class tar(Task.Task): + "quick tar creation" + run_str = '${TAR} ${TAROPTS} ${TGT} ${SRC}' + color = 'RED' + after = ['doxygen'] + def runnable_status(self): + for x in getattr(self, 'input_tasks', []): + if not x.hasrun: + return Task.ASK_LATER + + if not getattr(self, 'tar_done_adding', None): + # execute this only once + self.tar_done_adding = True + for x in getattr(self, 'input_tasks', []): + self.set_inputs(x.outputs) + if not self.inputs: + return Task.SKIP_ME + return Task.Task.runnable_status(self) + + def __str__(self): + tgt_str = ' '.join([a.nice_path(self.env) for a in self.outputs]) + return '%s: %s\n' % (self.__class__.__name__, tgt_str) + +@feature('doxygen') +def process_doxy(self): + if not getattr(self, 'doxyfile', None): + self.generator.bld.fatal('no doxyfile??') + + node = self.doxyfile + if not isinstance(node, Node.Node): + node = self.path.find_resource(node) + if not node: + raise ValueError('doxygen file not found') + + # the task instance + dsk = self.create_task('doxygen', node) + + if getattr(self, 'doxy_tar', None): + tsk = self.create_task('tar') + tsk.input_tasks = [dsk] + tsk.set_outputs(self.path.find_or_declare(self.doxy_tar)) + if self.doxy_tar.endswith('bz2'): + tsk.env['TAROPTS'] = ['cjf'] + elif self.doxy_tar.endswith('gz'): + tsk.env['TAROPTS'] = ['czf'] + else: + tsk.env['TAROPTS'] = ['cf'] + +def configure(conf): + conf.find_program('doxygen', var='DOXYGEN') + conf.find_program('tar', var='TAR') + diff --git a/waflib/extras/dumbpreproc.py b/waflib/extras/dumbpreproc.py new file mode 100644 index 00000000..62463019 --- /dev/null +++ b/waflib/extras/dumbpreproc.py @@ -0,0 +1,61 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +""" +Dumb C/C++ preprocessor for finding dependencies + +It will look at all include files it can find after removing the comments, so the following +will always add the dependency on both "a.h" and "b.h":: + + #include "a.h" + #ifdef B + #include "b.h" + #endif + int main() { + return 0; + } + +To use:: + + def configure(conf): + conf.load('compiler_c') + conf.load('c_dumbpreproc') +""" + +import re, sys, os, string, traceback +from waflib import Logs, Build, Utils, Errors +from waflib.Logs import debug, error +from waflib.Tools import c_preproc + +re_inc = re.compile( + '^[ \t]*(#|%:)[ \t]*(include)[ \t]*[<"](.*)[>"]\r*$', + re.IGNORECASE | re.MULTILINE) + +def lines_includes(node): + code = node.read() + if c_preproc.use_trigraphs: + for (a, b) in c_preproc.trig_def: code = code.split(a).join(b) + code = c_preproc.re_nl.sub('', code) + code = c_preproc.re_cpp.sub(c_preproc.repl, code) + return [(m.group(2), m.group(3)) for m in re.finditer(re_inc, code)] + +parser = c_preproc.c_parser +class dumb_parser(parser): + def addlines(self, node): + if node in self.nodes[:-1]: + return + self.currentnode_stack.append(node.parent) + self.lines = lines_includes(node) + [(c_preproc.POPFILE, '')] + self.lines + + def start(self, node, env): + self.addlines(node) + while self.lines: + (x, y) = self.lines.pop(0) + if x == c_preproc.POPFILE: + self.currentnode_stack.pop() + continue + self.tryfind(y) + +c_preproc.c_parser = dumb_parser + diff --git a/waflib/extras/eclipse.py b/waflib/extras/eclipse.py new file mode 100644 index 00000000..b109697e --- /dev/null +++ b/waflib/extras/eclipse.py @@ -0,0 +1,308 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Eclipse CDT 5.0 generator for Waf +# Richard Quirk 2009-1011 (New BSD License) +# Thomas Nagy 2011 (ported to Waf 1.6) + +""" +Usage: + +def options(opt): + opt.load('eclipse') + +$ waf configure eclipse +""" + +import sys, os +from waflib import Utils, Logs, Context, Options, Build, TaskGen, Scripting +from xml.dom.minidom import Document + +oe_cdt = 'org.eclipse.cdt' +cdt_mk = oe_cdt + '.make.core' +cdt_core = oe_cdt + '.core' +cdt_bld = oe_cdt + '.build.core' + +class eclipse(Build.BuildContext): + cmd = 'eclipse' + fun = Scripting.default_cmd + + def execute(self): + """ + Entry point + """ + self.restore() + if not self.all_envs: + self.load_envs() + self.recurse([self.run_dir]) + + appname = getattr(Context.g_module, Context.APPNAME, os.path.basename(self.srcnode.abspath())) + self.create_cproject(appname, pythonpath=self.env['ECLIPSE_PYTHON_PATH']) + + def create_cproject(self, appname, workspace_includes=[], pythonpath=[]): + """ + Create the Eclipse CDT .project and .cproject files + @param appname The name that will appear in the Project Explorer + @param build The BuildContext object to extract includes from + @param workspace_includes Optional project includes to prevent + "Unresolved Inclusion" errors in the Eclipse editor + @param pythonpath Optional project specific python paths + """ + source_dirs = [] + cpppath = self.env['CPPPATH'] + Logs.warn('Generating Eclipse CDT project files') + + for g in self.groups: + for tg in g: + if not isinstance(tg, TaskGen.task_gen): + continue + + tg.post() + if not getattr(tg, 'link_task', None): + continue + + l = Utils.to_list(getattr(tg, "includes", '')) + sources = Utils.to_list(getattr(tg, 'source', '')) + features = Utils.to_list(getattr(tg, 'features', '')) + + is_cc = 'c' in features or 'cxx' in features + + bldpath = tg.path.bldpath() + + base = os.path.normpath(os.path.join(self.bldnode.name, tg.path.srcpath())) + + if is_cc: + sources_dirs = set([src.parent for src in tg.to_nodes(sources)]) + + incnodes = tg.to_incnodes(tg.to_list(getattr(tg, 'includes', [])) + tg.env['INCLUDES']) + for p in incnodes: + path = p.path_from(self.srcnode) + workspace_includes.append(path) + + if is_cc and path not in source_dirs: + source_dirs.append(path) + + project = self.impl_create_project(sys.executable, appname) + self.srcnode.make_node('.project').write(project.toprettyxml()) + + waf = os.path.abspath(sys.argv[0]) + project = self.impl_create_cproject(sys.executable, waf, appname, workspace_includes, cpppath, source_dirs) + self.srcnode.make_node('.cproject').write(project.toprettyxml()) + + project = self.impl_create_pydevproject(appname, sys.path, pythonpath) + self.srcnode.make_node('.pydevproject').write(project.toprettyxml()) + + def impl_create_project(self, executable, appname): + doc = Document() + projectDescription = doc.createElement('projectDescription') + self.add(doc, projectDescription, 'name', appname) + self.add(doc, projectDescription, 'comment') + self.add(doc, projectDescription, 'projects') + buildSpec = self.add(doc, projectDescription, 'buildSpec') + buildCommand = self.add(doc, buildSpec, 'buildCommand') + self.add(doc, buildCommand, 'name', oe_cdt + '.managedbuilder.core.genmakebuilder') + self.add(doc, buildCommand, 'triggers', 'clean,full,incremental,') + arguments = self.add(doc, buildCommand, 'arguments') + # the default make-style targets are overwritten by the .cproject values + dictionaries = { + cdt_mk + '.contents': cdt_mk + '.activeConfigSettings', + cdt_mk + '.enableAutoBuild': 'false', + cdt_mk + '.enableCleanBuild': 'true', + cdt_mk + '.enableFullBuild': 'true', + } + for k, v in dictionaries.items(): + self.addDictionary(doc, arguments, k, v) + + natures = self.add(doc, projectDescription, 'natures') + nature_list = """ + core.ccnature + managedbuilder.core.ScannerConfigNature + managedbuilder.core.managedBuildNature + core.cnature + """.split() + for n in nature_list: + self.add(doc, natures, 'nature', oe_cdt + '.' + n) + + self.add(doc, natures, 'nature', 'org.python.pydev.pythonNature') + + doc.appendChild(projectDescription) + return doc + + def impl_create_cproject(self, executable, waf, appname, workspace_includes, cpppath, source_dirs=[]): + doc = Document() + doc.appendChild(doc.createProcessingInstruction('fileVersion', '4.0.0')) + cconf_id = cdt_core + '.default.config.1' + cproject = doc.createElement('cproject') + storageModule = self.add(doc, cproject, 'storageModule', + {'moduleId': cdt_core + '.settings'}) + cconf = self.add(doc, storageModule, 'cconfiguration', {'id':cconf_id}) + + storageModule = self.add(doc, cconf, 'storageModule', + {'buildSystemId': oe_cdt + '.managedbuilder.core.configurationDataProvider', + 'id': cconf_id, + 'moduleId': cdt_core + '.settings', + 'name': 'Default'}) + + self.add(doc, storageModule, 'externalSettings') + + extensions = self.add(doc, storageModule, 'extensions') + extension_list = """ + VCErrorParser + MakeErrorParser + GCCErrorParser + GASErrorParser + GLDErrorParser + """.split() + ext = self.add(doc, extensions, 'extension', + {'id': cdt_core + '.ELF', 'point':cdt_core + '.BinaryParser'}) + for e in extension_list: + ext = self.add(doc, extensions, 'extension', + {'id': cdt_core + '.' + e, 'point':cdt_core + '.ErrorParser'}) + + storageModule = self.add(doc, cconf, 'storageModule', + {'moduleId': 'cdtBuildSystem', 'version': '4.0.0'}) + config = self.add(doc, storageModule, 'configuration', + {'artifactName': appname, + 'id': cconf_id, + 'name': 'Default', + 'parent': cdt_bld + '.prefbase.cfg'}) + folderInfo = self.add(doc, config, 'folderInfo', + {'id': cconf_id+'.', 'name': '/', 'resourcePath': ''}) + + toolChain = self.add(doc, folderInfo, 'toolChain', + {'id': cdt_bld + '.prefbase.toolchain.1', + 'name': 'No ToolChain', + 'resourceTypeBasedDiscovery': 'false', + 'superClass': cdt_bld + '.prefbase.toolchain'}) + + targetPlatform = self.add(doc, toolChain, 'targetPlatform', + { 'binaryParser': 'org.eclipse.cdt.core.ELF', + 'id': cdt_bld + '.prefbase.toolchain.1', 'name': ''}) + + waf_build = '"%s" %s'%(waf, eclipse.fun) + waf_clean = '"%s" clean'%(waf) + builder = self.add(doc, toolChain, 'builder', + {'autoBuildTarget': waf_build, + 'command': executable, + 'enableAutoBuild': 'false', + 'cleanBuildTarget': waf_clean, + 'enableIncrementalBuild': 'true', + 'id': cdt_bld + '.settings.default.builder.1', + 'incrementalBuildTarget': waf_build, + 'managedBuildOn': 'false', + 'name': 'Gnu Make Builder', + 'superClass': cdt_bld + '.settings.default.builder'}) + + for tool_name in ("Assembly", "GNU C++", "GNU C"): + tool = self.add(doc, toolChain, 'tool', + {'id': cdt_bld + '.settings.holder.1', + 'name': tool_name, + 'superClass': cdt_bld + '.settings.holder'}) + if cpppath or workspace_includes: + incpaths = cdt_bld + '.settings.holder.incpaths' + option = self.add(doc, tool, 'option', + {'id': incpaths+'.1', + 'name': 'Include Paths', + 'superClass': incpaths, + 'valueType': 'includePath'}) + for i in workspace_includes: + self.add(doc, option, 'listOptionValue', + {'builtIn': 'false', + 'value': '"${workspace_loc:/%s/%s}"'%(appname, i)}) + for i in cpppath: + self.add(doc, option, 'listOptionValue', + {'builtIn': 'false', + 'value': '"%s"'%(i)}) + if source_dirs: + sourceEntries = self.add(doc, config, 'sourceEntries') + for i in source_dirs: + self.add(doc, sourceEntries, 'entry', + {'excluding': i, + 'flags': 'VALUE_WORKSPACE_PATH|RESOLVED', + 'kind': 'sourcePath', + 'name': ''}) + self.add(doc, sourceEntries, 'entry', + { + 'flags': 'VALUE_WORKSPACE_PATH|RESOLVED', + 'kind': 'sourcePath', + 'name': i}) + + storageModule = self.add(doc, cconf, 'storageModule', + {'moduleId': cdt_mk + '.buildtargets'}) + buildTargets = self.add(doc, storageModule, 'buildTargets') + def addTargetWrap(name, runAll): + return self.addTarget(doc, buildTargets, executable, name, + '"%s" %s'%(waf, name), runAll) + addTargetWrap('configure', True) + addTargetWrap('dist', False) + addTargetWrap('install', False) + addTargetWrap('check', False) + + storageModule = self.add(doc, cproject, 'storageModule', + {'moduleId': 'cdtBuildSystem', + 'version': '4.0.0'}) + + project = self.add(doc, storageModule, 'project', + {'id': '%s.null.1'%appname, 'name': appname}) + + doc.appendChild(cproject) + return doc + + def impl_create_pydevproject(self, appname, system_path, user_path): + # create a pydevproject file + doc = Document() + doc.appendChild(doc.createProcessingInstruction('eclipse-pydev', 'version="1.0"')) + pydevproject = doc.createElement('pydev_project') + prop = self.add(doc, pydevproject, + 'pydev_property', + 'python %d.%d'%(sys.version_info[0], sys.version_info[1])) + prop.setAttribute('name', 'org.python.pydev.PYTHON_PROJECT_VERSION') + prop = self.add(doc, pydevproject, 'pydev_property', 'Default') + prop.setAttribute('name', 'org.python.pydev.PYTHON_PROJECT_INTERPRETER') + # add waf's paths + wafadmin = [p for p in system_path if p.find('wafadmin') != -1] + if wafadmin: + prop = self.add(doc, pydevproject, 'pydev_pathproperty', + {'name':'org.python.pydev.PROJECT_EXTERNAL_SOURCE_PATH'}) + for i in wafadmin: + self.add(doc, prop, 'path', i) + if user_path: + prop = self.add(doc, pydevproject, 'pydev_pathproperty', + {'name':'org.python.pydev.PROJECT_SOURCE_PATH'}) + for i in user_path: + self.add(doc, prop, 'path', '/'+appname+'/'+i) + + doc.appendChild(pydevproject) + return doc + + def addDictionary(self, doc, parent, k, v): + dictionary = self.add(doc, parent, 'dictionary') + self.add(doc, dictionary, 'key', k) + self.add(doc, dictionary, 'value', v) + return dictionary + + def addTarget(self, doc, buildTargets, executable, name, buildTarget, runAllBuilders=True): + target = self.add(doc, buildTargets, 'target', + {'name': name, + 'path': '', + 'targetID': oe_cdt + '.build.MakeTargetBuilder'}) + self.add(doc, target, 'buildCommand', executable) + self.add(doc, target, 'buildArguments', None) + self.add(doc, target, 'buildTarget', buildTarget) + self.add(doc, target, 'stopOnError', 'true') + self.add(doc, target, 'useDefaultCommand', 'false') + self.add(doc, target, 'runAllBuilders', str(runAllBuilders).lower()) + + def add(self, doc, parent, tag, value = None): + el = doc.createElement(tag) + if (value): + if type(value) == type(str()): + el.appendChild(doc.createTextNode(value)) + elif type(value) == type(dict()): + self.setAttributes(el, value) + parent.appendChild(el) + return el + + def setAttributes(self, node, attrs): + for k, v in attrs.items(): + node.setAttribute(k, v) + diff --git a/waflib/extras/erlang.py b/waflib/extras/erlang.py new file mode 100644 index 00000000..4539ea9f --- /dev/null +++ b/waflib/extras/erlang.py @@ -0,0 +1,20 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +""" +Erlang support +""" + +from waflib import TaskGen + +TaskGen.declare_chain(name = 'erlc', + rule = '${ERLC} ${ERLC_FLAGS} ${SRC[0].abspath()} -o ${TGT[0].name}', + reentrant = False, + ext_in = '.erl', + ext_out = '.beam') + +def configure(conf): + conf.find_program('erlc', var='ERLC') + conf.env.ERLC_FLAGS = [] + diff --git a/waflib/extras/fc_bgxlf.py b/waflib/extras/fc_bgxlf.py new file mode 100644 index 00000000..9ba4b145 --- /dev/null +++ b/waflib/extras/fc_bgxlf.py @@ -0,0 +1,33 @@ +#! /usr/bin/env python +# encoding: utf-8 +# harald at klimachs.de + +import re +from waflib.Tools import fc, fc_config, fc_scan +from waflib.Configure import conf + +from waflib.Tools.compiler_fc import fc_compiler +fc_compiler['linux'].insert(0, 'fc_bgxlf') + +@conf +def find_bgxlf(conf): + fc = conf.find_program(['bgxlf2003_r','bgxlf2003'], var='FC') + fc = conf.cmd_to_list(fc) + conf.get_xlf_version(fc) + conf.env.FC_NAME = 'BGXLF' + +@conf +def bg_flags(self): + self.env.SONAME_ST = '' + self.env.FCSHLIB_MARKER = '' + self.env.FCSTLIB_MARKER = '' + self.env.FCFLAGS_fcshlib = ['-fPIC'] + self.env.LINKFLAGS_fcshlib = ['-G', '-Wl,-bexpfull'] + +def configure(conf): + conf.find_bgxlf() + conf.find_ar() + conf.fc_flags() + conf.xlf_flags() + conf.bg_flags() + diff --git a/waflib/extras/fc_cray.py b/waflib/extras/fc_cray.py new file mode 100644 index 00000000..2100217b --- /dev/null +++ b/waflib/extras/fc_cray.py @@ -0,0 +1,50 @@ +#! /usr/bin/env python +# encoding: utf-8 +# harald at klimachs.de + +import re +from waflib import Utils +from waflib.Tools import fc, fc_config, fc_scan +from waflib.Configure import conf + +from waflib.Tools.compiler_fc import fc_compiler +fc_compiler['linux'].append('fc_cray') + +@conf +def find_crayftn(conf): + """Find the Cray fortran compiler (will look in the environment variable 'FC')""" + fc = conf.find_program(['crayftn'], var='FC') + fc = conf.cmd_to_list(fc) + conf.get_crayftn_version(fc) + conf.env.FC_NAME = 'CRAY' + conf.env.FC_MOD_CAPITALIZATION = 'UPPER.mod' + +@conf +def crayftn_flags(conf): + v = conf.env + v['_FCMODOUTFLAGS'] = ['-em', '-J.'] # enable module files and put them in the current directoy + v['FCFLAGS_DEBUG'] = ['-m1'] # more verbose compiler warnings + v['FCFLAGS_fcshlib'] = ['-h pic'] + v['LINKFLAGS_fcshlib'] = ['-h shared'] + + v['FCSTLIB_MARKER'] = '-h static' + v['FCSHLIB_MARKER'] = '-h dynamic' + +@conf +def get_crayftn_version(conf, fc): + version_re = re.compile(r"Cray Fortran\s*:\s*Version\s*(?P\d*)\.(?P\d*)", re.I).search + cmd = fc + ['-V'] + out,err = fc_config.getoutput(conf, cmd, stdin=False) + if out: match = version_re(out) + else: match = version_re(err) + if not match: + conf.fatal('Could not determine the Cray Fortran compiler version.') + k = match.groupdict() + conf.env['FC_VERSION'] = (k['major'], k['minor']) + +def configure(conf): + conf.find_crayftn() + conf.find_ar() + conf.fc_flags() + conf.crayftn_flags() + diff --git a/waflib/extras/fc_open64.py b/waflib/extras/fc_open64.py new file mode 100644 index 00000000..eb3c028d --- /dev/null +++ b/waflib/extras/fc_open64.py @@ -0,0 +1,56 @@ +#! /usr/bin/env python +# encoding: utf-8 +# harald at klimachs.de + +import re +from waflib import Utils +from waflib.Tools import fc,fc_config,fc_scan +from waflib.Configure import conf + +from waflib.Tools.compiler_fc import fc_compiler +fc_compiler['linux'].insert(0, 'fc_open64') + +@conf +def find_openf95(conf): + """Find the Open64 Fortran Compiler (will look in the environment variable 'FC')""" + + fc = conf.find_program(['openf95', 'openf90'], var='FC') + fc = conf.cmd_to_list(fc) + conf.get_open64_version(fc) + conf.env.FC_NAME = 'OPEN64' + conf.env.FC_MOD_CAPITALIZATION = 'UPPER.mod' + +@conf +def openf95_flags(conf): + v = conf.env + v['FCFLAGS_DEBUG'] = ['-fullwarn'] + +@conf +def openf95_modifier_platform(conf): + dest_os = conf.env['DEST_OS'] or Utils.unversioned_sys_platform() + openf95_modifier_func = getattr(conf, 'openf95_modifier_' + dest_os, None) + if openf95_modifier_func: + openf95_modifier_func() + +@conf +def get_open64_version(conf, fc): + """Get the Open64 compiler version""" + + version_re = re.compile(r"Open64 Compiler Suite: *Version *(?P\d*)\.(?P\d*)", re.I).search + cmd = fc + ['-version'] + + out, err = fc_config.getoutput(conf,cmd,stdin=False) + if out: match = version_re(out) + else: match = version_re(err) + if not match: + conf.fatal('Could not determine the Open64 version.') + k = match.groupdict() + conf.env['FC_VERSION'] = (k['major'], k['minor']) + +def configure(conf): + conf.find_openf95() + conf.find_ar() + conf.fc_flags() + conf.openf95_flags() + conf.openf95_modifier_platform() + diff --git a/waflib/extras/fc_pgfortran.py b/waflib/extras/fc_pgfortran.py new file mode 100644 index 00000000..1907966b --- /dev/null +++ b/waflib/extras/fc_pgfortran.py @@ -0,0 +1,65 @@ +#! /usr/bin/env python +# encoding: utf-8 +# harald at klimachs.de + +import re +from waflib import Utils +from waflib.Tools import fc, fc_config, fc_scan +from waflib.Configure import conf + +from waflib.Tools.compiler_fc import fc_compiler +fc_compiler['linux'].append('fc_pgfortran') + +@conf +def find_pgfortran(conf): + """Find the PGI fortran compiler (will look in the environment variable 'FC')""" + fc = conf.find_program(['pgfortran', 'pgf95', 'pgf90'], var='FC') + fc = conf.cmd_to_list(fc) + conf.get_pgfortran_version(fc) + conf.env.FC_NAME = 'PGFC' + +@conf +def pgfortran_flags(conf): + v = conf.env + v['FCFLAGS_fcshlib'] = ['-shared'] + v['FCFLAGS_DEBUG'] = ['-Minform=inform', '-Mstandard'] # why not + v['FCSTLIB_MARKER'] = '-Bstatic' + v['FCSHLIB_MARKER'] = '-Bdynamic' + v['SONAME_ST'] = '-soname %s' + +@conf +def get_pgfortran_version(conf,fc): + version_re = re.compile(r"The Portland Group", re.I).search + cmd = fc + ['-V'] + out,err = fc_config.getoutput(conf, cmd, stdin=False) + if out: match = version_re(out) + else: match = version_re(err) + if not match: + conf.fatal('Could not verify PGI signature') + cmd = fc + ['-help=variable'] + out,err = fc_config.getoutput(conf, cmd, stdin=False) + if out.find('COMPVER')<0: + conf.fatal('Could not determine the compiler type') + k = {} + prevk = '' + out = out.split('\n') + for line in out: + lst = line.partition('=') + if lst[1] == '=': + key = lst[0].rstrip() + if key == '': key = prevk + val = lst[2].rstrip() + k[key] = val + else: prevk = line.partition(' ')[0] + def isD(var): + return var in k + def isT(var): + return var in k and k[var]!='0' + conf.env['FC_VERSION'] = (k['COMPVER'].split('.')) + +def configure(conf): + conf.find_pgfortran() + conf.find_ar() + conf.fc_flags() + conf.pgfortran_flags() + diff --git a/waflib/extras/fc_solstudio.py b/waflib/extras/fc_solstudio.py new file mode 100644 index 00000000..e30e8f39 --- /dev/null +++ b/waflib/extras/fc_solstudio.py @@ -0,0 +1,60 @@ +#! /usr/bin/env python +# encoding: utf-8 +# harald at klimachs.de + +import re +from waflib import Utils +from waflib.Tools import fc,fc_config,fc_scan +from waflib.Configure import conf + +from waflib.Tools.compiler_fc import fc_compiler +fc_compiler['linux'].append('fc_solstudio') + +@conf +def find_solstudio(conf): + """Find the Solaris Studio compiler (will look in the environment variable 'FC')""" + + fc = conf.find_program(['sunf95', 'f95', 'sunf90', 'f90'], var='FC') + fc = conf.cmd_to_list(fc) + conf.get_solstudio_version(fc) + conf.env.FC_NAME = 'SOL' + +@conf +def solstudio_flags(conf): + v = conf.env + v['FCFLAGS_fcshlib'] = ['-Kpic'] + v['FCFLAGS_DEBUG'] = ['-w3'] + v['LINKFLAGS_fcshlib'] = ['-G'] + v['FCSTLIB_MARKER'] = '-Bstatic' + v['FCSHLIB_MARKER'] = '-Bdynamic' + v['SONAME_ST'] = '-h %s' + +@conf +def solstudio_modifier_platform(conf): + dest_os = conf.env['DEST_OS'] or Utils.unversioned_sys_platform() + solstudio_modifier_func = getattr(conf, 'solstudio_modifier_' + dest_os, None) + if solstudio_modifier_func: + solstudio_modifier_func() + +@conf +def get_solstudio_version(conf, fc): + """Get the compiler version""" + + version_re = re.compile(r"Sun Fortran 95 *(?P\d*)\.(?P\d*)", re.I).search + cmd = fc + ['-V'] + + out, err = fc_config.getoutput(conf,cmd,stdin=False) + if out: match = version_re(out) + else: match = version_re(err) + if not match: + conf.fatal('Could not determine the Sun Studio Fortran version.') + k = match.groupdict() + conf.env['FC_VERSION'] = (k['major'], k['minor']) + +def configure(conf): + conf.find_solstudio() + conf.find_ar() + conf.fc_flags() + conf.solstudio_flags() + conf.solstudio_modifier_platform() + diff --git a/waflib/extras/fc_xlf.py b/waflib/extras/fc_xlf.py new file mode 100644 index 00000000..9e0ea4dd --- /dev/null +++ b/waflib/extras/fc_xlf.py @@ -0,0 +1,58 @@ +#! /usr/bin/env python +# encoding: utf-8 +# harald at klimachs.de + +import re +from waflib import Utils +from waflib.Tools import fc,fc_config,fc_scan +from waflib.Configure import conf + +from waflib.Tools.compiler_fc import fc_compiler +fc_compiler['aix'].insert(0, 'fc_xlf') + +@conf +def find_xlf(conf): + """Find the xlf program (will look in the environment variable 'FC')""" + + fc = conf.find_program(['xlf2003_r', 'xlf2003', 'xlf95_r', 'xlf95', 'xlf90_r', 'xlf90', 'xlf_r', 'xlf'], var='FC') + fc = conf.cmd_to_list(fc) + conf.get_xlf_version(fc) + conf.env.FC_NAME='XLF' + +@conf +def xlf_flags(conf): + v = conf.env + v['FCDEFINES_ST'] = '-WF,-D%s' + v['FCFLAGS_fcshlib'] = ['-qpic=small'] + v['FCFLAGS_DEBUG'] = ['-qhalt=w'] + v['LINKFLAGS_fcshlib'] = ['-Wl,-shared'] + +@conf +def xlf_modifier_platform(conf): + dest_os = conf.env['DEST_OS'] or Utils.unversioned_sys_platform() + xlf_modifier_func = getattr(conf, 'xlf_modifier_' + dest_os, None) + if xlf_modifier_func: + xlf_modifier_func() + +@conf +def get_xlf_version(conf, fc): + """Get the compiler version""" + + version_re = re.compile(r"IBM XL Fortran.*, V(?P\d*)\.(?P\d*)", re.I).search + cmd = fc + ['-qversion'] + + out, err = fc_config.getoutput(conf,cmd,stdin=False) + if out: match = version_re(out) + else: match = version_re(err) + if not match: + conf.fatal('Could not determine the XLF version.') + k = match.groupdict() + conf.env['FC_VERSION'] = (k['major'], k['minor']) + +def configure(conf): + conf.find_xlf() + conf.find_ar() + conf.fc_flags() + conf.xlf_flags() + conf.xlf_modifier_platform() + diff --git a/waflib/extras/fluid.py b/waflib/extras/fluid.py new file mode 100644 index 00000000..075a0c4f --- /dev/null +++ b/waflib/extras/fluid.py @@ -0,0 +1,30 @@ +#!/usr/bin/python +# encoding: utf-8 +# Grygoriy Fuchedzhy 2009 + +""" +Compile fluid files (fltk graphic library). Use the 'fluid' feature in conjuction with the 'cxx' feature. +""" + +from waflib import Task +from waflib.TaskGen import extension + +class fluid(Task.Task): + color = 'BLUE' + ext_out = ['.h'] + run_str = '${FLUID} -c -o ${TGT[0].abspath()} -h ${TGT[1].abspath()} ${SRC}' + +@extension('.fl') +def fluid(self, node): + """add the .fl to the source list; the cxx file generated will be compiled when possible""" + cpp = node.change_ext('.cpp') + hpp = node.change_ext('.hpp') + self.create_task('fluid', node, [cpp, hpp]) + + if 'cxx' in self.features: + self.source.append(cpp) + +def configure(conf): + conf.find_program('fluid', var='FLUID') + conf.check_cfg(path='fltk-config', package='', args='--cxxflags --ldflags', uselib_store='FLTK', mandatory=True) + diff --git a/waflib/extras/freeimage.py b/waflib/extras/freeimage.py new file mode 100644 index 00000000..7e8c82f3 --- /dev/null +++ b/waflib/extras/freeimage.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# encoding: utf-8 +# +# written by Sylvain Rouquette, 2011 + +''' +To add the freeimage tool to the waf file: +$ ./waf-light --tools=compat15,freeimage + or, if you have waf >= 1.6.2 +$ ./waf update --files=freeimage + +The wscript will look like: + +def options(opt): + opt.load('compiler_cxx freeimage') + +def configure(conf): + conf.load('compiler_cxx freeimage') + + # you can call check_freeimage with some parameters. + # It's optional on Linux, it's 'mandatory' on Windows if + # you didn't use --fi-path on the command-line + + # conf.check_freeimage(path='FreeImage/Dist', fip=True) + +def build(bld): + bld(source='main.cpp', target='app', use='FREEIMAGE') +''' + +from waflib import Utils +from waflib.Configure import conf + + +def options(opt): + opt.add_option('--fi-path', type='string', default='', dest='fi_path', + help='''path to the FreeImage directory \ + where the files are e.g. /FreeImage/Dist''') + opt.add_option('--fip', action='store_true', default=False, dest='fip', + help='link with FreeImagePlus') + opt.add_option('--fi-static', action='store_true', + default=False, dest='fi_static', + help="link as shared libraries") + + +@conf +def check_freeimage(self, path=None, fip=False): + self.start_msg('Checking FreeImage') + if not self.env['CXX']: + self.fatal('you must load compiler_cxx before loading freeimage') + prefix = self.options.fi_static and 'ST' or '' + platform = Utils.unversioned_sys_platform() + if platform == 'win32': + if not path: + self.fatal('you must specify the path to FreeImage. \ + use --fi-path=/FreeImage/Dist') + else: + self.env['INCLUDES_FREEIMAGE'] = path + self.env['%sLIBPATH_FREEIMAGE' % prefix] = path + libs = ['FreeImage'] + if self.options.fip: + libs.append('FreeImagePlus') + if platform == 'win32': + self.env['%sLIB_FREEIMAGE' % prefix] = libs + else: + self.env['%sLIB_FREEIMAGE' % prefix] = [i.lower() for i in libs] + self.end_msg('ok') + + +def configure(conf): + platform = Utils.unversioned_sys_platform() + if platform == 'win32' and not conf.options.fi_path: + return + conf.check_freeimage(conf.options.fi_path, conf.options.fip) + diff --git a/waflib/extras/fsb.py b/waflib/extras/fsb.py new file mode 100644 index 00000000..1b8f398f --- /dev/null +++ b/waflib/extras/fsb.py @@ -0,0 +1,31 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2011 (ita) + +""" +Fully sequential builds + +The previous tasks from task generators are re-processed, and this may lead to speed issues +Yet, if you are using this, speed is probably a minor concern +""" + +from waflib import Build + +def options(opt): + pass + +def configure(conf): + pass + +class FSBContext(Build.BuildContext): + def __call__(self, *k, **kw): + ret = Build.BuildContext.__call__(self, *k, **kw) + + # evaluate the results immediately + Build.BuildContext.compile(self) + + return ret + + def compile(self): + pass + diff --git a/waflib/extras/fsc.py b/waflib/extras/fsc.py new file mode 100644 index 00000000..150e6750 --- /dev/null +++ b/waflib/extras/fsc.py @@ -0,0 +1,66 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2011 (ita) + +""" +Experimental F# stuff + +FSC="mono /path/to/fsc.exe" waf configure build +""" + +from waflib import Utils, Task, Options, Logs, Errors +from waflib.TaskGen import before_method, after_method, feature +from waflib.Tools import ccroot, cs +from waflib.Configure import conf + +ccroot.USELIB_VARS['fsc'] = set(['CSFLAGS', 'ASSEMBLIES', 'RESOURCES']) + +@feature('fs') +@before_method('process_source') +def apply_fsc(self): + cs_nodes = [] + no_nodes = [] + for x in self.to_nodes(self.source): + if x.name.endswith('.fs'): + cs_nodes.append(x) + else: + no_nodes.append(x) + self.source = no_nodes + + bintype = getattr(self, 'type', self.gen.endswith('.dll') and 'library' or 'exe') + self.cs_task = tsk = self.create_task('fsc', cs_nodes, self.path.find_or_declare(self.gen)) + tsk.env.CSTYPE = '/target:%s' % bintype + tsk.env.OUT = '/out:%s' % tsk.outputs[0].abspath() + + inst_to = getattr(self, 'install_path', bintype=='exe' and '${BINDIR}' or '${LIBDIR}') + if inst_to: + # note: we are making a copy, so the files added to cs_task.outputs won't be installed automatically + mod = getattr(self, 'chmod', bintype=='exe' and Utils.O755 or Utils.O644) + self.install_task = self.bld.install_files(inst_to, self.cs_task.outputs[:], env=self.env, chmod=mod) + +feature('fs')(cs.use_cs) +after_method('apply_fsc')(cs.use_cs) + +feature('fs')(cs.debug_cs) +after_method('apply_fsc', 'use_cs')(cs.debug_cs) + +class fsc(Task.Task): + """ + Compile F# files + """ + color = 'YELLOW' + run_str = '${FSC} ${CSTYPE} ${CSFLAGS} ${ASS_ST:ASSEMBLIES} ${RES_ST:RESOURCES} ${OUT} ${SRC}' + +def configure(conf): + """ + Find a F# compiler, set the variable FSC for the compiler and FS_NAME (mono or fsc) + """ + conf.find_program(['fsc.exe', 'fsharpc'], var='FSC') + conf.env.FSC = conf.cmd_to_list(conf.env.FSC) + conf.env.ASS_ST = '/r:%s' + conf.env.RES_ST = '/resource:%s' + + conf.env.CS_NAME = 'fsc' + if str(conf.env.FSC).lower().find('fsharpc') > -1: + conf.env.CS_NAME = 'mono' + diff --git a/waflib/extras/gccdeps.py b/waflib/extras/gccdeps.py new file mode 100644 index 00000000..8b218f75 --- /dev/null +++ b/waflib/extras/gccdeps.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2008-2010 (ita) + +""" +Execute the tasks with gcc -MD, read the dependencies from the .d file +and prepare the dependency calculation for the next run +""" + +import os, re, threading +from waflib import Task, Logs, Utils, Errors +from waflib.Tools import c_preproc +from waflib.TaskGen import before_method, after_method, feature + +lock = threading.Lock() + +preprocessor_flag = '-MD' + +@feature('cc') +@before_method('apply_core') +def add_mmd_cc(self): + if self.env.get_flat('CFLAGS').find(preprocessor_flag) < 0: + self.env.append_value('CFLAGS', [preprocessor_flag]) + +@feature('cxx') +@before_method('apply_core') +def add_mmd_cxx(self): + if self.env.get_flat('CXXFLAGS').find(preprocessor_flag) < 0: + self.env.append_value('CXXFLAGS', [preprocessor_flag]) + +def scan(self): + "the scanner does not do anything initially" + nodes = self.generator.bld.node_deps.get(self.uid(), []) + names = [] + return (nodes, names) + +re_o = re.compile("\.o$") +def post_run(self): + # The following code is executed by threads, it is not safe, so a lock is needed... + + if getattr(self, 'cached', None): + return Task.Task.post_run(self) + + name = self.outputs[0].abspath() + name = re_o.sub('.d', name) + txt = Utils.readf(name) + #os.unlink(name) + + txt = txt.replace('\\\n', '') + + lst = txt.strip().split(':') + val = ":".join(lst[1:]) + val = val.split() + + nodes = [] + bld = self.generator.bld + + f = re.compile("^(\.\.)[\\/](.*)$") + for x in val: + + node = None + if os.path.isabs(x): + + if not c_preproc.go_absolute: + continue + + lock.acquire() + try: + node = bld.root.find_resource(x) + finally: + lock.release() + else: + path = bld.bldnode + x = [k for k in Utils.split_path(x) if k and k != '.'] + while lst and x[0] == '..': + x = x[1:] + path = path.parent + + # when calling find_resource, make sure the path does not begin by '..' + try: + lock.acquire() + node = path.find_resource(x) + finally: + lock.release() + + if not node: + raise ValueError('could not find %r for %r' % (x, self)) + else: + if id(node) == id(self.inputs[0]): + # ignore the source file, it is already in the dependencies + # this way, successful config tests may be retrieved from the cache + continue + + nodes.append(node) + + Logs.debug('deps: real scanner for %s returned %s' % (str(self), str(nodes))) + + bld.node_deps[self.uid()] = nodes + bld.raw_deps[self.uid()] = [] + + try: + del self.cache_sig + except: + pass + + Task.Task.post_run(self) + +def sig_implicit_deps(self): + try: + return Task.Task.sig_implicit_deps(self) + except Errors.WafError: + return Utils.SIG_NIL + +for name in 'cc cxx'.split(): + try: + cls = Task.classes[name] + except KeyError: + pass + else: + cls.post_run = post_run + cls.scan = scan + cls.sig_implicit_deps = sig_implicit_deps + diff --git a/waflib/extras/go.py b/waflib/extras/go.py new file mode 100644 index 00000000..5f04247a --- /dev/null +++ b/waflib/extras/go.py @@ -0,0 +1,262 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Tom Wambold tom5760 gmail.com 2009 +# Thomas Nagy 2010 + +""" +Go as a language may look nice, but its toolchain is one of the worse a developer +has ever seen. It keeps changing though, and I would like to believe that it will get +better eventually, but the crude reality is that this tool and the examples are +getting broken every few months. + +If you have been lured into trying to use Go, you should stick to their Makefiles. +""" + +import os, platform + +from waflib import Utils, Task, TaskGen +from waflib.TaskGen import feature, extension, after_method, before_method +from waflib.Tools.ccroot import link_task, stlink_task, propagate_uselib_vars, process_use + +class go(Task.Task): + run_str = '${GOC} ${GOCFLAGS} ${CPPPATH_ST:INCPATHS} -o ${TGT} ${SRC}' + +class gopackage(stlink_task): + run_str = '${GOP} grc ${TGT} ${SRC}' + +class goprogram(link_task): + run_str = '${GOL} ${GOLFLAGS} -o ${TGT} ${SRC}' + inst_to = '${BINDIR}' + chmod = Utils.O755 + +class cgopackage(stlink_task): + color = 'YELLOW' + inst_to = '${LIBDIR}' + ext_in = ['.go'] + ext_out = ['.a'] + + def run(self): + src_dir = self.generator.bld.path + source = self.inputs + target = self.outputs[0].change_ext('') + + #print ("--> %s" % self.outputs) + #print ('++> %s' % self.outputs[1]) + bld_dir = self.outputs[1] + bld_dir.mkdir() + obj_dir = bld_dir.make_node('_obj') + obj_dir.mkdir() + + bld_srcs = [] + for s in source: + # FIXME: it seems gomake/cgo stumbles on filenames like a/b/c.go + # -> for the time being replace '/' with '_'... + #b = bld_dir.make_node(s.path_from(src_dir)) + b = bld_dir.make_node(s.path_from(src_dir).replace(os.sep,'_')) + b.parent.mkdir() + #print ('++> %s' % (s.path_from(src_dir),)) + try: + try:os.remove(b.abspath()) + except Exception:pass + os.symlink(s.abspath(), b.abspath()) + except Exception: + # if no support for symlinks, copy the file from src + b.write(s.read()) + bld_srcs.append(b) + #print("--|> [%s]" % b.abspath()) + b.sig = Utils.h_file(b.abspath()) + pass + #self.set_inputs(bld_srcs) + #self.generator.bld.raw_deps[self.uid()] = [self.signature()] + bld_srcs + makefile_node = bld_dir.make_node("Makefile") + makefile_tmpl = '''\ +# Copyright 2009 The Go Authors. All rights reserved. +# Use of this source code is governed by a BSD-style +# license that can be found in the LICENSE file. --- + +include $(GOROOT)/src/Make.inc + +TARG=%(target)s + +GCIMPORTS= %(gcimports)s + +CGOFILES=\\ +\t%(source)s + +CGO_CFLAGS= %(cgo_cflags)s + +CGO_LDFLAGS= %(cgo_ldflags)s + +include $(GOROOT)/src/Make.pkg + +%%: install %%.go + $(GC) $*.go + $(LD) -o $@ $*.$O + +''' % { +'gcimports': ' '.join(l for l in self.env['GOCFLAGS']), +'cgo_cflags' : ' '.join(l for l in self.env['GOCFLAGS']), +'cgo_ldflags': ' '.join(l for l in self.env['GOLFLAGS']), +'target': target.path_from(obj_dir), +'source': ' '.join([b.path_from(bld_dir) for b in bld_srcs]) +} + makefile_node.write(makefile_tmpl) + #print ("::makefile: %s"%makefile_node.abspath()) + cmd = Utils.subst_vars('gomake ${GOMAKE_FLAGS}', self.env).strip() + o = self.outputs[0].change_ext('.gomake.log') + fout_node = bld_dir.find_or_declare(o.name) + fout = open(fout_node.abspath(), 'w') + rc = self.generator.bld.exec_command( + cmd, + stdout=fout, + stderr=fout, + cwd=bld_dir.abspath(), + ) + if rc != 0: + import waflib.Logs as msg + msg.error('** error running [%s] (cgo-%s)' % (cmd, target)) + msg.error(fout_node.read()) + return rc + self.generator.bld.read_stlib( + target, + paths=[obj_dir.abspath(),], + ) + tgt = self.outputs[0] + if tgt.parent != obj_dir: + install_dir = os.path.join('${LIBDIR}', + tgt.parent.path_from(obj_dir)) + else: + install_dir = '${LIBDIR}' + #print('===> %s (%s)' % (tgt.abspath(), install_dir)) + self.generator.bld.install_files( + install_dir, + tgt.abspath(), + relative_trick=False, + postpone=False, + ) + return rc + +@extension('.go') +def compile_go(self, node): + #print('*'*80, self.name) + if not ('cgopackage' in self.features): + return self.create_compiled_task('go', node) + #print ('compile_go-cgo...') + bld_dir = node.parent.get_bld() + obj_dir = bld_dir.make_node('_obj') + target = obj_dir.make_node(node.change_ext('.a').name) + return self.create_task('cgopackage', + node, + node.change_ext('.a')) + +@feature('gopackage', 'goprogram', 'cgopackage') +@before_method('process_source') +def go_compiler_is_foobar(self): + if self.env.GONAME == 'gcc': + return + self.source = self.to_nodes(self.source) + src = [] + go = [] + for node in self.source: + if node.name.endswith('.go'): + go.append(node) + else: + src.append(node) + self.source = src + if not ('cgopackage' in self.features): + #print('--> [%s]... (%s)' % (go[0], getattr(self, 'target', 'N/A'))) + tsk = self.create_compiled_task('go', go[0]) + tsk.inputs.extend(go[1:]) + else: + #print ('+++ [%s] +++' % self.target) + bld_dir = self.path.get_bld().make_node('cgopackage--%s' % + self.target.replace(os.sep,'_')) + obj_dir = bld_dir.make_node('_obj') + target = obj_dir.make_node(self.target+'.a') + tsk = self.create_task('cgopackage', + go, + [target, bld_dir]) + self.link_task = tsk + +@feature('gopackage', 'goprogram', 'cgopackage') +@after_method('process_source', 'apply_incpaths',) +def go_local_libs(self): + names = self.to_list(getattr(self, 'use', [])) + #print ('== go-local-libs == [%s] == use: %s' % (self.name, names)) + for name in names: + tg = self.bld.get_tgen_by_name(name) + if not tg: + raise Utils.WafError('no target of name %r necessary for %r in go uselib local' % (name, self)) + tg.post() + #print ("-- tg[%s]: %s" % (self.name,name)) + lnk_task = getattr(tg, 'link_task', None) + if lnk_task: + for tsk in self.tasks: + if isinstance(tsk, (go, gopackage, cgopackage)): + tsk.set_run_after(lnk_task) + tsk.dep_nodes.extend(lnk_task.outputs) + path = lnk_task.outputs[0].parent.abspath() + if isinstance(lnk_task, (go, gopackage)): + # handle hierarchical packages + path = lnk_task.generator.path.get_bld().abspath() + elif isinstance(lnk_task, (cgopackage,)): + # handle hierarchical cgopackages + cgo_obj_dir = lnk_task.outputs[1].find_or_declare('_obj') + path = cgo_obj_dir.abspath() + # recursively add parent GOCFLAGS... + self.env.append_unique('GOCFLAGS', + getattr(lnk_task.env, 'GOCFLAGS',[])) + # ditto for GOLFLAGS... + self.env.append_unique('GOLFLAGS', + getattr(lnk_task.env, 'GOLFLAGS',[])) + self.env.append_unique('GOCFLAGS', ['-I%s' % path]) + self.env.append_unique('GOLFLAGS', ['-L%s' % path]) + for n in getattr(tg, 'includes_nodes', []): + self.env.append_unique('GOCFLAGS', ['-I%s' % n.abspath()]) + pass + pass + +def configure(conf): + + def set_def(var, val): + if not conf.env[var]: + conf.env[var] = val + + goarch = os.getenv('GOARCH') + if goarch == '386': + set_def('GO_PLATFORM', 'i386') + elif goarch == 'amd64': + set_def('GO_PLATFORM', 'x86_64') + elif goarch == 'arm': + set_def('GO_PLATFORM', 'arm') + else: + set_def('GO_PLATFORM', platform.machine()) + + if conf.env.GO_PLATFORM == 'x86_64': + set_def('GO_COMPILER', '6g') + set_def('GO_LINKER', '6l') + elif conf.env.GO_PLATFORM in ['i386', 'i486', 'i586', 'i686']: + set_def('GO_COMPILER', '8g') + set_def('GO_LINKER', '8l') + elif conf.env.GO_PLATFORM == 'arm': + set_def('GO_COMPILER', '5g') + set_def('GO_LINKER', '5l') + set_def('GO_EXTENSION', '.5') + + if not (conf.env.GO_COMPILER or conf.env.GO_LINKER): + raise conf.fatal('Unsupported platform ' + platform.machine()) + + set_def('GO_PACK', 'gopack') + set_def('gopackage_PATTERN', '%s.a') + set_def('CPPPATH_ST', '-I%s') + + set_def('GOMAKE_FLAGS', ['--quiet']) + conf.find_program(conf.env.GO_COMPILER, var='GOC') + conf.find_program(conf.env.GO_LINKER, var='GOL') + conf.find_program(conf.env.GO_PACK, var='GOP') + + conf.find_program('cgo', var='CGO') + +TaskGen.feature('go')(process_use) +TaskGen.feature('go')(propagate_uselib_vars) + diff --git a/waflib/extras/gob2.py b/waflib/extras/gob2.py new file mode 100644 index 00000000..b4fa3b9a --- /dev/null +++ b/waflib/extras/gob2.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Ali Sabil, 2007 + +from waflib import TaskGen + +TaskGen.declare_chain( + name = 'gob2', + rule = '${GOB2} -o ${TGT[0].bld_dir()} ${GOB2FLAGS} ${SRC}', + ext_in = '.gob', + ext_out = '.c' +) + +def configure(conf): + conf.find_program('gob2', var='GOB2') + conf.env['GOB2FLAGS'] = '' + diff --git a/waflib/extras/local_rpath.py b/waflib/extras/local_rpath.py new file mode 100644 index 00000000..b2507e17 --- /dev/null +++ b/waflib/extras/local_rpath.py @@ -0,0 +1,19 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2011 (ita) + +from waflib.TaskGen import after_method, feature + +@after_method('propagate_uselib_vars') +@feature('cprogram', 'cshlib', 'cxxprogram', 'cxxshlib', 'fcprogram', 'fcshlib') +def add_rpath_stuff(self): + all = self.to_list(getattr(self, 'use', [])) + while all: + name = all.pop() + try: + tg = self.bld.get_tgen_by_name(name) + except: + continue + self.env.append_value('RPATH', tg.link_task.outputs[0].parent.abspath()) + all.extend(self.to_list(getattr(tg, 'use', []))) + diff --git a/waflib/extras/lru_cache.py b/waflib/extras/lru_cache.py new file mode 100644 index 00000000..0aa9e6de --- /dev/null +++ b/waflib/extras/lru_cache.py @@ -0,0 +1,98 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy 2011 + +import os, shutil, re +from waflib import Options, Build, Logs + +""" +Apply a least recently used policy to the Waf cache. + +For performance reasons, it is called after the build is complete. + +We assume that the the folders are written atomically + +Do export WAFCACHE=/tmp/foo-xyz where xyz represents the cache size in megabytes +If missing, the default cache size will be set to 10GB +""" + +re_num = re.compile('[a-zA-Z_]+(\d+)') + +CACHESIZE = 10*1024*1024*1024 # in bytes +CLEANRATIO = 0.8 +DIRSIZE = 4096 + +def compile(self): + if Options.cache_global and not Options.options.nocache: + try: + os.makedirs(Options.cache_global) + except: + pass + + try: + self.raw_compile() + finally: + if Options.cache_global and not Options.options.nocache: + self.sweep() + +def sweep(self): + global CACHESIZE + CACHEDIR = Options.cache_global + + # get the cache max size from the WAFCACHE filename + re_num = re.compile('[a-zA-Z_]+(\d+)') + val = re_num.sub('\\1', os.path.basename(Options.cache_global)) + try: + CACHESIZE = int(val) + except: + pass + + # map folder names to timestamps + flist = {} + for x in os.listdir(CACHEDIR): + j = os.path.join(CACHEDIR, x) + if os.path.isdir(j) and len(x) == 64: # dir names are md5 hexdigests + flist[x] = [os.stat(j).st_mtime, 0] + + for (x, v) in flist.items(): + cnt = DIRSIZE # each entry takes 4kB + d = os.path.join(CACHEDIR, x) + for k in os.listdir(d): + cnt += os.stat(os.path.join(d, k)).st_size + flist[x][1] = cnt + + total = sum([x[1] for x in flist.values()]) + Logs.debug('lru: Cache size is %r' % total) + + if total >= CACHESIZE: + Logs.debug('lru: Trimming the cache since %r > %r' % (total, CACHESIZE)) + + # make a list to sort the folders by timestamp + lst = [(p, v[0], v[1]) for (p, v) in flist.items()] + lst.sort(key=lambda x: x[1]) # sort by timestamp + lst.reverse() + + while total >= CACHESIZE * CLEANRATIO: + (k, t, s) = lst.pop() + p = os.path.join(CACHEDIR, k) + v = p + '.del' + try: + os.rename(p, v) + except: + # someone already did it + pass + else: + try: + shutil.rmtree(v) + except: + # this should not happen, but who knows? + Logs.warn('If you ever see this message, report it (%r)' % v) + total -= s + del flist[k] + + Logs.debug('lru: Total at the end %r' % total) + +Build.BuildContext.raw_compile = Build.BuildContext.compile +Build.BuildContext.compile = compile +Build.BuildContext.sweep = sweep + diff --git a/waflib/extras/make.py b/waflib/extras/make.py new file mode 100644 index 00000000..57e841d9 --- /dev/null +++ b/waflib/extras/make.py @@ -0,0 +1,142 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2011 (ita) + +""" +A make-like way of executing the build, following the relationships between inputs/outputs + +This algorithm will lead to slower builds, will not be as flexible as "waf build", but +it might be useful for building data files (?) + +It is likely to break in the following cases: +- files are created dynamically (no inputs or outputs) +- headers +- building two files from different groups +""" + +import re +from waflib import Options, Task, Logs +from waflib.Build import BuildContext + +class MakeContext(BuildContext): + '''executes tasks in a step-by-step manner, following dependencies between inputs/outputs''' + cmd = 'make' + fun = 'build' + + def __init__(self, **kw): + super(MakeContext, self).__init__(**kw) + self.files = Options.options.files + + def get_build_iterator(self): + if not self.files: + while 1: + yield super(MakeContext, self).get_build_iterator() + + for g in self.groups: + for tg in g: + try: + f = tg.post + except AttributeError: + pass + else: + f() + + provides = {} + uses = {} + all_tasks = [] + tasks = [] + for pat in self.files.split(','): + matcher = self.get_matcher(pat) + for tg in g: + if isinstance(tg, Task.TaskBase): + lst = [tg] + else: + lst = tg.tasks + for tsk in lst: + all_tasks.append(tsk) + + do_exec = False + for node in getattr(tsk, 'inputs', []): + try: + uses[node].append(tsk) + except: + uses[node] = [tsk] + + if matcher(node, output=False): + do_exec = True + break + + for node in getattr(tsk, 'outputs', []): + try: + provides[node].append(tsk) + except: + provides[node] = [tsk] + + if matcher(node, output=True): + do_exec = True + break + if do_exec: + tasks.append(tsk) + + # so we have the tasks that we need to process, the list of all tasks, + # the map of the tasks providing nodes, and the map of tasks using nodes + + if not tasks: + # if there are no tasks matching, return everything in the current group + result = all_tasks + else: + # this is like a big filter... + result = set([]) + seen = set([]) + cur = set(tasks) + while cur: + result |= cur + tosee = set([]) + for tsk in cur: + for node in getattr(tsk, 'inputs', []): + if node in seen: + continue + seen.add(node) + tosee |= set(provides.get(node, [])) + cur = tosee + result = list(result) + + Task.set_file_constraints(result) + Task.set_precedence_constraints(result) + yield result + + while 1: + yield [] + + def get_matcher(self, pat): + # this returns a function + inn = True + out = True + if pat.startswith('in:'): + out = False + pat = pat.replace('in:', '') + elif pat.startswith('out:'): + inn = False + pat = pat.replace('out:', '') + + anode = self.root.find_node(pat) + pattern = None + if not anode: + if not pat.startswith('^'): + pat = '^.+?%s' % pat + if not pat.endswith('$'): + pat = '%s$' % pat + pattern = re.compile(pat) + + def match(node, output): + if output == True and not out: + return False + if output == False and not inn: + return False + + if anode: + return anode == node + else: + return pattern.match(node.abspath()) + return match + diff --git a/waflib/extras/md5_tstamp.py b/waflib/extras/md5_tstamp.py new file mode 100644 index 00000000..02a29700 --- /dev/null +++ b/waflib/extras/md5_tstamp.py @@ -0,0 +1,56 @@ +#! /usr/bin/env python +# encoding: utf-8 + +""" +Store some values on the buildcontext mapping file paths to +stat values and md5 values (timestamp + md5) +this way the md5 hashes are computed only when timestamp change (can be faster) +There is usually little or no gain from enabling this, but it can be used to enable +the second level cache with timestamps (WAFCACHE) + +You may have to run distclean or to remove the build directory before enabling/disabling +this hashing scheme +""" + +import os, stat +try: import cPickle +except: import pickle as cPickle +from waflib import Utils, Build, Context + +Context.DBFILE += '_md5tstamp' + +Build.hash_cache = {} +Build.SAVED_ATTRS.append('hash_cache') +def store(self): + # save the hash cache as part of the default pickle file + self.hash_cache = Build.hash_cache + self.store_real() +Build.BuildContext.store_real = Build.BuildContext.store +Build.BuildContext.store = store + +def restore(self): + # we need a module variable for h_file below + self.restore_real() + try: + Build.hash_cache = self.hash_cache or {} + except Exception as e: + Build.hash_cache = {} +Build.BuildContext.restore_real = Build.BuildContext.restore +Build.BuildContext.restore = restore + +def h_file(filename): + st = os.stat(filename) + if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file') + + if filename in Build.hash_cache: + if Build.hash_cache[filename][0] == str(st.st_mtime): + return Build.hash_cache[filename][1] + m = Utils.md5() + m.update(str(st.st_mtime)) + m.update(str(st.st_size)) + m.update(filename) + # ensure that the cache is overwritten + Build.hash_cache[filename] = (str(st.st_mtime), m.digest()) + return m.digest() +Utils.h_file = h_file + diff --git a/waflib/extras/misc.py b/waflib/extras/misc.py new file mode 100644 index 00000000..e8620fb1 --- /dev/null +++ b/waflib/extras/misc.py @@ -0,0 +1,416 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +""" +This tool is totally deprecated + +Try using: + .pc.in files for .pc files + the feature intltool_in - see demos/intltool + make-like rules +""" + +import shutil, re, os +from waflib import TaskGen, Node, Task, Utils, Build, Errors +from waflib.TaskGen import feature, after_method, before_method +from waflib.Logs import debug + +def copy_attrs(orig, dest, names, only_if_set=False): + """ + copy class attributes from an object to another + """ + for a in Utils.to_list(names): + u = getattr(orig, a, ()) + if u or not only_if_set: + setattr(dest, a, u) + +def copy_func(tsk): + "Make a file copy. This might be used to make other kinds of file processing (even calling a compiler is possible)" + env = tsk.env + infile = tsk.inputs[0].abspath() + outfile = tsk.outputs[0].abspath() + try: + shutil.copy2(infile, outfile) + except (OSError, IOError): + return 1 + else: + if tsk.chmod: os.chmod(outfile, tsk.chmod) + return 0 + +def action_process_file_func(tsk): + "Ask the function attached to the task to process it" + if not tsk.fun: raise Errors.WafError('task must have a function attached to it for copy_func to work!') + return tsk.fun(tsk) + +@feature('cmd') +def apply_cmd(self): + "call a command everytime" + if not self.fun: raise Errors.WafError('cmdobj needs a function!') + tsk = Task.TaskBase() + tsk.fun = self.fun + tsk.env = self.env + self.tasks.append(tsk) + tsk.install_path = self.install_path + +@feature('copy') +@before_method('process_source') +def apply_copy(self): + Utils.def_attrs(self, fun=copy_func) + self.default_install_path = 0 + + lst = self.to_list(self.source) + self.meths.remove('process_source') + + for filename in lst: + node = self.path.find_resource(filename) + if not node: raise Errors.WafError('cannot find input file %s for processing' % filename) + + target = self.target + if not target or len(lst)>1: target = node.name + + # TODO the file path may be incorrect + newnode = self.path.find_or_declare(target) + + tsk = self.create_task('copy', node, newnode) + tsk.fun = self.fun + tsk.chmod = getattr(self, 'chmod', Utils.O644) + + if not tsk.env: + tsk.debug() + raise Errors.WafError('task without an environment') + +def subst_func(tsk): + "Substitutes variables in a .in file" + + m4_re = re.compile('@(\w+)@', re.M) + + code = tsk.inputs[0].read() #Utils.readf(infile) + + # replace all % by %% to prevent errors by % signs in the input file while string formatting + code = code.replace('%', '%%') + + s = m4_re.sub(r'%(\1)s', code) + + env = tsk.env + di = getattr(tsk, 'dict', {}) or getattr(tsk.generator, 'dict', {}) + if not di: + names = m4_re.findall(code) + for i in names: + di[i] = env.get_flat(i) or env.get_flat(i.upper()) + + tsk.outputs[0].write(s % di) + +@feature('subst') +@before_method('process_source') +def apply_subst(self): + Utils.def_attrs(self, fun=subst_func) + lst = self.to_list(self.source) + self.meths.remove('process_source') + + self.dict = getattr(self, 'dict', {}) + + for filename in lst: + node = self.path.find_resource(filename) + if not node: raise Errors.WafError('cannot find input file %s for processing' % filename) + + if self.target: + newnode = self.path.find_or_declare(self.target) + else: + newnode = node.change_ext('') + + try: + self.dict = self.dict.get_merged_dict() + except AttributeError: + pass + + if self.dict and not self.env['DICT_HASH']: + self.env = self.env.derive() + keys = list(self.dict.keys()) + keys.sort() + lst = [self.dict[x] for x in keys] + self.env['DICT_HASH'] = str(Utils.h_list(lst)) + + tsk = self.create_task('copy', node, newnode) + tsk.fun = self.fun + tsk.dict = self.dict + tsk.dep_vars = ['DICT_HASH'] + tsk.chmod = getattr(self, 'chmod', Utils.O644) + + if not tsk.env: + tsk.debug() + raise Errors.WafError('task without an environment') + +#################### +## command-output #### +#################### + +class cmd_arg(object): + """command-output arguments for representing files or folders""" + def __init__(self, name, template='%s'): + self.name = name + self.template = template + self.node = None + +class input_file(cmd_arg): + def find_node(self, base_path): + assert isinstance(base_path, Node.Node) + self.node = base_path.find_resource(self.name) + if self.node is None: + raise Errors.WafError("Input file %s not found in " % (self.name, base_path)) + + def get_path(self, env, absolute): + if absolute: + return self.template % self.node.abspath() + else: + return self.template % self.node.srcpath() + +class output_file(cmd_arg): + def find_node(self, base_path): + assert isinstance(base_path, Node.Node) + self.node = base_path.find_or_declare(self.name) + if self.node is None: + raise Errors.WafError("Output file %s not found in " % (self.name, base_path)) + + def get_path(self, env, absolute): + if absolute: + return self.template % self.node.abspath() + else: + return self.template % self.node.bldpath() + +class cmd_dir_arg(cmd_arg): + def find_node(self, base_path): + assert isinstance(base_path, Node.Node) + self.node = base_path.find_dir(self.name) + if self.node is None: + raise Errors.WafError("Directory %s not found in " % (self.name, base_path)) + +class input_dir(cmd_dir_arg): + def get_path(self, dummy_env, dummy_absolute): + return self.template % self.node.abspath() + +class output_dir(cmd_dir_arg): + def get_path(self, env, dummy_absolute): + return self.template % self.node.abspath() + + +class command_output(Task.Task): + color = "BLUE" + def __init__(self, env, command, command_node, command_args, stdin, stdout, cwd, os_env, stderr): + Task.Task.__init__(self, env=env) + assert isinstance(command, (str, Node.Node)) + self.command = command + self.command_args = command_args + self.stdin = stdin + self.stdout = stdout + self.cwd = cwd + self.os_env = os_env + self.stderr = stderr + + if command_node is not None: self.dep_nodes = [command_node] + self.dep_vars = [] # additional environment variables to look + + def run(self): + task = self + #assert len(task.inputs) > 0 + + def input_path(node, template): + if task.cwd is None: + return template % node.bldpath() + else: + return template % node.abspath() + def output_path(node, template): + fun = node.abspath + if task.cwd is None: fun = node.bldpath + return template % fun() + + if isinstance(task.command, Node.Node): + argv = [input_path(task.command, '%s')] + else: + argv = [task.command] + + for arg in task.command_args: + if isinstance(arg, str): + argv.append(arg) + else: + assert isinstance(arg, cmd_arg) + argv.append(arg.get_path(task.env, (task.cwd is not None))) + + if task.stdin: + stdin = open(input_path(task.stdin, '%s')) + else: + stdin = None + + if task.stdout: + stdout = open(output_path(task.stdout, '%s'), "w") + else: + stdout = None + + if task.stderr: + stderr = open(output_path(task.stderr, '%s'), "w") + else: + stderr = None + + if task.cwd is None: + cwd = ('None (actually %r)' % os.getcwd()) + else: + cwd = repr(task.cwd) + debug("command-output: cwd=%s, stdin=%r, stdout=%r, argv=%r" % + (cwd, stdin, stdout, argv)) + + if task.os_env is None: + os_env = os.environ + else: + os_env = task.os_env + command = Utils.subprocess.Popen(argv, stdin=stdin, stdout=stdout, stderr=stderr, cwd=task.cwd, env=os_env) + return command.wait() + +@feature('command-output') +def init_cmd_output(self): + Utils.def_attrs(self, + stdin = None, + stdout = None, + stderr = None, + # the command to execute + command = None, + + # whether it is an external command; otherwise it is assumed + # to be an executable binary or script that lives in the + # source or build tree. + command_is_external = False, + + # extra parameters (argv) to pass to the command (excluding + # the command itself) + argv = [], + + # dependencies to other objects -> this is probably not what you want (ita) + # values must be 'task_gen' instances (not names!) + dependencies = [], + + # dependencies on env variable contents + dep_vars = [], + + # input files that are implicit, i.e. they are not + # stdin, nor are they mentioned explicitly in argv + hidden_inputs = [], + + # output files that are implicit, i.e. they are not + # stdout, nor are they mentioned explicitly in argv + hidden_outputs = [], + + # change the subprocess to this cwd (must use obj.input_dir() or output_dir() here) + cwd = None, + + # OS environment variables to pass to the subprocess + # if None, use the default environment variables unchanged + os_env = None) + +@feature('command-output') +@after_method('init_cmd_output') +def apply_cmd_output(self): + if self.command is None: + raise Errors.WafError("command-output missing command") + if self.command_is_external: + cmd = self.command + cmd_node = None + else: + cmd_node = self.path.find_resource(self.command) + assert cmd_node is not None, ('''Could not find command '%s' in source tree. +Hint: if this is an external command, +use command_is_external=True''') % (self.command,) + cmd = cmd_node + + if self.cwd is None: + cwd = None + else: + assert isinstance(cwd, CmdDirArg) + self.cwd.find_node(self.path) + + args = [] + inputs = [] + outputs = [] + + for arg in self.argv: + if isinstance(arg, cmd_arg): + arg.find_node(self.path) + if isinstance(arg, input_file): + inputs.append(arg.node) + if isinstance(arg, output_file): + outputs.append(arg.node) + + if self.stdout is None: + stdout = None + else: + assert isinstance(self.stdout, str) + stdout = self.path.find_or_declare(self.stdout) + if stdout is None: + raise Errors.WafError("File %s not found" % (self.stdout,)) + outputs.append(stdout) + + if self.stderr is None: + stderr = None + else: + assert isinstance(self.stderr, str) + stderr = self.path.find_or_declare(self.stderr) + if stderr is None: + raise Errors.WafError("File %s not found" % (self.stderr,)) + outputs.append(stderr) + + if self.stdin is None: + stdin = None + else: + assert isinstance(self.stdin, str) + stdin = self.path.find_resource(self.stdin) + if stdin is None: + raise Errors.WafError("File %s not found" % (self.stdin,)) + inputs.append(stdin) + + for hidden_input in self.to_list(self.hidden_inputs): + node = self.path.find_resource(hidden_input) + if node is None: + raise Errors.WafError("File %s not found in dir %s" % (hidden_input, self.path)) + inputs.append(node) + + for hidden_output in self.to_list(self.hidden_outputs): + node = self.path.find_or_declare(hidden_output) + if node is None: + raise Errors.WafError("File %s not found in dir %s" % (hidden_output, self.path)) + outputs.append(node) + + if not (inputs or getattr(self, 'no_inputs', None)): + raise Errors.WafError('command-output objects must have at least one input file or give self.no_inputs') + if not (outputs or getattr(self, 'no_outputs', None)): + raise Errors.WafError('command-output objects must have at least one output file or give self.no_outputs') + + cwd = self.bld.variant_dir + task = command_output(self.env, cmd, cmd_node, self.argv, stdin, stdout, cwd, self.os_env, stderr) + task.generator = self + copy_attrs(self, task, 'before after ext_in ext_out', only_if_set=True) + self.tasks.append(task) + + task.inputs = inputs + task.outputs = outputs + task.dep_vars = self.to_list(self.dep_vars) + + for dep in self.dependencies: + assert dep is not self + dep.post() + for dep_task in dep.tasks: + task.set_run_after(dep_task) + + if not task.inputs: + # the case for svnversion, always run, and update the output nodes + task.runnable_status = type(Task.TaskBase.run)(runnable_status, task, task.__class__) # always run + task.post_run = type(Task.TaskBase.run)(post_run, task, task.__class__) + + # TODO the case with no outputs? + +def post_run(self): + for x in self.outputs: + x.sig = Utils.h_file(x.abspath()) + +def runnable_status(self): + return self.RUN_ME + +Task.task_factory('copy', vars=[], func=action_process_file_func) + diff --git a/waflib/extras/msvs.py b/waflib/extras/msvs.py new file mode 100644 index 00000000..9337ce00 --- /dev/null +++ b/waflib/extras/msvs.py @@ -0,0 +1,1016 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Avalanche Studios 2009-2011 +# Thomas Nagy 2011 + +""" +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: + +1. Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + +2. Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + +3. The name of the author may not be used to endorse or promote products + derived from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR +IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED +WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE +DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, +INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES +(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR +SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) +HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, +STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING +IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. +""" + +""" +To add this tool to your project: +def options(conf): + opt.load('msvs') + +It can be a good idea to add the sync_exec tool too. + +To generate solution files: +$ waf configure msvs + +To customize the outputs, provide subclasses in your wscript files: + +from waflib.extras import msvs +class vsnode_target(msvs.vsnode_target): + def get_build_command(self, props): + # likely to be required + return "waf.bat build" + def collect_source(self): + # likely to be required + ... +class msvs_bar(msvs.msvs_generator): + def init(self): + msvs.msvs_generator.init(self) + self.vsnode_target = vsnode_target + +The msvs class re-uses the same build() function for reading the targets (task generators), +you may therefore specify msvs settings on the context object: + +def build(bld): + bld.solution_name = 'foo.sln' + bld.waf_command = 'waf.bat' + bld.projects_dir = bld.srcnode.make_node('.depproj') + bld.projects_dir.mkdir() + +For visual studio 2008, the command is called 'msvs2008', and the classes +such as vsnode_target are wrapped by a decorator class 'wrap_2008' to +provide special functionality. + +ASSUMPTIONS: +* a project can be either a directory or a target, vcxproj files are written only for targets that have source files +* each project is a vcxproj file, therefore the project uuid needs only to be a hash of the absolute path +""" + +import os, re, sys +import uuid # requires python 2.5 +from waflib.Build import BuildContext +from waflib import Utils, TaskGen, Logs, Task, Context, Node, Options + +HEADERS_GLOB = '**/(*.h|*.hpp|*.H|*.inl)' + +PROJECT_TEMPLATE = r''' + + + + ${for b in project.build_properties} + + ${b.configuration} + ${b.platform} + + ${endfor} + + + + {${project.uuid}} + MakeFileProj + ${project.name} + + + + ${for b in project.build_properties} + + Makefile + ${b.outdir} + + ${endfor} + + + + + + ${for b in project.build_properties} + + + + ${endfor} + + ${for b in project.build_properties} + + ${xml:project.get_build_command(b)} + ${xml:project.get_rebuild_command(b)} + ${xml:project.get_clean_command(b)} + ${xml:b.includes_search_path} + ${xml:b.preprocessor_definitions};$(NMakePreprocessorDefinitions) + ${xml:b.includes_search_path} + $(ExecutablePath) + + ${if getattr(b, 'output_file', None)} + ${xml:b.output_file} + ${endif} + ${if getattr(b, 'deploy_dir', None)} + ${xml:b.deploy_dir} + ${endif} + + ${endfor} + + ${for b in project.build_properties} + ${if getattr(b, 'deploy_dir', None)} + + + CopyToHardDrive + + + ${endif} + ${endfor} + + + ${for x in project.source} + <${project.get_key(x)} Include='${x.abspath()}' /> + ${endfor} + + + + + +''' + +FILTER_TEMPLATE = ''' + + + ${for x in project.source} + <${project.get_key(x)} Include="${x.abspath()}"> + ${project.get_filter_name(x.parent)} + + ${endfor} + + + ${for x in project.dirs()} + + {${project.make_uuid(x.abspath())}} + + ${endfor} + + +''' + +PROJECT_2008_TEMPLATE = r''' + + + ${if project.build_properties} + ${for b in project.build_properties} + + ${endfor} + ${else} + + ${endif} + + + + + ${if project.build_properties} + ${for b in project.build_properties} + + + + ${endfor} + ${else} + + + ${endif} + + + + +${project.display_filter()} + + +''' + +SOLUTION_TEMPLATE = '''Microsoft Visual Studio Solution File, Format Version ${project.numver} +# Visual Studio ${project.vsver} +${for p in project.all_projects} +Project("{${p.ptype()}}") = "${p.name}", "${p.title}", "{${p.uuid}}" +EndProject${endfor} +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + ${if project.all_projects} + ${for (configuration, platform) in project.all_projects[0].ctx.project_configurations()} + ${configuration}|${platform} = ${configuration}|${platform} + ${endfor} + ${endif} + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + ${for p in project.all_projects} + ${if hasattr(p, 'source')} + ${for b in p.build_properties} + {${p.uuid}}.${b.configuration}|${b.platform}.ActiveCfg = ${b.configuration}|${b.platform} + ${if getattr(p, 'is_active', None)} + {${p.uuid}}.${b.configuration}|${b.platform}.Build.0 = ${b.configuration}|${b.platform} + ${endif} + ${endfor} + ${endif} + ${endfor} + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + ${for p in project.all_projects} + ${if p.parent} + {${p.uuid}} = {${p.parent.uuid}} + ${endif} + ${endfor} + EndGlobalSection +EndGlobal +''' + +COMPILE_TEMPLATE = '''def f(project): + lst = [] + def xml_escape(value): + return value.replace("&", "&").replace('"', """).replace("'", "'").replace("<", "<").replace(">", ">") + + %s + + #f = open('cmd.txt', 'w') + #f.write(str(lst)) + #f.close() + return ''.join(lst) +''' +reg_act = re.compile(r"(?P\\)|(?P\$\$)|(?P\$\{(?P[^}]*?)\})", re.M) +def compile_template(line): + """ + Compile a template expression into a python function (like jsps, but way shorter) + """ + extr = [] + def repl(match): + g = match.group + if g('dollar'): return "$" + elif g('backslash'): + return "\\" + elif g('subst'): + extr.append(g('code')) + return "<<|@|>>" + return None + + line2 = reg_act.sub(repl, line) + params = line2.split('<<|@|>>') + assert(extr) + + + indent = 0 + buf = [] + dvars = [] + app = buf.append + + def app(txt): + buf.append(indent * '\t' + txt) + + for x in range(len(extr)): + if params[x]: + app("lst.append(%r)" % params[x]) + + f = extr[x] + if f.startswith('if') or f.startswith('for'): + app(f + ':') + indent += 1 + elif f.startswith('py:'): + app(f[3:]) + elif f.startswith('endif') or f.startswith('endfor'): + indent -= 1 + elif f.startswith('else') or f.startswith('elif'): + indent -= 1 + app(f + ':') + indent += 1 + elif f.startswith('xml:'): + app('lst.append(xml_escape(%s))' % f[4:]) + else: + #app('lst.append((%s) or "cannot find %s")' % (f, f)) + app('lst.append(%s)' % f) + + if extr: + if params[-1]: + app("lst.append(%r)" % params[-1]) + + fun = COMPILE_TEMPLATE % "\n\t".join(buf) + #print(fun) + return Task.funex(fun) + + +re_blank = re.compile('(\n|\r|\\s)*\n', re.M) +def rm_blank_lines(txt): + txt = re_blank.sub('\r\n', txt) + return txt + +BOM = '\xef\xbb\xbf' +try: + BOM = bytes(BOM, 'iso8859-1') # python 3 +except: + pass + +def stealth_write(self, data, flags='wb'): + try: + x = unicode + except: + data = data.encode('utf-8') # python 3 + else: + data = data.decode(sys.getfilesystemencoding(), 'replace') + data = data.encode('utf-8') + + if self.name.endswith('.vcproj') or self.name.endswith('.vcxproj'): + data = BOM + data + + try: + txt = self.read(flags='rb') + if txt != data: + raise ValueError('must write') + except (IOError, ValueError): + self.write(data, flags=flags) + else: + Logs.debug('msvs: skipping %s' % self.abspath()) +Node.Node.stealth_write = stealth_write + +re_quote = re.compile("[^a-zA-Z0-9-]") +def quote(s): + return re_quote.sub("_", s) + +def xml_escape(value): + return value.replace("&", "&").replace('"', """).replace("'", "'").replace("<", "<").replace(">", ">") + +def make_uuid(v, prefix = None): + """ + simple utility function + """ + if isinstance(v, dict): + keys = list(v.keys()) + keys.sort() + tmp = str([(k, v[k]) for k in keys]) + else: + tmp = str(v) + d = Utils.md5(tmp.encode()).hexdigest().upper() + if prefix: + d = '%s%s' % (prefix, d[8:]) + gid = uuid.UUID(d, version = 4) + return str(gid).upper() + +def diff(node, fromnode): + # difference between two nodes, but with "(..)" instead of ".." + c1 = node + c2 = fromnode + + c1h = c1.height() + c2h = c2.height() + + lst = [] + up = 0 + + while c1h > c2h: + lst.append(c1.name) + c1 = c1.parent + c1h -= 1 + + while c2h > c1h: + up += 1 + c2 = c2.parent + c2h -= 1 + + while id(c1) != id(c2): + lst.append(c1.name) + up += 1 + + c1 = c1.parent + c2 = c2.parent + + for i in range(up): + lst.append('(..)') + lst.reverse() + return tuple(lst) + +class build_property(object): + pass + +class vsnode(object): + """ + Abstract class representing visual studio elements + We assume that all visual studio nodes have a uuid and a parent + """ + def __init__(self, ctx): + self.ctx = ctx # msvs context + self.name = '' # string, mandatory + self.vspath = '' # path in visual studio (name for dirs, absolute path for projects) + self.uuid = '' # string, mandatory + self.parent = None # parent node for visual studio nesting + + def get_waf(self): + """ + Override in subclasses... + """ + return 'cd /d "%s" & %s' % (self.ctx.srcnode.abspath(), getattr(self.ctx, 'waf_command', 'waf.bat')) + + def ptype(self): + """ + Return a special uuid for projects written in the solution file + """ + pass + + def write(self): + """ + Write the project file, by default, do nothing + """ + pass + + def make_uuid(self, val): + """ + Alias for creating uuid values easily (the templates cannot access global variables) + """ + return make_uuid(val) + +class vsnode_vsdir(vsnode): + """ + Nodes representing visual studio folders (which do not match the filesystem tree!) + """ + VS_GUID_SOLUTIONFOLDER = "2150E333-8FDC-42A3-9474-1A3956D46DE8" + def __init__(self, ctx, uuid, name, vspath=''): + vsnode.__init__(self, ctx) + self.title = self.name = name + self.uuid = uuid + self.vspath = vspath or name + + def ptype(self): + return self.VS_GUID_SOLUTIONFOLDER + +class vsnode_project(vsnode): + """ + Abstract class representing visual studio project elements + A project is assumed to be writable, and has a node representing the file to write to + """ + VS_GUID_VCPROJ = "8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942" + def ptype(self): + return self.VS_GUID_VCPROJ + + def __init__(self, ctx, node): + vsnode.__init__(self, ctx) + self.path = node + self.uuid = make_uuid(node.abspath()) + self.name = node.name + self.title = self.path.abspath() + self.source = [] # list of node objects + self.build_properties = [] # list of properties (nmake commands, output dir, etc) + + def dirs(self): + """ + Get the list of parent folders of the source files (header files included) + for writing the filters + """ + lst = [] + def add(x): + if x.height() > self.tg.path.height() and x not in lst: + lst.append(x) + add(x.parent) + for x in self.source: + add(x.parent) + return lst + + def write(self): + Logs.debug('msvs: creating %r' % self.path) + + # first write the project file + template1 = compile_template(PROJECT_TEMPLATE) + proj_str = template1(self) + proj_str = rm_blank_lines(proj_str) + self.path.stealth_write(proj_str) + + # then write the filter + template2 = compile_template(FILTER_TEMPLATE) + filter_str = template2(self) + filter_str = rm_blank_lines(filter_str) + tmp = self.path.parent.make_node(self.path.name + '.filters') + tmp.stealth_write(filter_str) + + def get_key(self, node): + """ + required for writing the source files + """ + name = node.name + if name.endswith('.cpp') or name.endswith('.c'): + return 'ClCompile' + return 'ClInclude' + + def collect_properties(self): + """ + Returns a list of triplet (configuration, platform, output_directory) + """ + ret = [] + for c in self.ctx.configurations: + for p in self.ctx.platforms: + x = build_property() + x.outdir = '' + + x.configuration = c + x.platform = p + + x.preprocessor_definitions = '' + x.includes_search_path = '' + + # can specify "deploy_dir" too + ret.append(x) + self.build_properties = ret + + def get_build_params(self, props): + opt = '--execsolution=%s' % self.ctx.get_solution_node().abspath() + return (self.get_waf(), opt) + + def get_build_command(self, props): + return "%s build %s" % self.get_build_params(props) + + def get_clean_command(self, props): + return "%s clean %s" % self.get_build_params(props) + + def get_rebuild_command(self, props): + return "%s clean build %s" % self.get_build_params(props) + + def get_filter_name(self, node): + lst = diff(node, self.tg.path) + return '\\'.join(lst) or '.' + +class vsnode_alias(vsnode_project): + def __init__(self, ctx, node, name): + vsnode_project.__init__(self, ctx, node) + self.name = name + self.output_file = '' + +class vsnode_build_all(vsnode_alias): + """ + Fake target used to emulate the behaviour of "make all" (starting one process by target is slow) + This is the only alias enabled by default + """ + def __init__(self, ctx, node, name='build_all_projects'): + vsnode_alias.__init__(self, ctx, node, name) + self.is_active = True + +class vsnode_install_all(vsnode_alias): + """ + Fake target used to emulate the behaviour of "make install" + """ + def __init__(self, ctx, node, name='install_all_projects'): + vsnode_alias.__init__(self, ctx, node, name) + + def get_build_command(self, props): + return "%s build install %s" % self.get_build_params(props) + + def get_clean_command(self, props): + return "%s clean %s" % self.get_build_params(props) + + def get_rebuild_command(self, props): + return "%s clean build install %s" % self.get_build_params(props) + +class vsnode_project_view(vsnode_alias): + """ + Fake target used to emulate a file system view + """ + def __init__(self, ctx, node, name='project_view'): + vsnode_alias.__init__(self, ctx, node, name) + self.tg = self.ctx() # fake one, cannot remove + self.exclude_files = Node.exclude_regs + ''' +waf-1.6.* +waf3-1.6.*/** +.waf-1.6.* +.waf3-1.6.*/** +**/*.sdf +**/*.suo +**/*.ncb +**/%s + ''' % Options.lockfile + + def collect_source(self): + # this is likely to be slow + self.source = self.ctx.srcnode.ant_glob('**', excl=self.exclude_files) + + def get_build_command(self, props): + params = self.get_build_params(props) + (self.ctx.cmd,) + return "%s %s %s" % params + + def get_clean_command(self, props): + return "" + + def get_rebuild_command(self, props): + return self.get_build_command(props) + +class vsnode_target(vsnode_project): + """ + Visual studio project representing a targets (programs, libraries, etc) and bound + to a task generator + """ + def __init__(self, ctx, tg): + """ + A project is more or less equivalent to a file/folder + """ + base = getattr(ctx, 'projects_dir', None) or tg.path + node = base.make_node(quote(tg.name) + ctx.project_extension) # the project file as a Node + vsnode_project.__init__(self, ctx, node) + self.name = quote(tg.name) + self.tg = tg # task generator + + def get_build_params(self, props): + """ + Override the default to add the target name + """ + opt = '--execsolution=%s' % self.ctx.get_solution_node().abspath() + if getattr(self, 'tg', None): + opt += " --targets=%s" % self.tg.name + return (self.get_waf(), opt) + + def collect_source(self): + tg = self.tg + source_files = tg.to_nodes(getattr(tg, 'source', [])) + include_dirs = Utils.to_list(getattr(tg, 'msvs_includes', [])) + include_files = [] + for x in include_dirs: + if isinstance(x, str): + x = tg.path.find_node(x) + if x: + lst = [y for y in x.ant_glob(HEADERS_GLOB, flat=False)] + include_files.extend(lst) + + # remove duplicates + self.source.extend(list(set(source_files + include_files))) + self.source.sort(key=lambda x: x.abspath()) + + def collect_properties(self): + """ + Visual studio projects are associated with platforms and configurations (for building especially) + """ + super(vsnode_target, self).collect_properties() + for x in self.build_properties: + x.outdir = self.path.parent.abspath() + x.preprocessor_definitions = '' + x.includes_search_path = '' + + try: + tsk = self.tg.link_task + except AttributeError: + pass + else: + x.output_file = tsk.outputs[0].abspath() + x.preprocessor_definitions = ';'.join(tsk.env.DEFINES) + x.includes_search_path = ';'.join(self.tg.env.INCPATHS) + +class msvs_generator(BuildContext): + '''generates a visual studio 2010 solution''' + cmd = 'msvs' + fun = 'build' + + def init(self): + """ + Some data that needs to be present + """ + if not getattr(self, 'configurations', None): + self.configurations = ['Release'] # LocalRelease, RemoteDebug, etc + if not getattr(self, 'platforms', None): + self.platforms = ['Win32'] + if not getattr(self, 'all_projects', None): + self.all_projects = [] + if not getattr(self, 'project_extension', None): + self.project_extension = '.vcxproj' + if not getattr(self, 'projects_dir', None): + self.projects_dir = self.srcnode.make_node('.depproj') + self.projects_dir.mkdir() + + # bind the classes to the object, so that subclass can provide custom generators + if not getattr(self, 'vsnode_vsdir', None): + self.vsnode_vsdir = vsnode_vsdir + if not getattr(self, 'vsnode_target', None): + self.vsnode_target = vsnode_target + if not getattr(self, 'vsnode_build_all', None): + self.vsnode_build_all = vsnode_build_all + if not getattr(self, 'vsnode_install_all', None): + self.vsnode_install_all = vsnode_install_all + if not getattr(self, 'vsnode_project_view', None): + self.vsnode_project_view = vsnode_project_view + + self.numver = '11.00' + self.vsver = '2010' + + def execute(self): + """ + Entry point + """ + self.restore() + if not self.all_envs: + self.load_envs() + self.recurse([self.run_dir]) + + # user initialization + self.init() + + # two phases for creating the solution + self.collect_projects() # add project objects into "self.all_projects" + self.write_files() # write the corresponding project and solution files + + def collect_projects(self): + """ + Fill the list self.all_projects with project objects + Fill the list of build targets + """ + self.collect_targets() + self.add_aliases() + self.collect_dirs() + self.all_projects.sort(key=lambda x: getattr(x, 'path', None) and x.path.abspath() or x.name) + + def write_files(self): + """ + Write the project and solution files from the data collected + so far. It is unlikely that you will want to change this + """ + for p in self.all_projects: + p.write() + + # and finally write the solution file + node = self.get_solution_node() + node.parent.mkdir() + Logs.warn('Creating %r' % node) + template1 = compile_template(SOLUTION_TEMPLATE) + sln_str = template1(self) + sln_str = rm_blank_lines(sln_str) + node.stealth_write(sln_str) + + def get_solution_node(self): + """ + The solution filename is required when writing the .vcproj files + return self.solution_node and if it does not exist, make one + """ + try: + return self.solution_node + except: + pass + + solution_name = getattr(self, 'solution_name', None) + if not solution_name: + solution_name = getattr(Context.g_module, Context.APPNAME, 'project') + '.sln' + if os.path.isabs(solution_name): + self.solution_node = self.root.make_node(solution_name) + else: + self.solution_node = self.srcnode.make_node(solution_name) + return self.solution_node + + def project_configurations(self): + """ + Helper that returns all the pairs (config,platform) + """ + ret = [] + for c in self.configurations: + for p in self.platforms: + ret.append((c, p)) + return ret + + def collect_targets(self): + """ + Process the list of task generators + """ + for g in self.groups: + for tg in g: + if not isinstance(tg, TaskGen.task_gen): + continue + + if not hasattr(tg, 'msvs_includes'): + tg.msvs_includes = tg.to_list(getattr(tg, 'includes', [])) + tg.to_list(getattr(tg, 'export_includes', [])) + tg.post() + if not getattr(tg, 'link_task', None): + continue + + p = self.vsnode_target(self, tg) + p.collect_source() # delegate this processing + p.collect_properties() + self.all_projects.append(p) + + def add_aliases(self): + """ + Add a specific target that emulates the "make all" necessary for Visual studio when pressing F7 + We also add an alias for "make install" (disabled by default) + """ + base = getattr(self, 'projects_dir', None) or self.tg.path + + node_project = base.make_node('build_all_projects' + self.project_extension) # Node + p_build = self.vsnode_build_all(self, node_project) + p_build.collect_properties() + self.all_projects.append(p_build) + + node_project = base.make_node('install_all_projects' + self.project_extension) # Node + p_install = self.vsnode_install_all(self, node_project) + p_install.collect_properties() + self.all_projects.append(p_install) + + node_project = base.make_node('project_view' + self.project_extension) # Node + p_view = self.vsnode_project_view(self, node_project) + p_view.collect_source() + p_view.collect_properties() + self.all_projects.append(p_view) + + n = self.vsnode_vsdir(self, make_uuid(self.srcnode.abspath() + 'build_aliases'), "build_aliases") + p_build.parent = p_install.parent = p_view.parent = n + self.all_projects.append(n) + + def collect_dirs(self): + """ + Create the folder structure in the Visual studio project view + """ + seen = {} + def make_parents(proj): + # look at a project, try to make a parent + if getattr(proj, 'parent', None): + # aliases already have parents + return + x = proj.iter_path + if x in seen: + proj.parent = seen[x] + return + + # There is not vsnode_vsdir for x. + # So create a project representing the folder "x" + n = proj.parent = seen[x] = self.vsnode_vsdir(self, make_uuid(x.abspath()), x.name) + n.iter_path = x.parent + self.all_projects.append(n) + + # recurse up to the project directory + if x.height() > self.srcnode.height() + 1: + make_parents(n) + + for p in self.all_projects[:]: # iterate over a copy of all projects + if not getattr(p, 'tg', None): + # but only projects that have a task generator + continue + + # make a folder for each task generator + p.iter_path = p.tg.path + make_parents(p) + +def wrap_2008(cls): + class dec(cls): + def __init__(self, *k, **kw): + cls.__init__(self, *k, **kw) + self.project_template = PROJECT_2008_TEMPLATE + + def display_filter(self): + + root = build_property() + root.subfilters = [] + root.sourcefiles = [] + root.source = [] + root.name = '' + + @Utils.run_once + def add_path(lst): + if not lst: + return root + child = build_property() + child.subfilters = [] + child.sourcefiles = [] + child.source = [] + child.name = lst[-1] + + par = add_path(lst[:-1]) + par.subfilters.append(child) + return child + + for x in self.source: + # this crap is for enabling subclasses to override get_filter_name + tmp = self.get_filter_name(x.parent) + tmp = tmp != '.' and tuple(tmp.split('\\')) or () + par = add_path(tmp) + par.source.append(x) + + def display(n): + buf = [] + for x in n.source: + buf.append('\n' % (xml_escape(x.abspath()), self.get_key(x))) + for x in n.subfilters: + buf.append('' % xml_escape(x.name)) + buf.append(display(x)) + buf.append('') + return '\n'.join(buf) + + return display(root) + + def get_key(self, node): + """ + If you do not want to let visual studio use the default file extensions, + override this method to return a value: + 0: C/C++ Code, 1: C++ Class, 2: C++ Header File, 3: C++ Form, + 4: C++ Control, 5: Text File, 6: DEF File, 7: IDL File, + 8: Makefile, 9: RGS File, 10: RC File, 11: RES File, 12: XSD File, + 13: XML File, 14: HTML File, 15: CSS File, 16: Bitmap, 17: Icon, + 18: Resx File, 19: BSC File, 20: XSX File, 21: C++ Web Service, + 22: ASAX File, 23: Asp Page, 24: Document, 25: Discovery File, + 26: C# File, 27: eFileTypeClassDiagram, 28: MHTML Document, + 29: Property Sheet, 30: Cursor, 31: Manifest, 32: eFileTypeRDLC + """ + return '' + + def write(self): + Logs.debug('msvs: creating %r' % self.path) + template1 = compile_template(self.project_template) + proj_str = template1(self) + proj_str = rm_blank_lines(proj_str) + self.path.stealth_write(proj_str) + + return dec + +class msvs_2008_generator(msvs_generator): + '''generates a visual studio 2008 solution''' + cmd = 'msvs2008' + fun = msvs_generator.fun + + def init(self): + if not getattr(self, 'project_extension', None): + self.project_extension = '_2008.vcproj' + if not getattr(self, 'solution_name', None): + self.solution_name = getattr(Context.g_module, Context.APPNAME, 'project') + '_2008.sln' + + if not getattr(self, 'vsnode_target', None): + self.vsnode_target = wrap_2008(vsnode_target) + if not getattr(self, 'vsnode_build_all', None): + self.vsnode_build_all = wrap_2008(vsnode_build_all) + if not getattr(self, 'vsnode_install_all', None): + self.vsnode_install_all = wrap_2008(vsnode_install_all) + if not getattr(self, 'vsnode_project_view', None): + self.vsnode_project_view = wrap_2008(vsnode_project_view) + + msvs_generator.init(self) + self.numver = '10.00' + self.vsver = '2008' + +def options(ctx): + """ + If the msvs option is used, try to detect if the build is made from visual studio + """ + ctx.add_option('--execsolution', action='store', help='when building with visual studio, use a build state file') + + old = BuildContext.execute + def override_build_state(ctx): + def lock(rm, add): + uns = ctx.options.execsolution.replace('.sln', rm) + uns = ctx.root.make_node(uns) + try: + uns.delete() + except: + pass + + uns = ctx.options.execsolution.replace('.sln', add) + uns = ctx.root.make_node(uns) + try: + uns.write('') + except: + pass + + if ctx.options.execsolution: + ctx.launch_dir = Context.top_dir # force a build for the whole project (invalid cwd when called by visual studio) + lock('.lastbuildstate', '.unsuccessfulbuild') + old(ctx) + lock('.unsuccessfulbuild', '.lastbuildstate') + else: + old(ctx) + BuildContext.execute = override_build_state + diff --git a/waflib/extras/netcache_client.py b/waflib/extras/netcache_client.py new file mode 100644 index 00000000..48aa7d6e --- /dev/null +++ b/waflib/extras/netcache_client.py @@ -0,0 +1,328 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2011 (ita) + +""" +A client for the network cache (playground/netcache/). Launch the server with: +./netcache_server, then use it for the builds by adding the following: + + def options(opt): + opt.load('netcache_client') + +The parameters should be present in the environment in the form: + NETCACHE=host:port@mode waf configure build + +where: + mode: PUSH, PULL, PUSH_PULL + host: host where the server resides, for example 127.0.0.1 + port: by default the server runs on port 51200 + +The cache can be enabled for the build only: + def options(opt): + opt.load('netcache_client', funs=[]) + def build(bld): + bld.setup_netcache('localhost', 51200, 'PUSH_PULL') +""" + +import os, socket, time, atexit +from waflib import Task, Logs, Utils, Build, Options, Runner +from waflib.Configure import conf + +BUF = 8192 * 16 +HEADER_SIZE = 128 +MODES = ['PUSH', 'PULL', 'PUSH_PULL'] +STALE_TIME = 30 # seconds + +GET = 'GET' +PUT = 'PUT' +LST = 'LST' +BYE = 'BYE' + +all_sigs_in_cache = (0.0, []) + +active_connections = Runner.Queue(0) +def get_connection(): + # return a new connection... do not forget to release it! + try: + ret = active_connections.get(block=False) + except Exception: + ret = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + ret.connect(Task.net_cache[:2]) + return ret + +def release_connection(conn, msg=''): + if conn: + active_connections.put(conn) + +def close_connection(conn, msg=''): + if conn: + data = '%s,%s' % (BYE, msg) + try: + conn.send(data.ljust(HEADER_SIZE)) + except: + pass + try: + conn.close() + except: + pass + +def close_all(): + while active_connections.qsize(): + conn = active_connections.get() + try: + close_connection(conn) + except: + pass +atexit.register(close_all) + +def read_header(conn): + cnt = 0 + buf = [] + while cnt < HEADER_SIZE: + data = conn.recv(HEADER_SIZE - cnt) + if not data: + #import traceback + #traceback.print_stack() + raise ValueError('connection ended when reading a header %r' % buf) + buf.append(data) + cnt += len(data) + return ''.join(buf) + +def check_cache(conn, ssig): + """ + List the files on the server, this is an optimization because it assumes that + concurrent builds are rare + """ + global all_sigs_in_cache + if not STALE_TIME: + return + if time.time() - all_sigs_in_cache[0] > STALE_TIME: + + params = (LST,'') + conn.send(','.join(params).ljust(HEADER_SIZE)) + + # read what is coming back + ret = read_header(conn) + size = int(ret.split(',')[0]) + + buf = [] + cnt = 0 + while cnt < size: + data = conn.recv(min(BUF, size-cnt)) + if not data: + raise ValueError('connection ended %r %r' % (cnt, size)) + buf.append(data) + cnt += len(data) + all_sigs_in_cache = (time.time(), ''.join(buf).split('\n')) + Logs.debug('netcache: server cache has %r entries' % len(all_sigs_in_cache[1])) + + if not ssig in all_sigs_in_cache[1]: + raise ValueError('no file %s in cache' % ssig) + +class MissingFile(Exception): + pass + +def recv_file(conn, ssig, count, p): + check_cache(conn, ssig) + + params = (GET, ssig, str(count)) + conn.send(','.join(params).ljust(HEADER_SIZE)) + data = read_header(conn) + + size = int(data.split(',')[0]) + + if size == -1: + raise MissingFile('no file %s - %s in cache' % (ssig, count)) + + # get the file, writing immediately + # TODO a tmp file would be better + f = open(p, 'wb') + cnt = 0 + while cnt < size: + data = conn.recv(min(BUF, size-cnt)) + if not data: + raise ValueError('connection ended %r %r' % (cnt, size)) + f.write(data) + cnt += len(data) + f.close() + +def put_data(conn, ssig, cnt, p): + #print "pushing %r %r %r" % (ssig, cnt, p) + size = os.stat(p).st_size + params = (PUT, ssig, str(cnt), str(size)) + conn.send(','.join(params).ljust(HEADER_SIZE)) + f = open(p, 'rb') + cnt = 0 + while cnt < size: + r = f.read(min(BUF, size-cnt)) + while r: + k = conn.send(r) + if not k: + raise ValueError('connection ended') + cnt += k + r = r[k:] + +#def put_data(conn, ssig, cnt, p): +# size = os.stat(p).st_size +# params = (PUT, ssig, str(cnt), str(size)) +# conn.send(','.join(params).ljust(HEADER_SIZE)) +# conn.send(','*size) +# params = (BYE, 'he') +# conn.send(','.join(params).ljust(HEADER_SIZE)) + +def can_retrieve_cache(self): + if not Task.net_cache: + return False + if not self.outputs: + return False + if Task.net_cache[-1] == 'PUSH': + return + self.cached = False + + cnt = 0 + sig = self.signature() + ssig = self.uid().encode('hex') + sig.encode('hex') + + conn = None + err = False + try: + try: + conn = get_connection() + for node in self.outputs: + p = node.abspath() + recv_file(conn, ssig, cnt, p) + cnt += 1 + except MissingFile as e: + Logs.debug('netcache: file is not in the cache %r' % e) + err = True + + except Exception as e: + Logs.debug('netcache: could not get the files %r' % e) + err = True + + # broken connection? remove this one + close_connection(conn) + conn = None + finally: + release_connection(conn) + if err: + return False + + for node in self.outputs: + node.sig = sig + #if self.generator.bld.progress_bar < 1: + # self.generator.bld.to_log('restoring from cache %r\n' % node.abspath()) + + self.cached = True + return True + +@Utils.run_once +def put_files_cache(self): + if not Task.net_cache: + return + if not self.outputs: + return + if Task.net_cache[-1] == 'PULL': + return + if getattr(self, 'cached', None): + return + + #print "called put_files_cache", id(self) + bld = self.generator.bld + sig = self.signature() + ssig = self.uid().encode('hex') + sig.encode('hex') + + conn = None + cnt = 0 + try: + for node in self.outputs: + # We could re-create the signature of the task with the signature of the outputs + # in practice, this means hashing the output files + # this is unnecessary + try: + if not conn: + conn = get_connection() + put_data(conn, ssig, cnt, node.abspath()) + except Exception as e: + Logs.debug("netcache: could not push the files %r" % e) + + # broken connection? remove this one + close_connection(conn) + conn = None + cnt += 1 + finally: + release_connection(conn) + + bld.task_sigs[self.uid()] = self.cache_sig + +def hash_env_vars(self, env, vars_lst): + if not env.table: + env = env.parent + if not env: + return Utils.SIG_NIL + + idx = str(id(env)) + str(vars_lst) + try: + cache = self.cache_env + except AttributeError: + cache = self.cache_env = {} + else: + try: + return self.cache_env[idx] + except KeyError: + pass + + v = str([env[a] for a in vars_lst]) + v = v.replace(self.srcnode.abspath(), '') # here + m = Utils.md5() + m.update(v.encode()) + ret = m.digest() + + Logs.debug('envhash: %r %r', ret, v) + + cache[idx] = ret + + return ret + +def uid(self): + try: + return self.uid_ + except AttributeError: + m = Utils.md5() + src = self.generator.bld.srcnode + up = m.update + up(self.__class__.__name__.encode()) + for x in self.inputs + self.outputs: + up(x.path_from(src).encode()) + self.uid_ = m.digest() + return self.uid_ + +@conf +def setup_netcache(ctx, host, port, mode): + Logs.warn('Using the network cache %s, %s, %s' % (host, port, mode)) + Task.net_cache = (host, port, mode) + Task.Task.can_retrieve_cache = can_retrieve_cache + Task.Task.put_files_cache = put_files_cache + Task.Task.uid = uid + Build.BuildContext.hash_env_vars = hash_env_vars + ctx.cache_global = Options.cache_global = True + +def options(opt): + if not 'NETCACHE' in os.environ: + Logs.warn('the network cache is disabled, set NETCACHE=host:port@mode to enable') + else: + v = os.environ['NETCACHE'] + if v in MODES: + host = socket.gethostname() + port = 51200 + mode = v + else: + mode = 'PUSH_PULL' + host, port = v.split(':') + if port.find('@'): + port, mode = port.split('@') + port = int(port) + if not mode in MODES: + opt.fatal('Invalid mode %s not in %r' % (mode, MODES)) + setup_netcache(opt, host, port, mode) + diff --git a/waflib/extras/objcopy.py b/waflib/extras/objcopy.py new file mode 100644 index 00000000..923a7f2e --- /dev/null +++ b/waflib/extras/objcopy.py @@ -0,0 +1,54 @@ +#!/usr/bin/python +# Grygoriy Fuchedzhy 2010 + +""" +Support for converting linked targets to ihex, srec or binary files using +objcopy. Use the 'objcopy' feature in conjuction with the 'cc' or 'cxx' +feature. The 'objcopy' feature uses the following attributes: + +objcopy_bfdname Target object format name (eg. ihex, srec, binary). + Defaults to ihex. +objcopy_target File name used for objcopy output. This defaults to the + target name with objcopy_bfdname as extension. +objcopy_install_path Install path for objcopy_target file. Defaults to ${PREFIX}/fw. +objcopy_flags Additional flags passed to objcopy. +""" + +from waflib.Utils import def_attrs +from waflib import Task +from waflib.TaskGen import feature, after_method + +class objcopy(Task.Task): + run_str = '${OBJCOPY} -O ${TARGET_BFDNAME} ${OBJCOPYFLAGS} ${SRC} ${TGT}' + color = 'CYAN' + +@feature('objcopy') +@after_method('apply_link') +def objcopy(self): + def_attrs(self, + objcopy_bfdname = 'ihex', + objcopy_target = None, + objcopy_install_path = "${PREFIX}/firmware", + objcopy_flags = '') + + link_output = self.link_task.outputs[0] + if not self.objcopy_target: + self.objcopy_target = link_output.change_ext('.' + self.objcopy_bfdname).name + task = self.create_task('objcopy', + src=link_output, + tgt=self.path.find_or_declare(self.objcopy_target)) + + task.env.append_unique('TARGET_BFDNAME', self.objcopy_bfdname) + try: + task.env.append_unique('OBJCOPYFLAGS', getattr(self, 'objcopy_flags')) + except AttributeError: + pass + + if self.objcopy_install_path: + self.bld.install_files(self.objcopy_install_path, + task.outputs[0], + env=task.env.derive()) + +def configure(ctx): + objcopy = ctx.find_program('objcopy', var='OBJCOPY', mandatory=True) + diff --git a/waflib/extras/ocaml.py b/waflib/extras/ocaml.py new file mode 100644 index 00000000..5cbe5b7e --- /dev/null +++ b/waflib/extras/ocaml.py @@ -0,0 +1,326 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2006-2010 (ita) + +"ocaml support" + +import os, re +from waflib import TaskGen, Utils, Task, Build +from waflib.Logs import error +from waflib.TaskGen import feature, before_method, after_method, extension + +EXT_MLL = ['.mll'] +EXT_MLY = ['.mly'] +EXT_MLI = ['.mli'] +EXT_MLC = ['.c'] +EXT_ML = ['.ml'] + +open_re = re.compile('^\s*open\s+([a-zA-Z]+)(;;){0,1}$', re.M) +foo = re.compile(r"""(\(\*)|(\*\))|("(\\.|[^"\\])*"|'(\\.|[^'\\])*'|.[^()*"'\\]*)""", re.M) +def filter_comments(txt): + meh = [0] + def repl(m): + if m.group(1): meh[0] += 1 + elif m.group(2): meh[0] -= 1 + elif not meh[0]: return m.group(0) + return '' + return foo.sub(repl, txt) + +def scan(self): + node = self.inputs[0] + code = filter_comments(node.read()) + + global open_re + names = [] + import_iterator = open_re.finditer(code) + if import_iterator: + for import_match in import_iterator: + names.append(import_match.group(1)) + found_lst = [] + raw_lst = [] + for name in names: + nd = None + for x in self.incpaths: + nd = x.find_resource(name.lower()+'.ml') + if not nd: nd = x.find_resource(name+'.ml') + if nd: + found_lst.append(nd) + break + else: + raw_lst.append(name) + + return (found_lst, raw_lst) + +native_lst=['native', 'all', 'c_object'] +bytecode_lst=['bytecode', 'all'] + +@feature('ocaml') +def init_ml(self): + Utils.def_attrs(self, + type = 'all', + incpaths_lst = [], + bld_incpaths_lst = [], + mlltasks = [], + mlytasks = [], + mlitasks = [], + native_tasks = [], + bytecode_tasks = [], + linktasks = [], + bytecode_env = None, + native_env = None, + compiled_tasks = [], + includes = '', + uselib = '', + are_deps_set = 0) + +@feature('ocaml') +@after_method('init_ml') +def init_envs_ml(self): + + self.islibrary = getattr(self, 'islibrary', False) + + global native_lst, bytecode_lst + self.native_env = None + if self.type in native_lst: + self.native_env = self.env.derive() + if self.islibrary: self.native_env['OCALINKFLAGS'] = '-a' + + self.bytecode_env = None + if self.type in bytecode_lst: + self.bytecode_env = self.env.derive() + if self.islibrary: self.bytecode_env['OCALINKFLAGS'] = '-a' + + if self.type == 'c_object': + self.native_env.append_unique('OCALINKFLAGS_OPT', '-output-obj') + +@feature('ocaml') +@before_method('apply_vars_ml') +@after_method('init_envs_ml') +def apply_incpaths_ml(self): + inc_lst = self.includes.split() + lst = self.incpaths_lst + for dir in inc_lst: + node = self.path.find_dir(dir) + if not node: + error("node not found: " + str(dir)) + continue + if not node in lst: + lst.append(node) + self.bld_incpaths_lst.append(node) + # now the nodes are added to self.incpaths_lst + +@feature('ocaml') +@before_method('process_source') +def apply_vars_ml(self): + for i in self.incpaths_lst: + if self.bytecode_env: + app = self.bytecode_env.append_value + app('OCAMLPATH', ['-I', i.bldpath(), '-I', i.srcpath()]) + + if self.native_env: + app = self.native_env.append_value + app('OCAMLPATH', ['-I', i.bldpath(), '-I', i.srcpath()]) + + varnames = ['INCLUDES', 'OCAMLFLAGS', 'OCALINKFLAGS', 'OCALINKFLAGS_OPT'] + for name in self.uselib.split(): + for vname in varnames: + cnt = self.env[vname+'_'+name] + if cnt: + if self.bytecode_env: self.bytecode_env.append_value(vname, cnt) + if self.native_env: self.native_env.append_value(vname, cnt) + +@feature('ocaml') +@after_method('process_source') +def apply_link_ml(self): + + if self.bytecode_env: + ext = self.islibrary and '.cma' or '.run' + + linktask = self.create_task('ocalink') + linktask.bytecode = 1 + linktask.set_outputs(self.path.find_or_declare(self.target + ext)) + linktask.env = self.bytecode_env + self.linktasks.append(linktask) + + if self.native_env: + if self.type == 'c_object': ext = '.o' + elif self.islibrary: ext = '.cmxa' + else: ext = '' + + linktask = self.create_task('ocalinkx') + linktask.set_outputs(self.path.find_or_declare(self.target + ext)) + linktask.env = self.native_env + self.linktasks.append(linktask) + + # we produce a .o file to be used by gcc + self.compiled_tasks.append(linktask) + +@extension(*EXT_MLL) +def mll_hook(self, node): + mll_task = self.create_task('ocamllex', node, node.change_ext('.ml')) + mll_task.env = self.native_env.derive() + self.mlltasks.append(mll_task) + + self.source.append(mll_task.outputs[0]) + +@extension(*EXT_MLY) +def mly_hook(self, node): + mly_task = self.create_task('ocamlyacc', node, [node.change_ext('.ml'), node.change_ext('.mli')]) + mly_task.env = self.native_env.derive() + self.mlytasks.append(mly_task) + self.source.append(mly_task.outputs[0]) + + task = self.create_task('ocamlcmi', mly_task.outputs[1], mly_task.outputs[1].change_ext('.cmi')) + task.env = self.native_env.derive() + +@extension(*EXT_MLI) +def mli_hook(self, node): + task = self.create_task('ocamlcmi', node, node.change_ext('.cmi')) + task.env = self.native_env.derive() + self.mlitasks.append(task) + +@extension(*EXT_MLC) +def mlc_hook(self, node): + task = self.create_task('ocamlcc', node, node.change_ext('.o')) + task.env = self.native_env.derive() + self.compiled_tasks.append(task) + +@extension(*EXT_ML) +def ml_hook(self, node): + if self.native_env: + task = self.create_task('ocamlx', node, node.change_ext('.cmx')) + task.env = self.native_env.derive() + task.incpaths = self.bld_incpaths_lst + self.native_tasks.append(task) + + if self.bytecode_env: + task = self.create_task('ocaml', node, node.change_ext('.cmo')) + task.env = self.bytecode_env.derive() + task.bytecode = 1 + task.incpaths = self.bld_incpaths_lst + self.bytecode_tasks.append(task) + +def compile_may_start(self): + + if not getattr(self, 'flag_deps', ''): + self.flag_deps = 1 + + # the evil part is that we can only compute the dependencies after the + # source files can be read (this means actually producing the source files) + if getattr(self, 'bytecode', ''): alltasks = self.generator.bytecode_tasks + else: alltasks = self.generator.native_tasks + + self.signature() # ensure that files are scanned - unfortunately + tree = self.generator.bld + env = self.env + for node in self.inputs: + lst = tree.node_deps[self.uid()] + for depnode in lst: + for t in alltasks: + if t == self: continue + if depnode in t.inputs: + self.set_run_after(t) + + # TODO necessary to get the signature right - for now + delattr(self, 'cache_sig') + self.signature() + + return Task.Task.runnable_status(self) + +class ocamlx(Task.Task): + """native caml compilation""" + color = 'GREEN' + run_str = '${OCAMLOPT} ${OCAMLPATH} ${OCAMLFLAGS} ${OCAMLINCLUDES} -c -o ${TGT} ${SRC}' + scan = scan + runnable_status = compile_may_start + +class ocaml(Task.Task): + """bytecode caml compilation""" + color = 'GREEN' + run_str = '${OCAMLC} ${OCAMLPATH} ${OCAMLFLAGS} ${OCAMLINCLUDES} -c -o ${TGT} ${SRC}' + scan = scan + runnable_status = compile_may_start + +class ocamlcmi(Task.Task): + """interface generator (the .i files?)""" + color = 'BLUE' + run_str = '${OCAMLC} ${OCAMLPATH} ${OCAMLINCLUDES} -o ${TGT} -c ${SRC}' + before = ['ocamlcc', 'ocaml', 'ocamlcc'] + +class ocamlcc(Task.Task): + """ocaml to c interfaces""" + color = 'GREEN' + run_str = 'cd ${TGT[0].bld_dir()} && ${OCAMLOPT} ${OCAMLFLAGS} ${OCAMLPATH} ${OCAMLINCLUDES} -c ${SRC[0].abspath()}' + +class ocamllex(Task.Task): + """lexical generator""" + color = 'BLUE' + run_str = '${OCAMLLEX} ${SRC} -o ${TGT}' + before = ['ocamlcmi', 'ocaml', 'ocamlcc'] + +class ocamlyacc(Task.Task): + """parser generator""" + color = 'BLUE' + run_str = '${OCAMLYACC} -b ${TGT[0].bld_base(env)} ${SRC}' + before = ['ocamlcmi', 'ocaml', 'ocamlcc'] + +def link_may_start(self): + + if getattr(self, 'bytecode', 0): alltasks = self.generator.bytecode_tasks + else: alltasks = self.generator.native_tasks + + for x in alltasks: + if not x.hasrun: + return Task.ASK_LATER + + if not getattr(self, 'order', ''): + + # now reorder the inputs given the task dependencies + # this part is difficult, we do not have a total order on the tasks + # if the dependencies are wrong, this may not stop + seen = [] + pendant = []+alltasks + while pendant: + task = pendant.pop(0) + if task in seen: continue + for x in task.run_after: + if not x in seen: + pendant.append(task) + break + else: + seen.append(task) + self.inputs = [x.outputs[0] for x in seen] + self.order = 1 + return Task.Task.runnable_status(self) + +class ocalink(Task.Task): + """bytecode caml link""" + color = 'YELLOW' + run_str = '${OCAMLC} -o ${TGT} ${OCAMLINCLUDES} ${OCALINKFLAGS} ${SRC}' + runnable_status = link_may_start + after = ['ocaml', 'ocamlcc'] + +class ocalinkx(Task.Task): + """native caml link""" + color = 'YELLOW' + run_str = '${OCAMLOPT} -o ${TGT} ${OCAMLINCLUDES} ${OCALINKFLAGS_OPT} ${SRC}' + runnable_status = link_may_start + after = ['ocamlx', 'ocamlcc'] + +def configure(conf): + opt = conf.find_program('ocamlopt', var='OCAMLOPT', mandatory=False) + occ = conf.find_program('ocamlc', var='OCAMLC', mandatory=False) + if (not opt) or (not occ): + conf.fatal('The objective caml compiler was not found:\ninstall it or make it available in your PATH') + + v = conf.env + v['OCAMLC'] = occ + v['OCAMLOPT'] = opt + v['OCAMLLEX'] = conf.find_program('ocamllex', var='OCAMLLEX', mandatory=False) + v['OCAMLYACC'] = conf.find_program('ocamlyacc', var='OCAMLYACC', mandatory=False) + v['OCAMLFLAGS'] = '' + v['OCAMLLIB'] = conf.cmd_and_log(conf.env['OCAMLC']+' -where').strip()+os.sep + v['LIBPATH_OCAML'] = conf.cmd_and_log(conf.env['OCAMLC']+' -where').strip()+os.sep + v['INCLUDES_OCAML'] = conf.cmd_and_log(conf.env['OCAMLC']+' -where').strip()+os.sep + v['LIB_OCAML'] = 'camlrun' + diff --git a/waflib/extras/package.py b/waflib/extras/package.py new file mode 100644 index 00000000..b03c95c2 --- /dev/null +++ b/waflib/extras/package.py @@ -0,0 +1,76 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2011 + +""" +Obtain packages, unpack them in a location, and add associated uselib variables +(CFLAGS_pkgname, LIBPATH_pkgname, etc). + +The default is use a Dependencies.txt file in the source directory. + +This is a work in progress. + +Usage: + +def options(opt): + opt.load('package') + +def configure(conf): + conf.load_packages() +""" + +from waflib import Logs +from waflib.Configure import conf + +try: + from urllib import request +except: + from urllib import urlopen +else: + urlopen = request.urlopen + + +CACHEVAR = 'WAFCACHE_PACKAGE' + +@conf +def get_package_cache_dir(self): + cache = None + if CACHEVAR in conf.environ: + cache = conf.environ[CACHEVAR] + cache = self.root.make_node(cache) + elif self.env[CACHEVAR]: + cache = self.env[CACHEVAR] + cache = self.root.make_node(cache) + else: + cache = self.srcnode.make_node('.wafcache_package') + cache.mkdir() + return cache + +@conf +def download_archive(self, src, dst): + for x in self.env.PACKAGE_REPO: + url = '/'.join((x, src)) + try: + web = urlopen(url) + try: + if web.getcode() != 200: + continue + except AttributeError: + pass + except Exception: + # on python3 urlopen throws an exception + # python 2.3 does not have getcode and throws an exception to fail + continue + else: + tmp = self.root.make_node(dst) + tmp.write(web.read()) + Logs.warn('Downloaded %s from %s' % (tmp.abspath(), url)) + break + else: + self.fatal('Could not get the package %s' % src) + +@conf +def load_packages(self): + cache = self.get_package_cache_dir() + # read the dependencies, get the archives, .. + diff --git a/waflib/extras/parallel_debug.py b/waflib/extras/parallel_debug.py new file mode 100644 index 00000000..25691d09 --- /dev/null +++ b/waflib/extras/parallel_debug.py @@ -0,0 +1,341 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2007-2010 (ita) + +""" +Debugging helper for parallel compilation, outputs +a file named pdebug.svg in the source directory:: + + def options(opt): + opt.load('parallel_debug') + def configure(conf): + conf.load('parallel_debug') + def build(bld): + ... +""" + +import os, time, sys +try: from Queue import Queue +except: from queue import Queue +from waflib import Runner, Options, Utils, Task, Logs, Errors + +#import random +#random.seed(100) + +def options(opt): + opt.add_option('--dtitle', action='store', default='Parallel build representation for %r' % ' '.join(sys.argv), + help='title for the svg diagram', dest='dtitle') + opt.add_option('--dwidth', action='store', type='int', help='diagram width', default=800, dest='dwidth') + opt.add_option('--dtime', action='store', type='float', help='recording interval in seconds', default=0.009, dest='dtime') + opt.add_option('--dband', action='store', type='int', help='band width', default=22, dest='dband') + opt.add_option('--dmaxtime', action='store', type='float', help='maximum time, for drawing fair comparisons', default=0, dest='dmaxtime') + +# red #ff4d4d +# green #4da74d +# lila #a751ff + +color2code = { + 'GREEN' : '#4da74d', + 'YELLOW' : '#fefe44', + 'PINK' : '#a751ff', + 'RED' : '#cc1d1d', + 'BLUE' : '#6687bb', + 'CYAN' : '#34e2e2', +} + +mp = {} +info = [] # list of (text,color) + +def map_to_color(name): + if name in mp: + return mp[name] + try: + cls = Task.classes[name] + except KeyError: + return color2code['RED'] + if cls.color in mp: + return mp[cls.color] + if cls.color in color2code: + return color2code[cls.color] + return color2code['RED'] + +def process(self): + m = self.master + if m.stop: + m.out.put(self) + return + + self.master.set_running(1, id(Utils.threading.currentThread()), self) + + # remove the task signature immediately before it is executed + # in case of failure the task will be executed again + try: + del self.generator.bld.task_sigs[self.uid()] + except: + pass + + try: + self.generator.bld.returned_tasks.append(self) + self.log_display(self.generator.bld) + ret = self.run() + except Exception: + self.err_msg = Utils.ex_stack() + self.hasrun = Task.EXCEPTION + + # TODO cleanup + m.error_handler(self) + m.out.put(self) + return + + if ret: + self.err_code = ret + self.hasrun = Task.CRASHED + else: + try: + self.post_run() + except Errors.WafError: + pass + except Exception: + self.err_msg = Utils.ex_stack() + self.hasrun = Task.EXCEPTION + else: + self.hasrun = Task.SUCCESS + if self.hasrun != Task.SUCCESS: + m.error_handler(self) + + self.master.set_running(-1, id(Utils.threading.currentThread()), self) + m.out.put(self) +Task.Task.process = process + +old_start = Runner.Parallel.start +def do_start(self): + try: + Options.options.dband + except AttributeError: + self.bld.fatal('use def options(opt): opt.load("parallel_debug")!') + + self.taskinfo = Queue() + old_start(self) + if self.dirty: + process_colors(self) +Runner.Parallel.start = do_start + +def set_running(self, by, i, tsk): + self.taskinfo.put( (i, id(tsk), time.time(), tsk.__class__.__name__, self.processed, self.count, by) ) +Runner.Parallel.set_running = set_running + +def name2class(name): + return name.replace(' ', '_').replace('.', '_') + +def process_colors(producer): + # first, cast the parameters + tmp = [] + try: + while True: + tup = producer.taskinfo.get(False) + tmp.append(list(tup)) + except: + pass + + try: + ini = float(tmp[0][2]) + except: + return + + if not info: + seen = [] + for x in tmp: + name = x[3] + if not name in seen: + seen.append(name) + else: + continue + + info.append((name, map_to_color(name))) + info.sort(key=lambda x: x[0]) + + thread_count = 0 + acc = [] + for x in tmp: + thread_count += x[6] + acc.append("%d %d %f %r %d %d %d" % (x[0], x[1], x[2] - ini, x[3], x[4], x[5], thread_count)) + data_node = producer.bld.path.make_node('pdebug.dat') + data_node.write('\n'.join(acc)) + + tmp = [lst[:2] + [float(lst[2]) - ini] + lst[3:] for lst in tmp] + + st = {} + for l in tmp: + if not l[0] in st: + st[l[0]] = len(st.keys()) + tmp = [ [st[lst[0]]] + lst[1:] for lst in tmp ] + THREAD_AMOUNT = len(st.keys()) + + st = {} + for l in tmp: + if not l[1] in st: + st[l[1]] = len(st.keys()) + tmp = [ [lst[0]] + [st[lst[1]]] + lst[2:] for lst in tmp ] + + + BAND = Options.options.dband + + seen = {} + acc = [] + for x in range(len(tmp)): + line = tmp[x] + id = line[1] + + if id in seen: + continue + seen[id] = True + + begin = line[2] + thread_id = line[0] + for y in range(x + 1, len(tmp)): + line = tmp[y] + if line[1] == id: + end = line[2] + #print id, thread_id, begin, end + #acc.append( ( 10*thread_id, 10*(thread_id+1), 10*begin, 10*end ) ) + acc.append( (BAND * begin, BAND*thread_id, BAND*end - BAND*begin, BAND, line[3]) ) + break + + if Options.options.dmaxtime < 0.1: + gwidth = 1 + for x in tmp: + m = BAND * x[2] + if m > gwidth: + gwidth = m + else: + gwidth = BAND * Options.options.dmaxtime + + ratio = float(Options.options.dwidth) / gwidth + gwidth = Options.options.dwidth + + gheight = BAND * (THREAD_AMOUNT + len(info) + 1.5) + + out = [] + + out.append(""" + + + + + + + + +\n + +""" % (0, 0, gwidth + 4, gheight + 4, 0, 0, gwidth + 4, gheight + 4)) + + # main title + if Options.options.dtitle: + out.append("""%s +""" % (gwidth/2, gheight - 5, Options.options.dtitle)) + + # the rectangles + + groups = {} + for (x, y, w, h, clsname) in acc: + try: + groups[clsname].append((x, y, w, h)) + except: + groups[clsname] = [(x, y, w, h)] + for cls in groups: + out.append("\n" % name2class(cls)) + + for (x, y, w, h) in groups[cls]: + out.append("""\n""" % (2 + x*ratio, 2 + y, w*ratio, h, map_to_color(cls))) + out.append("\n") + + # output the caption + cnt = THREAD_AMOUNT + + for (text, color) in info: + # caption box + b = BAND/2 + out.append("""\n""" % (name2class(text), 2 + BAND, 5 + (cnt + 0.5) * BAND, b, b, color)) + + # caption text + out.append("""%s\n""" % (2 + 2 * BAND, 5 + (cnt + 0.5) * BAND + 10, text)) + cnt += 1 + + out.append(""" + + + +""") + + out.append("\n") + + node = producer.bld.path.make_node('pdebug.svg') + node.write("".join(out)) + Logs.warn('Created the diagram %r' % node.abspath()) + + p = node.parent.abspath() + producer.bld.exec_command(['convert', p + os.sep + 'pdebug.svg', p + os.sep + 'pdebug.png']) + diff --git a/waflib/extras/pep8.py b/waflib/extras/pep8.py new file mode 100644 index 00000000..74511c6a --- /dev/null +++ b/waflib/extras/pep8.py @@ -0,0 +1,106 @@ +#! /usr/bin/env python +# encoding: utf-8 +# +# written by Sylvain Rouquette, 2011 + +''' +Install pep8 module: +$ easy_install pep8 + or +$ pip install pep8 + +To add the boost tool to the waf file: +$ ./waf-light --tools=compat15,pep8 + or, if you have waf >= 1.6.2 +$ ./waf update --files=pep8 + + +Then add this to your wscript: + +[at]extension('.py', 'wscript') +def run_pep8(self, node): + self.create_task('Pep8', node) + +''' + +import threading +from waflib import TaskGen, Task, Options + +pep8 = __import__('pep8') + + +class Pep8(Task.Task): + color = 'PINK' + lock = threading.Lock() + + def check_options(self): + if pep8.options: + return + pep8.options = Options.options + pep8.options.prog = 'pep8' + excl = pep8.options.exclude.split(',') + pep8.options.exclude = [s.rstrip('/') for s in excl] + if pep8.options.filename: + pep8.options.filename = pep8.options.filename.split(',') + if pep8.options.select: + pep8.options.select = pep8.options.select.split(',') + else: + pep8.options.select = [] + if pep8.options.ignore: + pep8.options.ignore = pep8.options.ignore.split(',') + elif pep8.options.select: + # Ignore all checks which are not explicitly selected + pep8.options.ignore = [''] + elif pep8.options.testsuite or pep8.options.doctest: + # For doctest and testsuite, all checks are required + pep8.options.ignore = [] + else: + # The default choice: ignore controversial checks + pep8.options.ignore = pep8.DEFAULT_IGNORE.split(',') + pep8.options.physical_checks = pep8.find_checks('physical_line') + pep8.options.logical_checks = pep8.find_checks('logical_line') + pep8.options.counters = dict.fromkeys(pep8.BENCHMARK_KEYS, 0) + pep8.options.messages = {} + + def run(self): + with Pep8.lock: + self.check_options() + pep8.input_file(self.inputs[0].abspath()) + return 0 if not pep8.get_count() else -1 + + +def options(opt): + opt.add_option('-q', '--quiet', default=0, action='count', + help="report only file names, or nothing with -qq") + opt.add_option('-r', '--repeat', action='store_true', + help="show all occurrences of the same error") + opt.add_option('--exclude', metavar='patterns', + default=pep8.DEFAULT_EXCLUDE, + help="exclude files or directories which match these " + "comma separated patterns (default: %s)" % + pep8.DEFAULT_EXCLUDE, + dest='exclude') + opt.add_option('--filename', metavar='patterns', default='*.py', + help="when parsing directories, only check filenames " + "matching these comma separated patterns (default: " + "*.py)") + opt.add_option('--select', metavar='errors', default='', + help="select errors and warnings (e.g. E,W6)") + opt.add_option('--ignore', metavar='errors', default='', + help="skip errors and warnings (e.g. E4,W)") + opt.add_option('--show-source', action='store_true', + help="show source code for each error") + opt.add_option('--show-pep8', action='store_true', + help="show text of PEP 8 for each error") + opt.add_option('--statistics', action='store_true', + help="count errors and warnings") + opt.add_option('--count', action='store_true', + help="print total number of errors and warnings " + "to standard error and set exit code to 1 if " + "total is not null") + opt.add_option('--benchmark', action='store_true', + help="measure processing speed") + opt.add_option('--testsuite', metavar='dir', + help="run regression tests from dir") + opt.add_option('--doctest', action='store_true', + help="run doctest on myself") diff --git a/waflib/extras/proc.py b/waflib/extras/proc.py new file mode 100644 index 00000000..417790df --- /dev/null +++ b/waflib/extras/proc.py @@ -0,0 +1,57 @@ +#! /usr/bin/env python +# per rosengren 2011 + +from os import environ, path +from waflib import TaskGen, Utils + +def options(opt): + grp = opt.add_option_group('Oracle ProC Options') + grp.add_option('--oracle_home', action='store', default=environ.get('PROC_ORACLE'), help='Path to Oracle installation home (has bin/lib)') + grp.add_option('--tns_admin', action='store', default=environ.get('TNS_ADMIN'), help='Directory containing server list (TNS_NAMES.ORA)') + grp.add_option('--connection', action='store', default='dummy-user/dummy-password@dummy-server', help='Format: user/password@server') + +def configure(cnf): + env = cnf.env + if not env.PROC_ORACLE: + env.PROC_ORACLE = cnf.options.oracle_home + if not env.PROC_TNS_ADMIN: + env.PROC_TNS_ADMIN = cnf.options.tns_admin + if not env.PROC_CONNECTION: + env.PROC_CONNECTION = cnf.options.connection + cnf.find_program('proc', var='PROC', path_list=env.PROC_ORACLE + path.sep + 'bin') + +def proc(tsk): + env = tsk.env + gen = tsk.generator + bld = gen.bld + inc_nodes = gen.to_incnodes(Utils.to_list(getattr(gen,'includes',[])) + env['INCLUDES']) + + # FIXME the if-else construct will not work in python 2 + cmd = ( + [env.PROC] + + ['SQLCHECK=SEMANTICS'] + + (['SYS_INCLUDE=(' + ','.join(env.PROC_INCLUDES) + ')'] + if env.PROC_INCLUDES else []) + + ['INCLUDE=(' + ','.join( + [i.bldpath() for i in inc_nodes] + ) + ')'] + + ['userid=' + env.PROC_CONNECTION] + + ['INAME=' + tsk.inputs[0].bldpath()] + + ['ONAME=' + tsk.outputs[0].bldpath()] + ) + exec_env = { + 'ORACLE_HOME': env.PROC_ORACLE, + 'LD_LIBRARY_PATH': env.PROC_ORACLE + path.sep + 'lib', + } + if env.PROC_TNS_ADMIN: + exec_env['TNS_ADMIN'] = env.PROC_TNS_ADMIN + return tsk.exec_command(cmd, env=exec_env) + +TaskGen.declare_chain( + name = 'proc', + rule = proc, + ext_in = '.pc', + ext_out = '.c', + reentrant = True, +) + diff --git a/waflib/extras/qnxnto.py b/waflib/extras/qnxnto.py new file mode 100644 index 00000000..d7641763 --- /dev/null +++ b/waflib/extras/qnxnto.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Jérôme Carretero 2011 (zougloub) +# QNX neutrino compatibility functions + +import sys, os +from waflib import Utils + +class Popen(object): + """ + Popen cannot work on QNX from a threaded program: + Forking in threads is not implemented in neutrino. + + Python's os.popen / spawn / fork won't work when running in threads (they will if in the main program thread) + + In waf, this happens mostly in build. + And the use cases can be replaced by os.system() calls. + """ + __slots__ = ["prog", "kw", "popen", "verbose"] + verbose = 0 + def __init__(self, prog, **kw): + try: + self.prog = prog + self.kw = kw + self.popen = None + if Popen.verbose: + sys.stdout.write("Popen created: %r, kw=%r..." % (prog, kw)) + + do_delegate = kw.get('stdout', None) == -1 and kw.get('stderr', None) == -1 + if do_delegate: + if Popen.verbose: + print("Delegating to real Popen") + self.popen = self.real_Popen(prog, **kw) + else: + if Popen.verbose: + print("Emulating") + except Exception, e: + if Popen.verbose: + print("Exception: %s" % e) + raise + + def __getattr__(self, name): + if Popen.verbose: + sys.stdout.write("Getattr: %s..." % name) + if name in Popen.__slots__: + if Popen.verbose: + print("In slots!") + return object.__getattr__(self, name) + else: + if self.popen is not None: + if Popen.verbose: + print("from Popen") + return getattr(self.popen, name) + else: + if name == "wait": + return self.emu_wait + else: + raise Exception("subprocess emulation: not implemented: %s" % name) + + def emu_wait(self): + if Popen.verbose: + print("emulated wait (%r kw=%r)" % (self.prog, self.kw)) + if isinstance(self.prog, str): + cmd = self.prog + else: + cmd = " ".join(self.prog) + if 'cwd' in self.kw: + cmd = 'cd "%s" && %s' % (self.kw['cwd'], cmd) + return os.system(cmd) + +if sys.platform == "qnx6": + Popen.real_Popen = Utils.subprocess.Popen + Utils.subprocess.Popen = Popen + diff --git a/waflib/extras/relocation.py b/waflib/extras/relocation.py new file mode 100644 index 00000000..ab8dd361 --- /dev/null +++ b/waflib/extras/relocation.py @@ -0,0 +1,85 @@ +#! /usr/bin/env python +# encoding: utf-8 + +""" +Waf 1.6 + +Try to detect if the project directory was relocated, and if it was, +change the node representing the project directory. Just call: + + waf configure build + +Note that if the project directory name changes, the signatures for the tasks using +files in that directory will change, causing a partial build. +""" + +import os +from waflib import Build, ConfigSet, Task, Utils, Errors +from waflib.TaskGen import feature, before_method, after_method + +EXTRA_LOCK = '.old_srcdir' + +old1 = Build.BuildContext.store +def store(self): + old1(self) + db = os.path.join(self.variant_dir, EXTRA_LOCK) + env = ConfigSet.ConfigSet() + env.SRCDIR = self.srcnode.abspath() + env.store(db) +Build.BuildContext.store = store + +old2 = Build.BuildContext.init_dirs +def init_dirs(self): + + if not (os.path.isabs(self.top_dir) and os.path.isabs(self.out_dir)): + raise Errors.WafError('The project was not configured: run "waf configure" first!') + + srcdir = None + db = os.path.join(self.variant_dir, EXTRA_LOCK) + env = ConfigSet.ConfigSet() + try: + env.load(db) + srcdir = env.SRCDIR + except: + pass + + if srcdir: + d = self.root.find_node(srcdir) + if d and srcdir != self.top_dir and getattr(d, 'children', ''): + srcnode = self.root.make_node(self.top_dir) + print("relocating the source directory %r -> %r" % (srcdir, self.top_dir)) + srcnode.children = {} + + for (k, v) in d.children.items(): + srcnode.children[k] = v + v.parent = srcnode + d.children = {} + + old2(self) + +Build.BuildContext.init_dirs = init_dirs + + +def uid(self): + try: + return self.uid_ + except AttributeError: + # this is not a real hot zone, but we want to avoid surprizes here + m = Utils.md5() + up = m.update + up(self.__class__.__name__.encode()) + for x in self.inputs + self.outputs: + up(x.path_from(x.ctx.srcnode).encode()) + self.uid_ = m.digest() + return self.uid_ +Task.Task.uid = uid + +@feature('c', 'cxx', 'd', 'go', 'asm', 'fc', 'includes') +@after_method('propagate_uselib_vars', 'process_source') +def apply_incpaths(self): + lst = self.to_incnodes(self.to_list(getattr(self, 'includes', [])) + self.env['INCLUDES']) + self.includes_nodes = lst + bld = self.bld + self.env['INCPATHS'] = [x.is_child_of(bld.srcnode) and x.path_from(bld.srcnode) or x.abspath() for x in lst] + + diff --git a/waflib/extras/review.py b/waflib/extras/review.py new file mode 100644 index 00000000..4a7ad2f4 --- /dev/null +++ b/waflib/extras/review.py @@ -0,0 +1,328 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Laurent Birtz, 2011 +# moved the code into a separate tool (ita) + +""" +There are several things here: +- a different command-line option management making options persistent +- the review command to display the options set + +Assumptions: +- configuration options are not always added to the right group (and do not count on the users to do it...) +- the options are persistent between the executions (waf options are NOT persistent by design), even for the configuration +- when the options change, the build is invalidated (forcing a reconfiguration) +""" + +import os, textwrap, shutil +from waflib import Logs, Context, ConfigSet, Options, Build, Configure + +class Odict(dict): + """Ordered dictionary""" + def __init__(self, data=None): + self._keys = [] + dict.__init__(self) + if data: + # we were provided a regular dict + if isinstance(data, dict): + self.append_from_dict(data) + + # we were provided a tuple list + elif type(data) == list: + self.append_from_plist(data) + + # we were provided invalid input + else: + raise Exception("expected a dict or a tuple list") + + def append_from_dict(self, dict): + map(self.__setitem__, dict.keys(), dict.values()) + + def append_from_plist(self, plist): + for pair in plist: + if len(pair) != 2: + raise Exception("invalid pairs list") + for (k, v) in plist: + self.__setitem__(k, v) + + def __delitem__(self, key): + if not key in self._keys: + raise KeyError(key) + dict.__delitem__(self, key) + self._keys.remove(key) + + def __setitem__(self, key, item): + dict.__setitem__(self, key, item) + if key not in self._keys: + self._keys.append(key) + + def clear(self): + dict.clear(self) + self._keys = [] + + def copy(self): + return Odict(self.plist()) + + def items(self): + return zip(self._keys, self.values()) + + def keys(self): + return list(self._keys) # return a copy of the list + + def values(self): + return map(self.get, self._keys) + + def plist(self): + p = [] + for k, v in self.items(): + p.append( (k, v) ) + return p + + def __str__(self): + s = "{" + l = len(self._keys) + for k, v in self.items(): + l -= 1 + strkey = str(k) + if isinstance(k, basestring): strkey = "'"+strkey+"'" + strval = str(v) + if isinstance(v, basestring): strval = "'"+strval+"'" + s += strkey + ":" + strval + if l > 0: s += ", " + s += "}" + return s + +review_options = Odict() +""" +Ordered dictionary mapping configuration option names to their optparse option. +""" + +review_defaults = {} +""" +Dictionary mapping configuration option names to their default value. +""" + +old_review_set = None +""" +Review set containing the configuration values before parsing the command line. +""" + +new_review_set = None +""" +Review set containing the configuration values after parsing the command line. +""" + +class OptionsReview(Options.OptionsContext): + def __init__(self, **kw): + super(self.__class__, self).__init__(**kw) + + def prepare_config_review(self): + """ + Find the configuration options that are reviewable, detach + their default value from their optparse object and store them + into the review dictionaries. + """ + gr = self.get_option_group('configure options') + for opt in gr.option_list: + if opt.action != 'store' or opt.dest in ("out", "top"): + continue + review_options[opt.dest] = opt + review_defaults[opt.dest] = opt.default + if gr.defaults.has_key(opt.dest): + del gr.defaults[opt.dest] + opt.default = None + + def parse_args(self): + self.prepare_config_review() + self.parser.get_option('--prefix').help = 'installation prefix' + super(OptionsReview, self).parse_args() + Context.create_context('review').refresh_review_set() + +class ReviewContext(Context.Context): + '''reviews the configuration values''' + + cmd = 'review' + + def __init__(self, **kw): + super(self.__class__, self).__init__(**kw) + + out = Options.options.out + if not out: + out = getattr(Context.g_module, Context.OUT, None) + if not out: + out = Options.lockfile.replace('.lock-waf', '') + self.build_path = (os.path.isabs(out) and self.root or self.path).make_node(out).abspath() + """Path to the build directory""" + + self.cache_path = os.path.join(self.build_path, Build.CACHE_DIR) + """Path to the cache directory""" + + self.review_path = os.path.join(self.cache_path, 'review.cache') + """Path to the review cache file""" + + def execute(self): + """ + Display and store the review set. Invalidate the cache as required. + """ + if not self.compare_review_set(old_review_set, new_review_set): + self.invalidate_cache() + self.store_review_set(new_review_set) + print(self.display_review_set(new_review_set)) + + def invalidate_cache(self): + """Invalidate the cache to prevent bad builds.""" + try: + Logs.warn("Removing the cached configuration since the options have changed") + shutil.rmtree(self.cache_path) + except: + pass + + def refresh_review_set(self): + """ + Obtain the old review set and the new review set, and import the new set. + """ + global old_review_set, new_review_set + old_review_set = self.load_review_set() + new_review_set = self.update_review_set(old_review_set) + self.import_review_set(new_review_set) + + def load_review_set(self): + """ + Load and return the review set from the cache if it exists. + Otherwise, return an empty set. + """ + if os.path.isfile(self.review_path): + return ConfigSet.ConfigSet(self.review_path) + return ConfigSet.ConfigSet() + + def store_review_set(self, review_set): + """ + Store the review set specified in the cache. + """ + if not os.path.isdir(self.cache_path): + os.makedirs(self.cache_path) + review_set.store(self.review_path) + + def update_review_set(self, old_set): + """ + Merge the options passed on the command line with those imported + from the previous review set and return the corresponding + preview set. + """ + + # Convert value to string. It's important that 'None' maps to + # the empty string. + def val_to_str(val): + if val == None or val == '': + return '' + return str(val) + + new_set = ConfigSet.ConfigSet() + opt_dict = Options.options.__dict__ + + for name in review_options.keys(): + # the option is specified explicitly on the command line + if name in opt_dict: + # if the option is the default, pretend it was never specified + if val_to_str(opt_dict[name]) != val_to_str(review_defaults[name]): + new_set[name] = opt_dict[name] + # the option was explicitly specified in a previous command + elif name in old_set: + new_set[name] = old_set[name] + + return new_set + + def import_review_set(self, review_set): + """ + Import the actual value of the reviewable options in the option + dictionary, given the current review set. + """ + for name in review_options.keys(): + if name in review_set: + value = review_set[name] + else: + value = review_defaults[name] + setattr(Options.options, name, value) + + def compare_review_set(self, set1, set2): + """ + Return true if the review sets specified are equal. + """ + if len(set1.keys()) != len(set2.keys()): return False + for key in set1.keys(): + if not key in set2 or set1[key] != set2[key]: + return False + return True + + def display_review_set(self, review_set): + """ + Return the string representing the review set specified. + """ + term_width = Logs.get_term_cols() + lines = [] + for dest in review_options.keys(): + opt = review_options[dest] + name = ", ".join(opt._short_opts + opt._long_opts) + help = opt.help + actual = None + if dest in review_set: actual = review_set[dest] + default = review_defaults[dest] + lines.append(self.format_option(name, help, actual, default, term_width)) + return "Configuration:\n\n" + "\n\n".join(lines) + "\n" + + def format_option(self, name, help, actual, default, term_width): + """ + Return the string representing the option specified. + """ + def val_to_str(val): + if val == None or val == '': + return "(void)" + return str(val) + + max_name_len = 20 + sep_len = 2 + + w = textwrap.TextWrapper() + w.width = term_width - 1 + if w.width < 60: w.width = 60 + + out = "" + + # format the help + out += w.fill(help) + "\n" + + # format the name + name_len = len(name) + out += Logs.colors.CYAN + name + Logs.colors.NORMAL + + # set the indentation used when the value wraps to the next line + w.subsequent_indent = " ".rjust(max_name_len + sep_len) + w.width -= (max_name_len + sep_len) + + # the name string is too long, switch to the next line + if name_len > max_name_len: + out += "\n" + w.subsequent_indent + + # fill the remaining of the line with spaces + else: + out += " ".rjust(max_name_len + sep_len - name_len) + + # format the actual value, if there is one + if actual != None: + out += Logs.colors.BOLD + w.fill(val_to_str(actual)) + Logs.colors.NORMAL + "\n" + w.subsequent_indent + + # format the default value + default_fmt = val_to_str(default) + if actual != None: + default_fmt = "default: " + default_fmt + out += Logs.colors.NORMAL + w.fill(default_fmt) + Logs.colors.NORMAL + + return out + +# Monkey-patch ConfigurationContext.execute() to have it store the review set. +old_configure_execute = Configure.ConfigurationContext.execute +def new_configure_execute(self): + old_configure_execute(self) + Context.create_context('review').store_review_set(new_review_set) +Configure.ConfigurationContext.execute = new_configure_execute + diff --git a/waflib/extras/sas.py b/waflib/extras/sas.py new file mode 100644 index 00000000..79c9a51d --- /dev/null +++ b/waflib/extras/sas.py @@ -0,0 +1,72 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Mark Coggeshall, 2010 + +"SAS support" + +import os, re +from waflib import Utils, Task, TaskGen, Runner, Build, Errors, Node +from waflib.TaskGen import feature, before_method +from waflib.Logs import error, warn, debug + +sas_fun, _ = Task.compile_fun('sas -sysin ${SRCFILE} -log ${LOGFILE} -print ${LSTFILE}', shell=False) + +class sas(Task.Task): + vars = ['SAS', 'SASFLAGS'] + def run(task): + command = 'SAS' + env = task.env + bld = task.generator.bld + + fun = sas_fun + + node = task.inputs[0] + logfilenode = node.change_ext('.log') + lstfilenode = node.change_ext('.lst') + + # set the cwd + task.cwd = task.inputs[0].parent.get_src().abspath() + debug('runner: %s on %s' % (command, node.abspath)) + + SASINPUTS = node.parent.get_bld().abspath() + os.pathsep + node.parent.get_src().abspath() + os.pathsep + task.env.env = {'SASINPUTS': SASINPUTS} + + task.env.SRCFILE = node.abspath() + task.env.LOGFILE = logfilenode.abspath() + task.env.LSTFILE = lstfilenode.abspath() + ret = fun(task) + if ret: + error('Running %s on %r returned a non-zero exit' % (command, node)) + error('SRCFILE = %r' % node) + error('LOGFILE = %r' % logfilenode) + error('LSTFILE = %r' % lstfilenode) + return ret + +@feature('sas') +@before_method('process_source') +def apply_sas(self): + if not getattr(self, 'type', None) in ['sas']: + self.type = 'sas' + + self.env['logdir'] = getattr(self, 'logdir', 'log') + self.env['lstdir'] = getattr(self, 'lstdir', 'lst') + + deps_lst = [] + + if getattr(self, 'deps', None): + deps = self.to_list(self.deps) + for filename in deps: + n = self.path.find_resource(filename) + if not n: n = self.bld.root.find_resource(filename) + if not n: raise Errors.WafError('cannot find input file %s for processing' % filename) + if not n in deps_lst: deps_lst.append(n) + + for node in self.to_nodes(self.source): + if self.type == 'sas': + task = self.create_task('sas', src=node) + task.dep_nodes = deps_lst + self.source = [] + +def configure(self): + self.find_program('sas', var='SAS', mandatory=False) + diff --git a/waflib/extras/scala.py b/waflib/extras/scala.py new file mode 100644 index 00000000..e7b6d23a --- /dev/null +++ b/waflib/extras/scala.py @@ -0,0 +1,130 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +""" +Scala support + +scalac outputs files a bit where it wants to +""" + +import os, re +from waflib.Configure import conf +from waflib import TaskGen, Task, Utils, Options, Build, Errors, Node +from waflib.TaskGen import feature, before_method, after_method + +from waflib.Tools import ccroot +ccroot.USELIB_VARS['scalac'] = set(['CLASSPATH', 'SCALACFLAGS']) + +from waflib.Tools import javaw + +@feature('scalac') +@before_method('process_source') +def apply_scalac(self): + + Utils.def_attrs(self, jarname='', classpath='', + sourcepath='.', srcdir='.', + jar_mf_attributes={}, jar_mf_classpath=[]) + + nodes_lst = [] + + outdir = getattr(self, 'outdir', None) + if outdir: + if not isinstance(outdir, Node.Node): + outdir = self.path.get_bld().make_node(self.outdir) + else: + outdir = self.path.get_bld() + outdir.mkdir() + self.env['OUTDIR'] = outdir.abspath() + + self.scalac_task = tsk = self.create_task('scalac') + tmp = [] + + srcdir = getattr(self, 'srcdir', '') + if isinstance(srcdir, Node.Node): + srcdir = [srcdir] + for x in Utils.to_list(srcdir): + if isinstance(x, Node.Node): + y = x + else: + y = self.path.find_dir(x) + if not y: + self.bld.fatal('Could not find the folder %s from %s' % (x, self.path)) + tmp.append(y) + tsk.srcdir = tmp + +# reuse some code +feature('scalac')(javaw.use_javac_files) +after_method('apply_scalac')(javaw.use_javac_files) + +feature('scalac')(javaw.set_classpath) +after_method('apply_scalac', 'use_scalac_files')(javaw.set_classpath) + + +SOURCE_RE = '**/*.scala' +class scalac(javaw.javac): + color = 'GREEN' + vars = ['CLASSPATH', 'SCALACFLAGS', 'SCALAC', 'OUTDIR'] + + def runnable_status(self): + """ + Wait for dependent tasks to be complete, then read the file system to find the input nodes. + """ + for t in self.run_after: + if not t.hasrun: + return Task.ASK_LATER + + if not self.inputs: + global SOURCE_RE + self.inputs = [] + for x in self.srcdir: + self.inputs.extend(x.ant_glob(SOURCE_RE, remove=False)) + return super(javaw.javac, self).runnable_status() + + def run(self): + """ + Execute the scalac compiler + """ + env = self.env + gen = self.generator + bld = gen.bld + wd = bld.bldnode.abspath() + def to_list(xx): + if isinstance(xx, str): return [xx] + return xx + self.last_cmd = lst = [] + lst.extend(to_list(env['SCALAC'])) + lst.extend(['-classpath']) + lst.extend(to_list(env['CLASSPATH'])) + lst.extend(['-d']) + lst.extend(to_list(env['OUTDIR'])) + lst.extend(to_list(env['SCALACFLAGS'])) + lst.extend([a.abspath() for a in self.inputs]) + lst = [x for x in lst if x] + try: + self.out = self.generator.bld.cmd_and_log(lst, cwd=wd, env=env.env or None, output=0, quiet=0)[1] + except: + self.generator.bld.cmd_and_log(lst, cwd=wd, env=env.env or None) + +def configure(self): + """ + Detect the scalac program + """ + # If SCALA_HOME is set, we prepend it to the path list + java_path = self.environ['PATH'].split(os.pathsep) + v = self.env + + if 'SCALA_HOME' in self.environ: + java_path = [os.path.join(self.environ['SCALA_HOME'], 'bin')] + java_path + self.env['SCALA_HOME'] = [self.environ['SCALA_HOME']] + + for x in 'scalac scala'.split(): + self.find_program(x, var=x.upper(), path_list=java_path) + self.env[x.upper()] = self.cmd_to_list(self.env[x.upper()]) + + if 'CLASSPATH' in self.environ: + v['CLASSPATH'] = self.environ['CLASSPATH'] + + v.SCALACFLAGS = ['-verbose'] + if not v['SCALAC']: self.fatal('scalac is required for compiling scala classes') + diff --git a/waflib/extras/slow_qt4.py b/waflib/extras/slow_qt4.py new file mode 100644 index 00000000..11d3eee0 --- /dev/null +++ b/waflib/extras/slow_qt4.py @@ -0,0 +1,81 @@ +#! /usr/bin/env python +# Thomas Nagy, 2011 (ita) + +""" +Create _moc.cpp files (the builds are 30-40% faster when .moc files are included). +To use, just create a waf file including slow_qt4 and change the project configuration: + +def configure(conf): + conf.load('compiler_cxx qt4') + conf.load('slow_qt4') + +See playground/slow_qt/wscript for a complete example. +""" + +from waflib.TaskGen import extension +from waflib import Task +import waflib.Tools.qt4 +import waflib.Tools.cxx + +@extension(*waflib.Tools.qt4.EXT_QT4) +def cxx_hook(self, node): + self.create_compiled_task('cxx_qt', node) + +class cxx_qt(waflib.Tools.cxx.cxx): + def runnable_status(self): + ret = waflib.Tools.cxx.cxx.runnable_status(self) + if ret != Task.ASK_LATER and not getattr(self, 'moc_done', None): + + try: + cache = self.generator.moc_cache + except: + cache = self.generator.moc_cache = {} + + + deps = self.generator.bld.node_deps[self.uid()] + for x in [self.inputs[0]] + deps: + if x.read().find('Q_OBJECT') > 0: + + cxx_node = x.parent.get_bld().make_node(x.name.replace('.', '_') + '_moc.cpp') + if cxx_node in cache: + continue + cache[cxx_node] = self + + tsk = Task.classes['moc'](env=self.env, generator=self.generator) + tsk.set_inputs(x) + tsk.set_outputs(cxx_node) + + if x.name.endswith('.cpp'): + # moc is trying to be too smart but it is too dumb: + # why forcing the #include when Q_OBJECT is in the cpp file? + gen = self.generator.bld.producer + gen.outstanding.insert(0, tsk) + gen.total += 1 + self.set_run_after(tsk) + else: + cxxtsk = Task.classes['cxx'](env=self.env, generator=self.generator) + cxxtsk.set_inputs(tsk.outputs) + cxxtsk.set_outputs(cxx_node.change_ext('.o')) + cxxtsk.set_run_after(tsk) + + try: + self.more_tasks.extend([tsk, cxxtsk]) + except AttributeError: + self.more_tasks = [tsk, cxxtsk] + + try: + link = self.generator.link_task + except: + pass + else: + link.set_run_after(cxxtsk) + link.inputs.extend(cxxtsk.outputs) + + self.moc_done = True + + for t in self.run_after: + if not t.hasrun: + return Task.ASK_LATER + + return ret + diff --git a/waflib/extras/softlink_libs.py b/waflib/extras/softlink_libs.py new file mode 100644 index 00000000..ad63da43 --- /dev/null +++ b/waflib/extras/softlink_libs.py @@ -0,0 +1,74 @@ +#! /usr/bin/env python +# per rosengren 2011 + +from waflib.TaskGen import feature, after_method +from waflib.Task import Task, always_run +from os.path import basename, isabs +from os import tmpfile, linesep + +def options(opt): + grp = opt.add_option_group('Softlink Libraries Options') + grp.add_option('--exclude', default='/usr/lib,/lib', help='No symbolic links are created for libs within [%default]') + +def configure(cnf): + cnf.find_program('ldd') + if not cnf.env.SOFTLINK_EXCLUDE: + cnf.env.SOFTLINK_EXCLUDE = cnf.options.exclude.split(',') + +@feature('softlink_libs') +@after_method('process_rule') +def add_finder(self): + tgt = self.path.find_or_declare(self.target) + self.create_task('sll_finder', tgt=tgt) + self.create_task('sll_installer', tgt=tgt) + always_run(sll_installer) + +class sll_finder(Task): + ext_out = 'softlink_libs' + def run(self): + bld = self.generator.bld + linked=[] + target_paths = [] + for g in bld.groups: + for tgen in g: + # FIXME it might be better to check if there is a link_task (getattr?) + target_paths += [tgen.path.get_bld().bldpath()] + linked += [t.outputs[0].bldpath() + for t in getattr(tgen, 'tasks', []) + if t.__class__.__name__ in + ['cprogram', 'cshlib', 'cxxprogram', 'cxxshlib']] + lib_list = [] + if len(linked): + cmd = [self.env.LDD] + linked + # FIXME add DYLD_LIBRARY_PATH+PATH for osx+win32 + ldd_env = {'LD_LIBRARY_PATH': ':'.join(target_paths + self.env.LIBPATH)} + # FIXME the with syntax will not work in python 2 + with tmpfile() as result: + self.exec_command(cmd, env=ldd_env, stdout=result) + result.seek(0) + for line in result.readlines(): + words = line.split() + if len(words) < 3 or words[1] != '=>': continue + lib = words[2] + if lib == 'not': continue + if any([lib.startswith(p) for p in + [bld.bldnode.abspath(), '('] + + self.env.SOFTLINK_EXCLUDE]): + continue + if not isabs(lib): + continue + lib_list.append(lib) + lib_list = sorted(set(lib_list)) + self.outputs[0].write(linesep.join(lib_list + self.env.DYNAMIC_LIBS)) + return 0 + +class sll_installer(Task): + ext_in = 'softlink_libs' + def run(self): + tgt = self.outputs[0] + self.generator.bld.install_files('${LIBDIR}', tgt, postpone=False) + lib_list=tgt.read().split() + for lib in lib_list: + self.generator.bld.symlink_as('${LIBDIR}/'+basename(lib), lib, postpone=False) + return 0 + diff --git a/waflib/extras/subprocess.py b/waflib/extras/subprocess.py new file mode 100644 index 00000000..cb15178b --- /dev/null +++ b/waflib/extras/subprocess.py @@ -0,0 +1,620 @@ +# borrowed from python 2.5.2c1 +# Copyright (c) 2003-2005 by Peter Astrand +# Licensed to PSF under a Contributor Agreement. + +import sys +mswindows = (sys.platform == "win32") + +import os +import types +import traceback +import gc + +class CalledProcessError(Exception): + def __init__(self, returncode, cmd): + self.returncode = returncode + self.cmd = cmd + def __str__(self): + return "Command '%s' returned non-zero exit status %d" % (self.cmd, self.returncode) + +if mswindows: + import threading + import msvcrt + if 0: + import pywintypes + from win32api import GetStdHandle, STD_INPUT_HANDLE, \ + STD_OUTPUT_HANDLE, STD_ERROR_HANDLE + from win32api import GetCurrentProcess, DuplicateHandle, \ + GetModuleFileName, GetVersion + from win32con import DUPLICATE_SAME_ACCESS, SW_HIDE + from win32pipe import CreatePipe + from win32process import CreateProcess, STARTUPINFO, \ + GetExitCodeProcess, STARTF_USESTDHANDLES, \ + STARTF_USESHOWWINDOW, CREATE_NEW_CONSOLE + from win32event import WaitForSingleObject, INFINITE, WAIT_OBJECT_0 + else: + from _subprocess import * + class STARTUPINFO: + dwFlags = 0 + hStdInput = None + hStdOutput = None + hStdError = None + wShowWindow = 0 + class pywintypes: + error = IOError +else: + import select + import errno + import fcntl + import pickle + +__all__ = ["Popen", "PIPE", "STDOUT", "call", "check_call", "CalledProcessError"] + +try: + MAXFD = os.sysconf("SC_OPEN_MAX") +except: + MAXFD = 256 + +try: + False +except NameError: + False = 0 + True = 1 + +_active = [] + +def _cleanup(): + for inst in _active[:]: + if inst.poll(_deadstate=sys.maxint) >= 0: + try: + _active.remove(inst) + except ValueError: + pass + +PIPE = -1 +STDOUT = -2 + + +def call(*popenargs, **kwargs): + return Popen(*popenargs, **kwargs).wait() + +def check_call(*popenargs, **kwargs): + retcode = call(*popenargs, **kwargs) + cmd = kwargs.get("args") + if cmd is None: + cmd = popenargs[0] + if retcode: + raise CalledProcessError(retcode, cmd) + return retcode + + +def list2cmdline(seq): + result = [] + needquote = False + for arg in seq: + bs_buf = [] + + if result: + result.append(' ') + + needquote = (" " in arg) or ("\t" in arg) or arg == "" + if needquote: + result.append('"') + + for c in arg: + if c == '\\': + bs_buf.append(c) + elif c == '"': + result.append('\\' * len(bs_buf)*2) + bs_buf = [] + result.append('\\"') + else: + if bs_buf: + result.extend(bs_buf) + bs_buf = [] + result.append(c) + + if bs_buf: + result.extend(bs_buf) + + if needquote: + result.extend(bs_buf) + result.append('"') + + return ''.join(result) + +class Popen(object): + def __init__(self, args, bufsize=0, executable=None, + stdin=None, stdout=None, stderr=None, + preexec_fn=None, close_fds=False, shell=False, + cwd=None, env=None, universal_newlines=False, + startupinfo=None, creationflags=0): + _cleanup() + + self._child_created = False + if not isinstance(bufsize, (int, long)): + raise TypeError("bufsize must be an integer") + + if mswindows: + if preexec_fn is not None: + raise ValueError("preexec_fn is not supported on Windows platforms") + if close_fds: + raise ValueError("close_fds is not supported on Windows platforms") + else: + if startupinfo is not None: + raise ValueError("startupinfo is only supported on Windows platforms") + if creationflags != 0: + raise ValueError("creationflags is only supported on Windows platforms") + + self.stdin = None + self.stdout = None + self.stderr = None + self.pid = None + self.returncode = None + self.universal_newlines = universal_newlines + + (p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) = self._get_handles(stdin, stdout, stderr) + + self._execute_child(args, executable, preexec_fn, close_fds, + cwd, env, universal_newlines, + startupinfo, creationflags, shell, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) + + if mswindows: + if stdin is None and p2cwrite is not None: + os.close(p2cwrite) + p2cwrite = None + if stdout is None and c2pread is not None: + os.close(c2pread) + c2pread = None + if stderr is None and errread is not None: + os.close(errread) + errread = None + + if p2cwrite: + self.stdin = os.fdopen(p2cwrite, 'wb', bufsize) + if c2pread: + if universal_newlines: + self.stdout = os.fdopen(c2pread, 'rU', bufsize) + else: + self.stdout = os.fdopen(c2pread, 'rb', bufsize) + if errread: + if universal_newlines: + self.stderr = os.fdopen(errread, 'rU', bufsize) + else: + self.stderr = os.fdopen(errread, 'rb', bufsize) + + + def _translate_newlines(self, data): + data = data.replace("\r\n", "\n") + data = data.replace("\r", "\n") + return data + + + def __del__(self, sys=sys): + if not self._child_created: + return + self.poll(_deadstate=sys.maxint) + if self.returncode is None and _active is not None: + _active.append(self) + + + def communicate(self, input=None): + if [self.stdin, self.stdout, self.stderr].count(None) >= 2: + stdout = None + stderr = None + if self.stdin: + if input: + self.stdin.write(input) + self.stdin.close() + elif self.stdout: + stdout = self.stdout.read() + elif self.stderr: + stderr = self.stderr.read() + self.wait() + return (stdout, stderr) + + return self._communicate(input) + + + if mswindows: + def _get_handles(self, stdin, stdout, stderr): + if stdin is None and stdout is None and stderr is None: + return (None, None, None, None, None, None) + + p2cread, p2cwrite = None, None + c2pread, c2pwrite = None, None + errread, errwrite = None, None + + if stdin is None: + p2cread = GetStdHandle(STD_INPUT_HANDLE) + if p2cread is not None: + pass + elif stdin is None or stdin == PIPE: + p2cread, p2cwrite = CreatePipe(None, 0) + p2cwrite = p2cwrite.Detach() + p2cwrite = msvcrt.open_osfhandle(p2cwrite, 0) + elif isinstance(stdin, int): + p2cread = msvcrt.get_osfhandle(stdin) + else: + p2cread = msvcrt.get_osfhandle(stdin.fileno()) + p2cread = self._make_inheritable(p2cread) + + if stdout is None: + c2pwrite = GetStdHandle(STD_OUTPUT_HANDLE) + if c2pwrite is not None: + pass + elif stdout is None or stdout == PIPE: + c2pread, c2pwrite = CreatePipe(None, 0) + c2pread = c2pread.Detach() + c2pread = msvcrt.open_osfhandle(c2pread, 0) + elif isinstance(stdout, int): + c2pwrite = msvcrt.get_osfhandle(stdout) + else: + c2pwrite = msvcrt.get_osfhandle(stdout.fileno()) + c2pwrite = self._make_inheritable(c2pwrite) + + if stderr is None: + errwrite = GetStdHandle(STD_ERROR_HANDLE) + if errwrite is not None: + pass + elif stderr is None or stderr == PIPE: + errread, errwrite = CreatePipe(None, 0) + errread = errread.Detach() + errread = msvcrt.open_osfhandle(errread, 0) + elif stderr == STDOUT: + errwrite = c2pwrite + elif isinstance(stderr, int): + errwrite = msvcrt.get_osfhandle(stderr) + else: + errwrite = msvcrt.get_osfhandle(stderr.fileno()) + errwrite = self._make_inheritable(errwrite) + + return (p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite) + def _make_inheritable(self, handle): + return DuplicateHandle(GetCurrentProcess(), handle, GetCurrentProcess(), 0, 1, DUPLICATE_SAME_ACCESS) + + def _find_w9xpopen(self): + w9xpopen = os.path.join(os.path.dirname(GetModuleFileName(0)), "w9xpopen.exe") + if not os.path.exists(w9xpopen): + w9xpopen = os.path.join(os.path.dirname(sys.exec_prefix), "w9xpopen.exe") + if not os.path.exists(w9xpopen): + raise RuntimeError("Cannot locate w9xpopen.exe, which is needed for Popen to work with your shell or platform.") + return w9xpopen + + def _execute_child(self, args, executable, preexec_fn, close_fds, + cwd, env, universal_newlines, + startupinfo, creationflags, shell, + p2cread, p2cwrite, + c2pread, c2pwrite, + errread, errwrite): + + if not isinstance(args, types.StringTypes): + args = list2cmdline(args) + + if startupinfo is None: + startupinfo = STARTUPINFO() + if None not in (p2cread, c2pwrite, errwrite): + startupinfo.dwFlags |= STARTF_USESTDHANDLES + startupinfo.hStdInput = p2cread + startupinfo.hStdOutput = c2pwrite + startupinfo.hStdError = errwrite + + if shell: + startupinfo.dwFlags |= STARTF_USESHOWWINDOW + startupinfo.wShowWindow = SW_HIDE + comspec = os.environ.get("COMSPEC", "cmd.exe") + args = comspec + " /c " + args + if (GetVersion() >= 0x80000000L or + os.path.basename(comspec).lower() == "command.com"): + w9xpopen = self._find_w9xpopen() + args = '"%s" %s' % (w9xpopen, args) + creationflags |= CREATE_NEW_CONSOLE + + try: + hp, ht, pid, tid = CreateProcess(executable, args, None, None, 1, creationflags, env, cwd, startupinfo) + except pywintypes.error, e: + raise WindowsError(*e.args) + + self._child_created = True + self._handle = hp + self.pid = pid + ht.Close() + + if p2cread is not None: + p2cread.Close() + if c2pwrite is not None: + c2pwrite.Close() + if errwrite is not None: + errwrite.Close() + + + def poll(self, _deadstate=None): + if self.returncode is None: + if WaitForSingleObject(self._handle, 0) == WAIT_OBJECT_0: + self.returncode = GetExitCodeProcess(self._handle) + return self.returncode + + + def wait(self): + if self.returncode is None: + obj = WaitForSingleObject(self._handle, INFINITE) + self.returncode = GetExitCodeProcess(self._handle) + return self.returncode + + def _readerthread(self, fh, buffer): + buffer.append(fh.read()) + + def _communicate(self, input): + stdout = None + stderr = None + + if self.stdout: + stdout = [] + stdout_thread = threading.Thread(target=self._readerthread, args=(self.stdout, stdout)) + stdout_thread.setDaemon(True) + stdout_thread.start() + if self.stderr: + stderr = [] + stderr_thread = threading.Thread(target=self._readerthread, args=(self.stderr, stderr)) + stderr_thread.setDaemon(True) + stderr_thread.start() + + if self.stdin: + if input is not None: + self.stdin.write(input) + self.stdin.close() + + if self.stdout: + stdout_thread.join() + if self.stderr: + stderr_thread.join() + + if stdout is not None: + stdout = stdout[0] + if stderr is not None: + stderr = stderr[0] + + if self.universal_newlines and hasattr(file, 'newlines'): + if stdout: + stdout = self._translate_newlines(stdout) + if stderr: + stderr = self._translate_newlines(stderr) + + self.wait() + return (stdout, stderr) + + else: + def _get_handles(self, stdin, stdout, stderr): + p2cread, p2cwrite = None, None + c2pread, c2pwrite = None, None + errread, errwrite = None, None + + if stdin is None: + pass + elif stdin == PIPE: + p2cread, p2cwrite = os.pipe() + elif isinstance(stdin, int): + p2cread = stdin + else: + p2cread = stdin.fileno() + + if stdout is None: + pass + elif stdout == PIPE: + c2pread, c2pwrite = os.pipe() + elif isinstance(stdout, int): + c2pwrite = stdout + else: + c2pwrite = stdout.fileno() + + if stderr is None: + pass + elif stderr == PIPE: + errread, errwrite = os.pipe() + elif stderr == STDOUT: + errwrite = c2pwrite + elif isinstance(stderr, int): + errwrite = stderr + else: + errwrite = stderr.fileno() + + return (p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite) + + def _set_cloexec_flag(self, fd): + try: + cloexec_flag = fcntl.FD_CLOEXEC + except AttributeError: + cloexec_flag = 1 + + old = fcntl.fcntl(fd, fcntl.F_GETFD) + fcntl.fcntl(fd, fcntl.F_SETFD, old | cloexec_flag) + + def _close_fds(self, but): + for i in xrange(3, MAXFD): + if i == but: + continue + try: + os.close(i) + except: + pass + + def _execute_child(self, args, executable, preexec_fn, close_fds, + cwd, env, universal_newlines, startupinfo, creationflags, shell, + p2cread, p2cwrite, c2pread, c2pwrite, errread, errwrite): + + if isinstance(args, types.StringTypes): + args = [args] + else: + args = list(args) + + if shell: + args = ["/bin/sh", "-c"] + args + + if executable is None: + executable = args[0] + + errpipe_read, errpipe_write = os.pipe() + self._set_cloexec_flag(errpipe_write) + + gc_was_enabled = gc.isenabled() + gc.disable() + try: + self.pid = os.fork() + except: + if gc_was_enabled: + gc.enable() + raise + self._child_created = True + if self.pid == 0: + try: + if p2cwrite: + os.close(p2cwrite) + if c2pread: + os.close(c2pread) + if errread: + os.close(errread) + os.close(errpipe_read) + + if p2cread: + os.dup2(p2cread, 0) + if c2pwrite: + os.dup2(c2pwrite, 1) + if errwrite: + os.dup2(errwrite, 2) + + if p2cread and p2cread not in (0,): + os.close(p2cread) + if c2pwrite and c2pwrite not in (p2cread, 1): + os.close(c2pwrite) + if errwrite and errwrite not in (p2cread, c2pwrite, 2): + os.close(errwrite) + + if close_fds: + self._close_fds(but=errpipe_write) + + if cwd is not None: + os.chdir(cwd) + + if preexec_fn: + apply(preexec_fn) + + if env is None: + os.execvp(executable, args) + else: + os.execvpe(executable, args, env) + + except: + exc_type, exc_value, tb = sys.exc_info() + exc_lines = traceback.format_exception(exc_type, exc_value, tb) + exc_value.child_traceback = ''.join(exc_lines) + os.write(errpipe_write, pickle.dumps(exc_value)) + + os._exit(255) + + if gc_was_enabled: + gc.enable() + os.close(errpipe_write) + if p2cread and p2cwrite: + os.close(p2cread) + if c2pwrite and c2pread: + os.close(c2pwrite) + if errwrite and errread: + os.close(errwrite) + + data = os.read(errpipe_read, 1048576) + os.close(errpipe_read) + if data != "": + os.waitpid(self.pid, 0) + child_exception = pickle.loads(data) + raise child_exception + + def _handle_exitstatus(self, sts): + if os.WIFSIGNALED(sts): + self.returncode = -os.WTERMSIG(sts) + elif os.WIFEXITED(sts): + self.returncode = os.WEXITSTATUS(sts) + else: + raise RuntimeError("Unknown child exit status!") + + def poll(self, _deadstate=None): + if self.returncode is None: + try: + pid, sts = os.waitpid(self.pid, os.WNOHANG) + if pid == self.pid: + self._handle_exitstatus(sts) + except os.error: + if _deadstate is not None: + self.returncode = _deadstate + return self.returncode + + def wait(self): + if self.returncode is None: + pid, sts = os.waitpid(self.pid, 0) + self._handle_exitstatus(sts) + return self.returncode + + def _communicate(self, input): + read_set = [] + write_set = [] + stdout = None + stderr = None + + if self.stdin: + self.stdin.flush() + if input: + write_set.append(self.stdin) + else: + self.stdin.close() + if self.stdout: + read_set.append(self.stdout) + stdout = [] + if self.stderr: + read_set.append(self.stderr) + stderr = [] + + input_offset = 0 + while read_set or write_set: + rlist, wlist, xlist = select.select(read_set, write_set, []) + + if self.stdin in wlist: + bytes_written = os.write(self.stdin.fileno(), buffer(input, input_offset, 512)) + input_offset += bytes_written + if input_offset >= len(input): + self.stdin.close() + write_set.remove(self.stdin) + + if self.stdout in rlist: + data = os.read(self.stdout.fileno(), 1024) + if data == "": + self.stdout.close() + read_set.remove(self.stdout) + stdout.append(data) + + if self.stderr in rlist: + data = os.read(self.stderr.fileno(), 1024) + if data == "": + self.stderr.close() + read_set.remove(self.stderr) + stderr.append(data) + + if stdout is not None: + stdout = ''.join(stdout) + if stderr is not None: + stderr = ''.join(stderr) + + if self.universal_newlines and hasattr(file, 'newlines'): + if stdout: + stdout = self._translate_newlines(stdout) + if stderr: + stderr = self._translate_newlines(stderr) + + self.wait() + return (stdout, stderr) + diff --git a/waflib/extras/swig.py b/waflib/extras/swig.py new file mode 100644 index 00000000..47ffdea7 --- /dev/null +++ b/waflib/extras/swig.py @@ -0,0 +1,172 @@ +#! /usr/bin/env python +# encoding: UTF-8 +# Petar Forai +# Thomas Nagy 2008-2010 (ita) + +import re +from waflib import Task, Utils, Logs +from waflib.TaskGen import extension, feature, after_method +from waflib.Configure import conf +from waflib.Tools import c_preproc + +""" +tasks have to be added dynamically: +- swig interface files may be created at runtime +- the module name may be unknown in advance +""" + +SWIG_EXTS = ['.swig', '.i'] + +re_module = re.compile('%module(?:\s*\(.*\))?\s+(.+)', re.M) + +re_1 = re.compile(r'^%module.*?\s+([\w]+)\s*?$', re.M) +re_2 = re.compile('%include "(.*)"', re.M) +re_3 = re.compile('#include "(.*)"', re.M) + +class swig(Task.Task): + color = 'BLUE' + run_str = '${SWIG} ${SWIGFLAGS} ${SWIGPATH_ST:INCPATHS} ${DEFINES_ST:DEFINES} ${SRC}' + ext_out = ['.h'] # might produce .h files although it is not mandatory + + def runnable_status(self): + for t in self.run_after: + if not t.hasrun: + return Task.ASK_LATER + + if not getattr(self, 'init_outputs', None): + self.init_outputs = True + if not getattr(self, 'module', None): + # search the module name + txt = self.inputs[0].read() + m = re_module.search(txt) + if not m: + raise ValueError("could not find the swig module name") + self.module = m.group(1) + + swig_c(self) + + # add the language-specific output files as nodes + # call funs in the dict swig_langs + for x in self.env['SWIGFLAGS']: + # obtain the language + x = x[1:] + try: + fun = swig_langs[x] + except KeyError: + pass + else: + fun(self) + + return super(swig, self).runnable_status() + + def scan(self): + "scan for swig dependencies, climb the .i files" + env = self.env + + lst_src = [] + + seen = [] + to_see = [self.inputs[0]] + + while to_see: + node = to_see.pop(0) + if node in seen: + continue + seen.append(node) + lst_src.append(node) + + # read the file + code = node.read() + code = c_preproc.re_nl.sub('', code) + code = c_preproc.re_cpp.sub(c_preproc.repl, code) + + # find .i files and project headers + names = re_2.findall(code) + re_3.findall(code) + for n in names: + for d in self.generator.includes_nodes + [node.parent]: + u = d.find_resource(n) + if u: + to_see.append(u) + break + else: + Logs.warn('could not find %r' % n) + + return (lst_src, []) + +# provide additional language processing +swig_langs = {} +def swigf(fun): + swig_langs[fun.__name__.replace('swig_', '')] = fun +swig.swigf = swigf + +def swig_c(self): + ext = '.swigwrap_%d.c' % self.generator.idx + flags = self.env['SWIGFLAGS'] + if '-c++' in flags: + ext += 'xx' + out_node = self.inputs[0].parent.find_or_declare(self.module + ext) + + if '-c++' in flags: + c_tsk = self.generator.cxx_hook(out_node) + else: + c_tsk = self.generator.c_hook(out_node) + + c_tsk.set_run_after(self) + + ge = self.generator.bld.producer + ge.outstanding.insert(0, c_tsk) + ge.total += 1 + + try: + ltask = self.generator.link_task + except AttributeError: + pass + else: + ltask.set_run_after(c_tsk) + ltask.inputs.append(c_tsk.outputs[0]) + + self.outputs.append(out_node) + + if not '-o' in self.env['SWIGFLAGS']: + self.env.append_value('SWIGFLAGS', ['-o', self.outputs[0].abspath()]) + +@swigf +def swig_python(tsk): + tsk.set_outputs(tsk.inputs[0].parent.find_or_declare(tsk.module + '.py')) + +@swigf +def swig_ocaml(tsk): + tsk.set_outputs(tsk.inputs[0].parent.find_or_declare(tsk.module + '.ml')) + tsk.set_outputs(tsk.inputs[0].parent.find_or_declare(tsk.module + '.mli')) + +@extension(*SWIG_EXTS) +def i_file(self, node): + # the task instance + tsk = self.create_task('swig') + tsk.set_inputs(node) + tsk.module = getattr(self, 'swig_module', None) + + flags = self.to_list(getattr(self, 'swig_flags', [])) + tsk.env.append_value('SWIGFLAGS', flags) + + # looks like this is causing problems + #if not '-outdir' in flags: + # tsk.env.append_value('SWIGFLAGS', ['-outdir', node.parent.abspath()]) + +@conf +def check_swig_version(self): + """Check for a minimum swig version like conf.check_swig_version('1.3.28') + or conf.check_swig_version((1,3,28)) """ + reg_swig = re.compile(r'SWIG Version\s(.*)', re.M) + swig_out = self.cmd_and_log('%s -version' % self.env['SWIG']) + + swigver = tuple([int(s) for s in reg_swig.findall(swig_out)[0].split('.')]) + self.env['SWIG_VERSION'] = swigver + msg = 'Checking for swig version' + self.msg(msg, '.'.join(map(str, swigver))) + return swigver + +def configure(conf): + swig = conf.find_program('swig', var='SWIG') + conf.env.SWIGPATH_ST = '-I%s' + diff --git a/waflib/extras/syms.py b/waflib/extras/syms.py new file mode 100644 index 00000000..2b7aeff1 --- /dev/null +++ b/waflib/extras/syms.py @@ -0,0 +1,71 @@ +#! /usr/bin/env python +# encoding: utf-8 + +""" +this tool supports the export_symbols_regex to export the symbols in a shared library. +by default, all symbols are exported by gcc, and nothing by msvc. +to use the tool, do something like: + +def build(ctx): + ctx(features='c cshlib syms', source='a.c b.c', export_symbols_regex='mylib_.*', target='testlib') + +only the symbols starting with 'mylib_' will be exported. +""" + +import re +from waflib.Context import STDOUT +from waflib.Task import Task +from waflib.Errors import WafError +from waflib.TaskGen import feature, after_method + +class gen_sym(Task): + def run(self): + obj = self.inputs[0] + if 'msvc' in (self.env.CC_NAME, self.env.CXX_NAME): + re_nm = re.compile(r'External\s+\|\s+_(' + self.generator.export_symbols_regex + r')\b') + cmd = ['dumpbin', '/symbols', obj.abspath()] + else: + if self.env.DEST_BINFMT == 'pe': #gcc uses nm, and has a preceding _ on windows + re_nm = re.compile(r'T\s+_(' + self.generator.export_symbols_regex + r')\b') + else: + re_nm = re.compile(r'T\s+(' + self.generator.export_symbols_regex + r')\b') + cmd = ['nm', '-g', obj.abspath()] + syms = re_nm.findall(self.generator.bld.cmd_and_log(cmd, quiet=STDOUT)) + self.outputs[0].write('%r' % syms) + +class compile_sym(Task): + def run(self): + syms = {} + for x in self.inputs: + slist = eval(x.read()) + for s in slist: + syms[s] = 1 + lsyms = syms.keys() + lsyms.sort() + if self.env.DEST_BINFMT == 'pe': + self.outputs[0].write('EXPORTS\n' + '\n'.join(lsyms)) + elif self.env.DEST_BINFMT == 'elf': + self.outputs[0].write('{ global:\n' + ';\n'.join(lsyms) + ";\nlocal: *; };\n") + else: + raise WafError('NotImplemented') + +@feature('syms') +@after_method('process_source', 'process_use', 'apply_link', 'process_uselib_local') +def do_the_symbol_stuff(self): + ins = [x.outputs[0] for x in self.compiled_tasks] + self.gen_sym_tasks = [self.create_task('gen_sym', x, x.change_ext('.%d.sym' % self.idx)) for x in ins] + + tsk = self.create_task('compile_sym', + [x.outputs[0] for x in self.gen_sym_tasks], + self.path.find_or_declare(getattr(self, 'sym_filename', self.target + '.def'))) + self.link_task.set_run_after(tsk) + self.link_task.dep_nodes = [tsk.outputs[0]] + if 'msvc' in (self.env.CC_NAME, self.env.CXX_NAME): + self.link_task.env.append_value('LINKFLAGS', ['/def:' + tsk.outputs[0].bldpath()]) + elif self.env.DEST_BINFMT == 'pe': #gcc on windows takes *.def as an additional input + self.link_task.inputs.append(tsk.outputs[0]) + elif self.env.DEST_BINFMT == 'elf': + self.link_task.env.append_value('LINKFLAGS', ['-Wl,-version-script', '-Wl,' + tsk.outputs[0].bldpath()]) + else: + raise WafError('NotImplemented') + diff --git a/waflib/extras/sync_exec.py b/waflib/extras/sync_exec.py new file mode 100644 index 00000000..baf4cfbd --- /dev/null +++ b/waflib/extras/sync_exec.py @@ -0,0 +1,31 @@ +#! /usr/bin/env python +# encoding: utf-8 + +""" +Force the execution output to be synchronized +May deadlock with a lot of output (subprocess limitation) +""" + +import sys +from waflib.Build import BuildContext +from waflib import Utils, Logs + +def exec_command(self, cmd, **kw): + subprocess = Utils.subprocess + kw['shell'] = isinstance(cmd, str) + Logs.debug('runner: %r' % cmd) + Logs.debug('runner_env: kw=%s' % kw) + try: + kw['stdout'] = kw['stderr'] = subprocess.PIPE + p = subprocess.Popen(cmd, **kw) + (out, err) = p.communicate() + if out: + sys.stdout.write(out.decode(sys.stdout.encoding or 'iso8859-1')) + if err: + sys.stdout.write(err.decode(sys.stdout.encoding or 'iso8859-1')) + return p.returncode + except OSError: + return -1 + +BuildContext.exec_command = exec_command + diff --git a/waflib/extras/valadoc.py b/waflib/extras/valadoc.py new file mode 100644 index 00000000..fcc11293 --- /dev/null +++ b/waflib/extras/valadoc.py @@ -0,0 +1,107 @@ +#! /usr/bin/env python +# encoding: UTF-8 +# Nicolas Joseph 2009 + +""" +ported from waf 1.5: +TODO: tabs vs spaces +""" + +from waflib import Task, Utils, Node, Errors +from waflib.TaskGen import feature, extension, after_method +from Logs import debug, warn, error + +VALADOC_STR = '${VALADOC}' + +class valadoc(Task.Task): + vars = ['VALADOC', 'VALADOCFLAGS'] + color = 'BLUE' + after = ['cprogram', 'cstlib', 'cshlib', 'cxxprogram', 'cxxstlib', 'cxxshlib'] + quiet = True # no outputs .. this is weird + + def __init__(self, *k, **kw): + Task.Task.__init__(*k, **kw) + self.output_dir = '' + self.doclet = '' + self.package_name = '' + self.package_version = '' + self.files = [] + self.protected = True + self.private = False + self.inherit = False + self.deps = False + self.enable_non_null_experimental = False + self.force = False + + def run(self): + if not self.env['VALADOCFLAGS']: + self.env['VALADOCFLAGS'] = '' + cmd = [Utils.subst_vars(VALADOC_STR, self.env)] + cmd.append ('-o %s' % self.output_dir) + if getattr(self, 'doclet', None): + cmd.append ('--doclet %s' % self.doclet) + cmd.append ('--package-name %s' % self.package_name) + if getattr(self, 'version', None): + cmd.append ('--package-version %s' % self.package_version) + if getattr(self, 'packages', None): + for package in self.packages: + cmd.append ('--pkg %s' % package) + if getattr(self, 'vapi_dirs', None): + for vapi_dir in self.vapi_dirs: + cmd.append ('--vapidir %s' % vapi_dir) + if not getattr(self, 'protected', None): + cmd.append ('--no-protected') + if getattr(self, 'private', None): + cmd.append ('--private') + if getattr(self, 'inherit', None): + cmd.append ('--inherit') + if getattr(self, 'deps', None): + cmd.append ('--deps') + if getattr(self, 'enable_non_null_experimental', None): + cmd.append ('--enable-non-null-experimental') + if getattr(self, 'force', None): + cmd.append ('--force') + cmd.append (' '.join ([x.relpath_gen (self.generator.bld.bldnode) for x in self.files])) + return self.generator.bld.exec_command(' '.join(cmd)) + +@feature('valadoc') +def process_valadoc(self): + task = self.create_task('valadoc') + if getattr(self, 'output_dir', None): + task.output_dir = self.output_dir + else: + Errors.WafError('no output directory') + if getattr(self, 'doclet', None): + task.doclet = self.doclet + else: + Errors.WafError('no doclet directory') + if getattr(self, 'package_name', None): + task.package_name = self.package_name + else: + Errors.WafError('no package name') + if getattr(self, 'package_version', None): + task.package_version = self.package_version + if getattr(self, 'packages', None): + task.packages = Utils.to_list(self.packages) + if getattr(self, 'vapi_dirs', None): + task.vapi_dirs = Utils.to_list(self.vapi_dirs) + if getattr(self, 'files', None): + task.files = self.files + else: + Errors.WafError('no input file') + if getattr(self, 'protected', None): + task.protected = self.protected + if getattr(self, 'private', None): + task.private = self.private + if getattr(self, 'inherit', None): + task.inherit = self.inherit + if getattr(self, 'deps', None): + task.deps = self.deps + if getattr(self, 'enable_non_null_experimental', None): + task.enable_non_null_experimental = self.enable_non_null_experimental + if getattr(self, 'force', None): + task.force = self.force + +def configure(conf): + conf.find_program('valadoc', errmsg='You must install valadoc for generate the API documentation') + diff --git a/waflib/extras/why.py b/waflib/extras/why.py new file mode 100644 index 00000000..dec5c0da --- /dev/null +++ b/waflib/extras/why.py @@ -0,0 +1,73 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +""" +This tool modifies the task signature scheme to store and obtain +information about the task execution (why it must run, etc):: + + def configure(conf): + conf.load('why') + +After adding the tool, a full rebuild is necessary. +""" + +from waflib import Task, Utils, Logs, Errors + +def signature(self): + # compute the result one time, and suppose the scan_signature will give the good result + try: return self.cache_sig + except AttributeError: pass + + self.m = Utils.md5() + self.m.update(self.hcode.encode()) + id_sig = self.m.digest() + + # explicit deps + self.sig_explicit_deps() + exp_sig = self.m.digest() + + # env vars + self.sig_vars() + var_sig = self.m.digest() + + # implicit deps / scanner results + if self.scan: + try: + imp_sig = self.sig_implicit_deps() + except Errors.TaskRescan: + return self.signature() + + ret = self.cache_sig = self.m.digest() + id_sig + exp_sig + var_sig + return ret + + +Task.Task.signature = signature + +old = Task.Task.runnable_status +def runnable_status(self): + ret = old(self) + if ret == Task.RUN_ME: + try: + old_sigs = self.generator.bld.task_sigs[self.uid()] + except: + Logs.debug("task: task must run as no previous signature exists") + else: + new_sigs = self.cache_sig + def v(x): + return Utils.to_hex(x) + + Logs.debug("Task %r" % self) + msgs = ['Task must run', '* Task code', '* Source file or manual dependency', '* Configuration data variable'] + tmp = 'task: -> %s: %s %s' + for x in range(len(msgs)): + l = len(Utils.SIG_NIL) + a = new_sigs[x*l : (x+1)*l] + b = old_sigs[x*l : (x+1)*l] + if (a != b): + Logs.debug(tmp % (msgs[x].ljust(35), v(a), v(b))) + if x > 0: + break + return ret +Task.Task.runnable_status = runnable_status + diff --git a/waflib/fixpy2.py b/waflib/fixpy2.py new file mode 100644 index 00000000..28969621 --- /dev/null +++ b/waflib/fixpy2.py @@ -0,0 +1,67 @@ +#!/usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2010 (ita) + +""" +burn a book, save a tree +""" + +import os +all_modifs = {} + +def fixdir(dir): + """call all the substitution functions on the waf folders""" + global all_modifs + for k in all_modifs: + for v in all_modifs[k]: + modif(os.path.join(dir, 'waflib'), k, v) + +def modif(dir, name, fun): + """execute a substitution function""" + if name == '*': + lst = [] + for y in '. Tools extras'.split(): + for x in os.listdir(os.path.join(dir, y)): + if x.endswith('.py'): + lst.append(y + os.sep + x) + for x in lst: + modif(dir, x, fun) + return + + filename = os.path.join(dir, name) + f = open(filename, 'r') + txt = f.read() + f.close() + + txt = fun(txt) + + f = open(filename, 'w') + f.write(txt) + f.close() + +def subst(*k): + """register a substitution function""" + def do_subst(fun): + global all_modifs + for x in k: + try: + all_modifs[x].append(fun) + except KeyError: + all_modifs[x] = [fun] + return fun + return do_subst + +@subst('*') +def r1(code): + "utf-8 fixes for python < 2.6" + code = code.replace('as e:', ',e:') + code = code.replace(".decode(sys.stdout.encoding or 'iso8859-1')", '') + code = code.replace('.encode()', '') + return code + +@subst('Runner.py') +def r4(code): + "generator syntax" + code = code.replace('next(self.biter)', 'self.biter.next()') + return code + diff --git a/wscript b/wscript new file mode 100644 index 00000000..c6b4a61e --- /dev/null +++ b/wscript @@ -0,0 +1,360 @@ +#! /usr/bin/env python +# encoding: utf-8 +# Thomas Nagy, 2005-2010 + +""" +to make a custom waf file use the option --tools + +To add a tool that does not exist in the folder compat15, pass an absolute path: +./waf-light --make-waf --tools=compat15,/comp/waf/aba.py --prelude=$'\tfrom waflib.extras import aba\n\taba.foo()' +""" + + +VERSION="1.6.8" +APPNAME='waf' +REVISION='' + +top = '.' +out = 'build' + +demos = ['cpp', 'qt4', 'tex', 'ocaml', 'kde3', 'adv', 'cc', 'idl', 'docbook', 'xmlwaf', 'gnome'] +zip_types = ['bz2', 'gz'] + +PRELUDE = '\timport waflib.extras.compat15' + +#from tokenize import * +import tokenize + +import os, sys, re, io, optparse + +from waflib import Utils, Options +from hashlib import md5 + +from waflib import Configure +Configure.autoconfig = 1 + +def sub_file(fname, lst): + + f = open(fname, 'rU') + txt = f.read() + f.close() + + for (key, val) in lst: + re_pat = re.compile(key, re.M) + txt = re_pat.sub(val, txt) + + f = open(fname, 'w') + f.write(txt) + f.close() + +print("------> Executing code from the top-level wscript <-----") +def init(*k, **kw): + if Options.options.setver: # maintainer only (ita) + ver = Options.options.setver + hexver = Utils.num2ver(ver) + hexver = '0x%x'%hexver + sub_file('wscript', (('^VERSION=(.*)', 'VERSION="%s"' % ver), )) + sub_file('waf-light', (('^VERSION=(.*)', 'VERSION="%s"' % ver), )) + + pats = [] + pats.append(('^WAFVERSION=(.*)', 'WAFVERSION="%s"' % ver)) + pats.append(('^HEXVERSION(.*)', 'HEXVERSION=%s' % hexver)) + + try: + rev = k[0].cmd_and_log('svnversion').strip().replace('M', '') + pats.append(('^WAFREVISION(.*)', 'WAFREVISION="%s"' % rev)) + except: + pass + + sub_file('waflib/Context.py', pats) + + sys.exit(0) + elif Options.options.waf: + create_waf() + sys.exit(0) + +def check(ctx): + sys.path.insert(0,'') + # some tests clobber g_module. We must preserve it here, otherwise we get an error + # about an undefined shutdown function + mod = Utils.g_module + import test.Test + test.Test.run_tests() + Utils.g_module = mod + +# this function is called before any other for parsing the command-line +def options(opt): + + # generate waf + opt.add_option('--make-waf', action='store_true', default=False, + help='creates the waf script', dest='waf') + + opt.add_option('--zip-type', action='store', default='bz2', + help='specify the zip type [Allowed values: %s]' % ' '.join(zip_types), dest='zip') + + opt.add_option('--make-batch', action='store_true', default=False, + help='creates a convenience waf.bat file (done automatically on win32 systems)', + dest='make_batch') + + opt.add_option('--yes', action='store_true', default=False, + help=optparse.SUPPRESS_HELP, + dest='yes') + + # those ones are not too interesting + opt.add_option('--set-version', default='', + help='sets the version number for waf releases (for the maintainer)', dest='setver') + + opt.add_option('--strip', action='store_true', default=True, + help='shrinks waf (strip docstrings, saves 33kb)', + dest='strip_comments') + opt.add_option('--nostrip', action='store_false', help='no shrinking', + dest='strip_comments') + opt.add_option('--tools', action='store', help='Comma-separated 3rd party tools to add, eg: "compat,ocaml" [Default: "compat15"]', + dest='add3rdparty', default='compat15') + opt.add_option('--prelude', action='store', help='Code to execute before calling waf', dest='prelude', default=PRELUDE) + opt.load('python') + +def compute_revision(): + global REVISION + + def visit(arg, dirname, names): + for pos, name in enumerate(names): + if name[0] == '.' or name in ['_build_', 'build']: + del names[pos] + elif name.endswith('.py'): + arg.append(os.path.join(dirname, name)) + sources = [] + os.path.walk('waflib', visit, sources) + sources.sort() + m = md5() + for source in sources: + f = file(source,'rb') + readBytes = 100000 + while (readBytes): + readString = f.read(readBytes) + m.update(readString) + readBytes = len(readString) + f.close() + REVISION = m.hexdigest() + +def process_tokens(tokens): + accu = [] + prev = tokenize.NEWLINE + + indent = 0 + line_buf = [] + + for (type, token, start, end, line) in tokens: + token = token.replace('\r\n', '\n') + if type == tokenize.NEWLINE: + if line_buf: + accu.append(indent * '\t') + ln = "".join(line_buf) + if ln == 'if __name__=="__main__":': break + #ln = ln.replace('\n', '') + accu.append(ln) + accu.append('\n') + line_buf = [] + prev = tokenize.NEWLINE + elif type == tokenize.INDENT: + indent += 1 + elif type == tokenize.DEDENT: + indent -= 1 + elif type == tokenize.NAME: + if prev == tokenize.NAME or prev == tokenize.NUMBER: line_buf.append(' ') + line_buf.append(token) + elif type == tokenize.NUMBER: + if prev == tokenize.NAME or prev == tokenize.NUMBER: line_buf.append(' ') + line_buf.append(token) + elif type == tokenize.STRING: + if not line_buf and token.startswith('"'): pass + else: line_buf.append(token) + elif type == tokenize.COMMENT: + pass + elif type == tokenize.OP: + line_buf.append(token) + else: + if token != "\n": line_buf.append(token) + + if token != '\n': + prev = type + + body = "".join(accu) + return body + +deco_re = re.compile('(def|class)\\s+(\w+)\\(.*') +def process_decorators(body): + lst = body.split('\n') + accu = [] + all_deco = [] + buf = [] # put the decorator lines + for line in lst: + if line.startswith('@'): + buf.append(line[1:]) + elif buf: + name = deco_re.sub('\\2', line) + if not name: + raise IOError("decorator not followed by a function!" + line) + for x in buf: + all_deco.append("%s(%s)" % (x, name)) + accu.append(line) + buf = [] + else: + accu.append(line) + return "\n".join(accu+all_deco) + +def sfilter(path): + if sys.version_info[0] >= 3 and Options.options.strip_comments: + f = open(path, "rb") + tk = tokenize.tokenize(f.readline) + next(tk) # the first one is always tokenize.ENCODING for Python 3, ignore it + cnt = process_tokens(tk) + elif Options.options.strip_comments and path.endswith('.py'): + f = open(path, "r") + cnt = process_tokens(tokenize.generate_tokens(f.readline)) + else: + f = open(path, "r") + cnt = f.read() + + f.close() + if path.endswith('.py') : + cnt = process_decorators(cnt) + + if cnt.find('set(') > -1: + cnt = 'import sys\nif sys.hexversion < 0x020400f0: from sets import Set as set\n' + cnt + cnt = '#! /usr/bin/env python\n# encoding: utf-8\n# WARNING! Do not edit! http://waf.googlecode.com/svn/docs/wafbook/single.html#_obtaining_the_waf_file\n\n' + cnt + + return (io.BytesIO(cnt.encode('utf-8')), len(cnt), cnt) + +def create_waf(*k, **kw): + #print("-> preparing waf") + mw = 'tmp-waf-'+VERSION + + import tarfile, re + + zipType = Options.options.zip.strip().lower() + if zipType not in zip_types: + zipType = zip_types[0] + + #open a file as tar.[extension] for writing + tar = tarfile.open('%s.tar.%s' % (mw, zipType), "w:%s" % zipType) + + files = [] + add3rdparty = [] + for x in Options.options.add3rdparty.split(','): + if os.path.isabs(x): + files.append(x) + else: + add3rdparty.append(x + '.py') + + for d in '. Tools extras'.split(): + dd = os.path.join('waflib', d) + for k in os.listdir(dd): + if k == '__init__.py': + files.append(os.path.join(dd, k)) + continue + if d == 'extras': + if not k in add3rdparty: + continue + if k.endswith('.py'): + files.append(os.path.join(dd, k)) + + for x in files: + tarinfo = tar.gettarinfo(x, x) + tarinfo.uid = tarinfo.gid = 0 + tarinfo.uname = tarinfo.gname = 'root' + (code, size, cnt) = sfilter(x) + tarinfo.size = size + + if os.path.isabs(x): + tarinfo.name = 'waflib/extras/' + os.path.split(x)[1] + + tar.addfile(tarinfo, code) + tar.close() + + f = open('waf-light', 'rU') + code1 = f.read() + f.close() + + # now store the revision unique number in waf + #compute_revision() + #reg = re.compile('^REVISION=(.*)', re.M) + #code1 = reg.sub(r'REVISION="%s"' % REVISION, code1) + code1 = code1.replace("if sys.hexversion<0x206000f:\n\traise ImportError('Python >= 2.6 is required to create the waf file')\n", '') + code1 = code1.replace('\timport waflib.extras.compat15#PRELUDE', Options.options.prelude) + + prefix = '' + reg = re.compile('^INSTALL=(.*)', re.M) + code1 = reg.sub(r'INSTALL=%r' % prefix, code1) + #change the tarfile extension in the waf script + reg = re.compile('bz2', re.M) + code1 = reg.sub(zipType, code1) + if zipType == 'gz': + code1 = code1.replace('bunzip2', 'gzip -d') + + f = open('%s.tar.%s' % (mw, zipType), 'rb') + cnt = f.read() + f.close() + + # the REVISION value is the md5 sum of the binary blob (facilitate audits) + m = md5() + m.update(cnt) + REVISION = m.hexdigest() + reg = re.compile('^REVISION=(.*)', re.M) + code1 = reg.sub(r'REVISION="%s"' % REVISION, code1) + + def find_unused(kd, ch): + for i in range(35, 125): + for j in range(35, 125): + if i==j: continue + if i == 39 or j == 39: continue + if i == 92 or j == 92: continue + s = chr(i) + chr(j) + if -1 == kd.find(s.encode()): + return (kd.replace(ch.encode(), s.encode()), s) + raise + + # The reverse order prevent collisions + (cnt, C2) = find_unused(cnt, '\r') + (cnt, C1) = find_unused(cnt, '\n') + f = open('waf', 'wb') + + ccc = code1.replace("C1='x'", "C1='%s'" % C1).replace("C2='x'", "C2='%s'" % C2) + + f.write(ccc.encode()) + f.write(b'#==>\n') + f.write(b'#') + f.write(cnt) + f.write(b'\n') + f.write(b'#<==\n') + f.close() + + if sys.platform == 'win32' or Options.options.make_batch: + f = open('waf.bat', 'w') + f.write('@python -x "%~dp0waf" %* & exit /b\n') + f.close() + + if sys.platform != 'win32': + os.chmod('waf', Utils.O755) + os.unlink('%s.tar.%s' % (mw, zipType)) + +def make_copy(inf, outf): + (a, b, cnt) = sfilter(inf) + f = open(outf, "wb") + f.write(cnt) + f.close() + +def configure(conf): + conf.load('python') + conf.check_python_version((2,4)) + + +def build(bld): + waf = bld.path.make_node('waf') # create the node right here + bld(name='create_waf', rule=create_waf, target=waf, always=True, color='PINK', update_outputs=True) + +#def dist(): +# import Scripting +# Scripting.g_dist_exts += ['Weak.py'] # shows how to exclude a file from dist +# Scripting.Dist(APPNAME, VERSION) +