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 =head1 Using and writing dependency scanners
30 QuickScan allows simple target-independent scanners to be set up for
31 source files. Only one QuickScan scanner may be associated with any given
32 source file and environment, although the same scanner may (and should)
33 be used for multiple files of a given type.
35 A QuickScan scanner is only ever invoked once for a given source file,
36 and it is only invoked if the file is used by some target in the tree
37 (i.e., there is a dependency on the source file).
39 QuickScan is invoked as follows:
41 QuickScan CONSENV CODEREF, FILENAME [, PATH]
43 The subroutine referenced by CODEREF is expected to return a list of
44 filenames included directly by FILE. These filenames will, in turn, be
45 scanned. The optional PATH argument supplies a lookup path for finding
46 FILENAME and/or files returned by the user-supplied subroutine. The PATH
47 may be a reference to an array of lookup-directory names, or a string of
48 names separated by the system's separator character (':' on UNIX systems,
51 The subroutine is called once for each line in the file, with $_ set to the
52 current line. If the subroutine needs to look at additional lines, or, for
53 that matter, the entire file, then it may read them itself, from the
54 filehandle SCAN. It may also terminate the loop, if it knows that no further
55 include information is available, by closing the filehandle.
57 Whether or not a lookup path is provided, QuickScan first tries to lookup
58 the file relative to the current directory (for the top-level file
59 supplied directly to QuickScan), or from the directory containing the
60 file which referenced the file. This is not very general, but seems good
61 enough, especially if you have the luxury of writing your own utilities
62 and can control the use of the search path in a standard way.
64 Here's a real example, taken from a F<Construct> file here:
67 my($env, @tables) = @_;
68 foreach $t (@tables) {
69 $env->QuickScan(sub { /\b\S*?\.smf\b/g }, "$t.smf",
70 $env->{SMF_INCLUDE_PATH});
71 $env->Command(["$t.smdb.cc","$t.smdb.h","$t.snmp.cc",
72 "$t.ami.cc", "$t.http.cc"], "$t.smf",
73 q(smfgen %( %SMF_INCLUDE_OPT %) %<));
77 The subroutine above finds all names of the form <name>.smf in the
78 file. It will return the names even if they're found within comments,
79 but that's OK (the mechanism is forgiving of extra files; they're just
80 ignored on the assumption that the missing file will be noticed when
81 the program, in this example, smfgen, is actually invoked).
83 [NOTE that the form C<$env-E<gt>QuickScan ...> and C<$env-E<gt>Command
84 ...> should not be necessary, but, for some reason, is required
85 for this particular invocation. This appears to be a bug in Perl or
86 a misunderstanding on my part; this invocation style does not always
87 appear to be necessary.]
89 Here is another way to build the same scanner. This one uses an
90 explicit code reference, and also (unnecessarily, in this case) reads
91 the whole file itself:
96 push(@includes, /\b\S*?\.smf\b/g);
101 Note that the order of the loop is reversed, with the loop test at the
102 end. This is because the first line is already read for you. This scanner
103 can be attached to a source file by:
105 QuickScan $env \&myscan, "$_.smf";
107 This final example, which scans a different type of input file, takes
108 over the file scanning rather than being called for each input line:
111 sub { my(@includes) = ();
114 if /^(#include|import)\s+(\")(.+)(\")/ && $3
119 "$env->{CPPPATH};$BUILD/ActiveContext/ACSCLientInterfaces"
126 &SCons; has built-in scanners that know how to look in
127 C, Fortran and IDL source files for information about
128 other files that targets built from those files depend on--for example,
129 in the case of files that use the C preprocessor,
130 the <filename>.h</filename> files that are specified
131 using <literal>#include</literal> lines in the source.
132 You can use the same mechanisms that &SCons; uses to create
133 its built-in scanners to write scanners of your own for file types
134 that &SCons; does not know how to scan "out of the box."
139 <title>A Simple Scanner Example</title>
143 Suppose, for example, that we want to create a simple scanner
144 for <filename>.foo</filename> files.
145 A <filename>.foo</filename> file contains some text that
147 and can include other files on lines that begin
148 with <literal>include</literal>
149 followed by a file name:
159 Scanning a file will be handled by a Python function
160 that you must supply.
161 Here is a function that will use the Python
162 <filename>re</filename> module
163 to scan for the <literal>include</literal> lines in our example:
170 include_re = re.compile(r'^include\s+(\S+)$', re.M)
172 def kfile_scan(node, env, path, arg):
173 contents = node.get_text_contents()
174 return include_re.findall(contents)
179 The scanner function must
180 accept the four specified arguments
181 and return a list of implicit dependencies.
182 Presumably, these would be dependencies found
183 from examining the contents of the file,
184 although the function can perform any
185 manipulation at all to generate the list of
198 An &SCons; node object representing the file being scanned.
199 The path name to the file can be
200 used by converting the node to a string
201 using the <literal>str()</literal> function,
202 or an internal &SCons; <literal>get_text_contents()</literal>
203 object method can be used to fetch the contents.
215 The construction environment in effect for this scan.
216 The scanner function may choose to use construction
217 variables from this environment to affect its behavior.
229 A list of directories that form the search path for included files
231 This is how &SCons; handles the &cv-link-CPPPATH; and &cv-link-LIBPATH;
244 An optional argument that you can choose to
245 have passed to this scanner function by
246 various scanner instances.
256 A Scanner object is created using the &Scanner; function,
257 which typically takes an <literal>skeys</literal> argument
258 to associate the type of file suffix with this scanner.
259 The Scanner object must then be associated with the
260 &cv-link-SCANNERS; construction variable of a construction environment,
261 typically by using the &Append; method:
266 kscan = Scanner(function = kfile_scan,
268 env.Append(SCANNERS = kscan)
273 When we put it all together, it looks like:
280 include_re = re.compile(r'^include\s+(\S+)$', re.M)
282 def kfile_scan(node, env, path):
283 contents = node.get_text_contents()
284 includes = include_re.findall(contents)
287 kscan = Scanner(function = kfile_scan,
290 env = Environment(ENV = {'PATH' : '/usr/local/bin'})
291 env.Append(SCANNERS = kscan)
293 env.Command('foo', 'foo.k', 'kprocess < $SOURCES > $TARGET')
300 Now if we run &scons;
301 and then re-run it after changing the contents of
302 <filename>other_file</filename>,
303 the <filename>foo</filename>
304 target file will be automatically rebuilt:
308 <scons_output example="scan">
309 <scons_output_command>scons -Q</scons_output_command>
310 <scons_output_command output=" [CHANGE THE CONTENTS OF other_file]">edit other_file</scons_output_command>
311 <scons_output_command>scons -Q</scons_output_command>
312 <scons_output_command>scons -Q</scons_output_command>