1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
|
<?xml version='1.0'?>
<!--
SPDX-License-Identifier: MIT
Copyright The SCons Foundation
-->
<!DOCTYPE sconsdoc [
<!ENTITY % scons SYSTEM "../scons.mod">
%scons;
<!ENTITY % builders-mod SYSTEM "../generated/builders.mod">
%builders-mod;
<!ENTITY % functions-mod SYSTEM "../generated/functions.mod">
%functions-mod;
<!ENTITY % tools-mod SYSTEM "../generated/tools.mod">
%tools-mod;
<!ENTITY % variables-mod SYSTEM "../generated/variables.mod">
%variables-mod;
]>
<chapter id="chap-separate"
xmlns="http://www.scons.org/dbxsd/v1.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd">
<title>Separating Source and Build Trees: Variant Directories</title>
<para>
It is often useful to keep built files completely
separate from the source files. Two main benefits are
the ability to have different configurations simultaneously
without build conflicts, and being version-control friendly.
</para>
<para>
Consider if you have a project to build an embedded
software system for a variety of different controller hardware.
The system is able to share a lot of code,
so it makes sense to use a common source tree,
but certain build options in the source code
and header files differ. For a regular in-place build,
the build outputs go in the same place as the source code.
If you build <emphasis>Controller A</emphasis> first,
followed by <emphasis>Controller B</emphasis>,
on the <emphasis>Controller B</emphasis> build everything that
uses different build options has to be rebuilt since those
objects will be different
(the build lines, including preprocessor defines, are part of
&SCons;'s out-of-date calculation for this reason).
If you go back and build for <emphasis>Controller A</emphasis> again,
things have to be rebuilt again for the same reason.
However, if you can separate the locations of the output files,
so each controller has its own location for build outputs,
this problem can be avoided.
</para>
<para>
Having a separated build tree also helps you keep your source tree clean -
there is less chance of accidentally checking in build products
to version control that were not intended to be checked in.
You can add a separated build directory to your
version control system's list of items not to track.
You can even remove the whole build tree with a single command without
risking removing any of the source code.
</para>
<para>
The key to making this separation work is the ability to
do out-of-tree builds: building under a separate root
than the sources being built.
You set up out of tree builds by establishing what &SCons;
calls a <firstterm>variant directory</firstterm>,
a place where you can build a single variant of your software
(of course you can define more than one of these if you need to).
Since &SCons; tracks targets by their path, it is able to distinguish
build products like <filename>build/A/network.obj</filename>
of the <emphasis>Controller A</emphasis> build
from <filename>build/B/network.obj</filename>
of the <emphasis>Controller B</emphasis> build,
thus avoiding conflicts.
</para>
<para>
&SCons; provides two ways to establish variant directories,
one through the &f-link-SConscript; function that we have already seen,
and the second through a more flexible &f-link-VariantDir; function.
</para>
<para>
The variant directory mechanism does support doing multiple builds
in one invocation of &SCons;, but the remainder of this chapter
will focus on setting up a single build. You can combine these
techniques with ones from the previous chapter and elsewhere
in this Guide to set up more complex scenarios.
</para>
<note> <para>
The &VariantDir; function used to be called &BuildDir;,
a name which was changed because it turned out to be confusing:
the &SCons; functionality
differs from a familiar model of a "build directory"
implemented by certain other build systems like GNU Autotools.
You might still find references to the old name on
the Internet in postings about &SCons;, but it no longer works.
</para> </note>
<section id="sect-variant-sconscript">
<title>Specifying a Variant Directory Tree as Part of an &SConscript; Call</title>
<para>
The most straightforward way to establish a variant directory tree
relies on the fact that the usual way to
set up a build hierarchy is to have an
&SConscript; file in the source directory.
If you pass a &variant_dir; argument to the
&f-link-SConscript; function call:
</para>
<scons_example name="separate_ex1">
<file name="SConstruct" printme="1">
SConscript('src/SConscript', variant_dir='build')
</file>
<file name="src/SConscript">
env = Environment()
env.Program('hello.c')
</file>
<file name="src/hello.c">
int main() { printf("Hello, world!\n"); }
</file>
</scons_example>
<para>
&SCons; will then build all of the files in
the &build; directory:
</para>
<scons_output example="separate_ex1" suffix="1">
<scons_output_command>ls src</scons_output_command>
<scons_output_command>scons -Q</scons_output_command>
<scons_output_command>ls src</scons_output_command>
<scons_output_command>ls build</scons_output_command>
</scons_output>
<para>
No files were built in &src;:
the object file
<filename>build/hello.o</filename>
and the executable file
<filename>build/hello</filename>
were built in the &build; directory, as expected.
But notice that even though our &hello_c; file actually
lives in the &src; directory, &SCons; has compiled a
<filename>build/hello.c</filename> file
to create the object file,
and that file is now seen in &build;.
</para>
<para>
You can ask &SCons; to show the dependency tree to illustrate
a bit more:
</para>
<scons_output example="separate_ex1" suffix="2">
<scons_output_command>scons -Q --tree=prune</scons_output_command>
</scons_output>
<para>
What's happened is that &SCons; has <emphasis>duplicated</emphasis>
the &hello_c; file from the &src; directory
to the &build; directory,
and built the program from there (it also duplicated &SConscript;).
The next section explains why &SCons; does this.
</para>
<para>
The nice thing about the &SConscript; approach is it is almost
invisible to you:
this build looks just like an ordinary in-place build
except for the extra &variant_dir; argument in the
&f-link-SConscript; call.
&SCons; handles all the path adjustments for the
out of tree &build; directory while it processes that SConscript file.
</para>
</section>
<section id="sect-variant-duplication">
<title>Why &SCons; Duplicates Source Files in a Variant Directory Tree</title>
<para>
When you set up a variant directory &SCons; conceptually behaves as
if you requested a build in that directory.
As noted in the previous chapter,
all builds actually happen from the top level directory,
but as an aid to understanding how &SCons; operates, think
of it as <emphasis>build in place in the variant directory</emphasis>,
not <emphasis>build in source but send build artifacts
to the variant directory</emphasis>.
It turns out in place builds are easier to get right than out
of tree builds - so by default &SCons; simulates an in place build
by making the variant directory look just like the source directory.
The most straightforward way to do that is by making copies
of the files needed for the build.
</para>
<para>
The most direct reason to duplicate source files
in variant directories
is simply that some tools (mostly older versions)
are written to only build their output files
in the same directory as the source files - such tools often don't
have any option to specify the output file, and the tool just
uses a predefined output file name,
or uses a derived variant of the source file name,
dropping the result in the same directory.
In this case, the choices are either
to build the output file in the source directory
and move it to the variant directory,
or to duplicate the source files in the variant directory.
</para>
<para>
Additionally,
relative references between files
can cause problems which are resolved by
just duplicating the hierarchy of source files
into the variant directory.
You can see this at work in
use of the C preprocessor <literal>#include</literal>
mechanism with double quotes, not angle brackets:
</para>
<sconstruct>
#include "file.h"
</sconstruct>
<para>
The <emphasis>de facto</emphasis> standard behavior
for most C compilers in this case
is to first look in the same directory
as the source file that contains the <literal>#include</literal> line,
then to look in the directories in the preprocessor search path.
Add to this that the &SCons; implementation of
support for code repositories
(described below)
means not all of the files
will be found in the same directory hierarchy,
and the simplest way to make sure
that the right include file is found
is to duplicate the source files into the variant directory,
which provides a correct build
regardless of the original location(s) of the source files.
</para>
<para>
Although source-file duplication guarantees a correct build
even in these edge cases,
it can <emphasis>usually</emphasis> be safely disabled.
The next section describes
how you can disable the duplication of source files
in the variant directory.
</para>
</section>
<section id="sect-variant-no-duplication">
<title>Telling &SCons; to Not Duplicate Source Files in the Variant Directory Tree</title>
<para>
In most cases and with most tool sets,
&SCons; can use sources directly from the source directory
<emphasis>without</emphasis>
duplicating them into the variant directory before building,
and everything will work just fine.
You can disable the default &SCons; duplication behavior
by specifying <literal>duplicate=False</literal>
when you call the &f-link-SConscript; function:
</para>
<sconstruct>
SConscript('src/SConscript', variant_dir='build', duplicate=False)
</sconstruct>
<para>
When this flag is specified, the results of a build
look more like the mental model people may have from other
build systems - that is,
the output files end up in the variant directory
while the source files do not.
</para>
<screen>
% <userinput>ls src</userinput>
SConscript
hello.c
% <userinput>scons -Q</userinput>
cc -c src/hello.c -o build/hello.o
cc -o build/hello build/hello.o
% <userinput>ls build</userinput>
hello
hello.o
</screen>
<para>
If disabling duplication causes any problems,
just return to the more cautious approach by letting
&SCons; go back to duplicating files.
</para>
</section>
<section id="sect-variantdir-function">
<title>The &VariantDir; Function</title>
<para>
You can also use the &f-link-VariantDir; function to establish
that target files should be built in a separate directory tree
from the source files:
</para>
<scons_example name="separate_builddir">
<file name="SConstruct" printme="1">
VariantDir('build', 'src')
env = Environment()
env.Program('build/hello.c')
</file>
<file name="src/hello.c">
int main() { printf("Hello, world!\n"); }
</file>
</scons_example>
<para>
When using this form, you have to tell &SCons; that
sources and targets are in the variant directory,
and those references will trigger the remapping,
necessary file copying, etc. for an already established
variant directory. Here is the same example in a more
spelled out form to show this more clearly:
<programlisting language="python">
VariantDir('build', 'src')
env = Environment()
env.Program(target='build/hello', source=['build/hello.c'])
</programlisting>
</para>
<para>
When using the &VariantDir; function directly,
&SCons; still duplicates the source files
in the variant directory by default:
</para>
<scons_output example="separate_builddir" suffix="1">
<scons_output_command>ls src</scons_output_command>
<scons_output_command>scons -Q</scons_output_command>
<scons_output_command>ls build</scons_output_command>
</scons_output>
<para>
You can specify the same <literal>duplicate=False</literal> argument
that you can specify for an &f-link-SConscript; call:
</para>
<scons_example name="separate_duplicate0">
<file name="SConstruct" printme="1">
VariantDir('build', 'src', duplicate=False)
env = Environment()
env.Program('build/hello.c')
</file>
<file name="src/hello.c">
int main() { printf("Hello, world!\n"); }
</file>
</scons_example>
<para>
In which case &SCons;
will disable duplication of the source files:
</para>
<scons_output example="separate_duplicate0" suffix="1">
<scons_output_command>ls src</scons_output_command>
<scons_output_command>scons -Q</scons_output_command>
<scons_output_command>ls build</scons_output_command>
</scons_output>
</section>
<section id="sect-variantdir-sconscript">
<title>Using &VariantDir; With an &SConscript; File</title>
<para>
Even when using the &f-link-VariantDir; function,
it is more natural to use it with
a subsidiary &SConscript; file,
because then you don't have to adjust your individual
build instructions to use the variant directory path.
For example, if the
<filename>src/SConscript</filename>
looks like this:
</para>
<scons_example name="separate_builddir_sconscript">
<file name="SConstruct">
VariantDir('build', 'src')
SConscript('build/SConscript')
</file>
<file name="src/SConscript" printme="1">
env = Environment()
env.Program('hello.c')
</file>
<file name="src/hello.c">
int main() { printf("Hello, world!\n"); }
</file>
</scons_example>
<para>
Then our &SConstruct; file could look like:
</para>
<scons_example_file example="separate_builddir_sconscript" name="SConstruct">
</scons_example_file>
<para>
Yielding the following output:
</para>
<scons_output example="separate_builddir_sconscript" suffix="1">
<scons_output_command>ls src</scons_output_command>
<scons_output_command>scons -Q</scons_output_command>
<scons_output_command>ls build</scons_output_command>
</scons_output>
<para>
This is completely equivalent
to the use of &f-link-SConscript; with the
<parameter>variant_dir</parameter> argument
from earlier in this chapter,
but did require callng the SConscript using the already established
variant directory path to trigger that behavior.
If you call <userinput>SConscript('src/SConscript')</userinput>
you would get a normal in-place build in &src;.
</para>
</section>
<section id="sect-variantdir-glob">
<title>Using &Glob; with &VariantDir;</title>
<para>
The &f-link-Glob; file name pattern matching function
works just as usual when using &f-link-VariantDir;.
For example, if the
<filename>src/SConscript</filename>
looks like this:
</para>
<scons_example name="separate_glob_builddir_sconscript">
<file name="SConstruct">
VariantDir('build', 'src')
SConscript('build/SConscript')
</file>
<file name="src/SConscript" printme="1">
env = Environment()
env.Program('hello', Glob('*.c'))
</file>
<file name="src/f1.c">
#include "f2.h"
int main() { printf(f2()); }
</file>
<file name="src/f2.c">
const char * f2() { return("Hello, world!\n"); }
</file>
<file name="src/f2.h">
const char * f2();
</file>
</scons_example>
<para>
Then with the same &SConstruct; file as in the previous section,
and source files <filename>f1.c</filename>
and <filename>f2.c</filename> in src,
we would see the following output:
</para>
<scons_output example="separate_glob_builddir_sconscript" suffix="1">
<scons_output_command>ls src</scons_output_command>
<scons_output_command>scons -Q</scons_output_command>
<scons_output_command>ls build</scons_output_command>
</scons_output>
<para>
The &Glob; function returns Nodes in the
<filename>build/</filename> tree, as you'd expect.
</para>
</section>
<!--
<section id="sect-variantdir-over-sconscript">
<title>Why You'd Want to Call &VariantDir; Instead of &SConscript;</title>
<para>
XXX why call VariantDir() instead of SConscript(variant_dir=)
</para>
</section>
-->
<xi:include xmlns:xi="http://www.w3.org/2001/XInclude" href="variants.xml"/>
</chapter>
|