diff --git a/docs/book/advbuild.txt b/docs/book/advbuild.txt index 4a71053a..5519e2dd 100644 --- a/docs/book/advbuild.txt +++ b/docs/book/advbuild.txt @@ -214,11 +214,11 @@ The _WAFLOCK_ environment variable is used to control the configuration lock and // advbuild_waflock [source,python] --------------- -def configure(conf): +def configure(ctx): pass -def build(bld): - bld(rule='touch ${TGT}', target='foo.txt') +def build(ctx): + ctx(rule='touch ${TGT}', target='foo.txt') --------------- We will change the _WAFLOCK_ variable in the execution: diff --git a/docs/book/chains.txt b/docs/book/chains.txt index 120b32df..267dd462 100644 --- a/docs/book/chains.txt +++ b/docs/book/chains.txt @@ -5,7 +5,7 @@ Transformations may be performed automatically based on the file name or on the ==== 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): +The explicit rules described in the previous chapter become a limitation for processing several files of the same extension. The following code may lead to unmaintainable scripts and to slow builds (large amount of objects): [source, python] ---------------- @@ -16,7 +16,7 @@ def build(bld): bld.install_files('${LUADIR}', x) ---------------- -Rather, the rule should be removed from the user script, like this: +It is desirable to extract the rule from the user scripts in the following manner: [source,python] --------------- @@ -24,7 +24,7 @@ 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: +The following piece of code will enable this functionality. It may be inserted in a waf tool or in the same `wscript` file: [source,python] --------------- @@ -41,11 +41,11 @@ TaskGen.declare_chain( --------------- <1> The name for the corresponding task class to use -<2> The rule is the same as for any rule-based task generator +<2> The rule is the same as for any rule-based task generator. It is passed to the `run_str` attribute of a task class. <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. +<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` or `None`. ==== Chaining more than one command diff --git a/docs/book/examples/tasks_chains/wscript b/docs/book/examples/tasks_chains/wscript new file mode 100644 index 00000000..2207ab80 --- /dev/null +++ b/docs/book/examples/tasks_chains/wscript @@ -0,0 +1,32 @@ +#! /usr/bin/env python +# encoding: utf-8 + +def configure(ctx): + pass + +def build(ctx): + import os + from waflib import Utils, Task + + def chmod_fun(task): + for x in task.outputs: + os.chmod(x.abspath(), Utils.O755) + + def remove_fun(task): + for x in task.outputs: + try: + os.remove(x.abspath()) + except OSError: + if os.path.exists(x.abspath()): + raise ValueError('Cannot remove %r' % x) + + class complex_copy(Task.Task): + run_str = (remove_fun, "${CP} ${SRC} ${TGT}", chmod_fun) + + ctx.env.CP = '/bin/cp' + tsk = complex_copy(env=ctx.env.derive()) + tsk.set_inputs(ctx.path.find_resource('wscript')) + tsk.set_outputs(ctx.path.find_or_declare('wscript.out')) + ctx.add_to_group(tsk) + + diff --git a/docs/book/task_generators.txt b/docs/book/task_generators.txt index 2f6372c8..273674be 100644 --- a/docs/book/task_generators.txt +++ b/docs/book/task_generators.txt @@ -96,7 +96,7 @@ $ waf distclean configure build --zones=task_gen <1> 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_rule (139657958706768) <3> 23:03:44 task_gen -> process_source (139657958706768) <4> 23:03:44 task_gen -> methodName (139657958706768) <5> Hello, world! @@ -108,8 +108,8 @@ Waf: Leaving directory `/tmp/simpleproject/build' <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 +<3> The method 'process_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 'process_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) @@ -150,13 +150,13 @@ $ waf distclean configure build --zones=task_gen 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_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_rule (140631018237776) 16:22:07 task_gen -> process_source (140631018237776) 16:22:07 task_gen -> pong (140631018237776) pong @@ -188,7 +188,7 @@ def build(bld): from waflib import TaskGen @TaskGen.feature('*') -@TaskGen.before('process_source', 'exec_rule') +@TaskGen.before('process_source', 'process_rule') def method1(self): print('method 1 %r' % getattr(self, 'myattr', None)) @@ -211,7 +211,7 @@ Waf: Entering directory `/tmp/simpleproject/build' 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 -> process_rule (139808568487632) 15:54:02 task_gen -> method2 (139808568487632) method 2 'Hello, world!' 15:54:02 task_gen -> process_source (139808568487632) diff --git a/docs/book/tasks.txt b/docs/book/tasks.txt index a50be86a..c4ddd6dd 100644 --- a/docs/book/tasks.txt +++ b/docs/book/tasks.txt @@ -514,6 +514,51 @@ ${CPPPATH_ST:INCPATHS} <4> <3> Print the task unique identifier <4> Perform a map replacement equivalent to _[env.CPPPATH_ST % x for x in env.INCPATHS]_ +==== Command chains + +In order to improve script portability 'run_str' can also be a list of command strings or functions. All sub-commands must complete for the task to succeed. + +The following example shows how to chain three commands to copy `wscript` to `wscript.out`. The output file will be removed first if it already exists. +The copy is then performed. When successful, the output file permissions are changed to turn it into an executable. + +// tasks_chains +[source,python] +--------------- +def build(ctx): + import os + from waflib import Utils, Task + + def chmod_fun(task): <1> + for x in task.outputs: + os.chmod(x.abspath(), Utils.O755) + return 0 + + def remove_fun(task): + for x in task.outputs: + try: + os.remove(x.abspath()) + except OSError: + if os.path.exists(x.abspath()): + raise ValueError('Cannot remove %r' % x) <2> + return 0 + + class complex_copy(Task.Task): + run_str = (remove_fun, "${CP} ${SRC} ${TGT}", chmod_fun) <3> + + ctx.env.CP = '/bin/cp' <4> + tsk = complex_copy(env=ctx.env.derive()) + tsk.set_inputs(ctx.path.find_resource('wscript')) + tsk.set_outputs(ctx.path.find_or_declare('wscript.out')) + ctx.add_to_group(tsk) +--------------- + +<1> The function arguments is the same as that of 'run' methods. Function commands return 0 or None in case of success. +<2> Function commands can raise exceptions such as OSError, ValueError or WafError +<3> Several functions or strings can be chained together; the build will run the commands in the order specified +<4> Rebuilds will occur when variables used in command strings are modified, and when command strings or function definitions change + +NOTE: Chaining is also possible through subclassing. + ==== Direct class modifications ===== Always execute