http://scons.tigris.org/issues/show_bug.cgi?id=2345
[scons.git] / doc / user / repositories.in
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   <para>
27
28   Often, a software project will have
29   one or more central repositories,
30   directory trees that contain
31   source code, or derived files, or both.
32   You can eliminate additional unnecessary
33   rebuilds of files by having &SCons;
34   use files from one or more code repositories
35   to build files in your local build tree.
36
37   </para>
38
39   <section>
40   <title>The &Repository; Method</title>
41
42  <!--
43
44  The repository directories specified may contain source files, derived files
45  (objects, libraries and executables), or both.  If there is no local file
46  (source or derived) under the directory in which Cons is executed, then the
47  first copy of a same-named file found under a repository directory will be
48  used to build any local derived files.
49
50  -->
51
52     <para>
53
54     It's often useful to allow multiple programmers working
55     on a project to build software from
56     source files and/or derived files that
57     are stored in a centrally-accessible repository,
58     a directory copy of the source code tree.
59     (Note that this is not the sort of repository
60     maintained by a source code management system
61     like BitKeeper, CVS, or Subversion.)
62     <!--
63     For information about using &SCons;
64     with these systems, see the section,
65     "Fetching Files From Source Code Management Systems,"
66     below.)
67     -->
68     You use the &Repository; method
69     to tell &SCons; to search one or more
70     central code repositories (in order)
71     for any source files and derived files
72     that are not present in the local build tree:
73
74     </para>
75
76     <scons_example name="ex1">
77       <file name="SConstruct" printme="1">
78        env = Environment()
79        env.Program('hello.c')
80        Repository('__ROOT__/usr/repository1', '__ROOT__/usr/repository2')
81       </file>
82       <file name="hello.c">
83       int main() { printf("Hello, world!\n"); }
84       </file>
85     </scons_example>
86
87     <para>
88
89     Multiple calls to the &Repository; method
90     will simply add repositories to the global list
91     that &SCons; maintains,
92     with the exception that &SCons; will automatically eliminate
93     the current directory and any non-existent
94     directories from the list.
95
96     </para>
97
98   </section>
99
100   <section>
101   <title>Finding source files in repositories</title>
102
103     <para>
104
105     The above example
106     specifies that &SCons;
107     will first search for files under
108     the <filename>/usr/repository1</filename> tree
109     and next under the <filename>/usr/repository2</filename> tree.
110     &SCons; expects that any files it searches
111     for will be found in the same position
112     relative to the top-level directory.
113     In the above example, if the &hello_c; file is not
114     found in the local build tree,
115     &SCons; will search first for
116     a <filename>/usr/repository1/hello.c</filename> file
117     and then for a <filename>/usr/repository2/hello.c</filename> file
118     to use in its place.
119
120     </para>
121
122     <para>
123
124     So given the &SConstruct; file above,
125     if the &hello_c; file exists in the local
126     build directory,
127     &SCons; will rebuild the &hello; program
128     as normal:
129
130     </para>
131
132     <scons_output example="ex1">
133       <scons_output_command>scons -Q</scons_output_command>
134     </scons_output>
135
136     <para>
137
138     If, however, there is no local &hello_c; file,
139     but one exists in <filename>/usr/repository1</filename>,
140     &SCons; will recompile the &hello; program
141     from the source file it finds in the repository:
142
143     </para>
144
145     <scons_example name="ex2">
146       <file name="SConstruct">
147        env = Environment()
148        env.Program('hello.c')
149        Repository('__ROOT__/usr/repository1', '__ROOT__/usr/repository2')
150       </file>
151       <file name="__ROOT__/usr/repository1/hello.c">
152       int main() { printf("Hello, world!\n"); }
153       </file>
154     </scons_example>
155
156     <scons_output example="ex2">
157       <scons_output_command>scons -Q</scons_output_command>
158     </scons_output>
159
160     <para>
161
162     And similarly, if there is no local &hello_c; file
163     and no <filename>/usr/repository1/hello.c</filename>,
164     but one exists in <filename>/usr/repository2</filename>:
165
166     </para>
167
168     <scons_example name="ex3">
169       <file name="SConstruct">
170        env = Environment()
171        env.Program('hello.c')
172        Repository('__ROOT__/usr/repository1', '__ROOT__/usr/repository2')
173       </file>
174       <file name="__ROOT__/usr/repository2/hello.c">
175       int main() { printf("Hello, world!\n"); }
176       </file>
177     </scons_example>
178
179     <scons_output example="ex3">
180       <scons_output_command>scons -Q</scons_output_command>
181     </scons_output>
182
183     <para>
184
185     </para>
186
187   </section>
188
189   <section>
190   <title>Finding <literal>#include</literal> files in repositories</title>
191
192     <para>
193
194     We've already seen that SCons will scan the contents of
195     a source file for <literal>#include</literal> file names
196     and realize that targets built from that source file
197     also depend on the <literal>#include</literal> file(s).
198     For each directory in the &cv-link-CPPPATH; list,
199     &SCons; will actually search the corresponding directories
200     in any repository trees and establish the
201     correct dependencies on any
202     <literal>#include</literal> files that it finds
203     in repository directory.
204
205     </para>
206
207     <para>
208
209     Unless the C compiler also knows about these directories
210     in the repository trees, though,
211     it will be unable to find the <literal>#include</literal> files.
212     If, for example, the &hello_c; file in
213     our previous example includes the &hello.h;
214     in its current directory,
215     and the &hello.h; only exists in the repository:
216
217     </para>
218
219     <screen>
220       % <userinput>scons -Q</userinput>
221       cc -o hello.o -c hello.c
222       hello.c:1: hello.h: No such file or directory
223     </screen>
224
225     <para>
226
227     In order to inform the C compiler about the repositories,
228     &SCons; will add appropriate
229     <literal>-I</literal> flags to the compilation commands
230     for each directory in the &cv-CPPPATH; list.
231     So if we add the current directory to the
232     construction environment &cv-CPPPATH; like so:
233
234     </para>
235
236     <scons_example name="CPPPATH">
237       <file name="SConstruct" printme="1">
238        env = Environment(CPPPATH = ['.'])
239        env.Program('hello.c')
240        Repository('__ROOT__/usr/repository1')
241       </file>
242       <file name="hello.c">
243       int main() { printf("Hello, world!\n"); }
244       </file>
245     </scons_example>
246
247     <para>
248
249     Then re-executing &SCons; yields:
250
251     </para>
252
253     <scons_output example="CPPPATH">
254       <scons_output_command>scons -Q</scons_output_command>
255     </scons_output>
256
257     <para>
258
259     The order of the <literal>-I</literal> options replicates,
260     for the C preprocessor,
261     the same repository-directory search path
262     that &SCons; uses for its own dependency analysis.
263     If there are multiple repositories and multiple &cv-CPPPATH;
264     directories, &SCons; will add the repository directories
265     to the beginning of each &cv-CPPPATH; directory,
266     rapidly multiplying the number of <literal>-I</literal> flags.
267     If, for example, the &cv-CPPPATH; contains three directories
268     (and shorter repository path names!):
269
270     </para>
271
272     <scons_example name="CPPPATH3">
273       <file name="SConstruct" printme="1">
274        env = Environment(CPPPATH = ['dir1', 'dir2', 'dir3'])
275        env.Program('hello.c')
276        Repository('__ROOT__/r1', '__ROOT__/r2')
277       </file>
278       <file name="hello.c">
279       int main() { printf("Hello, world!\n"); }
280       </file>
281     </scons_example>
282
283     <para>
284
285     Then we'll end up with nine <literal>-I</literal> options
286     on the command line,
287     three (for each of the &cv-CPPPATH; directories)
288     times three (for the local directory plus the two repositories):
289
290     </para>
291
292     <scons_output example="CPPPATH3">
293       <scons_output_command>scons -Q</scons_output_command>
294     </scons_output>
295
296 <!--
297
298 Cons classic did the following, does SCons?
299
300 In order to shorten the command lines as much as possible, Cons will
301 remove C<-I> flags for any directories, locally or in the repositories,
302 which do not actually exist.  (Note that the C<-I> flags are not included
303 in the MD5 signature calculation for the target file, so the target will
304 not be recompiled if the compilation command changes due to a directory
305 coming into existence.)
306
307 -->
308
309     <section>
310     <title>Limitations on <literal>#include</literal> files in repositories</title>
311
312       <para>
313
314       &SCons; relies on the C compiler's
315       <literal>-I</literal> options to control the order in which
316       the preprocessor will search the repository directories
317       for <literal>#include</literal> files.
318       This causes a problem, however, with how the C preprocessor
319       handles <literal>#include</literal> lines with
320       the file name included in double-quotes.
321
322       </para>
323
324       <para>
325
326       As we've seen,
327       &SCons; will compile the &hello_c; file from
328       the repository if it doesn't exist in
329       the local directory.
330       If, however, the &hello_c; file in the repository contains
331       a <literal>#include</literal> line with the file name in
332       double quotes:
333
334       </para>
335
336       <programlisting>
337         #include "hello.h"
338         int
339         main(int argc, char *argv[])
340         {
341             printf(HELLO_MESSAGE);
342             return (0);
343         }
344       </programlisting>
345
346       <para>
347
348       Then the C preprocessor will <emphasis>always</emphasis>
349       use a &hello_h; file from the repository directory first,
350       even if there is a &hello_h; file in the local directory,
351       despite the fact that the command line specifies
352       <literal>-I</literal> as the first option:
353
354       </para>
355
356       <scons_example name="quote1">
357         <file name="SConstruct">
358          env = Environment(CPPPATH = ['.'])
359          env.Program('hello.c')
360          Repository('__ROOT__/usr/repository1')
361         </file>
362         <file name="__ROOT__/usr/repository1/hello.c">
363         int main() { printf("Hello, world!\n"); }
364         </file>
365       </scons_example>
366
367       <scons_output example="quote1">
368         <scons_output_command>scons -Q</scons_output_command>
369       </scons_output>
370
371       <para>
372
373       This behavior of the C preprocessor--always search
374       for a <literal>#include</literal> file in double-quotes
375       first in the same directory as the source file,
376       and only then search the <literal>-I</literal>--can
377       not, in general, be changed.
378       In other words, it's a limitation
379       that must be lived with if you want to use
380       code repositories in this way.
381       There are three ways you can possibly
382       work around this C preprocessor behavior:
383
384       </para>
385
386       <orderedlist>
387
388         <listitem>
389         <para>
390
391         Some modern versions of C compilers do have an option
392         to disable or control this behavior.
393         If so, add that option to &cv-link-CFLAGS;
394         (or &cv-link-CXXFLAGS; or both) in your construction environment(s).
395         Make sure the option is used for all construction
396         environments that use C preprocessing!
397
398         </para>
399         </listitem>
400
401         <listitem>
402         <para>
403
404         Change all occurrences of <literal>#include "file.h"</literal>
405         to <literal>#include &amp;lt;file.h&amp;gt;</literal>.
406         Use of <literal>#include</literal> with angle brackets
407         does not have the same behavior--the <literal>-I</literal>
408         directories are searched first
409         for <literal>#include</literal> files--which
410         gives &SCons; direct control over the list of
411         directories the C preprocessor will search.
412
413         </para>
414         </listitem>
415
416         <listitem>
417         <para>
418
419         Require that everyone working with compilation from
420         repositories check out and work on entire directories of files,
421         not individual files.
422         (If you use local wrapper scripts around
423         your source code control system's command,
424         you could add logic to enforce this restriction there.
425
426         </para>
427         </listitem>
428
429       </orderedlist>
430
431     </section>
432
433   </section>
434
435   <section>
436   <title>Finding the &SConstruct; file in repositories</title>
437
438     <para>
439
440     &SCons; will also search in repositories
441     for the &SConstruct; file and any specified &SConscript; files.
442     This poses a problem, though:  how can &SCons; search a
443     repository tree for an &SConstruct; file
444     if the &SConstruct; file itself contains the information
445     about the pathname of the repository?
446     To solve this problem, &SCons; allows you
447     to specify repository directories
448     on the command line using the <literal>-Y</literal> option:
449
450     </para>
451
452     <screen>
453       % <userinput>scons -Q -Y /usr/repository1 -Y /usr/repository2</userinput>
454     </screen>
455
456     <para>
457
458     When looking for source or derived files,
459     &SCons; will first search the repositories
460     specified on the command line,
461     and then search the repositories
462     specified in the &SConstruct; or &SConscript; files.
463
464     </para>
465
466   </section>
467
468   <section>
469   <title>Finding derived files in repositories</title>
470
471     <para>
472
473     If a repository contains not only source files,
474     but also derived files (such as object files,
475     libraries, or executables), &SCons; will perform
476     its normal MD5 signature calculation to
477     decide if a derived file in a repository is up-to-date,
478     or the derived file must be rebuilt in the local build directory.
479     For the &SCons; signature calculation to work correctly,
480     a repository tree must contain the &sconsign; files
481     that &SCons; uses to keep track of signature information.
482
483     </para>
484
485     <para>
486
487     Usually, this would be done by a build integrator
488     who would run &SCons; in the repository
489     to create all of its derived files and &sconsign; files,
490     or who would run &SCons; in a separate build directory
491     and copy the resulting tree to the desired repository:
492
493     </para>
494
495     <scons_example name="ex4">
496       <file name="SConstruct">
497        env = Environment()
498        env.Program(['hello.c', 'file1.c', 'file2.c'])
499        Repository('/usr/repository1', '/usr/repository2')
500       </file>
501       <file name="hello.c">
502       int main() { printf("Hello, world!\n"); }
503       </file>
504       <file name="file1.c">
505       int f1() { printf("file1\n"); }
506       </file>
507       <file name="file2.c">
508       int f2() { printf("file2.c\n"); }
509       </file>
510     </scons_example>
511
512     <scons_output example="ex4">
513       <scons_output_command>cd /usr/repository1</scons_output_command>
514       <scons_output_command>scons -Q</scons_output_command>
515     </scons_output>
516
517     <para>
518     
519     (Note that this is safe even if the &SConstruct; file
520     lists <filename>/usr/repository1</filename> as a repository,
521     because &SCons; will remove the current build directory
522     from its repository list for that invocation.)
523
524     </para>
525
526     <para>
527
528     Now, with the repository populated,
529     we only need to create the one local source file
530     we're interested in working with at the moment,
531     and use the <literal>-Y</literal> option to
532     tell &SCons; to fetch any other files it needs
533     from the repository:
534
535     </para>
536
537     <!--
538     <scons_output example="ex4">
539       <scons_output_command>cd $HOME/build</scons_output_command>
540       <scons_output_command>edit hello.c</scons_output_command>
541       <scons_output_command>scons -Q -Y __ROOT__/usr/repository1</scons_output_command>
542     </scons_output>
543     -->
544     <screen>
545       % <userinput>cd $HOME/build</userinput>
546       % <userinput>edit hello.c</userinput>
547       % <userinput>scons -Q -Y /usr/repository1</userinput>
548       cc -c -o hello.o hello.c
549       cc -o hello hello.o /usr/repository1/file1.o /usr/repository1/file2.o
550     </screen>
551
552     <para>
553
554     Notice that &SCons; realizes that it does not need to
555     rebuild local copies <filename>file1.o</filename> and <filename>file2.o</filename> files,
556     but instead uses the already-compiled files
557     from the repository.
558
559     </para>
560
561   </section>
562
563   <section>
564   <title>Guaranteeing local copies of files</title>
565
566     <para>
567
568     If the repository tree contains the complete results of a build,
569     and we try to build from the repository
570     without any files in our local tree,
571     something moderately surprising happens:
572
573     </para>
574
575     <screen>
576       % <userinput>mkdir $HOME/build2</userinput>
577       % <userinput>cd $HOME/build2</userinput>
578       % <userinput>scons -Q -Y /usr/all/repository hello</userinput>
579       scons: `hello' is up-to-date.
580     </screen>
581
582     <para>
583
584     Why does &SCons; say that the &hello; program
585     is up-to-date when there is no &hello; program
586     in the local build directory?
587     Because the repository (not the local directory)
588     contains the up-to-date &hello; program,
589     and &SCons; correctly determines that nothing
590     needs to be done to rebuild that
591     up-to-date copy of the file.
592
593     </para>
594
595     <para>
596
597     There are, however, many times when you want to ensure that a
598     local copy of a file always exists.
599     A packaging or testing script, for example,
600     may assume that certain generated files exist locally.
601     To tell &SCons; to make a copy of any up-to-date repository
602     file in the local build directory,
603     use the &Local; function:
604
605     </para>
606
607     <scons_example name="ex5">
608       <file name="SConstruct" printme="1">
609        env = Environment()
610        hello = env.Program('hello.c')
611        Local(hello)
612       </file>
613       <file name="hello.c">
614       int main() { printf("Hello, world!\n"); }
615       </file>
616     </scons_example>
617
618     <para>
619
620     If we then run the same command,
621     &SCons; will make a local copy of the program
622     from the repository copy,
623     and tell you that it is doing so:
624
625     </para>
626
627     <screen>
628       % <userinput>scons -Y /usr/all/repository hello</userinput>
629       Local copy of hello from /usr/all/repository/hello
630       scons: `hello' is up-to-date.
631     </screen>
632
633     <para>
634
635     (Notice that, because the act of making the local copy
636     is not considered a "build" of the &hello; file,
637     &SCons; still reports that it is up-to-date.)
638
639     </para>
640
641   </section>