Fix XML in documentation, and in the bin/scons-doc.py script that generates
[scons.git] / doc / user / scanners.xml
1 <!--
2
3   __COPYRIGHT__
4
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:
12
13   The above copyright notice and this permission notice shall be included
14   in all copies or substantial portions of the Software.
15
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.
23
24 -->
25
26 <!--
27
28 =head1 Using and writing dependency scanners
29
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.
34
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).
38
39 QuickScan is invoked as follows:
40
41   QuickScan CONSENV CODEREF, FILENAME [, PATH]
42
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,
49 ';' on Windows NT).
50
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.
56
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.
63
64 Here's a real example, taken from a F<Construct> file here:
65
66   sub cons::SMFgen {
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 %) %<));
74       }
75   }
76
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).
82
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.]
88
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:
92
93   sub myscan {
94       my(@includes);
95       do {
96           push(@includes, /\b\S*?\.smf\b/g);
97       } while <SCAN>;
98       @includes
99   }
100
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:
104
105   QuickScan $env \&myscan, "$_.smf";
106
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:
109
110   $env->QuickScan(
111       sub { my(@includes) = ();
112           do {
113              push(@includes, $3)
114                  if /^(#include|import)\s+(\")(.+)(\")/ && $3
115           } while <SCAN>;
116           @includes
117       },
118       "$idlFileName",
119       "$env->{CPPPATH};$BUILD/ActiveContext/ACSCLientInterfaces"
120   );
121
122 -->
123
124   <para>
125
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."
135
136   </para>
137
138   <section>
139   <title>A Simple Scanner Example</title>
140
141     <para>
142
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
146       will be processed,
147       and can include other files on lines that begin
148       with <literal>include</literal>
149       followed by a file name:
150
151     </para>
152
153     <programlisting>
154       include filename.foo
155     </programlisting>
156
157     <para>
158
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:
164
165     </para>
166
167     <programlisting>
168       import re
169       
170       include_re = re.compile(r'^include\s+(\S+)$', re.M)
171       
172       def kfile_scan(node, env, path, arg):
173           contents = node.get_text_contents()
174           return include_re.findall(contents)
175     </programlisting>
176
177     <para>
178
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
186       dependencies.
187
188     </para>
189
190     <variablelist>
191
192       <varlistentry>
193       <term>node</term>
194
195       <listitem>
196       <para>
197
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.
204
205       </para>
206       </listitem>
207       </varlistentry>
208
209       <varlistentry>
210       <term>env</term>
211
212       <listitem>
213       <para>
214
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.
218
219       </para>
220       </listitem>
221       </varlistentry>
222
223       <varlistentry>
224       <term>path</term>
225
226       <listitem>
227       <para>
228
229       A list of directories that form the search path for included files
230       for this scanner.
231       This is how &SCons; handles the &cv-link-CPPPATH; and &cv-link-LIBPATH;
232       variables.
233
234       </para>
235       </listitem>
236       </varlistentry>
237
238       <varlistentry>
239       <term>arg</term>
240
241       <listitem>
242       <para>
243
244       An optional argument that you can choose to
245       have passed to this scanner function by
246       various scanner instances.
247
248       </para>
249       </listitem>
250       </varlistentry>
251
252     </variablelist>
253
254     <para>
255
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:
262
263     </para>
264
265     <programlisting>
266        kscan = Scanner(function = kfile_scan,
267                        skeys = ['.k'])
268        env.Append(SCANNERS = kscan)
269     </programlisting>
270
271     <para>
272
273     When we put it all together, it looks like:
274
275     </para>
276
277     <programlisting>
278         import re
279
280         include_re = re.compile(r'^include\s+(\S+)$', re.M)
281
282         def kfile_scan(node, env, path):
283             contents = node.get_text_contents()
284             includes = include_re.findall(contents)
285             return includes
286
287         kscan = Scanner(function = kfile_scan,
288                         skeys = ['.k'])
289
290         env = Environment(ENV = {'PATH' : '/usr/local/bin'})
291         env.Append(SCANNERS = kscan)
292
293         env.Command('foo', 'foo.k', 'kprocess &lt; $SOURCES &gt; $TARGET')
294     </programlisting>
295
296     <!--
297
298     <para>
299
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:
305
306     </para>
307
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>
313     </scons_output>
314
315     -->
316
317   </section>