5 Permission is hereby granted, free of charge, to any person obtaining
6 a copy of this software and associated documentation files (the
7 "Software"), to deal in the Software without restriction, including
8 without limitation the rights to use, copy, modify, merge, publish,
9 distribute, sublicense, and/or sell copies of the Software, and to
10 permit persons to whom the Software is furnished to do so, subject to
11 the following conditions:
13 The above copyright notice and this permission notice shall be included
14 in all copies or substantial portions of the Software.
16 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY
17 KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
18 WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
28 =head2 Adding new methods
30 For slightly more demanding changes, you may wish to add new methods to the
31 C<cons> package. Here's an example of a very simple extension,
32 C<InstallScript>, which installs a tcl script in a requested location, but
33 edits the script first to reflect a platform-dependent path that needs to be
34 installed in the script:
36 # cons::InstallScript - Create a platform dependent version of a shell
37 # script by replacing string ``#!your-path-here'' with platform specific
40 sub cons::InstallScript {
41 my ($env, $dst, $src) = @_;
42 Command $env $dst, $src, qq(
43 sed s+your-path-here+$BIN_DIR+ %< > %>
48 Notice that this method is defined directly in the C<cons> package (by
49 prefixing the name with C<cons::>). A change made in this manner will be
50 globally visible to all environments, and could be called as in the
53 InstallScript $env "$BIN/foo", "foo.tcl";
55 For a small improvement in generality, the C<BINDIR> variable could be
56 passed in as an argument or taken from the construction environment-,-as
60 =head2 Overriding methods
62 Instead of adding the method to the C<cons> name space, you could define a
63 new package which inherits existing methods from the C<cons> package and
64 overrides or adds others. This can be done using Perl's inheritance
67 The following example defines a new package C<cons::switch> which
68 overrides the standard C<Library> method. The overridden method builds
69 linked library modules, rather than library archives. A new
70 constructor is provided. Environments created with this constructor
71 will have the new library method; others won't.
84 my(@objs) = Objects $env @_;
85 Command $env $lib, @objs, q(
86 %LD -r %LDFLAGS %< -o %>
90 This functionality could be invoked as in the following example:
92 $env = new cons::switch(@overrides);
94 Library $env 'lib.o', 'foo.c', 'bar.c';
100 Although &SCons; provides many useful methods
101 for building common software products:
102 programs, libraries, documents.
103 you frequently want to be
104 able to build some other type of file
105 not supported directly by &SCons;.
106 Fortunately, &SCons; makes it very easy
107 to define your own &Builder; objects
108 for any custom file types you want to build.
109 (In fact, the &SCons; interfaces for creating
110 &Builder; objects are flexible enough and easy enough to use
111 that all of the the &SCons; built-in &Builder; objects
112 are created the mechanisms described in this section.)
117 <title>Writing Builders That Execute External Commands</title>
121 The simplest &Builder; to create is
122 one that executes an external command.
123 For example, if we want to build
124 an output file by running the contents
125 of the input file through a command named
126 <literal>foobuild</literal>,
127 creating that &Builder; might look like:
132 bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
137 All the above line does is create a free-standing
139 The next section will show us how to actually use it.
146 <title>Attaching a Builder to a &ConsEnv;</title>
150 A &Builder; object isn't useful
151 until it's attached to a &consenv;
152 so that we can call it to arrange
153 for files to be built.
154 This is done through the &cv-link-BUILDERS;
155 &consvar; in an environment.
156 The &cv-BUILDERS; variable is a Python dictionary
157 that maps the names by which you want to call
158 various &Builder; objects to the objects themselves.
159 For example, if we want to call the
160 &Builder; we just defined by the name
161 <function>Foo</function>,
162 our &SConstruct; file might look like:
166 <scons_example name="ex1">
167 <file name="SConstruct">
168 bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
169 env = Environment(BUILDERS = {'Foo' : bld})
171 env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd()
172 env.Foo('file.foo', 'file.input')
174 <file name="file.input">
177 <file name="foobuild" chmod="0755">
183 bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
184 env = Environment(BUILDERS = {'Foo' : bld})
189 With the &Builder; attached to our &consenv;
190 with the name <function>Foo</function>,
191 we can now actually call it like so:
196 env.Foo('file.foo', 'file.input')
201 Then when we run &SCons; it looks like:
205 <scons_output example="ex1">
206 <scons_output_command>scons -Q</scons_output_command>
211 Note, however, that the default &cv-BUILDERS;
212 variable in a &consenv;
213 comes with a default set of &Builder; objects
215 &b-link-Program;, &b-link-Library;, etc.
216 And when we explicitly set the &cv-BUILDERS; variable
217 when we create the &consenv;,
218 the default &Builder;s are no longer part of
224 The ToolSurrogate stuff that's used to capture output initializes
225 SCons.Defaults.ConstructionEnvironment with its own list of TOOLS.
226 In this next example, we want to show the user that when they
227 set the BUILDERS explicitly, the call to env.Program() generates
228 an AttributeError. This won't happen with all of the default
229 ToolSurrogates in the default construction environment. To make the
230 AttributeError show up, we have to overwite the default construction
231 environment's TOOLS variable so Program() builder doesn't show up.
233 We do this by executing a slightly different SConstruct file than the
234 one we print in the guide, with two extra statements at the front
235 that overwrite the TOOLS variable as described. Note that we have
236 to jam those statements on to the first line to keep the line number
237 in the generated error consistent with what the user will see in the
240 <scons_example name="ex2">
241 <file name="SConstruct">
242 import SCons.Defaults; SCons.Defaults.ConstructionEnvironment['TOOLS'] = {}; bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
243 env = Environment(BUILDERS = {'Foo' : bld})
244 env.Foo('file.foo', 'file.input')
245 env.Program('hello.c')
247 <file name="SConstruct.printme" printme="1">
248 bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
249 env = Environment(BUILDERS = {'Foo' : bld})
250 env.Foo('file.foo', 'file.input')
251 env.Program('hello.c')
253 <file name="file.input">
256 <file name="hello.c">
261 <scons_output example="ex2">
262 <scons_output_command>scons -Q</scons_output_command>
267 To be able to use both our own defined &Builder; objects
268 and the default &Builder; objects in the same &consenv;,
269 you can either add to the &cv-BUILDERS; variable
270 using the &Append; function:
274 <scons_example name="ex3">
275 <file name="SConstruct">
278 env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd()
279 bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
280 env.Append(BUILDERS = {'Foo' : bld})
281 env.Foo('file.foo', 'file.input')
282 env.Program('hello.c')
284 <file name="file.input">
287 <file name="hello.c">
290 <file name="foobuild" chmod="0755">
297 bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
298 env.Append(BUILDERS = {'Foo' : bld})
299 env.Foo('file.foo', 'file.input')
300 env.Program('hello.c')
305 Or you can explicitly set the appropriately-named
306 key in the &cv-BUILDERS; dictionary:
312 bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
313 env['BUILDERS']['Foo'] = bld
314 env.Foo('file.foo', 'file.input')
315 env.Program('hello.c')
320 Either way, the same &consenv;
321 can then use both the newly-defined
322 <function>Foo</function> &Builder;
323 and the default &b-link-Program; &Builder;:
327 <scons_output example="ex3">
328 <scons_output_command>scons -Q</scons_output_command>
334 <title>Letting &SCons; Handle The File Suffixes</title>
338 By supplying additional information
339 when you create a &Builder;,
340 you can let &SCons; add appropriate file
341 suffixes to the target and/or the source file.
342 For example, rather than having to specify
343 explicitly that you want the <literal>Foo</literal>
344 &Builder; to build the <literal>file.foo</literal>
345 target file from the <literal>file.input</literal> source file,
346 you can give the <literal>.foo</literal>
347 and <literal>.input</literal> suffixes to the &Builder;,
348 making for more compact and readable calls to
349 the <literal>Foo</literal> &Builder;:
353 <scons_example name="ex4">
354 <file name="SConstruct">
355 bld = Builder(action = 'foobuild < $SOURCE > $TARGET',
357 src_suffix = '.input')
358 env = Environment(BUILDERS = {'Foo' : bld})
360 env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd()
364 <file name="file1.input">
367 <file name="file2.input">
370 <file name="foobuild" chmod="0755">
376 bld = Builder(action = 'foobuild < $SOURCE > $TARGET',
378 src_suffix = '.input')
379 env = Environment(BUILDERS = {'Foo' : bld})
384 <scons_output example="ex4">
385 <scons_output_command>scons -Q</scons_output_command>
390 You can also supply a <literal>prefix</literal> keyword argument
391 if it's appropriate to have &SCons; append a prefix
392 to the beginning of target file names.
399 <title>Builders That Execute Python Functions</title>
403 In &SCons;, you don't have to call an external command
405 You can, instead, define a Python function
406 that a &Builder; object can invoke
407 to build your target file (or files).
408 Such a &buildfunc; definition looks like:
413 def build_function(target, source, env):
414 # Code to build "target" from "source"
420 The arguments of a &buildfunc; are:
432 A list of Node objects representing
433 the target or targets to be
434 built by this builder function.
435 The file names of these target(s)
436 may be extracted using the Python &str; function.
448 A list of Node objects representing
450 used by this builder function to build the targets.
451 The file names of these source(s)
452 may be extracted using the Python &str; function.
464 The &consenv; used for building the target(s).
465 The builder function may use any of the
466 environment's construction variables
467 in any way to affect how it builds the targets.
477 The builder function must
478 return a <literal>0</literal> or <literal>None</literal> value
479 if the target(s) are built successfully.
481 may raise an exception
482 or return any non-zero value
483 to indicate that the build is unsuccessful,
489 Once you've defined the Python function
490 that will build your target file,
491 defining a &Builder; object for it is as
492 simple as specifying the name of the function,
493 instead of an external command,
495 <literal>action</literal>
500 <scons_example name="ex5">
501 <file name="SConstruct" printme="1">
502 def build_function(target, source, env):
503 # Code to build "target" from "source"
505 bld = Builder(action = build_function,
507 src_suffix = '.input')
508 env = Environment(BUILDERS = {'Foo' : bld})
511 <file name="file.input">
518 And notice that the output changes slightly,
519 reflecting the fact that a Python function,
520 not an external command,
521 is now called to build the target file:
525 <scons_output example="ex5">
526 <scons_output_command>scons -Q</scons_output_command>
532 <title>Builders That Create Actions Using a &Generator;</title>
536 &SCons; Builder objects can create an action "on the fly"
537 by using a function called a &generator;.
538 This provides a great deal of flexibility to
539 construct just the right list of commands
540 to build your target.
541 A &generator; looks like:
546 def generate_actions(source, target, env, for_signature):
547 return 'foobuild < %s > %s' % (target[0], source[0])
552 The arguments of a &generator; are:
564 A list of Node objects representing
565 the sources to be built
566 by the command or other action
567 generated by this function.
568 The file names of these source(s)
569 may be extracted using the Python &str; function.
582 A list of Node objects representing
583 the target or targets to be built
584 by the command or other action
585 generated by this function.
586 The file names of these target(s)
587 may be extracted using the Python &str; function.
600 The &consenv; used for building the target(s).
601 The generator may use any of the
602 environment's construction variables
603 in any way to determine what command
604 or other action to return.
612 <term>for_signature</term>
617 A flag that specifies whether the
618 generator is being called to contribute to a build signature,
619 as opposed to actually executing the command.
621 <!-- XXX NEED MORE HERE, describe generators use in signatures -->
632 The &generator; must return a
633 command string or other action that will be used to
634 build the specified target(s) from the specified source(s).
640 Once you've defined a &generator;,
641 you create a &Builder; to use it
642 by specifying the generator keyword argument
643 instead of <literal>action</literal>.
647 <scons_example name="ex6">
648 <file name="SConstruct">
649 def generate_actions(source, target, env, for_signature):
650 return 'foobuild < %s > %s' % (source[0], target[0])
651 bld = Builder(generator = generate_actions,
653 src_suffix = '.input')
654 env = Environment(BUILDERS = {'Foo' : bld})
656 env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd()
659 <file name="file.input">
662 <file name="foobuild" chmod="0755">
668 def generate_actions(source, target, env, for_signature):
669 return 'foobuild < %s > %s' % (source[0], target[0])
670 bld = Builder(generator = generate_actions,
672 src_suffix = '.input')
673 env = Environment(BUILDERS = {'Foo' : bld})
677 <scons_output example="ex6">
678 <scons_output_command>scons -Q</scons_output_command>
683 Note that it's illegal to specify both an
684 <literal>action</literal>
686 <literal>generator</literal>
694 <title>Builders That Modify the Target or Source Lists Using an &Emitter;</title>
698 &SCons; supports the ability for a Builder to modify the
699 lists of target(s) from the specified source(s).
700 You do this by defining an &emitter; function
701 that takes as its arguments
702 the list of the targets passed to the builder,
703 the list of the sources passed to the builder,
704 and the construction environment.
705 The emitter function should return the modified
706 lists of targets that should be built
707 and sources from which the targets will be built.
713 For example, suppose you want to define a Builder
714 that always calls a <filename>foobuild</filename> program,
715 and you want to automatically add
716 a new target file named
717 <filename>new_target</filename>
718 and a new source file named
719 <filename>new_source</filename>
720 whenever it's called.
721 The &SConstruct; file might look like this:
725 <scons_example name="ex7">
726 <file name="SConstruct">
727 def modify_targets(target, source, env):
728 target.append('new_target')
729 source.append('new_source')
730 return target, source
731 bld = Builder(action = 'foobuild $TARGETS - $SOURCES',
733 src_suffix = '.input',
734 emitter = modify_targets)
735 env = Environment(BUILDERS = {'Foo' : bld})
737 env['ENV']['PATH'] = env['ENV']['PATH'] + os.pathsep + os.getcwd()
740 <file name="file.input">
743 <file name="new_source">
746 <file name="foobuild" chmod="0755">
752 def modify_targets(target, source, env):
753 target.append('new_target')
754 source.append('new_source')
755 return target, source
756 bld = Builder(action = 'foobuild $TARGETS - $SOURCES',
758 src_suffix = '.input',
759 emitter = modify_targets)
760 env = Environment(BUILDERS = {'Foo' : bld})
766 And would yield the following output:
770 <scons_output example="ex7">
771 <scons_output_command>scons -Q</scons_output_command>
776 One very flexible thing that you can do is
777 use a construction variable to specify
778 different emitter functions for different
779 construction variable.
780 To do this, specify a string
781 containing a construction variable
782 expansion as the emitter when you call
783 the &Builder; function,
784 and set that construction variable to
785 the desired emitter function
786 in different construction environments:
790 <scons_example name="MY_EMITTER">
792 <file name="SConstruct" printme="1">
793 bld = Builder(action = 'my_command $SOURCES > $TARGET',
795 src_suffix = '.input',
796 emitter = '$MY_EMITTER')
797 def modify1(target, source, env):
798 return target, source + ['modify1.in']
799 def modify2(target, source, env):
800 return target, source + ['modify2.in']
801 env1 = Environment(BUILDERS = {'Foo' : bld},
802 MY_EMITTER = modify1)
803 env2 = Environment(BUILDERS = {'Foo' : bld},
804 MY_EMITTER = modify2)
808 env1['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd()
809 env2['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd()
811 <file name="file1.input">
814 <file name="file2.input">
817 <file name="modify1.in">
820 <file name="modify2.in">
823 <file name="my_command" chmod="0755">
831 bld = Builder(action = 'my_command $SOURCES > $TARGET',
833 src_suffix = '.input',
834 emitter = '$MY_EMITTER')
835 def modify1(target, source, env):
836 return target, source + ['modify1.in']
837 def modify2(target, source, env):
838 return target, source + ['modify2.in']
839 env1 = Environment(BUILDERS = {'Foo' : bld},
840 MY_EMITTER = modify1)
841 env2 = Environment(BUILDERS = {'Foo' : bld},
842 MY_EMITTER = modify2)
850 In this example, the <filename>modify1.in</filename>
851 and <filename>modify2.in</filename> files
852 get added to the source lists
853 of the different commands:
857 <scons_output example="MY_EMITTER">
858 <scons_output_command>scons -Q</scons_output_command>
866 <title>target_factor=, source_factory=</title>
871 <title>target_scanner=, source_scanner=</title>
876 <title>multi=</title>
881 <title>single_source=</title>
886 <title>src_builder=</title>
891 <title>ensure_suffix=</title>
898 <title>Where To Put Your Custom Builders and Tools</title>
902 The <filename>site_scons</filename> directory gives you a place to
903 put Python modules you can import into your &SConscript; files
904 (<filename>site_scons</filename>),
905 add-on tools that can integrate into &SCons;
906 (<filename>site_scons/site_tools</filename>),
907 and a <filename>site_scons/site_init.py</filename> file that
908 gets read before any &SConstruct; or &SConscript; file,
909 allowing you to change &SCons;'s default behavior.
915 If you get a tool from somewhere (the &SCons; wiki or a third party,
916 for instance) and you'd like to use it in your project, the
917 <filename>site_scons</filename> dir is the simplest place to put it.
918 Tools come in two flavors; either a Python function that operates on
919 an &Environment; or a Python file containing two functions,
920 <function>exists()</function> and <function>generate()</function>.
926 A single-function Tool can just be included in your
927 <filename>site_scons/site_init.py</filename> file where it will be
928 parsed and made available for use. For instance, you could have a
929 <filename>site_scons/site_init.py</filename> file like this:
933 <scons_example name="site1">
934 <file name="site_scons/site_init.py" printme=1>
935 def TOOL_ADD_HEADER(env):
936 """A Tool to add a header from $HEADER to the source file"""
937 add_header = Builder(action=['echo "$HEADER" > $TARGET',
938 'cat $SOURCE >> $TARGET'])
939 env.Append(BUILDERS = {'AddHeader' : add_header})
940 env['HEADER'] = '' # set default value
942 <file name="SConstruct">
943 env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====")
944 env.AddHeader('tgt', 'src')
953 and a &SConstruct; like this:
958 # Use TOOL_ADD_HEADER from site_scons/site_init.py
959 env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====")
960 env.AddHeader('tgt', 'src')
965 The <function>TOOL_ADD_HEADER</function> tool method will be
966 called to add the <function>AddHeader</function> tool to the
972 <scons_output example="site1" os="posix">
973 <scons_output_command>scons -Q</scons_output_command>
978 Similarly, a more full-fledged tool with
979 <function>exists()</function> and <function>generate()</function>
980 methods can be installed in
981 <filename>site_scons/site_tools/toolname.py</filename>. Since
982 <filename>site_scons/site_tools</filename> is automatically added
983 to the head of the tool search path, any tool found there will be
984 available to all environments. Furthermore, a tool found there
985 will override a built-in tool of the same name, so if you need to
986 change the behavior of a built-in tool, site_scons gives you the
991 Many people have a library of utility Python functions they'd like
992 to include in &SConscript;s; just put that module in
993 <filename>site_scons/my_utils.py</filename> or any valid Python module name of your
994 choice. For instance you can do something like this in
995 <filename>site_scons/my_utils.py</filename> to add
996 <function>build_id</function> and <function>MakeWorkDir</function>
1000 <scons_example name="site2">
1001 <file name="site_scons/my_utils.py" printme=1>
1002 from SCons.Script import * # for Execute and Mkdir
1004 """Return a build ID (stub version)"""
1006 def MakeWorkDir(workdir):
1007 """Create the specified dir immediately"""
1008 Execute(Mkdir(workdir))
1010 <file name="SConscript">
1012 MakeWorkDir('/tmp/work')
1013 print "build_id=" + my_utils.build_id()
1019 And then in your &SConscript; or any sub-&SConscript; anywhere in
1020 your build, you can import <filename>my_utils</filename> and use it:
1026 print "build_id=" + my_utils.build_id()
1027 my_utils.MakeWorkDir('/tmp/work')
1031 Note that although you can put this library in
1032 <filename>site_scons/site_init.py</filename>,
1033 it is no better there than <filename>site_scons/my_utils.py</filename>
1034 since you still have to import that module into your &SConscript;.
1035 Also note that in order to refer to objects in the SCons namespace
1036 such as &Environment; or &Mkdir; or &Execute; in any file other
1037 than a &SConstruct; or &SConscript; you always need to do
1040 from SCons.Script import *
1044 This is true in modules in <filename>site_scons</filename> such as
1045 <filename>site_scons/site_init.py</filename> as well.
1050 If you have a machine-wide site dir you'd like to use instead of
1051 <filename>./site_scons</filename>, use the
1052 <literal>--site-dir</literal> option to point to your dir.
1053 <filename>site_init.py</filename> and
1054 <filename>site_tools</filename> will be located under that dir.
1055 To avoid using a <filename>site_scons</filename> dir at all, even
1056 if it exists, use the <literal>--no-site-dir</literal> option.
1066 <title>Builders That Use Other Builders</title>
1074 <scons_example name="ex8">
1075 <file name="SConstruct" printme="1">
1077 #env.SourceCode('.', env.BitKeeper('my_command'))
1078 env.Program('hello.c')
1080 <file name="hello.c">
1085 <scons_output example="ex8">
1086 <scons_output_command>scons -Q</scons_output_command>