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:
169 bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
170 env = Environment(BUILDERS = {'Foo' : bld})
175 With the &Builder; attached to our &consenv;
176 with the name <function>Foo</function>,
177 we can now actually call it like so:
182 env.Foo('file.foo', 'file.input')
187 Then when we run &SCons; it looks like:
192 % <userinput>scons -Q</userinput>
193 foobuild < file.input > file.foo
198 Note, however, that the default &cv-BUILDERS;
199 variable in a &consenv;
200 comes with a default set of &Builder; objects
202 &b-link-Program;, &b-link-Library;, etc.
203 And when we explicitly set the &cv-BUILDERS; variable
204 when we create the &consenv;,
205 the default &Builder;s are no longer part of
211 The ToolSurrogate stuff that's used to capture output initializes
212 SCons.Defaults.ConstructionEnvironment with its own list of TOOLS.
213 In this next example, we want to show the user that when they
214 set the BUILDERS explicitly, the call to env.Program() generates
215 an AttributeError. This won't happen with all of the default
216 ToolSurrogates in the default construction environment. To make the
217 AttributeError show up, we have to overwite the default construction
218 environment's TOOLS variable so Program() builder doesn't show up.
220 We do this by executing a slightly different SConstruct file than the
221 one we print in the guide, with two extra statements at the front
222 that overwrite the TOOLS variable as described. Note that we have
223 to jam those statements on to the first line to keep the line number
224 in the generated error consistent with what the user will see in the
228 bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
229 env = Environment(BUILDERS = {'Foo' : bld})
230 env.Foo('file.foo', 'file.input')
231 env.Program('hello.c')
235 % <userinput>scons -Q</userinput>
236 AttributeError: SConsEnvironment instance has no attribute 'Program':
237 File "/home/my/project/SConstruct", line 4:
238 env.Program('hello.c')
243 To be able to use both our own defined &Builder; objects
244 and the default &Builder; objects in the same &consenv;,
245 you can either add to the &cv-BUILDERS; variable
246 using the &Append; function:
254 bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
255 env.Append(BUILDERS = {'Foo' : bld})
256 env.Foo('file.foo', 'file.input')
257 env.Program('hello.c')
262 Or you can explicitly set the appropriately-named
263 key in the &cv-BUILDERS; dictionary:
269 bld = Builder(action = 'foobuild < $SOURCE > $TARGET')
270 env['BUILDERS']['Foo'] = bld
271 env.Foo('file.foo', 'file.input')
272 env.Program('hello.c')
277 Either way, the same &consenv;
278 can then use both the newly-defined
279 <function>Foo</function> &Builder;
280 and the default &b-link-Program; &Builder;:
285 % <userinput>scons -Q</userinput>
286 foobuild < file.input > file.foo
287 cc -o hello.o -c hello.c
294 <title>Letting &SCons; Handle The File Suffixes</title>
298 By supplying additional information
299 when you create a &Builder;,
300 you can let &SCons; add appropriate file
301 suffixes to the target and/or the source file.
302 For example, rather than having to specify
303 explicitly that you want the <literal>Foo</literal>
304 &Builder; to build the <literal>file.foo</literal>
305 target file from the <literal>file.input</literal> source file,
306 you can give the <literal>.foo</literal>
307 and <literal>.input</literal> suffixes to the &Builder;,
308 making for more compact and readable calls to
309 the <literal>Foo</literal> &Builder;:
316 bld = Builder(action = 'foobuild < $SOURCE > $TARGET',
318 src_suffix = '.input')
319 env = Environment(BUILDERS = {'Foo' : bld})
325 % <userinput>scons -Q</userinput>
326 foobuild < file1.input > file1.foo
327 foobuild < file2.input > file2.foo
332 You can also supply a <literal>prefix</literal> keyword argument
333 if it's appropriate to have &SCons; append a prefix
334 to the beginning of target file names.
341 <title>Builders That Execute Python Functions</title>
345 In &SCons;, you don't have to call an external command
347 You can, instead, define a Python function
348 that a &Builder; object can invoke
349 to build your target file (or files).
350 Such a &buildfunc; definition looks like:
355 def build_function(target, source, env):
356 # Code to build "target" from "source"
362 The arguments of a &buildfunc; are:
374 A list of Node objects representing
375 the target or targets to be
376 built by this builder function.
377 The file names of these target(s)
378 may be extracted using the Python &str; function.
390 A list of Node objects representing
392 used by this builder function to build the targets.
393 The file names of these source(s)
394 may be extracted using the Python &str; function.
406 The &consenv; used for building the target(s).
407 The builder function may use any of the
408 environment's construction variables
409 in any way to affect how it builds the targets.
419 The builder function must
420 return a <literal>0</literal> or <literal>None</literal> value
421 if the target(s) are built successfully.
423 may raise an exception
424 or return any non-zero value
425 to indicate that the build is unsuccessful,
431 Once you've defined the Python function
432 that will build your target file,
433 defining a &Builder; object for it is as
434 simple as specifying the name of the function,
435 instead of an external command,
437 <literal>action</literal>
443 def build_function(target, source, env):
444 # Code to build "target" from "source"
446 bld = Builder(action = build_function,
448 src_suffix = '.input')
449 env = Environment(BUILDERS = {'Foo' : bld})
455 And notice that the output changes slightly,
456 reflecting the fact that a Python function,
457 not an external command,
458 is now called to build the target file:
463 % <userinput>scons -Q</userinput>
464 build_function(["file.foo"], ["file.input"])
470 <title>Builders That Create Actions Using a &Generator;</title>
474 &SCons; Builder objects can create an action "on the fly"
475 by using a function called a &generator;.
476 This provides a great deal of flexibility to
477 construct just the right list of commands
478 to build your target.
479 A &generator; looks like:
484 def generate_actions(source, target, env, for_signature):
485 return 'foobuild < %s > %s' % (target[0], source[0])
490 The arguments of a &generator; are:
502 A list of Node objects representing
503 the sources to be built
504 by the command or other action
505 generated by this function.
506 The file names of these source(s)
507 may be extracted using the Python &str; function.
520 A list of Node objects representing
521 the target or targets to be built
522 by the command or other action
523 generated by this function.
524 The file names of these target(s)
525 may be extracted using the Python &str; function.
538 The &consenv; used for building the target(s).
539 The generator may use any of the
540 environment's construction variables
541 in any way to determine what command
542 or other action to return.
550 <term>for_signature</term>
555 A flag that specifies whether the
556 generator is being called to contribute to a build signature,
557 as opposed to actually executing the command.
559 <!-- XXX NEED MORE HERE, describe generators use in signatures -->
570 The &generator; must return a
571 command string or other action that will be used to
572 build the specified target(s) from the specified source(s).
578 Once you've defined a &generator;,
579 you create a &Builder; to use it
580 by specifying the generator keyword argument
581 instead of <literal>action</literal>.
588 def generate_actions(source, target, env, for_signature):
589 return 'foobuild < %s > %s' % (source[0], target[0])
590 bld = Builder(generator = generate_actions,
592 src_suffix = '.input')
593 env = Environment(BUILDERS = {'Foo' : bld})
598 % <userinput>scons -Q</userinput>
599 foobuild < file.input > file.foo
604 Note that it's illegal to specify both an
605 <literal>action</literal>
607 <literal>generator</literal>
615 <title>Builders That Modify the Target or Source Lists Using an &Emitter;</title>
619 &SCons; supports the ability for a Builder to modify the
620 lists of target(s) from the specified source(s).
621 You do this by defining an &emitter; function
622 that takes as its arguments
623 the list of the targets passed to the builder,
624 the list of the sources passed to the builder,
625 and the construction environment.
626 The emitter function should return the modified
627 lists of targets that should be built
628 and sources from which the targets will be built.
634 For example, suppose you want to define a Builder
635 that always calls a <filename>foobuild</filename> program,
636 and you want to automatically add
637 a new target file named
638 <filename>new_target</filename>
639 and a new source file named
640 <filename>new_source</filename>
641 whenever it's called.
642 The &SConstruct; file might look like this:
649 def modify_targets(target, source, env):
650 target.append('new_target')
651 source.append('new_source')
652 return target, source
653 bld = Builder(action = 'foobuild $TARGETS - $SOURCES',
655 src_suffix = '.input',
656 emitter = modify_targets)
657 env = Environment(BUILDERS = {'Foo' : bld})
663 And would yield the following output:
668 % <userinput>scons -Q</userinput>
669 foobuild file.foo new_target - file.input new_source
674 One very flexible thing that you can do is
675 use a construction variable to specify
676 different emitter functions for different
677 construction variable.
678 To do this, specify a string
679 containing a construction variable
680 expansion as the emitter when you call
681 the &Builder; function,
682 and set that construction variable to
683 the desired emitter function
684 in different construction environments:
689 bld = Builder(action = 'my_command $SOURCES > $TARGET',
691 src_suffix = '.input',
692 emitter = '$MY_EMITTER')
693 def modify1(target, source, env):
694 return target, source + ['modify1.in']
695 def modify2(target, source, env):
696 return target, source + ['modify2.in']
697 env1 = Environment(BUILDERS = {'Foo' : bld},
698 MY_EMITTER = modify1)
699 env2 = Environment(BUILDERS = {'Foo' : bld},
700 MY_EMITTER = modify2)
704 env1['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd()
705 env2['ENV']['PATH'] = env2['ENV']['PATH'] + os.pathsep + os.getcwd()
711 bld = Builder(action = 'my_command $SOURCES > $TARGET',
713 src_suffix = '.input',
714 emitter = '$MY_EMITTER')
715 def modify1(target, source, env):
716 return target, source + ['modify1.in']
717 def modify2(target, source, env):
718 return target, source + ['modify2.in']
719 env1 = Environment(BUILDERS = {'Foo' : bld},
720 MY_EMITTER = modify1)
721 env2 = Environment(BUILDERS = {'Foo' : bld},
722 MY_EMITTER = modify2)
730 In this example, the <filename>modify1.in</filename>
731 and <filename>modify2.in</filename> files
732 get added to the source lists
733 of the different commands:
738 % <userinput>scons -Q</userinput>
739 my_command file1.input modify1.in > file1.foo
740 my_command file2.input modify2.in > file2.foo
748 <title>target_factor=, source_factory=</title>
753 <title>target_scanner=, source_scanner=</title>
758 <title>multi=</title>
763 <title>single_source=</title>
768 <title>src_builder=</title>
773 <title>ensure_suffix=</title>
780 <title>Where To Put Your Custom Builders and Tools</title>
784 The <filename>site_scons</filename> directory gives you a place to
785 put Python modules you can import into your &SConscript; files
786 (<filename>site_scons</filename>),
787 add-on tools that can integrate into &SCons;
788 (<filename>site_scons/site_tools</filename>),
789 and a <filename>site_scons/site_init.py</filename> file that
790 gets read before any &SConstruct; or &SConscript; file,
791 allowing you to change &SCons;'s default behavior.
797 If you get a tool from somewhere (the &SCons; wiki or a third party,
798 for instance) and you'd like to use it in your project, the
799 <filename>site_scons</filename> dir is the simplest place to put it.
800 Tools come in two flavors; either a Python function that operates on
801 an &Environment; or a Python file containing two functions,
802 <function>exists()</function> and <function>generate()</function>.
808 A single-function Tool can just be included in your
809 <filename>site_scons/site_init.py</filename> file where it will be
810 parsed and made available for use. For instance, you could have a
811 <filename>site_scons/site_init.py</filename> file like this:
816 def TOOL_ADD_HEADER(env):
817 """A Tool to add a header from $HEADER to the source file"""
818 add_header = Builder(action=['echo "$HEADER" > $TARGET',
819 'cat $SOURCE >> $TARGET'])
820 env.Append(BUILDERS = {'AddHeader' : add_header})
821 env['HEADER'] = '' # set default value
826 and a &SConstruct; like this:
831 # Use TOOL_ADD_HEADER from site_scons/site_init.py
832 env=Environment(tools=['default', TOOL_ADD_HEADER], HEADER="=====")
833 env.AddHeader('tgt', 'src')
838 The <function>TOOL_ADD_HEADER</function> tool method will be
839 called to add the <function>AddHeader</function> tool to the
845 <scons_output example="site1" os="posix">
846 <scons_output_command>scons -Q</scons_output_command>
851 Similarly, a more full-fledged tool with
852 <function>exists()</function> and <function>generate()</function>
853 methods can be installed in
854 <filename>site_scons/site_tools/toolname.py</filename>. Since
855 <filename>site_scons/site_tools</filename> is automatically added
856 to the head of the tool search path, any tool found there will be
857 available to all environments. Furthermore, a tool found there
858 will override a built-in tool of the same name, so if you need to
859 change the behavior of a built-in tool, site_scons gives you the
864 Many people have a library of utility Python functions they'd like
865 to include in &SConscript;s; just put that module in
866 <filename>site_scons/my_utils.py</filename> or any valid Python module name of your
867 choice. For instance you can do something like this in
868 <filename>site_scons/my_utils.py</filename> to add
869 <function>build_id</function> and <function>MakeWorkDir</function>
874 from SCons.Script import * # for Execute and Mkdir
876 """Return a build ID (stub version)"""
878 def MakeWorkDir(workdir):
879 """Create the specified dir immediately"""
880 Execute(Mkdir(workdir))
885 And then in your &SConscript; or any sub-&SConscript; anywhere in
886 your build, you can import <filename>my_utils</filename> and use it:
892 print "build_id=" + my_utils.build_id()
893 my_utils.MakeWorkDir('/tmp/work')
897 Note that although you can put this library in
898 <filename>site_scons/site_init.py</filename>,
899 it is no better there than <filename>site_scons/my_utils.py</filename>
900 since you still have to import that module into your &SConscript;.
901 Also note that in order to refer to objects in the SCons namespace
902 such as &Environment; or &Mkdir; or &Execute; in any file other
903 than a &SConstruct; or &SConscript; you always need to do
906 from SCons.Script import *
910 This is true in modules in <filename>site_scons</filename> such as
911 <filename>site_scons/site_init.py</filename> as well.
916 If you have a machine-wide site dir you'd like to use instead of
917 <filename>./site_scons</filename>, use the
918 <literal>--site-dir</literal> option to point to your dir.
919 <filename>site_init.py</filename> and
920 <filename>site_tools</filename> will be located under that dir.
921 To avoid using a <filename>site_scons</filename> dir at all, even
922 if it exists, use the <literal>--no-site-dir</literal> option.
932 <title>Builders That Use Other Builders</title>
940 <scons_example name="ex8">
941 <file name="SConstruct" printme="1">
943 #env.SourceCode('.', env.BitKeeper('my_command'))
944 env.Program('hello.c')
946 <file name="hello.c">
951 <scons_output example="ex8">
952 <scons_output_command>scons -Q</scons_output_command>