diff options
author | Steven Knight <knight@baldmt.com> | 2003-08-16 04:19:30 (GMT) |
---|---|---|
committer | Steven Knight <knight@baldmt.com> | 2003-08-16 04:19:30 (GMT) |
commit | 9d21228a092cc048be6e60053d0ed739eec5b629 (patch) | |
tree | d2447f2650bf7782e58826ad0c16364496722d1c | |
parent | 601839d06d9563854ce22a615d6670a7651cd858 (diff) | |
download | SCons-9d21228a092cc048be6e60053d0ed739eec5b629.zip SCons-9d21228a092cc048be6e60053d0ed739eec5b629.tar.gz SCons-9d21228a092cc048be6e60053d0ed739eec5b629.tar.bz2 |
Branch for User's Guide changes.
37 files changed, 9192 insertions, 27 deletions
@@ -878,11 +878,9 @@ SConscript('etc/SConscript') # # Documentation. # -BuildDir('build/doc', 'doc') - Export('env', 'whereis') -SConscript('build/doc/SConscript') +SConscript('doc/SConscript') # # If we're running in the actual Aegis project, pack up a complete diff --git a/bin/sconsexamples.py b/bin/sconsexamples.py new file mode 100644 index 0000000..5fbdc41 --- /dev/null +++ b/bin/sconsexamples.py @@ -0,0 +1,502 @@ +#!/usr/bin/env python2 +# +# scons_examples.py - an SGML preprocessor for capturing SCons output +# and inserting into examples in our DocBook +# documentation +# + +# This script looks for some SGML tags that describe SCons example +# configurations and commands to execute in those configurations, and +# uses TestCmd.py to execute the commands and insert the output into +# the output SGML. This way, we can run a script and update all of +# our example output without having to do a lot of laborious by-hand +# checking. +# +# An "SCons example" looks like this, and essentially describes a set of +# input files (program source files as well as SConscript files): +# +# <scons_example name="ex1"> +# <file name="SConstruct" printme="1"> +# env = Environment() +# env.Program('foo') +# </file> +# <file name="foo.c"> +# int main() { printf("foo.c\n"); } +# </file> +# </scons_example> +# +# The <file> contents within the <scons_example> tag will get written +# into a temporary directory whenever example output needs to be +# generated. By default, the <file> contents are not inserted into text +# directly, unless you set the "printme" attribute on one or more files, +# in which case they will get inserted within a <programlisting> tag. +# This makes it easy to define the example at the appropriate +# point in the text where you intend to show the SConstruct file. +# +# Note that you should usually give the <scons_example> a "name" +# attribute so that you can refer to the example configuration later to +# run SCons and generate output. +# +# If you just want to show a file's contents without worry about running +# SCons, there's a shorter <sconstruct> tag: +# +# <sconstruct> +# env = Environment() +# env.Program('foo') +# </sconstruct> +# +# This is essentially equivalent to <scons_example><file printme="1">, +# but it's more straightforward. +# +# SCons output is generated from the following sort of tag: +# +# <scons_output example="ex1" os="posix"> +# <command>scons -Q foo</command> +# <command>scons -Q foo</command> +# </scons_output> +# +# You tell it which example to use with the "example" attribute, and +# then give it a list of <command> tags to execute. You can also supply +# an "os" tag, which specifies the type of operating system this example +# is intended to show; if you omit this, default value is "posix". +# +# The generated SGML will show the command line (with the appropriate +# command-line prompt for the operating system), execute the command in +# a temporary directory with the example files, capture the standard +# output from SCons, and insert it into the text as appropriate. +# Error output gets passed through to your error output so you +# can see if there are any problems executing the command. +# + +import os +import os.path +import re +import sgmllib +import string +import sys + +sys.path.append(os.path.join(os.getcwd(), 'etc')) +sys.path.append(os.path.join(os.getcwd(), 'build', 'etc')) + +scons_py = os.path.join('bootstrap', 'src', 'script', 'scons.py') +if not os.path.exists(scons_py): + scons_py = os.path.join('src', 'script', 'scons.py') + +scons_lib_dir = os.path.join(os.getcwd(), 'bootstrap', 'src', 'engine') +if not os.path.exists(scons_lib_dir): + scons_lib_dir = os.path.join(os.getcwd(), 'src', 'engine') + +import TestCmd + +# The regular expression that identifies entity references in the +# standard sgmllib omits the underscore from the legal characters. +# Override it with our own regular expression that adds underscore. +sgmllib.entityref = re.compile('&([a-zA-Z][-_.a-zA-Z0-9]*)[^-_a-zA-Z0-9]') + +class DataCollector: + """Generic class for collecting data between a start tag and end + tag. We subclass for various types of tags we care about.""" + def __init__(self): + self.data = "" + def afunc(self, data): + self.data = self.data + data + +class Example(DataCollector): + """An SCons example. This is essentially a list of files that + will get written to a temporary directory to collect output + from one or more SCons runs.""" + def __init__(self): + DataCollector.__init__(self) + self.files = [] + self.dirs = [] + +class File(DataCollector): + """A file, that will get written out to a temporary directory + for one or more SCons runs.""" + def __init__(self, name): + DataCollector.__init__(self) + self.name = name + +class Directory(DataCollector): + """A directory, that will get created in a temporary directory + for one or more SCons runs.""" + def __init__(self, name): + DataCollector.__init__(self) + self.name = name + +class Output(DataCollector): + """Where the command output goes. This is essentially + a list of commands that will get executed.""" + def __init__(self): + DataCollector.__init__(self) + self.commandlist = [] + +class Command(DataCollector): + """A tag for where the command output goes. This is essentially + a list of commands that will get executed.""" + pass + +Prompt = { + 'posix' : '% ', + 'win32' : 'C:\\>' +} + +# Magick SCons hackery. +# +# So that our examples can still use the default SConstruct file, we +# actually feed the following into SCons via stdin and then have it +# SConscript() the SConstruct file. This stdin wrapper creates a set +# of ToolSurrogates for the tools for the appropriate platform. These +# Surrogates print output like the real tools and behave like them +# without actually having to be on the right platform or have the right +# tool installed. +# +# The upshot: We transparently change the world out from under the +# top-level SConstruct file in an example just so we can get the +# command output. + +Stdin = """\ +import SCons.Defaults + +platform = '%s' + +class Curry: + def __init__(self, fun, *args, **kwargs): + self.fun = fun + self.pending = args[:] + self.kwargs = kwargs.copy() + + def __call__(self, *args, **kwargs): + if kwargs and self.kwargs: + kw = self.kwargs.copy() + kw.update(kwargs) + else: + kw = kwargs or self.kwargs + + return apply(self.fun, self.pending + args, kw) + +def Str(target, source, env, cmd=""): + return env.subst(cmd, target=target, source=source) + +class ToolSurrogate: + def __init__(self, tool, variable, func): + self.tool = tool + self.variable = variable + self.func = func + def __call__(self, env): + t = Tool(self.tool) + t.generate(env) + orig = env[self.variable] + env[self.variable] = Action(self.func, strfunction=Curry(Str, cmd=orig)) + +def Null(target, source, env): + pass + +def Cat(target, source, env): + target = str(target[0]) + f = open(target, "wb") + for src in map(str, source): + f.write(open(src, "rb").read()) + f.close() + +ToolList = { + 'posix' : [('cc', 'CCCOM', Cat), + ('link', 'LINKCOM', Cat), + ('tar', 'TARCOM', Null), + ('zip', 'ZIPCOM', Null)], + 'win32' : [('msvc', 'CCCOM', Cat), + ('mslink', 'LINKCOM', Cat)] +} + +tools = map(lambda t: apply(ToolSurrogate, t), ToolList[platform]) + +SCons.Defaults.ConstructionEnvironment.update({ + 'PLATFORM' : platform, + 'TOOLS' : tools, +}) + +SConscript('SConstruct') +""" + +class MySGML(sgmllib.SGMLParser): + """A subclass of the standard Python 2.2 sgmllib SGML parser. + + Note that this doesn't work with the 1.5.2 sgmllib module, because + that didn't have the ability to work with ENTITY declarations. + """ + def __init__(self): + sgmllib.SGMLParser.__init__(self) + self.examples = {} + self.afunclist = [] + + def handle_data(self, data): + try: + f = self.afunclist[-1] + except IndexError: + sys.stdout.write(data) + else: + f(data) + + def handle_comment(self, data): + sys.stdout.write('<!--' + data + '-->') + + def handle_decl(self, data): + sys.stdout.write('<!' + data + '>') + + def unknown_starttag(self, tag, attrs): + try: + f = self.example.afunc + except AttributeError: + f = sys.stdout.write + if not attrs: + f('<' + tag + '>') + else: + f('<' + tag) + for name, value in attrs: + f(' ' + name + '=' + '"' + value + '"') + f('>') + + def unknown_endtag(self, tag): + sys.stdout.write('</' + tag + '>') + + def unknown_entityref(self, ref): + sys.stdout.write('&' + ref + ';') + + def unknown_charref(self, ref): + sys.stdout.write('&#' + ref + ';') + + def start_scons_example(self, attrs): + t = filter(lambda t: t[0] == 'name', attrs) + if t: + name = t[0][1] + try: + e = self.examples[name] + except KeyError: + e = self.examples[name] = Example() + else: + e = Example() + for name, value in attrs: + setattr(e, name, value) + self.e = e + self.afunclist.append(e.afunc) + + def end_scons_example(self): + e = self.e + files = filter(lambda f: f.printme, e.files) + if files: + sys.stdout.write('<programlisting>') + for f in files: + if f.printme: + i = len(f.data) - 1 + while f.data[i] == ' ': + i = i - 1 + output = string.replace(f.data[:i+1], '__ROOT__', '') + sys.stdout.write(output) + if e.data and e.data[0] == '\n': + e.data = e.data[1:] + sys.stdout.write(e.data + '</programlisting>') + delattr(self, 'e') + self.afunclist = self.afunclist[:-1] + + def start_file(self, attrs): + try: + e = self.e + except AttributeError: + self.error("<file> tag outside of <scons_example>") + t = filter(lambda t: t[0] == 'name', attrs) + if not t: + self.error("no <file> name attribute found") + try: + e.prefix + except AttributeError: + e.prefix = e.data + e.data = "" + f = File(t[0][1]) + f.printme = None + for name, value in attrs: + setattr(f, name, value) + e.files.append(f) + self.afunclist.append(f.afunc) + + def end_file(self): + self.e.data = "" + self.afunclist = self.afunclist[:-1] + + def start_directory(self, attrs): + try: + e = self.e + except AttributeError: + self.error("<directory> tag outside of <scons_example>") + t = filter(lambda t: t[0] == 'name', attrs) + if not t: + self.error("no <directory> name attribute found") + try: + e.prefix + except AttributeError: + e.prefix = e.data + e.data = "" + d = Directory(t[0][1]) + for name, value in attrs: + setattr(d, name, value) + e.dirs.append(d) + self.afunclist.append(d.afunc) + + def end_directory(self): + self.e.data = "" + self.afunclist = self.afunclist[:-1] + + def start_scons_example_file(self, attrs): + t = filter(lambda t: t[0] == 'example', attrs) + if not t: + self.error("no <scons_example_file> example attribute found") + exname = t[0][1] + try: + e = self.examples[exname] + except KeyError: + self.error("unknown example name '%s'" % exname) + fattrs = filter(lambda t: t[0] == 'name', attrs) + if not fattrs: + self.error("no <scons_example_file> name attribute found") + fname = fattrs[0][1] + f = filter(lambda f, fname=fname: f.name == fname, e.files) + if not f: + self.error("example '%s' does not have a file named '%s'" % (exname, fname)) + self.f = f[0] + + def end_scons_example_file(self): + f = self.f + sys.stdout.write('<programlisting>') + i = len(f.data) - 1 + while f.data[i] == ' ': + i = i - 1 + sys.stdout.write(f.data[:i+1] + '</programlisting>') + delattr(self, 'f') + + def start_scons_output(self, attrs): + t = filter(lambda t: t[0] == 'example', attrs) + if not t: + self.error("no <scons_output> example attribute found") + exname = t[0][1] + try: + e = self.examples[exname] + except KeyError: + self.error("unknown example name '%s'" % exname) + # Default values for an example. + o = Output() + o.os = 'posix' + o.e = e + # Locally-set. + for name, value in attrs: + setattr(o, name, value) + self.o = o + self.afunclist.append(o.afunc) + + def end_scons_output(self): + o = self.o + e = o.e + t = TestCmd.TestCmd(workdir='', combine=1) + t.subdir('ROOT', 'WORK') + for d in e.dirs: + dir = t.workpath('WORK', d.name) + if not os.path.exists(dir): + os.makedirs(dir) + for f in e.files: + i = 0 + while f.data[i] == '\n': + i = i + 1 + lines = string.split(f.data[i:], '\n') + i = 0 + while lines[0][i] == ' ': + i = i + 1 + lines = map(lambda l, i=i: l[i:], lines) + path = string.replace(f.name, '__ROOT__', t.workpath('ROOT')) + dir, name = os.path.split(f.name) + if dir: + dir = t.workpath('WORK', dir) + if not os.path.exists(dir): + os.makedirs(dir) + content = string.join(lines, '\n') + content = string.replace(content, + '__ROOT__', + t.workpath('ROOT')) + t.write(t.workpath('WORK', f.name), content) + i = len(o.prefix) + while o.prefix[i-1] != '\n': + i = i - 1 + sys.stdout.write('<literallayout>' + o.prefix[:i]) + p = o.prefix[i:] + for c in o.commandlist: + sys.stdout.write(p + Prompt[o.os]) + d = string.replace(c.data, '__ROOT__', '') + sys.stdout.write('<userinput>' + d + '</userinput>\n') + e = string.replace(c.data, '__ROOT__', t.workpath('ROOT')) + args = string.split(e)[1:] + os.environ['SCONS_LIB_DIR'] = scons_lib_dir + t.run(interpreter = sys.executable, + program = scons_py, + arguments = '-f - ' + string.join(args), + chdir = t.workpath('WORK'), + stdin = Stdin % o.os) + out = string.replace(t.stdout(), t.workpath('ROOT'), '') + if out: + lines = string.split(out, '\n') + if lines: + while lines[-1] == '': + lines = lines[:-1] + for l in lines: + sys.stdout.write(p + l + '\n') + #err = t.stderr() + #if err: + # sys.stderr.write(err) + if o.data[0] == '\n': + o.data = o.data[1:] + sys.stdout.write(o.data + '</literallayout>') + delattr(self, 'o') + self.afunclist = self.afunclist[:-1] + + def start_command(self, attrs): + try: + o = self.o + except AttributeError: + self.error("<command> tag outside of <scons_output>") + try: + o.prefix + except AttributeError: + o.prefix = o.data + o.data = "" + c = Command() + o.commandlist.append(c) + self.afunclist.append(c.afunc) + + def end_command(self): + self.o.data = "" + self.afunclist = self.afunclist[:-1] + + def start_sconstruct(self, attrs): + sys.stdout.write('<programlisting>') + + def end_sconstruct(self): + sys.stdout.write('</programlisting>') + +try: + file = sys.argv[1] +except IndexError: + file = '-' + +if file == '-': + f = sys.stdin +else: + try: + f = open(file, 'r') + except IOError, msg: + print file, ":", msg + sys.exit(1) + +data = f.read() +if f is not sys.stdin: + f.close() + +x = MySGML() +for c in data: + x.feed(c) +x.close() diff --git a/doc/SConscript b/doc/SConscript index 25ad6bf..e3b6eff 100644 --- a/doc/SConscript +++ b/doc/SConscript @@ -31,6 +31,8 @@ import string Import('env', 'whereis') +build = os.path.join('#build', 'doc') + # # # @@ -53,7 +55,7 @@ jw = whereis('jw') tidy = whereis('tidy') tar_deps = [] -tar_list = "" +tar_list = [] entity_re = re.compile(r'<!entity\s+(?:%\s+)?(?:\S+)\s+SYSTEM\s+"([^"]*)">', re.I) format_re = re.compile(r'<(?:graphic|imagedata)\s+fileref="([^"]*)"(?:\s+format="([^"]*)")?') @@ -104,7 +106,9 @@ if jw: # rebuild all the docs every time just because the date changes. # date, ver, rev = env.Dictionary('DATE', 'VERSION', 'REVISION') - verfile = str(File("version.sgml")) + #version_sgml = File(os.path.join(build, "version.sgml")) + version_sgml = File("version.sgml") + verfile = str(version_sgml) try: os.unlink(verfile) except OSError: @@ -174,12 +178,17 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. main = os.path.join(doc, 'main.sgml') out = 'main.out' - htmldir = os.path.join('HTML', 'scons-%s' % doc) + # Hard-coding the scons-src path is a bit of a hack. This can + # be reworked when a better solution presents itself. + scons_src_main = os.path.join('#build', 'scons-src', 'doc', main) + env.Ignore(scons_src_main, version_sgml) + + htmldir = os.path.join(build, 'HTML', 'scons-%s' % doc) htmlindex = os.path.join(htmldir, docs[doc]['htmlindex']) - html = os.path.join('HTML', 'scons-%s.html' % doc) - ps = os.path.join('PS', 'scons-%s.ps' % doc) - pdf = os.path.join('PDF', 'scons-%s.pdf' % doc) - text = os.path.join('TEXT', 'scons-%s.txt' % doc) + html = os.path.join(build, 'HTML', 'scons-%s.html' % doc) + ps = os.path.join(build, 'PS', 'scons-%s.ps' % doc) + pdf = os.path.join(build, 'PDF', 'scons-%s.pdf' % doc) + text = os.path.join(build, 'TEXT', 'scons-%s.txt' % doc) if docs[doc].get('html') and jade: cmds = [ @@ -202,10 +211,10 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. env.Command(html, main, cmds) Local(html) - env.Ignore([html, htmlindex], "version.sgml") + env.Ignore([html, htmlindex], version_sgml) tar_deps.extend([html, htmlindex]) - tar_list = string.join([tar_list, html, htmldir], " ") + tar_list.extend([html, htmldir]) if fig2dev: for g in docs[doc].get('graphics', []): @@ -225,15 +234,15 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. ]) Local(ps) - env.Ignore(ps, "version.sgml") + env.Ignore(ps, version_sgml) tar_deps.append(ps) - tar_list = tar_list + " " + ps + tar_list.append(ps) if fig2dev: for g in docs[doc].get('graphics', []): fig = os.path.join(doc, '%s.fig' % g) - eps = os.path.join('PS', '%s.eps' % g) + eps = os.path.join(build, 'PS', '%s.eps' % g) env.Command(eps, fig, "%s -L eps $SOURCES $TARGET" % fig2dev) env.Depends(ps, eps) Local(eps) @@ -247,19 +256,19 @@ THIS IS AN AUTOMATICALLY-GENERATED FILE. DO NOT EDIT. ]) Local(pdf) - env.Ignore(pdf, "version.sgml") + env.Ignore(pdf, version_sgml) tar_deps.append(pdf) - tar_list = tar_list + " " + pdf + tar_list.append(pdf) if docs[doc].get('text') and jade and lynx: env.Command(text, html, "lynx -dump ${SOURCE.abspath} > $TARGET") Local(text) - env.Ignore(text, "version.sgml") + env.Ignore(text, version_sgml) tar_deps.append(text) - tar_list = tar_list + " " + text + tar_list.append(text) # # Man page(s), in good ol' troff format. @@ -270,8 +279,8 @@ for man in man_page_list: man_1 = os.path.join('man', '%s.1' % man) if groff: - ps = os.path.join('PS', '%s-man.ps' % man) - text = os.path.join('TEXT', '%s-man.txt' % man) + ps = os.path.join(build, 'PS', '%s-man.ps' % man) + text = os.path.join(build, 'TEXT', '%s-man.txt' % man) env.Command(ps, man_1, "groff -man -Tps $SOURCES > $TARGET") Local(ps) @@ -280,10 +289,10 @@ for man in man_page_list: Local(text) tar_deps.extend([ps, text]) - tar_list = string.join([tar_list, ps, text], " ") + tar_list.extend([ps, text]) if man2html: - html = os.path.join('HTML' , '%s-man.html' % man) + html = os.path.join(build, 'HTML' , '%s-man.html' % man) cmds = [ "man2html $SOURCES > $TARGET" ] if tidy: @@ -292,13 +301,14 @@ for man in man_page_list: Local(html) tar_deps.append(html) - tar_list = tar_list + " " + html + tar_list.append(html) # # Now actually create the tar file of the documentation, # for easy distribution to the web site. # if tar_deps: + tar_list = map(lambda x: x[11:], tar_list) env.Command(doc_tar_gz, tar_deps, "tar cf${TAR_HFLAG} - -C build/doc %s | gzip > $TARGET" % tar_list) Local(doc_tar_gz) diff --git a/doc/user/actions.in b/doc/user/actions.in new file mode 100644 index 0000000..c35ccdd --- /dev/null +++ b/doc/user/actions.in @@ -0,0 +1,240 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + +<!-- + +=head1 Build actions + +Cons supports several types of B<build actions> that can be performed +to construct one or more target files. Usually, a build action is +a construction command, that is, a command-line string that invokes +an external command. Cons can also execute Perl code embedded in a +command-line string, and even supports an experimental ability to build +a target file by executing a Perl code reference directly. + +A build action is usually specified as the value of a construction +variable: + + $env = new cons( + CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>', + LINKCOM => '[perl] &link_executable("%>", "%<")', + ARCOM => sub { my($env, $target, @sources) = @_; + # code to create an archive + } + ); + +A build action may be associated directly with one or more target files +via the C<Command> method; see below. + +=head2 Construction commands + +A construction command goes through expansion of construction variables +and C<%-> pseudo-variables, as described above, to create the actual +command line that Cons will execute to generate the target file or +files. + +After substitution occurs, strings of white space are converted into +single blanks, and leading and trailing white space is eliminated. It +is therefore currently not possible to introduce variable length white +space in strings passed into a command. + +If a multi-line command string is provided, the commands are executed +sequentially. If any of the commands fails, then none of the rest are +executed, and the target is not marked as updated, i.e. a new signature is +not stored for the target. + +Normally, if all the commands succeed, and return a zero status (or whatever +platform-specific indication of success is required), then a new signature +is stored for the target. If a command erroneously reports success even +after a failure, then Cons will assume that the target file created by that +command is accurate and up-to-date. + +The first word of each command string, after expansion, is assumed to be an +executable command looked up on the C<PATH> environment variable (which is, +in turn, specified by the C<ENV> construction variable). If this command is +found on the path, then the target will depend upon it: the command will +therefore be automatically built, as necessary. It's possible to write +multi-part commands to some shells, separated by semi-colons. Only the first +command word will be depended upon, however, so if you write your command +strings this way, you must either explicitly set up a dependency (with the +C<Depends> method), or be sure that the command you are using is a system +command which is expected to be available. If it isn't available, you will, +of course, get an error. + +Cons normally prints a command before executing it. This behavior is +suppressed if the first character of the command is C<@>. Note that +you may need to separate the C<@> from the command name or escape it to +prevent C<@cmd> from looking like an array to Perl quote operators that +perform interpolation: + + # The first command line is incorrect, + # because "@cp" looks like an array + # to the Perl qq// function. + # Use the second form instead. + Command $env 'foo', 'foo.in', qq( + @cp %< tempfile + @ cp tempfile %> + ); + +If there are shell meta characters anywhere in the expanded command line, +such as C<E<lt>>, C<E<gt>>, quotes, or semi-colon, then the command +will actually be executed by invoking a shell. This means that a command +such as: + + cd foo + +alone will typically fail, since there is no command C<cd> on the path. But +the command string: + + cd $<:d; tar cf $>:f $<:f + +when expanded will still contain the shell meta character semi-colon, and a +shell will be invoked to interpret the command. Since C<cd> is interpreted +by this sub-shell, the command will execute as expected. + +=head2 Perl expressions + +If any command (even one within a multi-line command) begins with +C<[perl]>, the remainder of that command line will be evaluated by the +running Perl instead of being forked by the shell. If an error occurs +in parsing the Perl code, or if the Perl expression returns 0 or undef, +the command will be considered to have failed. For example, here is a +simple command which creates a file C<foo> directly from Perl: + + $env = new cons(); + Command $env 'foo', + qq([perl] open(FOO,'>foo');print FOO "hi\\n"; close(FOO); 1); + +Note that when the command is executed, you are in the same package as +when the F<Construct> or F<Conscript> file was read, so you can call +Perl functions you've defined in the same F<Construct> or F<Conscript> +file in which the C<Command> appears: + + $env = new cons(); + sub create_file { + my $file = shift; + open(FILE, ">$file"); + print FILE "hi\n"; + close(FILE); + return 1; + } + Command $env 'foo', "[perl] &create_file('%>')"; + +The Perl string will be used to generate the signature for the derived +file, so if you change the string, the file will be rebuilt. The contents +of any subroutines you call, however, are not part of the signature, +so if you modify a called subroutine such as C<create_file> above, +the target will I<not> be rebuilt. Caveat user. + +=head2 Perl code references [EXPERIMENTAL] + +Cons supports the ability to create a derived file by directly executing +a Perl code reference. This feature is considered EXPERIMENTAL and +subject to change in the future. + +A code reference may either be a named subroutine referenced by the +usual C<\&> syntax: + + sub build_output { + my($env, $target, @sources) = @_; + print "build_output building $target\n"; + open(OUT, ">$target"); + foreach $src (@sources) { + if (! open(IN, "<$src")) { + print STDERR "cannot open '$src': $!\n"; + return undef; + } + print OUT, <IN>; + } + close(OUT); + return 1; + } + Command $env 'output', \&build_output; + +or the code reference may be an anonymous subroutine: + + Command $env 'output', sub { + my($env, $target, @sources) = @_; + print "building $target\n"; + open(FILE, ">$target"); + print FILE "hello\n"; + close(FILE); + return 1; + }; + +To build the target file, the referenced subroutine is passed, in order: +the construction environment used to generate the target; the path +name of the target itself; and the path names of all the source files +necessary to build the target file. + +The code reference is expected to generate the target file, of course, +but may manipulate the source and target files in any way it chooses. +The code reference must return a false value (C<undef> or C<0>) if +the build of the file failed. Any true value indicates a successful +build of the target. + +Building target files using code references is considered EXPERIMENTAL +due to the following current limitations: + +=over 4 + +Cons does I<not> print anything to indicate the code reference is being +called to build the file. The only way to give the user any indication +is to have the code reference explicitly print some sort of "building" +message, as in the above examples. + +Cons does not generate any signatures for code references, so if the +code in the reference changes, the target will I<not> be rebuilt. + +Cons has no public method to allow a code reference to extract +construction variables. This would be good to allow generalization of +code references based on the current construction environment, but would +also complicate the problem of generating meaningful signatures for code +references. + +=back + +Support for building targets via code references has been released in +this version to encourage experimentation and the seeking of possible +solutions to the above limitations. + +--> + + <para> + + XXX + + </para> + + <section> + <title>XXX</title> + + <para> + + XXX + + </para> + + </section> diff --git a/doc/user/alias.in b/doc/user/alias.in new file mode 100644 index 0000000..22fc128 --- /dev/null +++ b/doc/user/alias.in @@ -0,0 +1,102 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + + <para> + + We've already seen how you can use the &Alias; + function to create a target named <literal>install</literal>: + + </para> + + <scons_example name="ex1"> + <file name="SConstruct" printme="1"> + env = Environment() + hello = env.Program('hello.c') + env.Install('__ROOT__/usr/bin', hello) + env.Alias('install', '__ROOT__/usr/bin') + </file> + <file name="hello.c"> + int main() { printf("Hello, world!\n"); } + </file> + </scons_example> + + <para> + + You can then use this alias on the command line + to tell &SCons; more naturally that you want to install files: + + </para> + + <scons_output example="ex1" os="posix"> + <command>scons install</command> + </scons_output> + + <para> + + Like other &Builder; methods, though, + the &Alias; method returns an object + representing the alias being built. + You can then use this object as input to anothother &Builder;. + This is especially useful if you use such an object + as input to another call to the &Alias; &Builder;, + allowing you to create a hierarchy + of nested aliases: + + </para> + + <scons_example name="ex2"> + <file name="SConstruct" printme="1"> + env = Environment() + p = env.Program('foo.c') + l = env.Library('bar.c') + env.Install('__ROOT__/usr/bin', p) + env.Install('__ROOT__/usr/lib', l) + ib = env.Alias('install-bin', '__ROOT__/usr/bin') + il = env.Alias('install-lib', '__ROOT__/usr/lib') + env.Alias('install', [ib, il]) + </file> + <file name="foo.c"> + int main() { printf("foo.c\n"); } + </file> + <file name="bar.c"> + void bar() { printf("bar.c\n"); } + </file> + </scons_example> + + <para> + + This example defines separate <literal>install</literal>, + <literal>install-bin</literal>, + and <literal>install-lib</literal> aliases, + allowing you finer control over what gets installed: + + </para> + + <scons_output example="ex2" os="posix"> + <command>scons install-bin</command> + <command>scons install-lib</command> + <command>scons -c __ROOT__/</command> + <command>scons install</command> + </scons_output> diff --git a/doc/user/ant.in b/doc/user/ant.in new file mode 100644 index 0000000..4eb5007 --- /dev/null +++ b/doc/user/ant.in @@ -0,0 +1,52 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + + <para> + + XXX + + </para> + + <section> + <title>Differences Between &Ant; and &SCons;</title> + + <para> + + XXX + + </para> + + </section> + + <section> + <title>Advantages of &SCons; Over &Ant;</title> + + <para> + + XXX + + </para> + + </section> diff --git a/doc/user/builders-built-in.in b/doc/user/builders-built-in.in new file mode 100644 index 0000000..89f0af3 --- /dev/null +++ b/doc/user/builders-built-in.in @@ -0,0 +1,652 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + + <para> + + &SCons; provides the ability to build a lot of different + types of files right "out of the box." + So far, we've been using &SCons;' ability to build + programs, objects and libraries to + illustrate much of the underlying functionality of &SCons; + This section will describe all of the different + types of files that you can build with &SCons;, + and the built-in &Builder; objects used to build them. + + </para> + + <section> + <title>Programs: the &Program; Builder</title> + + <para> + + As we've seen, the &Program; Builder + is used to build an executable program. + The &source; argument is one or more + source-code files or object files, + and the ⌖ argument is the + name of the executable program name to be created. + For example: + + </para> + + <programlisting> + env = Environment() + env.Program('prog', 'file1.o') + </programlisting> + + <para> + + Will create the &prog; + executable on a POSIX system, + the &prog_exe; executable on a Windows system. + + </para> + + <para> + + The target file's prefix and suffix may be omitted, + and the values from the + $PROGPREFIX + and + $PROGSUFFIX + construction variables + will be appended appropriately. + For example: + + </para> + + <programlisting> + env = Environment(PROGPREFIX='my', PROGSUFFIX='.xxx') + env.Program('prog', ['file1.o', 'file2.o']) + </programlisting> + + <para> + + Will create a program named + <filename>myprog.xxx</filename> + regardless of the system on which it is run. + + </para> + + <para> + + If you omit the ⌖, + the base of the first input + file name specified + because the base of the target + program created. + For example: + + </para> + + <programlisting> + env = Environment() + env.Program(['hello.c', 'goodbye.c']) + </programlisting> + + <para> + + Will create the &hello; + executable on a POSIX system, + the &hello_exe; executable on a Windows system. + + </para> + + </section> + + <section> + <title>Object-File Builders</title> + + <para> + + &SCons; provides separate Builder objects + to create both static and shared object files. + + </para> + + <section> + <title>The &StaticObject; Builder</title> + + <para> + + XXX + + </para> + + <programlisting> + XXX + </programlisting> + + <literallayout> + XXX + </literallayout> + + </section> + + <section> + <title>The &SharedObject; Builder</title> + + <para> + + XXX + + </para> + + <programlisting> + XXX + </programlisting> + + <literallayout> + XXX + </literallayout> + + </section> + + <section> + <title>The &Object; Builder</title> + + <para> + + XXX + + </para> + + <programlisting> + XXX + </programlisting> + + <para> + + Creates a static object file. + + </para> + + </section> + + </section> + + <section> + <title>Library Builders</title> + + <para> + + &SCons; provides separate Builder objects + to create both static and shared libraries. + + </para> + + <section> + <title>The &StaticLibrary; Builder</title> + + <para> + + XXX + + </para> + + <programlisting> + XXX + </programlisting> + + <literallayout> + XXX + </literallayout> + + </section> + + <section> + <title>The &SharedLibrary; Builder</title> + + <para> + + XXX + + </para> + + </section> + + <section> + <title>The &Library; Builder</title> + + <para> + + XXX + + </para> + + <programlisting> + XXX + </programlisting> + + <literallayout> + XXX + </literallayout> + + <para> + + Creates a static library file. + + </para> + + </section> + + </section> + + <section> + <title>Pre-Compiled Headers: the &PCH; Builder</title> + + <para> + + XXX + + </para> + + </section> + + <section> + <title>Microsoft Visual C++ Resource Files: the &RES; Builder</title> + + <para> + + XXX + + </para> + + </section> + + <section> + <title>Source Files</title> + + <para> + + By default + &SCons; supports two Builder objects + that know how to build source files + from other input files. + + </para> + + <section> + <title>The &CFile; Builder</title> + + <para> + + XXX + + </para> + + <programlisting> + XXX + </programlisting> + + <literallayout> + XXX + </literallayout> + + </section> + + <section> + <title>The &CXXFile; Builder</title> + + <para> + + XXX + + </para> + + <programlisting> + XXX + </programlisting> + + <literallayout> + XXX + </literallayout> + + </section> + + </section> + + <section> + <title>Documents</title> + + <para> + + &SCons; provides a number of Builder objects + for creating different types of documents. + + </para> + + <section> + <title>The &DVI; Builder</title> + + <para> + + XXX + + </para> + + <programlisting> + XXX + </programlisting> + + <literallayout> + XXX + </literallayout> + + </section> + + <section> + <title>The &PDF; Builder</title> + + <para> + + XXX + + </para> + + </section> + + <section> + <title>The &PostScript; Builder</title> + + <para> + + XXX + + </para> + + <programlisting> + XXX + </programlisting> + + <literallayout> + XXX + </literallayout> + + </section> + + </section> + + <section> + <title>Archives</title> + + <para> + + &SCons; provides Builder objects + for creating two different types of archive files. + + </para> + + <section> + <title>The &Tar; Builder</title> + + <para> + + The &Tar; Builder object uses the &tar; + utility to create archives of files + and/or directory trees: + + </para> + + <scons_example name="ex1"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Tar('out1.tar', ['file1', 'file2']) + env.Tar('out2', 'directory') + </file> + <file name="file1"> + file1 + </file> + <file name="file2"> + file2 + </file> + <file name="directory/file3"> + directory/file3 + </file> + </scons_example> + + <scons_output example="ex1" os="posix"> + <command>scons .</command> + </scons_output> + + <para> + + One common requirement when creating a &tar; archive + is to create a compressed archive using the + <option>-z</option> option. + This is easily handled by specifying + the value of the &TARFLAGS; variable + when you create the construction environment. + Note, however, that the <option>-c</option> used to + to instruct &tar; to create the archive + is part of the default value of &TARFLAGS;, + so you need to set it both options: + + </para> + + <scons_example name="ex2"> + <file name="SConstruct" printme="1"> + env = Environment(TARFLAGS = '-c -z') + env.Tar('out.tar.gz', 'directory') + </file> + <file name="directory/file"> + directory/file + </file> + </scons_example> + + <scons_output example="ex2" os="posix"> + <command>scons .</command> + </scons_output> + + <para> + + you may also wish to set the value of the + &TARSUFFIX; construction variable + to your desired suffix for compress &tar; archives, + so that &SCons; can append it to the target file name + without your having to specify it explicitly: + + </para> + + <scons_example name="ex3"> + <file name="SConstruct" printme="1"> + env = Environment(TARFLAGS = '-c -z', + TARSUFFIX = '.tgz') + env.Tar('out', 'directory') + </file> + <file name="directory/file"> + directory/file + </file> + </scons_example> + + <scons_output example="ex3" os="posix"> + <command>scons .</command> + </scons_output> + + </section> + + <section> + <title>The &Zip; Builder</title> + + <para> + + The &Zip; Builder object creates archives of files + and/or directory trees in the ZIP file format. + Python versions 1.6 or later + contain an internal &zipfile; module + that &SCons; will use. + In this case, given the following + &SConstruct; file: + + </para> + + <scons_example name="ex4"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Zip('out', ['file1', 'file2']) + </file> + <file name="file1"> + file1 + </file> + <file name="file2"> + file2 + </file> + </scons_example> + + <para> + + Your output will reflect the fact + that an internal Python function + is being used to create the output ZIP archive: + + </para> + + <scons_output example="ex4" os="posix"> + <command>scons .</command> + </scons_output> + + <para> + + If you're using Python version 1.5.2 to run &SCons;, + then &SCons; will try to use an external + &zip; program as follows: + + </para> + + <literallayout> + % <userinput>scons .</userinput> + zip /home/my/project/zip.out file1 file2 + </literallayout> + + </section> + + </section> + + <section> + <title>Java</title> + + <para> + + &SCons; provides Builder objects + for creating various types of Java output files. + + </para> + + <section> + <title>Building Class Files: the &Java; Builder</title> + + <para> + + The &Java; builder takes one or more input + <filename>.java</filename> files + and turns them into one or more + <filename>.class</filename> files + Unlike most builders, however, + the &Java; builder takes + target and source <emphasis>directories</emphasis>, + not files, as input. + + </para> + + <programlisting> + env = Environment() + env.Java(target = 'classes', source = 'src') + </programlisting> + + <para> + + The &Java; builder will then + search the specified source directory + tree for all <filename>.java</filename> files, + and pass any out-of-date + + </para> + + <literallayout> + XXX + </literallayout> + + </section> + + <section> + <title>The &Jar; Builder</title> + + <para> + + The &Jar; builder object XXX + + </para> + + <programlisting> + env = Environment() + env.Java(target = 'classes', source = 'src') + env.Jar(target = '', source = 'classes') + </programlisting> + + <literallayout> + XXX + </literallayout> + + </section> + + <section> + <title>Building C header and stub files: the &JavaH; Builder</title> + + <para> + + XXX + + </para> + + <programlisting> + XXX + </programlisting> + + <literallayout> + XXX + </literallayout> + + </section> + + <section> + <title>Building RMI stub and skeleton class files: the &RMIC; Builder</title> + + <para> + + XXX + + </para> + + <programlisting> + XXX + </programlisting> + + <literallayout> + XXX + </literallayout> + + </section> + + </section> diff --git a/doc/user/builders-commands.in b/doc/user/builders-commands.in new file mode 100644 index 0000000..5fc2c73 --- /dev/null +++ b/doc/user/builders-commands.in @@ -0,0 +1,116 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + + <!-- + + =head2 The C<Command> method + + + The C<Command> method is called as follows: + + Command $env <target>, <inputs>, <build action>; + + The target is made dependent upon the list of input files specified, and the + inputs must be built successfully or Cons will not attempt to build the + target. + + To specify a command with multiple targets, you can specify a reference to a + list of targets. In Perl, a list reference can be created by enclosing a + list in square brackets. Hence the following command: + + Command $env ['foo.h', 'foo.c'], 'foo.template', q( + gen %1 + ); + + could be used in a case where the command C<gen> creates two files, both + F<foo.h> and F<foo.c>. + + --> + + <para> + + Creating a &Builder; and attaching it to a &consenv; + allows for a lot of flexibility when you + want to re-use actions + to build multiple files of the same type. + This can, however, be cumbersome + if you only need to execute one specific command + to build a single file (or group of files). + For these situations, &SCons; supports a + &Command; &Builder; that arranges + for a specific action to be executed + to build a specific file or files. + This looks a lot like the other builders + (like &Program;, &Object;, etc.), + but takes as an additional argument + the command to be executed to build the file: + + </para> + + <scons_example name="ex1"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Command('foo.out', 'foo.in', "sed 's/x/y/' < $SOURCE > $TARGET") + </file> + <file name="foo.in"> + foo.in + </file> + </scons_example> + + <scons_output example="ex1"> + <command>scons .</command> + </scons_output> + + <para> + + This is often more convenient than + creating a &Builder; object + and adding it to the &BUILDERS; variable + of a &consenv; + + </para> + + <para> + + Note that the action you + + </para> + + <scons_example name="ex2"> + <file name="SConstruct" printme="1"> + env = Environment() + def build(target, source, env): + # Whatever it takes to build + return None + env.Command('foo.out', 'foo.in', build) + </file> + <file name="foo.in"> + foo.in + </file> + </scons_example> + + <scons_output example="ex2"> + <command>scons .</command> + </scons_output> diff --git a/doc/user/builders-writing.in b/doc/user/builders-writing.in new file mode 100644 index 0000000..d911f6c --- /dev/null +++ b/doc/user/builders-writing.in @@ -0,0 +1,703 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + +<!-- + +=head2 Adding new methods + +For slightly more demanding changes, you may wish to add new methods to the +C<cons> package. Here's an example of a very simple extension, +C<InstallScript>, which installs a tcl script in a requested location, but +edits the script first to reflect a platform-dependent path that needs to be +installed in the script: + + # cons::InstallScript - Create a platform dependent version of a shell + # script by replacing string ``#!your-path-here'' with platform specific + # path $BIN_DIR. + + sub cons::InstallScript { + my ($env, $dst, $src) = @_; + Command $env $dst, $src, qq( + sed s+your-path-here+$BIN_DIR+ %< > %> + chmod oug+x %> + ); + } + +Notice that this method is defined directly in the C<cons> package (by +prefixing the name with C<cons::>). A change made in this manner will be +globally visible to all environments, and could be called as in the +following example: + + InstallScript $env "$BIN/foo", "foo.tcl"; + +For a small improvement in generality, the C<BINDIR> variable could be +passed in as an argument or taken from the construction environment-,-as +C<%BINDIR>. + + +=head2 Overriding methods + +Instead of adding the method to the C<cons> name space, you could define a +new package which inherits existing methods from the C<cons> package and +overrides or adds others. This can be done using Perl's inheritance +mechanisms. + +The following example defines a new package C<cons::switch> which +overrides the standard C<Library> method. The overridden method builds +linked library modules, rather than library archives. A new +constructor is provided. Environments created with this constructor +will have the new library method; others won't. + + package cons::switch; + BEGIN {@ISA = 'cons'} + + sub new { + shift; + bless new cons(@_); + } + + sub Library { + my($env) = shift; + my($lib) = shift; + my(@objs) = Objects $env @_; + Command $env $lib, @objs, q( + %LD -r %LDFLAGS %< -o %> + ); + } + +This functionality could be invoked as in the following example: + + $env = new cons::switch(@overrides); + ... + Library $env 'lib.o', 'foo.c', 'bar.c'; + +--> + + <para> + + Although &SCons; provides many useful methods + for building common software products: + programs, libraries, documents. + you frequently want to be + able to build some other type of file + not supported directly by &SCons; + Fortunately, &SCons; makes it very easy + to define your own &Builder; objects + for any custom file types you want to build. + (In fact, the &SCons; interfaces for creating + &Builder; objects are flexible enough and easy enough to use + that all of the the &SCons; built-in &Builder; objects + are created the mechanisms described in this section.) + + </para> + + <section> + <title>Writing Builders That Execute External Commands</title> + + <para> + + The simplest &Builder; to create is + one that executes an external command. + For example, if we want to build + an output file by running the contents + of the input file through a command named + <literal>foobuild</literal>, + creating that &Builder; might look like: + + </para> + + <programlisting> + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + </programlisting> + + <para> + + All the above line does is create a free-standing + &Builder; object. + The next section will show us how to actually use it. + + </para> + + </section> + + <section> + <title>Attaching a Builder to a &ConsEnv;</title> + + <para> + + A &Builder; object isn't useful + until it's attached to a &consenv; + so that we can call it to arrange + for files to be built. + This is done through the &BUILDERS; + &consvar; in an environment. + The &BUILDERS; variable is a Python dictionary + that maps the names by which you want to call + various &Builder; objects to the objects themselves. + For example, if we want to call the + &Builder; we just defined by the name + <function>Foo</function>, + our &SConstruct; file might look like: + + </para> + + <scons_example name="ex1"> + <file name="SConstruct"> + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file.foo', 'file.input') + </file> + <file name="file.input"> + file.input + </file> + </scons_example> + + <programlisting> + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env = Environment(BUILDERS = {'Foo' : bld}) + </programlisting> + + <para> + + With the &Builder; so attached to our &consenv; + we can now actually call it like so: + + </para> + + <programlisting> + env.Foo('file.foo', 'file.input') + </programlisting> + + <para> + + Then when we run &SCons; it looks like: + + </para> + + <scons_output example="ex1"> + <command>scons</command> + </scons_output> + + <para> + + Note, however, that the default &BUILDERS; + variable in a &consenv; + comes with a default set of &Builder; objects + already defined: + &Program;, &Library;, etc. + And when we explicitly set the &BUILDERS; variable + when we create the &consenv;, + the default &Builder;s are no longer part of + the environment: + + </para> + + <scons_example name="ex2"> + <file name="SConstruct" printme="1"> + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file.foo', 'file.input') + env.Program('hello.c') + </file> + <file name="file.input"> + file.input + </file> + <file name="hello.c"> + hello.c + </file> + </scons_example> + + <!-- + scons: Reading SConscript files ... + other errors + Traceback (most recent call last): + File "/usr/lib/scons/SCons/Script/__init__.py", line 901, in main + _main() + File "/usr/lib/scons/SCons/Script/__init__.py", line 762, in _main + SCons.Script.SConscript.SConscript(script) + File "/usr/lib/scons/SCons/Script/SConscript.py", line 207, in SConscript + exec _file_ in stack[-1].globals + File "SConstruct", line 4, in ? + env.Program('hello.c') + scons: Environment instance has no attribute 'Program' + --> + + <scons_output example="ex2"> + <command>scons</command> + </scons_output> + + <para> + + To be able use both our own defined &Builder; objects + and the default &Builder; objects in the same &consenv;, + you can either add to the &BUILDERS; variable + using the &Append; function: + + </para> + + <scons_example name="ex3"> + <file name="SConstruct"> + env = Environment() + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env.Append(BUILDERS = {'Foo' : bld}) + env.Foo('file.foo', 'file.input') + env.Program('hello.c') + </file> + <file name="file.input"> + file.input + </file> + <file name="hello.c"> + hello.c + </file> + </scons_example> + + <para> + + Or you can explicitly set the appropriately-named + key in the &BUILDERS; dictionary: + + </para> + + <programlisting> + env = Environment() + bld = Builder(action = 'foobuild < $SOURCE > $TARGET') + env['BUILDERS']['Foo'] = bld + env.Foo('file.foo', 'file.input') + env.Program('hello.c') + </programlisting> + + <para> + + Either way, the same &consenv; + can then use both the newly-defined + <function>Foo</function> &Builder; + and the default &Program; &Builder;: + + </para> + + <scons_output example="ex3"> + <command>scons</command> + </scons_output> + + </section> + + <section> + <title>Letting &SCons; Handle The File Suffixes</title> + + <para> + + By supplying additional information + when you create a &Builder;, + you can let &SCons; add appropriate file + suffixes to the target and/or the source file. + For example, rather than having to specify + explicitly that you want the <literal>Foo</literal> + &Builder; to build the <literal>file.foo</literal> + target file from the <literal>file.input</literal> source file, + you can give the <literal>.foo</literal> + and <literal>.input</literal> suffixes to the &Builder;, + making for more compact and readable calls to + the <literal>Foo</literal> &Builder;: + + </para> + + <scons_example name="ex4"> + <file name="SConstruct" printme="1"> + bld = Builder(action = 'foobuild < $TARGET > $SOURCE', + suffix = '.foo', + src_suffix = '.input') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file1') + env.Foo('file2') + </file> + <file name="file1.input"> + file1.input + </file> + <file name="file2.input"> + file2.input + </file> + </scons_example> + + <scons_output example="ex4"> + <command>scons</command> + </scons_output> + + <para> + + You can also supply a <literal>prefix</literal> keyword argument + if it's appropriate to have &SCons; append a prefix + to the beginning of target file names. + + </para> + + </section> + + <section> + <title>Builders That Execute Python Functions</title> + + <para> + + In &SCons;, you don't have to call an external command + to build a file. + You can, instead, define a Python function + that a &Builder; object can invoke + to build your target file (or files). + Such a &buildfunc; definition looks like: + + </para> + + <programlisting> + def build_function(target, source, env): + # XXX + return None + </programlisting> + + <para> + + The arguments of a &buildfunc; are: + + </para> + + <variablelist> + + <varlistentry> + <term>target</term> + + <listitem> + <para> + + A list of Node objects representing + the target or targets to be + built by this builder function. + The file names of these target(s) + may be extracted using the Python &str; funcion. + + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>source</term> + + <listitem> + <para> + + A list of Node objects representing + the sources to be + used by this builder function to build the targets. + The file names of these source(s) + may be extracted using the Python &str; funcion. + + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>env</term> + + <listitem> + <para> + + The &consenv; used for building the target(s). + The builder function may use any of the + environment's construction variables + in any way to affect how it builds the targets. + + </para> + </listitem> + </varlistentry> + + </variablelist> + + <para> + + The builder function must + return a <literal>0</literal> or <literal>None</literal> value + if the target(s) are built successfully. + The builder function + may raise an exception + or return any non-zero value + to indicate that the build is unsuccessful, + + </para> + + <para> + + Once you've defined the Python function + that will build your target file, + defining a &Builder; object for it is as + simple as specifying the name of the function, + instead of an external command, + as the &Builder;'s + <literal>action</literal> + argument: + + </para> + + <scons_example name="ex5"> + <file name="SConstruct" printme="1"> + def build_function(target, source, env): + # XXX + return None + bld = Builder(action = build_function, + suffix = '.foo', + src_suffix = '.input') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file') + </file> + <file name="file.input"> + file.input + </file> + </scons_example> + + <para> + + And notice that the output changes slightly, + reflecting the fact that a Python function, + not an external command, + is now called to build the target file: + + </para> + + <scons_output example="ex5"> + <command>scons</command> + </scons_output> + + </section> + + <section> + <title>Builders That Create Actions Using a &Generator;</title> + + <para> + + &SCons; Builder objects can create an action "on the fly" + by using a function called a &generator;. + This provides a great deal of flexibility XXX + A &generator; looks like: + + </para> + + <programlisting> + def generate_actions(source, target, env, for_signature): + return XXX + </programlisting> + + <para> + + The arguments of a &generator; are: + + </para> + + <variablelist> + + <varlistentry> + <term>source</term> + + <listitem> + <para> + + A list of Node objects representing + the sources to be built + by the command or other action + generated by this function. + The file names of these source(s) + may be extracted using the Python &str; funcion. + + </para> + </listitem> + + </varlistentry> + + <varlistentry> + <term>target</term> + + <listitem> + <para> + + A list of Node objects representing + the target or targets to be built + by the command or other action + generated by this function. + The file names of these target(s) + may be extracted using the Python &str; funcion. + + </para> + </listitem> + + </varlistentry> + + <varlistentry> + <term>env</term> + + <listitem> + <para> + + The &consenv; used for building the target(s). + The generator may use any of the + environment's construction variables + in any way to determine what command + or other action to return. + + </para> + </listitem> + + </varlistentry> + + <varlistentry> + <term>for_signature</term> + + <listitem> + <para> + + A flag that specifies whether the + generator is being called to contribute to a build signature, + as opposed to actually executing the command. + + XXX + + </para> + </listitem> + + </varlistentry> + + </variablelist> + + <para> + + The &generator; must return a + command string or other action that will be used to + build the specified target(s) from the specified source(s). + + </para> + + <para> + + Once you've defined a &generator;, + you create a &Builder; to use it + by specifying the generator keyword argument + instead of <literal>action</literal>. + + </para> + + <scons_example name="ex6"> + <file name="SConstruct" printme="1"> + bld = Builder(generator = generate_actions, + suffix = '.foo', + src_suffix = '.input') + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file') + </file> + <file name="file.input"> + file.input + </file> + </scons_example> + + <scons_output example="ex6"> + <command>scons</command> + </scons_output> + + <para> + + Note that it's illegal to specify both an + <literal>action</literal> + and a + <literal>generator</literal> + for a &Builder;. + + </para> + + </section> + + <section> + <title>Builders That Modify the Target or Source Lists Using an &Emitter;</title> + + <para> + + &SCons; supports the ability for a Builder to modify the + lists of target(s) from the specified source(s). + + </para> + + <scons_example name="ex7"> + <file name="SConstruct" printme="1"> + def modify_targets(XXX): + return XXX + bld = Builder(action = 'XXX', + suffix = '.foo', + src_suffix = '.input', + emitter = modify_targets) + env = Environment(BUILDERS = {'Foo' : bld}) + env.Foo('file') + </file> + <file name="file.input"> + file.input + </file> + </programlisting> + + <scons_output example="ex7"> + <command>scons</command> + </scons_output> + + <programlisting> + bld = Builder(action = 'XXX', + suffix = '.foo', + src_suffix = '.input', + emitter = 'MY_EMITTER') + def modify1(XXX): + return XXX + def modify2(XXX): + return XXX + env1 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify1) + env2 = Environment(BUILDERS = {'Foo' : bld}, + MY_EMITTER = modify2) + env1.Foo('file1') + env2.Foo('file2') + </programlisting> + + </section> + + <section> + <title>Builders That Use Other Builders</title> + + <para> + + XXX + + </para> + + <scons_example name="ex8"> + <file name="SConstruct" printme="1"> + env = Environment() + env.SourceCode('.', env.BitKeeper('XXX')) + env.Program('hello.c') + </file> + </programlisting> + + <scons_output example="ex8"> + <command>scons</command> + </scons_output> + + </section> diff --git a/doc/user/caching.in b/doc/user/caching.in new file mode 100644 index 0000000..2301784 --- /dev/null +++ b/doc/user/caching.in @@ -0,0 +1,242 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + + <para> + + On multi-developer software projects, + you can sometimes speed up every developer's builds a lot by + allowing them to share the derived files that they build. + &SCons; makes this easy, as well as reliable. + + </para> + + <section> + <title>Specifying the Shared Cache Directory</title> + + <para> + + To enable sharing of derived files, + use the &CacheDir; function + in any &SConscript; file: + + </para> + + <scons_example name="ex1"> + <file name="SConstruct"> + env = Environment() + env.Program('hello.c') + CacheDir('cache') + </file> + <file name="hello.c"> + hello.c + </file> + <directory name="cache"> + </directory> + <file name="not_used" printme="1"> + CacheDir('/usr/local/build_cache') + </file> + </scons_example> + + <para> + + Note that the directory you specify must already exist + and be readable and writable by all developers + who will be sharing derived files. + It should also be in some central location + that all builds will be able to access. + In environments where developers are using separate systems + (like individual workstations) for builds, + this directory would typically be + on a shared or NFS-mounted file system. + + </para> + + <para> + + Here's what happens: + When a build has a &CacheDir; specified, + every time a file is built, + it is stored in the shared cache directory + along with its MD5 build signature. + On subsequent builds, + before an action is invoked to build a file, + &SCons; will check the shared cache directory + to see if a file with the exact same build + signature already exists. + If so, the derived file will not be built locally, + but will be copied into the local build directory + from the shared cache directory, + like so: + + </para> + + <scons_output example="ex1"> + <command>scons</command> + <command>scons -c</command> + <command>scons</command> + </scons_output> + + </section> + + <section> + <title>Keeping Build Output Consistent</title> + + <para> + + One potential drawback to using a shared cache + is that your build output can be inconsistent + from invocation to invocation, + because any given file may be rebuilt one time + and retrieved from the shared cache the next time. + This can make analyzing build output more difficult, + especially for automated scripts that + expect consistent output each time. + + </para> + + <para> + + If, however, you use the <literal>--cache-show</literal> option, + &SCons; will print the command line that it + <emphasis>would</emphasis> have executed + to build the file, + even when it is retrieving the file from the shared cache. + This makes the build output consistent + every time the build is run: + + </para> + + <scons_output example="ex1"> + <command>scons</command> + <command>scons -c</command> + <command>scons --cache-show</command> + </scons_output> + + <para> + + The trade-off, of course, is that you no longer + know whether or not &SCons; + has retrieved a derived file from cache + or has rebuilt it locally. + + </para> + + </section> + + <section> + <title>Not Retrieving Files From a Shared Cache</title> + + <para> + + Retrieving an already-built file + from the shared cache + is usually a significant time-savings + over rebuilding the file, + but how much of a savings + (or even whether it saves time at all) + can depend a great deal on your + system or network configuration. + For example, retrieving cached files + from a busy server over a busy network + might end up being slower than + rebuilding the files locally. + + </para> + + <para> + + In these cases, you can specify + the <literal>--cache-disable</literal> + command-line option to tell &SCons; + to not retrieve already-built files from the + shared cache directory: + + </para> + + <scons_output example="ex1"> + <command>scons</command> + <command>scons -c</command> + <command>scons</command> + <command>scons -c</command> + <command>scons --cache-disable</command> + </scons_output> + + </section> + + <section> + <title>Populating a Shared Cache With Already-Built Files</title> + + <para> + + Sometimes, you may have one or more derived files + already built in your local build tree + that you wish to make available to other people doing builds. + For example, you may find it more effective to perform + integration builds with the cache disabled + (per the previous section) + and only populate the shared cache directory + with the built files after the integration build + has completed successfully. + This way, the cache will only get filled up + with derived files that are part of a complete, successful build + not with files that might be later overwritten + while you debug integration problems. + + </para> + + <para> + + In this case, you can use the + the <literal>--cache-force</literal> option + to tell &SCons; to put all derived files in the cache, + even if the files had already been built + by a previous invocation: + + </para> + + <scons_output example="ex1"> + <command>scons --cache-disable</command> + <command>scons -c</command> + <command>scons --cache-disable</command> + <command>scons --cache-force</command> + <command>scons -c</command> + <command>scons</command> + </scons_output> + + <para> + + Notice how the above sample run + demonstrates that the <literal>--cache-disable</literal> + option avoids putting the built + <filename>hello.o</filename> + and + <filename>hello</filename> files in the cache, + but after using the <literal>--cache-force</literal> option, + the files have been put in the cache + for the next invocation to retrieve. + + </para> + + </section> diff --git a/doc/user/cons.in b/doc/user/cons.in new file mode 100644 index 0000000..be02a51 --- /dev/null +++ b/doc/user/cons.in @@ -0,0 +1,52 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + + <para> + + XXX + + </para> + + <section> + <title>Differences Between &Cons; and &SCons;</title> + + <para> + + XXX + + </para> + + </section> + + <section> + <title>Advantages of &SCons; Over &Cons;</title> + + <para> + + XXX + + </para> + + </section> diff --git a/doc/user/copyright.in b/doc/user/copyright.in new file mode 100644 index 0000000..7f6059c --- /dev/null +++ b/doc/user/copyright.in @@ -0,0 +1,32 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + +<blockquote> + <para> + + SCons User's Guide Copyright (c) 2003 Steven Knight + + </para> +</blockquote> diff --git a/doc/user/default.in b/doc/user/default.in new file mode 100644 index 0000000..dbe6ecd --- /dev/null +++ b/doc/user/default.in @@ -0,0 +1,216 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + +<!-- + +=head1 Default targets + +Until now, we've demonstrated invoking Cons with an explicit target +to build: + + % cons hello + +Normally, Cons does not build anything unless a target is specified, +but specifying '.' (the current directory) will build everything: + + % cons # does not build anything + + % cons . # builds everything under the top-level directory + +Adding the C<Default> method to any F<Construct> or F<Conscript> file will add +the specified targets to a list of default targets. Cons will build +these defaults if there are no targets specified on the command line. +So adding the following line to the top-level F<Construct> file will mimic +Make's typical behavior of building everything by default: + + Default '.'; + +The following would add the F<hello> and F<goodbye> commands (in the +same directory as the F<Construct> or F<Conscript> file) to the default list: + + Default qw( + hello + goodbye + ); + +The C<Default> method may be used more than once to add targets to the +default list. + +--> + + <para> + + As mentioned previously, + &SCons; will build every target + in or below the current directory + by default--that is, when you don't + explicitly specify one or more targets + on the command line. + Sometimes, however, you may want + to specify explicitly that only + certain programs should be built by default. + You do this with the &Default; function: + + </para> + + <scons_example name="ex1"> + <file name="SConstruct" printme="1"> + env = Environment() + hello = env.Program('hello.c') + env.Program('goodbye.c') + Default(hello) + </file> + <file name="hello.c"> + hello.c + </file> + <file name="goodbye.c"> + goodbye.c + </file> + </scons_example> + + <para> + + This &SConstruct; file knows how to build two programs, + &hello; and &goodbye;, + but only builds the + &hello; program by default: + + </para> + + <scons_output example="ex1"> + <command>scons</command> + <command>scons</command> + <command>scons goodbye</command> + </scons_output> + + <para> + + Note that, even when you use the &Default; + function in your &SConstruct; file, + you can still explicitly specify the current directory + (<literal>.</literal>) on the command line + to tell &SCons; to build + everything in (or below) the current directory: + + </para> + + <scons_output example="ex1"> + <command>scons .</command> + </scons_output> + + <para> + + You can also call the &Default; + function more than once, + in which case each call + adds to the list of targets to be built by default: + + </para> + + <scons_example name="ex2"> + <file name="SConstruct" printme="1"> + env = Environment() + prog1 = env.Program('prog1.c') + Default(prog1) + prog2 = env.Program('prog2.c') + prog3 = env.Program('prog3.c') + Default(prog3) + </file> + <file name="prog1.c"> + prog1.c + </file> + <file name="prog2.c"> + prog2.c + </file> + <file name="prog3.c"> + prog3.c + </file> + </scons_example> + + <para> + + Or you can specify more than one target + in a single call to the &Default; function: + + </para> + + <programlisting> + env = Environment() + prog1 = env.Program('prog1.c') + prog2 = env.Program('prog2.c') + prog3 = env.Program('prog3.c') + Default(prog1, prog3) + </programlisting> + + <para> + + Either of these last two examples + will build only the + <application>prog1</application> + and + <application>prog3</application> + programs by default: + + </para> + + <scons_output example="ex2"> + <command>scons</command> + <command>scons .</command> + </scons_output> + + <para> + + Lastly, if for some reason you don't want + any targets built by default, + you can use the Python <literal>None</literal> + variable: + + </para> + + <scons_example name="ex3"> + <file name="SConstruct" printme="1"> + env = Environment() + prog1 = env.Program('prog1.c') + prog2 = env.Program('prog2.c') + Default(None) + </file> + <file name="prog1.c"> + prog1.c + </file> + <file name="prog2.c"> + prog2.c + </file> + </scons_example> + + <para> + + Which would produce build output like: + + </para> + + <scons_output example="ex3"> + <command>scons</command> + <command>scons .</command> + </scons_output> diff --git a/doc/user/depends.in b/doc/user/depends.in new file mode 100644 index 0000000..aa69dc7 --- /dev/null +++ b/doc/user/depends.in @@ -0,0 +1,766 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + +<!-- + +=head2 The C<Salt> method + +The C<Salt> method adds a constant value to the signature calculation +for every derived file. It is invoked as follows: + + Salt $string; + +Changing the Salt value will force a complete rebuild of every derived +file. This can be used to force rebuilds in certain desired +circumstances. For example, + + Salt `uname -s`; + +Would force a complete rebuild of every derived file whenever the +operating system on which the build is performed (as reported by C<uname +-s>) changes. + +--> + + <para> + + So far we've seen how &SCons; handles one-time builds. + But the real point of a build tool like &SCons; + is to rebuild only the necessary things + when source files change--or, put another way, + &SCons; should <emphasis>not</emphasis> + waste time rebuilding things that have already been built. + You can see this at work simply be re-invoking &SCons; + after building our simple &hello; example: + + </para> + + <scons_example name="ex1"> + <file name="SConstruct"> + env = Environment() + env.Program('hello.c') + </file> + <file name="hello.c"> + int main() { printf("Hello, world!\n"); } + </file> + </scons_example> + + <scons_output example="ex1" os="posix"> + <command>scons</command> + <command>scons</command> + </scons_output> + + <para> + + The second time it is executed, + &SCons; realizes that the &hello; program + is up-to-date with respect to the current &hello_c; source file, + and avoids rebuilding it. + You can see this more clearly by naming + the &hello; program explicitly on the command line: + + </para> + + <scons_output example="ex1" os="posix"> + <command>scons hello</command> + <command>scons hello</command> + </scons_output> + + <para> + + Note that &SCons; reports <literal>"...is up to date"</literal> + only for target files named explicitly on the command line, + to avoid cluttering the output. + + </para> + + <section> + <title>Source File Signatures</title> + + <para> + + The other side of avoiding unnecessary rebuilds + is the fundamental build tool behavior + of <emphasis>rebuilding</emphasis> + things when a source file changes, + so that the built software is up to date. + &SCons; keeps track of this through a + &signature; for each source file, + and allows you to configure + whether you want to use the source + file contents or the modification time (timestamp) + as the signature. + + </para> + + <section> + <title>MD5 Source File Signatures</title> + + <para> + + By default, + &SCons; keeps track of whether a source file has changed + based on the file's contents, + not the modification time. + This means that you may be surprised by the + default &SCons; behavior if you are used to the + &Make; convention of forcing + a rebuild by updating the file's modification time + (using the &touch; command, for example): + + </para> + + <scons_output example="ex1" os="posix"> + <command>scons hello</command> + <command>touch hello.c</command> + <command>scons hello</command> + </scons_output> + + <para> + + Even though the file's modification time has changed, + &SCons; realizes that the contents of the + &hello_c; file have <emphasis>not</emphasis> changed, + and therefore that the &hello; program + need not be rebuilt. + This avoids unnecessary rebuilds when, + for example, someone rewrites the + contents of a file without making a change. + But if the contents of the file really do change, + then &SCons; detects the change + and rebuilds the program as required: + + </para> + + <scons_output example="ex1" os="posix"> + <command>scons hello</command> + <command output="[CHANGE THE CONTENTS OF hello.c]">edit hello.c</command> + <command>scons hello</command> + </scons> + + <!-- + + <literallayout> + % <userinput>scons hello</userinput> + cc -c hello.c -o hello.o + cc -o hello hello.o + % <userinput>edit hello.c</userinput> + [CHANGE THE CONTENTS OF hello.c] + % <userinput>scons hello</userinput> + cc -c hello.c -o hello.o + cc -o hello hello.o + % + </literallayout> + + --> + + <para> + + Note that you can, if you wish, + specify this default behavior + (MD5 signatures) explicitly + using the &SourceSignatures; function as follows: + + </para> + + <sconstruct> + env = Environment() + env.Program('hello.c') + SourceSignatures('MD5') + </sconstruct> + + </section> + + <section> + <title>Source File Time Stamps</title> + + <para> + + If you prefer, you can + configure &SCons; to use the modification time + of source files, + not the file contents, + when deciding if something needs to be rebuilt. + To do this, call the &SourceSignatures; + function as follows: + + </para> + + <scons_example name="ex2"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Program('hello.c') + SourceSignatures('timestamp') + </file> + <file name="hello.c"> + int main() { printf("Hello, world!\n"); } + </file> + </scons_example> + + <para> + + This makes &SCons; act like &Make; + when a file's modification time is updated + (using the &touch; command, for example): + + </para> + + <scons_output example="ex1" os="posix"> + <command>scons hello</command> + <command>touch hello.c</command> + <command>scons hello</command> + </scons> + + </section> + + </section> + + <section> + <title>Target File Signatures</title> + + <para> + + As you've just seen, + &SCons; uses signatures to decide whether a + target file is up to date or must be rebuilt. + When a target file depends on another target file, + &SCons; allows you to separately configure + how the signatures of an "intermediate" target file + is used when deciding if a dependent target file + must be rebuilt. + + </para> + + <section> + <title>Build Signatures</title> + + <para> + + Modifying a source file + will cause not only its direct target file to be rebuilt, + but also the target file(s) + that depend on that direct target file. + In our example, + changing the contents of the &hello_c; file causes + the &hello_o; file to be rebuilt, + which in turn causes the + &hello; program to be rebuilt: + + </para> + + <scons_output example="ex1" os="posix"> + <command>scons hello</command> + <command output="[CHANGE THE CONTENTS OF hello.c]">edit hello.c</command> + <command>scons hello</command> + </scons> + + <!-- + + <literallayout> + % <userinput>scons hello</userinput> + cc -c hello.c -o hello.o + cc -o hello hello.o + % <userinput>edit hello.c</userinput> + [CHANGE THE CONTENTS OF hello.c] + % <userinput>scons hello</userinput> + cc -c hello.c -o hello.o + cc -o hello hello.o + % + </literallayout> + + --> + + <para> + + What's not obvious, though, + is that &SCons; internally handles the signature of + the target file(s) + (&hello_o; in the above example) + differently from the signature of the source file + (&hello_c;). + By default, + &SCons; tracks whether a target file must be rebuilt + by using a &buildsignature; + that consists of the combined + signatures of all the files + that go into making the target file. + This is efficient because + the accumulated signatures + actually give &SCons; all of the + information it needs + to decide if the target file is out of date. + + </para> + + <para> + + If you wish, you can + specify this default behavior + (build signatures) explicitly + using the &TargetSignatures; function: + + </para> + + <sconstruct> + env = Environment() + env.Program('hello.c') + TargetSignatures('build') + </sconstruct> + + </section> + + <section> + <title>File Contents</title> + + <para> + + Sometimes a source file can be changed + in such a way that the contents of the + rebuilt target file(s) + will be exactly the same as the last time + the file was built. + If so, then any other target files + that depend on such a built-but-not-changed target + file actually need not be rebuilt. + You can have &SCons; + realize that a dependent target file + need not be rebuilt in this situation + using the &TargetSignatures; function as follows: + + </para> + + <scons_example name="ex3"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Program('hello.c') + TargetSignatures('content') + </file> + <file name="hello.c"> + int main() { printf("Hello, world!\n"); } + </file> + </scons_example> + + <para> + + So if, for example, + a user were to only change a comment in a C file, + then the rebuilt &hello_o; file + would be exactly the same as the one previously built + (assuming the compiler doesn't put any build-specific + information in the object file). + &SCons; would then realize that it would not + need to rebuild the &hello; program as follows: + + </para> + + <scons_output example="ex3" os="posix"> + <command>scons hello</command> + <command output="[CHANGE A COMMENT IN hello.c]">edit hello.c</command> + <command>scons hello</command> + </scons> + + <para> + + In essence, &SCons; has + "short-circuited" any dependent builds + when it realizes that a target file + has been rebuilt to exactly the same file as the last build. + So configured, + &SCons; does take some extra processing time + to scan the contents of the target (&hello_o;) file, + but this may save time + if the rebuild that was avoided + would have been very time-consuming and expensive. + + </para> + + </section> + + </section> + + <section> + <title>Implicit Dependencies: The &CPPPATH; Construction Variable</title> + + <para> + + Now suppose that our "Hello, World!" program + actually has a <literal>#include</literal> line + to include the &hello_h; file in the compilation: + + </para> + + <scons_example name="ex4"> + <file name="SConstruct"> + env = Environment(CPPPATH = '.') + hello = env.Program('hello.c') + </file> + <file name="hello.c" printme="1"> + #include "hello.h" + int + main() + { + printf("Hello, %s!\n", string); + } + </file> + <file name="hello.h"> + #define string "world" + </file> + </scons_example> + + <para> + + And, for completeness, the &hello_h; file looks like this: + + </para> + + <scons_example_file example="ex4" name="hello.h"> + </scons_example_file> + + <para> + + In this case, we want &SCons; to recognize that, + if the contents of the &hello_h; file change, + the &hello; program must be recompiled. + To do this, we need to modify the + &SConstruct; file like so: + + </para> + + <scons_example_file example="ex4" name="SConstruct"> + </scons_example_file> + + <para> + + The &CPPPATH; assignment in the &Environment; call + tells &SCons; to look in the current directory + (<literal>'.'</literal>) + for any files included by C source files + (<filename>.c</filename> or <filename>.h</filename> files). + With this assignment in the &SConstruct; file: + + </para> + + <scons_output example="ex4" os="posix"> + <command>scons hello</command> + <command>scons hello</command> + <command output="[CHANGE THE CONTENTS IN hello.h]">edit hello.h</command> + <command>scons hello</command> + </scons> + + <para> + + First, notice that &SCons; + added the <literal>-I.</literal> argument + from the &CPPPATH; variable + so that the compilation would find the + &hello_h; file in the local directory. + + </para> + + <para> + + Second, realize that &SCons; knows that the &hello; + program must be rebuilt + because it scans the contents of + the &hello_c; file + for the <literal>#include</literal> lines that indicate + another file is being included in the compilation. + &SCons; records these as + <emphasis>implicit dependencies</emphasis> + of the target file, + Consequently, + when the &hello_h; file changes, + &SCons; realizes that the &hello_c; file includes it, + and rebuilds the resulting &hello; program + that depends on both the &hello_c; and &hello_h; files. + + </para> + + <para> + + Like the &LIBPATH; variable, + the &CPPPATH; variable + may be a list of directories, + or a string separated by + the system-specific path separate character + (':' on POSIX/Linux, ';' on Windows). + Either way, &SCons; creates the + right command-line options + so that the following example: + + </para> + + <programlisting> + </programlisting> + + <scons_example name="ex5"> + <file name="SConstruct"> + env = Environment(CPPPATH = ['include', '/home/project/inc']) + hello = env.Program('hello.c') + </file> + <file name="hello.c"> + int main() { printf("Hello, world!\n"); } + </file> + </scons_example> + + <para> + + Will look like this on POSIX or Linux: + + </para> + + <scons_output example="ex4" os="posix"> + <command>scons hello</command> + </scons_output> + + <para> + + And like this on Windows: + + </para> + + <scons_output example="ex4" os="win32"> + <command>scons hello</command> + </scons_output> + + </section> + + <section> + <title>Caching Implicit Dependencies</title> + + <para> + + Scanning each file for <literal>#include</literal> lines + does take some extra processing time. + When you're doing a full build of a large system, + the scanning time is usually a very small percentage + of the overall time spent on the build. + You're most likely to notice the scanning time, + however, when you <emphasis>rebuild</emphasis> + all or part of a large system: + &SCons; will likely take some extra time to "think about" + what must be built before it issues the + first build command + (or decides that everything is up to date + and nothing must be rebuilt). + + <!-- + Isn't this expensive? The answer is, it depends. If you do a full build of a + large system, the scanning time is insignificant. If you do a rebuild of a + large system, then Cons will spend a fair amount of time thinking about it + before it decides that nothing has to be done (although not necessarily more + time than make!). The good news is that Cons makes it very easy to + intelligently subset your build, when you are working on localized changes. + --> + + </para> + + <para> + + In practice, having &SCons; scan files saves time + relative to the amount of potential time + lost to tracking down subtle problems + introduced by incorrect dependencies. + Nevertheless, the "waiting time" + while &SCons; scans files can annoy + individual developers waiting for their builds to finish. + Consequently, &SCons; lets you cache + the implicit dependencies + that its scanners find, + for use by later builds. + You do this either by specifying the + &implicit-cache; option on the command line: + + </para> + + <scons_output example="ex1" os="win32"> + <command>scons --implicit-cache hello</command> + <command>scons hello</command> + </scons_output> + + <para> + + Or by setting the &implicit_cache; option + in an &SConscript; file: + + </para> + + <sconstruct> + SetOption('implicit_cache', 1) + </sconstruct> + + <para> + + &SCons; does not cache implicit dependencies like this by default + because XXX + + </para> + + <para> + + XXX + + </para> + + <section> + <title>The &implicit-deps-changed; Option</title> + + <para> + + XXX + + </para> + + </section> + + <section> + <title>The &implicit-deps-unchanged; Option</title> + + <para> + + XXX + + </para> + + </section> + + </section> + + <section> + <title>The &Ignore; Method</title> + + <para> + + Sometimes it makes sense + to not rebuild a program, + even if a dependency file changes. + In this case, + you would tell &SCons; specifically + to ignore a dependency as follows: + + </para> + + <programlisting> + env = Environment() + hello = env.Program('hello.c') + env.Ignore(hello, 'hello.h') + </programlisting> + + <!-- XXX mention that you can use arrays for target and source? --> + + <literallayout> + % <userinput>scons hello</userinput> + cc -c hello.c -o hello.o + cc -o hello hello.o + % <userinput>scons hello</userinput> + scons: `hello' is up to date. + % <userinput>edit hello.h</userinput> + [CHANGE THE CONTENTS OF hello.h] + % <userinput>scons hello</userinput> + scons: `hello' is up to date. + </literallayout> + + <para> + + Now, the above example is a little contrived, + because it's hard to imagine a real-world situation + where you wouldn't to rebuild &hello; + if the &hello_h; file changed. + A more realistic example + might be if the &hello; + program is being built in a + directory that is shared between multiple systems + that have different copies of the + &stdio_h; include file. + In that case, + &SCons; would notice the differences between + the different systems' copies of &stdio_h; + and would rebuild &hello; + each time you change systems. + You could avoid these rebuilds as follows: + + </para> + + <programlisting> + env = Environment() + hello = env.Program('hello.c') + env.Ignore(hello, '/usr/include/stdio.h') + </programlisting> + + </section> + + <section> + <title>The &Depends; Method</title> + + <para> + + On the other hand, + sometimes a file depends on another file + that has no &SCons; scanner will detect. + For this situation, + &SCons; allows you to specific explicitly that one file + depends on another file, + and must be rebuilt whenever that file changes. + This is specified using the &Depends; method: + + </para> + + <programlisting> + env = Environment() + hello = env.Program('hello.c') + env.Depends(hello, 'other_file') + </programlisting> + + <!-- XXX mention that you can use arrays for target and source? --> + + <literallayout> + % <userinput>scons hello</userinput> + cc -c hello.c -o hello.o + cc -o hello hello.o + % <userinput>scons hello</userinput> + scons: `hello' is up to date. + % <userinput>edit other_file</userinput> + [CHANGE THE CONTENTS OF other_file] + % <userinput>scons hello</userinput> + cc -c hello.c -o hello.o + cc -o hello hello.o + </literallayout> + + </section> + + <!--> + + <section> + <title>The &Salt; Method</title> + + <para> + + XXX + + </para> + + </section> + + --> diff --git a/doc/user/environments.in b/doc/user/environments.in new file mode 100644 index 0000000..7f022c1 --- /dev/null +++ b/doc/user/environments.in @@ -0,0 +1,829 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + +<!-- + +=head1 More on construction environments + +As previously mentioned, a B<construction environment> is an object that +has a set of keyword/value pairs and a set of methods, and which is used +to tell Cons how target files should be built. This section describes +how Cons uses and expands construction environment values to control its +build behavior. + +=head2 Construction variable expansion + +Construction variables from a construction environment are expanded +by preceding the keyword with a C<%> (percent sign): + + Construction variables: + XYZZY => 'abracadabra', + + The string: "The magic word is: %XYZZY!" + expands to: "The magic word is: abracadabra!" + +A construction variable name may be surrounded by C<{> and C<}> (curly +braces), which are stripped as part of the expansion. This can +sometimes be necessary to separate a variable expansion from trailing +alphanumeric characters: + + Construction variables: + OPT => 'value1', + OPTION => 'value2', + + The string: "%OPT %{OPT}ION %OPTION %{OPTION}" + expands to: "value1 value1ION value2 value2" + +Construction variable expansion is recursive, that is, a string +containing C<%->expansions after substitution will be re-expanded until +no further substitutions can be made: + + Construction variables: + STRING => 'The result is: %FOO', + FOO => '%BAR', + BAR => 'final value', + + The string: "The string says: %STRING" + expands to: "The string says: The result is: final value" + +If a construction variable is not defined in an environment, then the +null string is substituted: + + Construction variables: + FOO => 'value1', + BAR => 'value2', + + The string: "%FOO <%NO_VARIABLE> %BAR" + expands to: "value1 <> value2" + +A doubled C<%%> will be replaced by a single C<%>: + + The string: "Here is a percent sign: %%" + expands to: "Here is a percent sign: %" + +=head2 Default construction variables + +When you specify no arguments when creating a new construction +environment: + + $env = new cons(); + +Cons creates a reference to a new, default construction +environment. This contains a number of construction variables and some +methods. At the present writing, the default construction variables on a +UNIX system are: + + CC => 'cc', + CFLAGS => '', + CCCOM => '%CC %CFLAGS %_IFLAGS -c %< -o %>', + CXX => '%CC', + CXXFLAGS => '%CFLAGS', + CXXCOM => '%CXX %CXXFLAGS %_IFLAGS -c %< -o %>', + INCDIRPREFIX => '-I', + INCDIRSUFFIX => '', + LINK => '%CXX', + LINKCOM => '%LINK %LDFLAGS -o %> %< %_LDIRS %LIBS', + LINKMODULECOM => '%LD -r -o %> %<', + LIBDIRPREFIX => '-L', + LIBDIRSUFFIX => '', + AR => 'ar', + ARFLAGS => 'r', + ARCOM => ['%AR %ARFLAGS %> %<', '%RANLIB %>'], + RANLIB => 'ranlib', + AS => 'as', + ASFLAGS => '', + ASCOM => '%AS %ASFLAGS %< -o %>', + LD => 'ld', + LDFLAGS => '', + PREFLIB => 'lib', + SUFLIB => '.a', + SUFLIBS => '.so:.a', + SUFOBJ => '.o', + SIGNATURE => [ '*' => 'build' ], + ENV => { 'PATH' => '/bin:/usr/bin' }, + + +And on a Win32 system (Windows NT), the default construction variables +are (unless the default rule style is set using the B<DefaultRules> +method): + + CC => 'cl', + CFLAGS => '/nologo', + CCCOM => '%CC %CFLAGS %_IFLAGS /c %< /Fo%>', + CXXCOM => '%CXX %CXXFLAGS %_IFLAGS /c %< /Fo%>', + INCDIRPREFIX => '/I', + INCDIRSUFFIX => '', + LINK => 'link', + LINKCOM => '%LINK %LDFLAGS /out:%> %< %_LDIRS %LIBS', + LINKMODULECOM => '%LD /r /o %> %<', + LIBDIRPREFIX => '/LIBPATH:', + LIBDIRSUFFIX => '', + AR => 'lib', + ARFLAGS => '/nologo ', + ARCOM => "%AR %ARFLAGS /out:%> %<", + RANLIB => '', + LD => 'link', + LDFLAGS => '/nologo ', + PREFLIB => '', + SUFEXE => '.exe', + SUFLIB => '.lib', + SUFLIBS => '.dll:.lib', + SUFOBJ => '.obj', + SIGNATURE => [ '*' => 'build' ], + +These variables are used by the various methods associated with the +environment. In particular, any method that ultimately invokes an external +command will substitute these variables into the final command, as +appropriate. For example, the C<Objects> method takes a number of source +files and arranges to derive, if necessary, the corresponding object +files: + + Objects $env 'foo.c', 'bar.c'; + +This will arrange to produce, if necessary, F<foo.o> and F<bar.o>. The +command invoked is simply C<%CCCOM>, which expands, through substitution, +to the appropriate external command required to build each object. The +substitution rules will be discussed in detail in the next section. + +The construction variables are also used for other purposes. For example, +C<CPPPATH> is used to specify a colon-separated path of include +directories. These are intended to be passed to the C preprocessor and are +also used by the C-file scanning machinery to determine the dependencies +involved in a C Compilation. + +Variables beginning with underscore are created by various methods, +and should normally be considered ``internal'' variables. For example, +when a method is called which calls for the creation of an object from +a C source, the variable C<_IFLAGS> is created: this corresponds to the +C<-I> switches required by the C compiler to represent the directories +specified by C<CPPPATH>. + +Note that, for any particular environment, the value of a variable is set +once, and then never reset (to change a variable, you must create a new +environment. Methods are provided for copying existing environments for this +purpose). Some internal variables, such as C<_IFLAGS> are created on demand, +but once set, they remain fixed for the life of the environment. + +The C<CFLAGS>, C<LDFLAGS>, and C<ARFLAGS> variables all supply a place +for passing options to the compiler, loader, and archiver, respectively. + +The C<INCDIRPREFIX> and C<INCDIRSUFFIX> variables specify option +strings to be appended to the beginning and end, respectively, of each +include directory so that the compiler knows where to find F<.h> files. +Similarly, the C<LIBDIRPREFIX> and C<LIBDIRSUFFIX> variables specify the +option string to be appended to the beginning of and end, respectively, +of each directory that the linker should search for libraries. + +Another variable, C<ENV>, is used to determine the system environment during +the execution of an external command. By default, the only environment +variable that is set is C<PATH>, which is the execution path for a UNIX +command. For the utmost reproducibility, you should really arrange to set +your own execution path, in your top-level F<Construct> file (or perhaps by +importing an appropriate construction package with the Perl C<use> +command). The default variables are intended to get you off the ground. + +=head2 Expanding variables in construction commands + +Within a construction command, construction variables will be expanded +according to the rules described above. In addition to normal variable +expansion from the construction environment, construction commands also +expand the following pseudo-variables to insert the specific input and +output files in the command line that will be executed: + +=over 10 + +=item %> + +The target file name. In a multi-target command, this expands to the +first target mentioned.) + +=item %0 + +Same as C<%E<gt>>. + +=item %1, %2, ..., %9 + +These refer to the first through ninth input file, respectively. + +=item %E<lt> + +The full set of input file names. If any of these have been used +anywhere else in the current command line (via C<%1>, C<%2>, etc.), then +those will be deleted from the list provided by C<%E<lt>>. Consider the +following command found in a F<Conscript> file in the F<test> directory: + + Command $env 'tgt', qw(foo bar baz), qq( + echo %< -i %1 > %> + echo %< -i %2 >> %> + echo %< -i %3 >> %> + ); + +If F<tgt> needed to be updated, then this would result in the execution of +the following commands, assuming that no remapping has been established for +the F<test> directory: + + echo test/bar test/baz -i test/foo > test/tgt + echo test/foo test/baz -i test/bar >> test/tgt + echo test/foo test/bar -i test/baz >> test/tgt + +=back + +Any of the above pseudo-variables may be followed immediately by one of +the following suffixes to select a portion of the expanded path name: + + :a the absolute path to the file name + :b the directory plus the file name stripped of any suffix + :d the directory + :f the file name + :s the file name suffix + :F the file name stripped of any suffix + :S the absolute path path to a Linked source file + +Continuing with the above example, C<%E<lt>:f> would expand to C<foo bar baz>, +and C<%E<gt>:d> would expand to C<test>. + +There are additional C<%> elements which affect the command line(s): + +=over 10 + +=item %[ %] + +It is possible to programmatically rewrite part of the command by +enclosing part of it between C<%[> and C<%]>. This will call the +construction variable named as the first word enclosed in the brackets +as a Perl code reference; the results of this call will be used to +replace the contents of the brackets in the command line. For example, +given an existing input file named F<tgt.in>: + + @keywords = qw(foo bar baz); + $env = new cons(X_COMMA => sub { join(",", @_) }); + Command $env 'tgt', 'tgt.in', qq( + echo '# Keywords: %[X_COMMA @keywords %]' > %> + cat %< >> %> + ); + +This will execute: + + echo '# Keywords: foo,bar,baz' > tgt + cat tgt.in >> tgt + +=item %( %) + +Cons includes the text of the command line in the MD5 signature for a +build, so that targets get rebuilt if you change the command line (to +add or remove an option, for example). Command-line text in between +C<%(> and C<%)>, however, will be ignored for MD5 signature calculation. + +Internally, Cons uses C<%(> and C<%)> around include and library +directory options (C<-I> and C<-L> on UNIX systems, C</I> and +C</LIBPATH> on Windows NT) to avoid rebuilds just because the directory +list changes. Rebuilds occur only if the changed directory list causes +any included I<files> to change, and a changed include file is detected +by the MD5 signature calculation on the actual file contents. + +=back + +XXX + +DESCRIBE THE Literal() FUNCTION, TOO + +XXX + +=head2 Expanding construction variables in file names + +Cons expands construction variables in the source and target file names +passed to the various construction methods according to the expansion +rules described above: + + $env = new cons( + DESTDIR => 'programs', + SRCDIR => 'src', + ); + Program $env '%DESTDIR/hello', '%SRCDIR/hello.c'; + +This allows for flexible configuration, through the construction +environment, of directory names, suffixes, etc. + +=head1 Default construction methods + +The list of default construction methods includes the following: + + +=head2 The C<new> constructor + +The C<new> method is a Perl object constructor. That is, it is not invoked +via a reference to an existing construction environment B<reference>, but, +rather statically, using the name of the Perl B<package> where the +constructor is defined. The method is invoked like this: + + $env = new cons(<overrides>); + +The environment you get back is blessed into the package C<cons>, which +means that it will have associated with it the default methods described +below. Individual construction variables can be overridden by providing +name/value pairs in an override list. Note that to override any command +environment variable (i.e. anything under C<ENV>), you will have to override +all of them. You can get around this difficulty by using the C<copy> method +on an existing construction environment. + + +=head2 The C<clone> method + +The C<clone> method creates a clone of an existing construction environment, +and can be called as in the following example: + + $env2 = $env1->clone(<overrides>); + +You can provide overrides in the usual manner to create a different +environment from the original. If you just want a new name for the same +environment (which may be helpful when exporting environments to existing +components), you can just use simple assignment. + + +=head2 The C<copy> method + +The C<copy> method extracts the externally defined construction variables +from an environment and returns them as a list of name/value +pairs. Overrides can also be provided, in which case, the overridden values +will be returned, as appropriate. The returned list can be assigned to a +hash, as shown in the prototype, below, but it can also be manipulated in +other ways: + + %env = $env1->copy(<overrides>); + +The value of C<ENV>, which is itself a hash, is also copied to a new hash, +so this may be changed without fear of affecting the original +environment. So, for example, if you really want to override just the +C<PATH> variable in the default environment, you could do the following: + + %cons = new cons()->copy(); + $cons{ENV}{PATH} = "<your path here>"; + $cons = new cons(%cons); + +This will leave anything else that might be in the default execution +environment undisturbed. + +--> + + <para> + + It is rare that all of the software in a large, + complicated system needs to be built the same way. + For example, different source files may need different options + enabled on the command line, + or different executable programs need to be linked + with different libraries. + &SCons; accomodates these different build + requirements by allowing you to create and + configure multiple &consenvs; + that control how the software is built. + Technically, a &consenv; is an object + that has a number of associated + &consvars;, each with a name and a value. + (A &consenv; also has an attached + set of &Builder; methods, + about which we'll learn more later.) + + </para> + + <para> + + A &consenv; is created by the &Environment; + method which you have already seen. + What you haven't seen, though, + is that when you initialize a &consenv;, + you can set the values of the + environment's &consvars; + to control how a program is built. + For example: + + </para> + + <scons_example name="ex1"> + <file name="SConstruct"> + env = Environment(CC = 'gcc', + CCFLAGS = '-O2') + + env.Program('foo.c') + </file> + <file name="foo.c"> + int main() { } + </file> + </scons_example> + + <para> + + This example, rather than using the default, + explicitly specifies use of the + GNU C compiler &gcc;, + and further specifies that the <literal>-O2</literal> + (optimization level two) + flag should be used when compiling the object file. + So a run from this example would look like: + + </para> + + <scons_output example="ex1"> + <command>scons</command> + </scons_output> + + <section> + <title>Multiple &ConsEnvs;</title> + + <para> + + So far, + all of our examples have + created a single &consenv; named + <literal>env</literal>. + <literal>env</literal>, however, + is simply a Python variable name, + and you can use any other variable name that you like. + For example: + + </para> + + <sconstruct> + my_env = Environment(CC = 'gcc', + CCFLAGS = '-O2') + + my_env.Program('foo.c') + </sconstruct> + + <para> + + This opens up the possibility of + using multiple &consenvs;, + each with a separate variable name. + We can then use these separate &consenvs; + to build different programs in different ways: + + </para> + + <scons_example name="ex2"> + <file name="SConstruct"> + opt = Environment(CCFLAGS = '-O2') + dbg = Environment(CCFLAGS = '-g') + + opt.Program('foo', 'foo.c') + + dbg.Program('bar', 'bar.c') + </file> + <file name="foo.c"> + int main() { } + </file> + <file name="bar.c"> + int main() { } + </file> + </scons_example> + + <scons_output example="ex2"> + <command>scons</command> + </scons_output> + + <para> + + We can even use multiple &consenvs; to build + multiple versions of a single program. + If you do this by simply trying to use the + &Program; builder with both environments, though, + like this: + + </para> + + <scons_example name="ex3"> + <file name="SConstruct"> + opt = Environment(CCFLAGS = '-O2') + dbg = Environment(CCFLAGS = '-g') + + opt.Program('foo', 'foo.c') + + dbg.Program('foo', 'foo.c') + </file> + <file name="foo.c"> + int main() { } + </file> + </scons_example> + + <para> + + Then &SCons; generates the following error: + + </para> + + <scons_output example="ex3"> + <command>scons</command> + </scons_output> + + <para> + + This is because the two &Program; calls have + each implicitly told &SCons; to generate an object file named + <filename>foo.o</filename>, + one with a &CCFLAGS; value of + <literal>-O2</literal> + and one with a &CCFLAGS; value of + <literal>-g</literal>. + To avoid this problem, + we must explicitly specify + that each environment compile + <filename>foo.c</filename> + to a separately-named object file + using the &Object; call, like so: + + </para> + + <programlisting> + </programlisting> + + <scons_example name="ex4"> + <file name="SConstruct"> + opt = Environment(CCFLAGS = '-O2') + dbg = Environment(CCFLAGS = '-g') + + o = opt.Object('foo-opt', 'foo.c') + opt.Program(o) + + d = dbg.Object('foo-dbg', 'foo.c') + dbg.Program(d) + </file> + <file name="foo.c"> + int main() { } + </file> + </scons_example> + + <para> + + Notice that each call to the &Object; builder + returns a value, + an internal &SCons; object that + represents the file that will be built. + We then use that object + as input to the &Program; builder. + This avoids having to specify explicitly + the object file name in multiple places, + and makes for a compact, readable + &SConstruct; file. + Our &SCons; output then looks like: + + </para> + + <scons_output example="ex4"> + <command>scons</command> + </scons_output> + + </section> + + <section> + <title>Copying &ConsEnvs;</title> + + <para> + + Sometimes you want more than one &consenv; + to share the same values for one or more variables. + Rather than always having to repeat all of the common + variables when you create each &consenv;, + you can use the &Copy; method + to create a copy of a &consenv;. + + </para> + + <para> + + Like the &Environment; call that creates a &consenv;, + the &Copy; method takes &consvar; assignments, + which will override the values in the copied &consenv;. + For example, suppose we want to use &gcc; + to create three versions of a program, + one optimized, one debug, and one with neither. + We could do this by creating a "base" &consenv; + that sets &CC; to &gcc;, + and then creating two copies, + one which sets &CCFLAGS; for optimization + and the other with sets &CCFLAGS; for debugging: + + </para> + + <scons_example name="ex5"> + <file name="SConstruct" printme="1"> + env = Environment(CC = 'gcc') + opt = env.Copy(CCFLAGS = '-O2') + dbg = env.Copy(CCFLAGS = '-g') + + e = opt.Object('foo', 'foo.c') + + o = opt.Object('foo-opt', 'foo.c') + opt.Program(o) + + d = dbg.Object('foo-dbg', 'foo.c') + dbg.Program(d) + </file> + <file name="foo.c"> + int main() { } + </file> + </scons_example> + + <para> + + Then our output would look like: + + </para> + + <scons_output example="ex5"> + <command>scons</command> + </scons_output> + + </section> + + <section> + <title>Fetching Values From a &ConsEnv;</title> + + <para> + + You can fetch individual construction variables + using the normal syntax + for accessing individual named items in a Python dictionary: + + </para> + + <scons_example name="ex6"> + <file name="SConstruct" printme="1"> + env = Environment() + print "CC is:", env['CC'] + </file> + </scons_example> + + <para> + + This example &SConstruct; file doesn't build anything, + but because it's actually a Python script, + it will print the value of &CC; for us: + + </para> + + <scons_output example="ex6"> + <command>scons</command> + CC is: cc + </scons_output> + + <para> + + A &consenv;, however, + is actually a Python object with + associated methods, etc. + If you want to have direct access to only the + dictionary of construction variables, + you can fetch this using the &Dictionary; method: + + </para> + + <scons_example name="ex6b"> + <file name="SConstruct" printme="1"> + env = Environment(FOO = 'foo', BAR = 'bar') + dict = env.Dictionary() + for key, value in dict.items(): + print "key = %s, value = %s % (key, value) + </file> + </scons_Example> + + <para> + + This &SConstruct; file + will print the dictionary items for us as follows: + + </para> + + <scons_output example="ex6b"> + % <userinput>scons</userinput> + key = FOO, value = foo + key = BAR, value = bar + </scons_output> + + </section> + + <section> + <title>Modifying a &ConsEnv;</title> + + <para> + + &SCons; provides various methods that + support modifying existing values in a &consenv;. + + </para> + + <section> + <title>Replacing Values in a &ConsEnv;</title> + + <para> + + You can replace existing construction variable values + using the &Replace; method: + + </para> + + <scons_example name="ex7"> + <file name="SConstruct" printme="1"> + env = Environment(CCFLAGS = '-DDEFINE1') + env.Program('foo.c') + env.Replace(CCFLAGS = '-DDEFINE2') + env.Program('bar.c') + </file> + <file name="foo.c"> + int main() { } + </file> + <file name="bar.c"> + int main() { } + </file> + </scons_example> + + <para> + + The replaced value completely overwrites + + </para> + + <scons_output example="ex7"> + <command>scons</command> + </scons_output> + + </section> + + <section> + <title>Appending to the End of Values in a &ConsEnv;</title> + + <para> + + You can append a value to + an existing construction variable + using the &Append; method: + + </para> + + <scons_example name="ex8"> + <file name="SConstruct" printme="1"> + env = Environment(CCFLAGS = '-DMY_VALUE') + env.Append(CCFLAGS = ' -DLAST') + env.Program('foo.c') + </file> + <file name="foo.c"> + int main() { } + </file> + </scons_example> + + <scons_output example="ex8"> + <command>scons</command> + </scons_output> + + </section> + + <section> + <title>Appending to the Beginning of Values in a &ConsEnv;</title> + + <para> + + You can append a value to the beginning + an existing construction variable + using the &Prepend; method: + + </para> + + <scons_example name="ex9"> + <file name="SConstruct" printme="1"> + env = Environment(CCFLAGS = '-DMY_VALUE') + env.Prepend(CCFLAGS = '-DFIRST ') + env.Program('foo.c') + </file> + <file name="foo.c"> + int main() { } + </file> + </scons_example> + + <scons_output example="ex9"> + <command>scons</command> + </scons_output> + + </section> + + </section> diff --git a/doc/user/errors.in b/doc/user/errors.in new file mode 100644 index 0000000..f83ab63 --- /dev/null +++ b/doc/user/errors.in @@ -0,0 +1,41 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + + <para> + + XXX + + </para> + + <section> + <title>XXX</title> + + <para> + + XXX + + </para> + + </section> diff --git a/doc/user/example.in b/doc/user/example.in new file mode 100644 index 0000000..f83ab63 --- /dev/null +++ b/doc/user/example.in @@ -0,0 +1,41 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + + <para> + + XXX + + </para> + + <section> + <title>XXX</title> + + <para> + + XXX + + </para> + + </section> diff --git a/doc/user/help.in b/doc/user/help.in new file mode 100644 index 0000000..96e775b --- /dev/null +++ b/doc/user/help.in @@ -0,0 +1,77 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + + <para> + + It's often very useful to be able to give + users some help that describes the + specific targets, build options, etc., + that can be used for the build. + &SCons; provides the &Help; function + to allow you to specify this help text: + + </para> + + <scons_example name="ex1"> + <file name="SConstruct"> + Help(""" + Type: 'scons program' to build the production program, + 'scons debug' to build the debug version. + """) + </file> + </scons_example> + + <para> + + (Note the above use of the Python triple-quote syntax, + which comes in very handy for + specifying multi-line strings like help text.) + + </para> + + <para> + + When the &SConstruct; or &SConscript; files + contain such a call to the &Help; function, + the specified help text will be displayed in response to + the &SCons; <literal>-h</literal> option: + + </para> + + <scons_output example="ex1"> + <command>scons -h</command> + </scons_output> + + <para> + + If there is no &Help; text in the &SConstruct; or + &SConscript; files, + &SCons; will revert to displaying its + standard list that describes the &SCons; command-line + options. + This list is also always displayed whenever + the <literal>-H</literal> option is used. + + </para> diff --git a/doc/user/hierarchy.in b/doc/user/hierarchy.in new file mode 100644 index 0000000..5a34be2 --- /dev/null +++ b/doc/user/hierarchy.in @@ -0,0 +1,683 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + +<!-- + + +=head2 The Build command + +By default, Cons does not change its working directory to the directory +containing a subsidiary F<Conscript> file it is including. This behavior +can be enabled for a build by specifying, in the top-level F<Construct> +file: + + Conscript_chdir 1; + +When enabled, Cons will change to the subsidiary F<Conscript> file's +containing directory while reading in that file, and then change back +to the top-level directory once the file has been processed. + +It is expected that this behavior will become the default in some future +version of Cons. To prepare for this transition, builds that expect +Cons to remain at the top of the build while it reads in a subsidiary +F<Conscript> file should explicitly disable this feature as follows: + + Conscript_chdir 0; + +=head2 Relative, top-relative, and absolute file names + +(There is another file prefix, ``!'', that is interpreted specially by +Cons. See discussion of the C<Link> command, below, for details.) + + +=head2 Using modules in build scripts + +You may pull modules into each F<Conscript> file using the normal Perl +C<use> or C<require> statements: + + use English; + require My::Module; + +Each C<use> or C<require> only affects the one F<Conscript> file in which +it appears. To use a module in multiple F<Conscript> files, you must +put a C<use> or C<require> statement in each one that needs the module. + + +=head2 Scope of variables + +The top-level F<Construct> file and all F<Conscript> files begin life in +a common, separate Perl package. B<Cons> controls the symbol table for +the package so that, the symbol table for each script is empty, except +for the F<Construct> file, which gets some of the command line arguments. +All of the variables that are set or used, therefore, are set by the +script itself, not by some external script. + +Variables can be explicitly B<imported> by a script from its parent +script. To import a variable, it must have been B<exported> by the parent +and initialized (otherwise an error will occur). + + +=head2 The Export command + +The C<Export> command is used as in the following example: + + $env = new cons(); + $INCLUDE = "#export/include"; + $LIB = "#export/lib"; + Export qw( env INCLUDE LIB ); + Build qw( util/Conscript ); + +The values of the simple variables mentioned in the C<Export> list will be +squirreled away by any subsequent C<Build> commands. The C<Export> command +will only export Perl B<scalar> variables, that is, variables whose name +begins with C<$>. Other variables, objects, etc. can be exported by +reference, but all scripts will refer to the same object, and this object +should be considered to be read-only by the subsidiary scripts and by the +original exporting script. It's acceptable, however, to assign a new value +to the exported scalar variable, that won't change the underlying variable +referenced. This sequence, for example, is OK: + + $env = new cons(); + Export qw( env INCLUDE LIB ); + Build qw( util/Conscript ); + $env = new cons(CFLAGS => '-O'); + Build qw( other/Conscript ); + +It doesn't matter whether the variable is set before or after the C<Export> +command. The important thing is the value of the variable at the time the +C<Build> command is executed. This is what gets squirreled away. Any +subsequent C<Export> commands, by the way, invalidate the first: you must +mention all the variables you wish to export on each C<Export> command. + + +=head2 The Import command + +Variables exported by the C<Export> command can be imported into subsidiary +scripts by the C<Import> command. The subsidiary script always imports +variables directly from the superior script. Consider this example: + + Import qw( env INCLUDE ); + +This is only legal if the parent script exported both C<$env> and +C<$INCLUDE>. It also must have given each of these variables values. It is +OK for the subsidiary script to only import a subset of the exported +variables (in this example, C<$LIB>, which was exported by the previous +example, is not imported). + +All the imported variables are automatically re-exported, so the sequence: + + Import qw ( env INCLUDE ); + Build qw ( beneath-me/Conscript ); + +will supply both C<$env> and C<$INCLUDE> to the subsidiary file. If only +C<$env> is to be exported, then the following will suffice: + + Import qw ( env INCLUDE ); + Export qw ( env ); + Build qw ( beneath-me/Conscript ); + +Needless to say, the variables may be modified locally before invoking +C<Build> on the subsidiary script. + +=head2 Build script evaluation order + +The only constraint on the ordering of build scripts is that superior +scripts are evaluated before their inferior scripts. The top-level +F<Construct> file, for instance, is evaluated first, followed by any +inferior scripts. This is all you really need to know about the evaluation +order, since order is generally irrelevant. Consider the following C<Build> +command: + + Build qw( + drivers/display/Conscript + drivers/mouse/Conscript + parser/Conscript + utilities/Conscript + ); + +We've chosen to put the script names in alphabetical order, simply because +that's the most convenient for maintenance purposes. Changing the order will +make no difference to the build. + +--> + + <para> + + The source code for large software projects + rarely stays in a single directory, + but is nearly always divided into a + hierarchy of directories. + Organizing a large software build using &SCons; + involves creating a hierarchy of build scripts + using the &SConscript; function. + + </para> + + <section> + <title>&SConscript; Files</title> + + <para> + + As we've already seen, + the build script at the top of the tree is called &SConstruct;. + The top-level &SConstruct; file can + use the &SConscript; function to + include other subsidiary scripts in the build. + These subsidiary scripts can, in turn, + use the &SConscript; function + to include still other scripts in the build. + By convention, these subsidiary scripts are usually + named &SConscript;. + For example, a top-level &SConstruct; file might + arrange for four subsidiary scripts to be included + in the build as follows: + + </para> + + <sconstruct> + SConscript(['drivers/display/SConscript', + 'drivers/mouse/SConscript', + 'parser/SConscript', + 'utilities/SConscript']) + </sconstruct> + + <para> + + In this case, the &SConstruct; file + lists all of the &SConscript; files in the build explicitly. + (Note, however, that not every directory in the tree + necessarily has an &SConscript; file.) + Alternatively, the <literal>drivers</literal> + subdirectory might contain an intermediate + &SConscript; file, + in which case the &SConscript; call in + the top-level &SConstruct; file + would look like: + + </para> + + <sconstruct> + SConscript(['drivers/SConscript', + 'parser/SConscript', + 'utilities/SConscript']) + </sconstruct> + + <para> + + And the subsidiary &SConscript; file in the + <literal>drivers</literal> subdirectory + would look like: + + </para> + + <sconstruct> + SConscript(['display/SConscript', + 'mouse/SConscript']) + </sconstruct> + + <para> + + Whether you list all of the &SConscript; files in the + top-level &SConstruct; file, + or place a subsidiary &SConscript; file in + intervening directories, + or use some mix of the two schemes, + is up to you and the needs of your software. + + </para> + + </section> + + <section> + <title>Path Names Are Relative to the &SConscript; Directory</title> + + <para> + + Subsidiary &SConscript; files make it easy to create a build + hierarchy because all of the file and directory names + in a subsidiary &SConscript; files are interpreted + relative to the directory in which the &SConscript; file lives. + Typically, this allows the &SConscript; file containing the + instructions to build a target file + to live in the same directory as the source files + from which the target will be built, + making it easy to update how the software is built + whenever files are added or deleted + (or other changes are made). + + </para> + + <para> + + For example, suppose we want to build two programs + &prog1; and &prog2; in two separate directories + with the same names as the programs. + One typical way to do this would be + with a top-level &SConstruct; file like this: + + </para> + + <scons_example name="ex1"> + <file name="SConstruct" printme="1"> + SConscript(['prog1/SConscript', + 'prog2/SConscript']) + </file> + <file name="prog1/SConscript"> + env = Environment() + env.Program('prog1', ['main.c', 'foo1.c', 'foo2.c']) + </file> + <file name="prog2/SConscript"> + env = Environment() + env.Program('prog2', ['main.c', 'bar1.c', 'bar2.c']) + </file> + <directory name="prog1"></directory> + <file name="prog1/main.c"> + x + </file> + <file name="prog1/foo1.c"> + x + </file> + <file name="prog1/foo2.c"> + x + </file> + <directory name="prog2"></directory> + <file name="prog2/main.c"> + x + </file> + <file name="prog2/bar1.c"> + x + </file> + <file name="prog2/bar2.c"> + x + </file> + </scons_example> + + <para> + + And subsidiary &SConscript; files that look like this: + + </para> + + <scons_example_file example="ex1" name="prog1/SConscript"> + </scons_example_file> + + <para> + + And this: + + </para> + + <scons_example_file example="ex1" name="prog2/SConscript"> + </scons_example_file> + + <para> + + Then, when we run &SCons; in the top-level directory, + our build looks like: + + </para> + + <scons_output example="ex1"> + <command>scons</command> + </scons_output> + + <para> + + Notice the following: + + First, you can have files with the same names + in multiple directories, like main.c in the above example. + + Second, unlike standard recursive use of &Make;, + &SCons; stays in the top-level directory and + issues commands + + </para> + + </section> + + <section> + <title>Top-Level Path Names in Subsidiary &SConscript; Files</title> + + <para> + + If you need to use a file from another directory, + it's sometimes more convenient to specify + the path to a file in another directory + from the top-level &SConstruct; directory, + even when you're using that file in + a subsidiary &SConscript; file in a subdirectory. + You can tell &SCons; to interpret a path name + as relative to the top-level &SConstruct; directory, + not the local directory of the &SConscript; file, + by appending a &hash; (hash mark) + to the beginning of the path name: + + + </para> + + <scons_example name="ex2"> + <file name="SConstruct"> + SConscript('src/prog/SConscript') + </file> + <file name="src/prog/SConscript" printme="1"> + env = Environment() + env.Program('prog', ['main.c', '#lib/foo1.c', 'foo2.c']) + </file> + <file name="src/prog/main.c"> + x + </file> + <file name="lib/foo1.c"> + x + </file> + <file name="src/prog/foo2.c"> + x + </file> + </scons_example> + + <para> + + In this example, + the <literal>lib</literal> directory is + directly underneath the top-level &SConstruct; directory. + If the above &SConscript; file is in a subdirectory + named <literal>src/prog</literal>, + the output would look like: + + </para> + + <scons_output example="ex2"> + <command>scons</command> + </scons_output> + + <para> + + (Notice that the <literal>lib/foo1.o</literal> object file + is built in the same directory as its source file. + See section XXX, below, + for information about + how to build the object file in a different subdirectory.) + + </para> + + </section> + + <section> + <title>Absolute Path Names</title> + + <para> + + Of course, you can always specify + an absolute path name for a file--for example: + + </para> + + <scons_example name="ex3"> + <file name="SConstruct"> + SConscript('src/prog/SConscript') + </file> + <file name="src/prog/SConscript" printme="1"> + env = Environment() + env.Program('prog', ['main.c', '__ROOT__/usr/joe/lib/foo1.c', 'foo2.c']) + </file> + <file name="src/prog/main.c"> + x + </file> + <file name="__ROOT__/usr/joe/lib/foo1.c"> + x + </file> + <file name="src/prog/foo2.c"> + x + </file> + </scons_example> + + <para> + + Which, when executed, would yield: + + </para> + + <scons_output example="ex3"> + <command>scons</command> + </scons_output> + + <para> + + (As was the case with top-relative path names, + notice that the <literal>/usr/joe/lib/foo1.o</literal> object file + is built in the same directory as its source file. + See section XXX, below, + for information about + how to build the object file in a different subdirectory.) + + </para> + + </section> + + <section> + <title>Sharing Environments (and Other Variables) Between &SConscript; Files</title> + + <para> + + In the previous example, + each of the subsidiary &SConscript; files + created its own construction environment + by calling &Environment; separately. + This obviously works fine, + but if each program must be built + with the same construction variables, + it's cumbersome and error-prone to initialize + separate construction environments + in the same way over and over in each subsidiary + &SConscript; file. + + </para> + + <para> + + &SCons; supports the ability to <emphasis>export</emphasis> variables + from a parent &SConscript; file + to its subsidiary &SConscript; files, + which allows you to share common initialized + values throughout your build hierarchy. + + </para> + + <section> + <title>Exporting Variables</title> + + <para> + + There are two ways to export a variable, + such as a construction environment, + from one &SConscript; file, + so that it may be used by other &SConscript; files. + First, you can call the &Export; + function with a list of variables, + or a string white-space separated variable names. + Each call to &Export; adds one + or more variables to a global list + of variables that are available for import + by other &SConscript; files. + + </para> + + <sconstruct> + env = Environment() + Export('env') + </sconstruct> + + <para> + + XXX + + </para> + + <sconstruct> + env = Environment() + debug = ARGUMENTS['debug'] + Export('env', 'debug') + </sconstruct> + + <para> + + XXX + + </para> + + <sconstruct> + Export('env debug') + </sconstruct> + + <para> + + Second, you can specify a list of + variables to export as a second argument + to the &SConscript; function call: + + </para> + + <sconstruct> + SConscript('src/SConscript', 'env') + </sconstruct> + + <para> + + Or as the &exports; keyword argument: + + </para> + + <sconstruct> + SConscript('src/SConscript', exports='env') + </sconstruct> + + <para> + + These calls export the specified variables + to only the listed &SConscript; files. + You may, however, specify more than one + &SConscript; file in a list: + + </para> + + <sconstruct> + SConscript(['src1/SConscript', + 'src2/SConscript'], exports='env') + </sconstruct> + + <para> + + This is functionally equivalent to + calling the &SConscript; function + multiple times with the same &exports; argument, + one per &SConscript; file. + + </para> + + </section> + + <section> + <title>Importing Variables</title> + + <para> + + XXX + + </para> + + <sconstruct> + Import('env') + env.Program('prog', ['prog.c']) + </sconstruct> + + <para> + + XXX + + </para> + + <sconstruct> + Import('env', 'debug') + env = env.Copy(DEBUG = debug) + env.Program('prog', ['prog.c']) + </sconstruct> + + <para> + + Which is exactly equivalent to: + + </para> + + <sconstruct> + Import('env debug') + env = env.Copy(DEBUG = debug) + env.Program('prog', ['prog.c']) + </sconstruct> + + <para> + + XXX + + </para> + + </section> + + <section> + <title>Returning Values From an &SConscript; File</title> + + <para> + + XXX + + </para> + + <sconstruct> + obj = env.Object('foo.c') + Return('obj') + </sconstruct> + + <para> + + XXX + + </para> + + <sconstruct> + objs = [] + for subdir in ['foo', 'bar']: + o = SConscript('%s/SConscript' % subdir) + objs.append(o) + env.Library('prog', objs) + </sconstruct> + + <para> + + XXX + + </para> + + </section> + + </section> diff --git a/doc/user/install.in b/doc/user/install.in new file mode 100644 index 0000000..a263f65 --- /dev/null +++ b/doc/user/install.in @@ -0,0 +1,240 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + + <para> + + Once a program is built, + it is often appropriate to install it in another + directory for public use. + You use the &Install; method + to arrange for a program, or any other file, + to be copied into a destination directory: + + </para> + + <scons_example name="ex1"> + <file name="SConstruct" printme="1"> + env = Environment() + hello = env.Program('hello.c') + env.Install('__ROOT__/usr/bin', hello) + </file> + <file name="hello.c"> + int main() { printf("Hello, world!\n"); } + </file> + </scons_example> + + <para> + + Note, however, that installing a file is + still considered a type of file "build." + This is important when you remember that + the default behavior of &SCons; is + to build files in or below the current directory. + If, as in the example above, + you are installing files in a directory + outside of the top-level &SConstruct; file's directory tree, + you must specify that directory + (or a higher directory, such as <literal>/</literal>) + for it to install anything there: + + </para> + + <scons_output example="ex1"> + <command>scons</command> + <command>scons __ROOT__/usr/bin</command> + </scons_output> + + <para> + + It can, however, be cumbersome to remember + (and type) the specific destination directory + in which the program (or any other file) + should be installed. + This is an area where the &Alias; + function comes in handy, + allowing you, for example, + to create a pseudo-target named <literal>install</literal> + that can expand to the specified destination directory: + + </para> + + <scons_example name="ex2"> + <file name="SConstruct" printme="1"> + env = Environment() + hello = env.Program('hello.c') + env.Install('__ROOT__/usr/bin', hello) + env.Alias('install', '__ROOT__/usr/bin') + </file> + <file name="hello.c"> + int main() { printf("Hello, world!\n"); } + </file> + </scons_example> + + <para> + + This then yields the more natural + ability to install the program + in its destination as follows: + + </para> + + <scons_output example="ex1"> + <command>scons</command> + <command>scons install</command> + </scons_output> + + <section> + <title>Installing Multiple Files in a Directory</title> + + <para> + + You can install multiple files into a directory + simply by calling the &Install; function multiple times: + + </para> + + <scons_example name="ex3"> + <file name="SConstruct" printme="1"> + env = Environment() + hello = env.Program('hello.c') + goodbye = env.Program('goodbye.c') + env.Install('__ROOT__/usr/bin', hello) + env.Install('__ROOT__/usr/bin', goodbye) + env.Alias('install', '__ROOT__/usr/bin') + </file> + <file name="hello.c"> + int main() { printf("Hello, world!\n"); } + </file> + </scons_example> + + <para> + + Or, more succinctly, listing the multiple input + files in a list + (just like you can do with any other builder): + + </para> + + <sconstruct> + env = Environment() + hello = env.Program('hello.c') + goodbye = env.Program('goodbye.c') + env.Install('__ROOT__/usr/bin', [hello, goodbye]) + env.Alias('install', '__ROOT__/usr/bin') + </sconstruct> + + <para> + + Either of these two examples yields: + + </para> + + <scons_output example="ex3"> + <command>scons install</command> + </scons_output> + + </section> + + <section> + <title>Installing a File Under a Different Name</title> + + <para> + + The &Install; method preserves the name + of the file when it is copied into the + destination directory. + If you need to change the name of the file + when you copy it, use the &InstallAs; function: + + </para> + + <scons_example name="ex4"> + <file name="SConstruct" printme="1"> + env = Environment() + hello = env.Program('hello.c') + env.InstallAs('__ROOT__/usr/bin/hello-new', hello) + env.Alias('install', '__ROOT__/usr/bin') + </file> + <file name="hello.c"> + int main() { printf("Hello, world!\n"); } + </file> + </scons_example> + + <para> + + This installs the <literal>hello</literal> + program with the name <literal>hello-new</literal> + as follows: + + </para> + + <scons_output example="ex4"> + <command>scons install</command> + </scons_output> + + </section> + + <section> + <title>Installing Multiple Files Under Different Names</title> + + <para> + + Lastly, if you have multiple files that all + need to be installed with different file names, + you can either call the &InstallAs; function + multiple times, or as a shorthand, + you can supply same-length lists + for the both the target and source arguments: + + </para> + + <scons_example name="ex5"> + <file name="SConstruct" printme="1"> + env = Environment() + hello = env.Program('hello.c') + goodbye = env.Program('goodbye.c') + env.InstallAs(['__ROOT__/usr/bin/hello-new', + '__ROOT__/usr/bin/goodbye-new'], + [hello, goodbye]) + </file> + <file name="hello.c"> + int main() { printf("Hello, world!\n"); } + </file> + </scons_example> + + <para> + + In this case, the &InstallAs; function + loops through both lists simultaneously, + and copies each source file into its corresponding + target file name: + + </para> + + <scons_output example="ex5"> + <command>scons install</command> + </scons_output> + + </section> diff --git a/doc/user/libraries.in b/doc/user/libraries.in new file mode 100644 index 0000000..337b0da --- /dev/null +++ b/doc/user/libraries.in @@ -0,0 +1,261 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + + <para> + + One of the more useful ways in which you can use multiple + construction environments is to link programs + with different sets of libraries. + + </para> + + <section> + <title>Building Libraries</title> + + <para> + + You build your own libraries by specifying &Library; + instead of &Program;: + + </para> + + <scons_example name="ex1" printme="1"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) + </file> + <file name="hello.c"> + int main() { printf("Hello, world!\n"); } + </file> + <file name="f1.c"> + void f1() { printf("f1.c\n"); } + </file> + <file name="f2.c"> + void f2() { printf("f2.c\n"); } + </file> + <file name="f3.c"> + void f3() { printf("f3.c\n"); } + </file> + </scons_example> + + <para> + + &SCons; uses the appropriate library prefix and suffix for your system. + So on POSIX or Linux systems, + the above example would build as follows + (although &ranlib may not be called on all systems): + + </para> + + <scons_output example="ex1"> + <command>scons</command> + </scons_output> + + <para> + + On a Windows system, + a build of the above example would look like: + + </para> + + <scons_output example="ex1"> + <command>scons</command> + </scons_output> + + <para> + + The rules for the target name of the library + are similar to those for programs: + if you don't explicitly specify a target library name, + &SCons; will deduce one from the + name of the first source file specified, + and &SCons; will add an appropriate + file prefix and suffix if you leave them off. + + </para> + + </section> + + <section> + <title>Linking with Libraries</title> + + <para> + + Usually, you build a library + because you want to link it with one or more programs. + You link libraries with a program by specifying + the libraries in the &LIBS; construction variable, + and by specifying the directory in which + the library will be found in the + &LIBPATH; construction variable: + + </para> + + <scons_example name="ex2"> + <file name="SConstruct"> + env = Environment(LIBS = 'foo', LIBPATH = '.') + env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) + env.Program('prog.c') + </file> + <file name="f1.c"> + int main() { printf("Hello, world!\n"); } + </file> + <file name="f2.c"> + int main() { printf("Hello, world!\n"); } + </file> + <file name="f3.c"> + int main() { printf("Hello, world!\n"); } + </file> + <file name="prog.c"> + int main() { printf("Hello, world!\n"); } + </file> + </scons_example> + + <para> + + Notice, of course, that you don't need to specify a library + prefix (like <literal>lib</literal>) + or suffix (like <literal>.a</literal> or <literal>.lib</literal>). + &SCons; uses the correct prefix or suffix for the current system. + + </para> + + <para> + + On a POSIX or Linux system, + a build of the above example would look like: + + </para> + + <scons_output example="ex2"> + <command>scons</command> + </scons_output> + + <para> + + On a Windows system, + a build of the above example would look like: + + </para> + + <scons_output example="ex2" os="win32"> + <command>scons</command> + </scons_output> + + <para> + + As usual, notice that &SCons; has taken care + of constructing the correct command lines + to link with the specified library on each system. + + </para> + + </section> + + <section> + <title>Finding Libraries: the &LIBPATH; Construction Variable</title> + + <para> + + By default, the linker will only look in + certain system-defined directories for libraries. + &SCons; knows how to look for libraries + in directories that you specify with the + &LIBPATH; construction variable. + &LIBPATH; consists of a list of + directory names, like so: + + </para> + + <scons_example name="ex3"> + <file name="SConstruct" printme="1"> + env = Environment(LIBS = 'm', + LIBPATH = ['/usr/lib', '/usr/local/lib']) + env.Program('prog.c') + </file> + <file name="prog.c"> + int main() { printf("prog.c\n"); } + </file> + </scons_example> + + <para> + + Using a Python list is preferred because it's portable + across systems. Alternatively, you could put all of + the directory names in a single string, separated by the + system-specific path separator character: + a colon on POSIX systems: + + </para> + + <sconstruct> + LIBPATH = '/usr/lib:/usr/local/lib' + </sconstruct> + + <para> + + or a semi-colon on Windows systems: + + </para> + + <sconstruct> + LIBPATH = 'C:\lib;D:\lib' + </sconstruct> + + <para> + + When the linker is executed, + &SCons; will create appropriate flags + so that the linker will look for + libraries in the same directories as &SCons;. + So on a POSIX or Linux system, + a build of the above example would look like: + + </para> + + <scons_output example="ex3"> + <command>scons</command> + </scons_output> + + <para> + + On a Windows system, + a build of the above example would look like: + + </para> + + <scons_output example="ex3" os="win32"> + <command>scons</command> + </scons_output> + + <para> + + Note again that &SCons; has taken care of + the system-specific details of creating + the right command-line options. + + </para> + + </section> diff --git a/doc/user/main.in b/doc/user/main.in new file mode 100644 index 0000000..12b0480 --- /dev/null +++ b/doc/user/main.in @@ -0,0 +1,243 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + +<!doctype book PUBLIC "-//OASIS//DTD DocBook V4.1//EN" +[ + + <!ENTITY % version SYSTEM "../version.sgml"> + %version; + + <!ENTITY % scons SYSTEM "../scons.mod"> + %scons; + + <!ENTITY actions SYSTEM "actions.sgml"> + <!ENTITY alias SYSTEM "alias.sgml"> + <!ENTITY ant SYSTEM "ant.sgml"> + <!ENTITY builders-built-in SYSTEM "builders-built-in.sgml"> + <!ENTITY builders-commands SYSTEM "builders-commands.sgml"> + <!ENTITY builders-writing SYSTEM "builders-writing.sgml"> + <!ENTITY caching SYSTEM "caching.sgml"> + <!ENTITY cons SYSTEM "cons.sgml"> + <!ENTITY copyright SYSTEM "copyright.sgml"> + <!ENTITY default SYSTEM "default.sgml"> + <!ENTITY depends SYSTEM "depends.sgml"> + <!ENTITY environments SYSTEM "environments.sgml"> + <!ENTITY errors SYSTEM "errors.sgml"> + <!ENTITY example SYSTEM "example.sgml"> + <!ENTITY help SYSTEM "help.sgml"> + <!ENTITY hierarchy SYSTEM "hierarchy.sgml"> + <!ENTITY install SYSTEM "install.sgml"> + <!ENTITY libraries SYSTEM "libraries.sgml"> + <!ENTITY make SYSTEM "make.sgml"> + <!ENTITY precious SYSTEM "precious.sgml"> + <!ENTITY preface SYSTEM "preface.sgml"> + <!ENTITY repositories SYSTEM "repositories.sgml"> + <!ENTITY run SYSTEM "run.sgml"> + <!ENTITY scanners SYSTEM "scanners.sgml"> + <!ENTITY separate SYSTEM "separate.sgml"> + <!ENTITY simple SYSTEM "simple.sgml"> + <!ENTITY sourcecode SYSTEM "sourcecode.sgml"> + <!ENTITY troubleshoot SYSTEM "troubleshoot.sgml"> + <!ENTITY variants SYSTEM "variants.sgml"> + +]> + +<book> + <bookinfo> + <title>SCons User Guide &buildversion;</title> + + <author> + <firstname>Steven</firstname> + <surname>Knight</surname> + </author> + + <edition>Revision &buildrevision; (&builddate;)</edition> + + <pubdate>2003</pubdate> + + <copyright> + <year>2003</year> + <holder>Steven Knight</holder> + </copyright> + + <legalnotice> + ©right; + </legalnotice> + + <releaseinfo>version &buildversion;</releaseinfo> + + </bookinfo> + + <chapter id="chap-preface"> + <title>Preface</title> + &preface; + </chapter> + + <chapter id="chap-simple"> + <title>Simple Builds</title> + &simple; + </chapter> + + <chapter id="chap-environments"> + <title>Construction Environments</title> + &environments; + </chapter> + + <chapter id="chap-libraries"> + <title>Building and Linking with Libraries</title> + &libraries; + </chapter> + + <chapter id="chap-depends"> + <title>Dependencies</title> + &depends; + </chapter> + + <chapter id="chap-default"> + <title>Default Targets</title> + &default; + </chapter> + + <chapter id="chap-help"> + <title>Providing Build Help</title> + &help; + </chapter> + + <chapter id="chap-install"> + <title>Installing Files in Other Directories</title> + &install; + </chapter> + + <chapter id="chap-precious"> + <title>Preventing Removal of Targets</title> + &precious; + </chapter> + + <chapter id="chap-hierarchical"> + <title>Hierarchical Builds</title> + &hierarchy; + </chapter> + + <chapter id="chap-separate"> + <title>Separating Source and Build Directories</title> + &separate; + </chapter> + + <chapter id="chap-variants"> + <title>Variant Builds</title> + &variants; + </chapter> + + <chapter id="chap-builders-built-in"> + <title>Built-In Builders</title> + &builders-built-in; + </chapter> + + <chapter id="chap-builders-writing"> + <title>Writing Your Own Builders</title> + &builders-writing; + </chapter> + + <chapter id="chap-builders-commands"> + <title>Not Writing a Builder: The &Command; Builder</title> + &builders-commands; + </chapter> + + <chapter id="chap-actions"> + <title>SCons Actions</title> + &actions; + </chapter> + + <chapter id="chap-scanners"> + <title>Writing Scanners</title> + &scanners; + </chapter> + + <chapter id="chap-repositories"> + <title>Building From Code Repositories</title> + &repositories; + </chapter> + + <chapter id="chap-sourcecode"> + <title>Fetching Files From Source Code Management Systems</title> + &sourcecode; + </chapter> + + <chapter id="chap-caching"> + <title>Caching Built Files</title> + &caching; + </chapter> + + <chapter id="chap-alias"> + <title>Alias Targets</title> + &alias; + </chapter> + + <chapter id="chap-run"> + <title>How to Run &SCons;</title> + &run; + </chapter> + + <chapter id="chap-troubleshooting"> + <title>Troubleshooting</title> + &troubleshoot; + </chapter> + + <!-- + AddPostAction() + AddPreAction() + Clean() + Dir() + File() + FindFile() + GetJobs() + SetJobs() + SideEffect() + ParseConfig() + Platform() + Tools() + --> + + <appendix id="app-example"> + <title>Complex &SCons; Example</title> + &example; + </appendix> + + <appendix id="app-make"> + <title>Converting From Make</title> + &make; + </appendix> + + <appendix id="app-cons"> + <title>Converting From Cons</title> + &cons; + </appendix> + + <appendix id="app-ant"> + <title>Converting From Ant</title> + &ant; + </appendix> + +</book> diff --git a/doc/user/make.in b/doc/user/make.in new file mode 100644 index 0000000..fe697a0 --- /dev/null +++ b/doc/user/make.in @@ -0,0 +1,121 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + +<!-- + +=head1 Why Cons? Why not Make? + +Cons is a B<make> replacement. In the following paragraphs, we look at a few +of the undesirable characteristics of make, and typical build environments +based on make, that motivated the development of Cons. + +=head2 Build complexity + +Traditional make-based systems of any size tend to become quite complex. The +original make utility and its derivatives have contributed to this tendency +in a number of ways. Make is not good at dealing with systems that are +spread over multiple directories. Various work-arounds are used to overcome +this difficulty; the usual choice is for make to invoke itself recursively +for each sub-directory of a build. This leads to complicated code, in which +it is often unclear how a variable is set, or what effect the setting of a +variable will have on the build as a whole. The make scripting language has +gradually been extended to provide more possibilities, but these have +largely served to clutter an already overextended language. Often, builds +are done in multiple passes in order to provide appropriate products from +one directory to another directory. This represents a further increase in +build complexity. + + +=head2 Build reproducibility + +The bane of all makes has always been the correct handling of +dependencies. Most often, an attempt is made to do a reasonable job of +dependencies within a single directory, but no serious attempt is made to do +the job between directories. Even when dependencies are working correctly, +make's reliance on a simple time stamp comparison to determine whether a +file is out of date with respect to its dependents is not, in general, +adequate for determining when a file should be rederived. If an external +library, for example, is rebuilt and then ``snapped'' into place, the +timestamps on its newly created files may well be earlier than the last +local build, since it was built before it became visible. + + +=head2 Variant builds + +Make provides only limited facilities for handling variant builds. With the +proliferation of hardware platforms and the need for debuggable +vs. optimized code, the ability to easily create these variants is +essential. More importantly, if variants are created, it is important to +either be able to separate the variants or to be able to reproduce the +original or variant at will. With make it is very difficult to separate the +builds into multiple build directories, separate from the source. And if +this technique isn't used, it's also virtually impossible to guarantee at +any given time which variant is present in the tree, without resorting to a +complete rebuild. + + +=head2 Repositories + +Make provides only limited support for building software from code that +exists in a central repository directory structure. The VPATH feature of +GNU make (and some other make implementations) is intended to provide this, +but doesn't work as expected: it changes the path of target file to the +VPATH name too early in its analysis, and therefore searches for all +dependencies in the VPATH directory. To ensure correct development builds, +it is important to be able to create a file in a local build directory and +have any files in a code repository (a VPATH directory, in make terms) that +depend on the local file get rebuilt properly. This isn't possible with +VPATH, without coding a lot of complex repository knowledge directly into +the makefiles. + +--> + + <para> + + XXX + + </para> + + <section> + <title>Differences Between &Make; and &SCons;</title> + + <para> + + XXX + + </para> + + </section> + + <section> + <title>Advantages of &SCons; Over &Make;</title> + + <para> + + XXX + + </para> + + </section> diff --git a/doc/user/precious.in b/doc/user/precious.in new file mode 100644 index 0000000..2be22ec --- /dev/null +++ b/doc/user/precious.in @@ -0,0 +1,89 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + +<!-- + +=head2 The C<AfterBuild> method + +The C<AfterBuild> method evaluates the specified perl string after +building the given file or files (or finding that they are up to date). +The eval will happen once per specified file. C<AfterBuild> is called +as follows: + + AfterBuild $env 'foo.o', qq(print "foo.o is up to date!\n"); + +The perl string is evaluated in the C<script> package, and has access +to all variables and subroutines defined in the F<Conscript> file in +which the C<AfterBuild> method is called. + +--> + + <para> + + By default, &SCons; removes targets before building them. + Sometimes, however, this is not what you want. + For example, you may want to update a library incrementally, + not by having it deleted and then rebuilt from all + of the constituent object files. + In such cases, you can use the + &Precious; method to prevent + &SCons; from removing the target before it is built: + + </para> + + <scons_example name="ex1"> + <file name="SConstruct" printme="1"> + env = Environment(XXX NEED LIBRARY FLAGS + LIBFLAGS = '-r') + lib = env.Library('foo', ['f1.c', 'f2.c', 'f3.c']) + env.Precious(lib) + </file> + <file name="f1.c"> + int f1() { } + </file> + <file name="f2.c"> + int f2() { } + </file> + <file name="f3.c"> + int f3() { } + </file> + </scons_example> + + <para> + + XXX: + + </para> + + <scons_output example="ex1"> + <command>scons</command> + </scons_output> + + <para> + + &SCons; will still delete files marked as &Precious; + when the <literal>-c</literal> option is used. + + </para> diff --git a/doc/user/preface.in b/doc/user/preface.in new file mode 100644 index 0000000..589399d --- /dev/null +++ b/doc/user/preface.in @@ -0,0 +1,373 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + + <para> + + Thank you for taking the time to read about &SCons;. + &SCons; is a next-generation + software construction tool, + or make tool--that is, a software utility + for building software (or other files) + and keeping built software up-to-date + whenever the underlying input files change. + + </para> + + <para> + + The most distinctive thing about &SCons; + is that its configuration files are + actually <emphasis>scripts</emphasis>, + written in the &Python; programming language. + This is in contrast to most alternative build tools, + which typically invent a new language to + configure the build. + &SCons; still has a learning curve, of course, + because you have to know what functions to call + to set up your build properly, + but the underlying syntax used should be familiar + to anyone who has ever looked at a Python script. + + </para> + + <para> + + Paradoxically, + using Python as the configuration file format + makes &SCons; + <emphasis>easier</emphasis> + for non-programmers to learn + than the cryptic languages of other build tools, + which are usally invented by programmers for other programmers. + This is in no small part to the + consistency and readability that are built in to Python. + It just so happens that making a real, live + scripting language the basis for the + configuration files + makes it a snap for more accomplished programmers + to do more complicated things with builds, + as necessary. + + </para> + + <section> + <title>Why &SCons;?</title> + + <para> + + &SCons; is a response to a perennial problem: + building software is harder than it should be. + In a nutshell: the old, reliable model of the + venerable and ubiquitous &Make; program + has had a hard time keeping up with + how complicated building software has become. + The fact that &Make; has kept up as well as it has is impressive, + and a testament to how the simplicity. + But anyone who has wrestled with &Automake; and &Autoconf; + to try to guarantee that a bit of software + will build correctly on multiple platforms + can tell you that it takes a lot of work to get right. + + </para> + + </section> + + <section> + <title>&SCons; Principles</title> + + <para> + + There are a few overriding principles + we try to live up to in designing and implementing &SCons: + + </para> + + <variablelist> + + <varlistentry> + <term>Correctness</term> + + <listitem> + <para> + + First and foremost, + by default, &SCons; guarantees a correct build + even if it means sacrificing performance a little. + We strive to guarantee the build is correct + regardless of how the software being built is structured, + how it may have been written, + or how unusual the tools are that build it. + + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>Performance</term> + + <listitem> + <para> + + Given that the build is correct, + we try to make &SCons; build software + as quickly as possible. + In particular, wherever we may have needed to slow + down the default &SCons; behavior to guarantee a correct build, + we also try to make it easy to speed up &SCons; + through optimization options that let you trade off + guaranteed correctness for speed. + + </para> + </listitem> + </varlistentry> + + <varlistentry> + <term>Convenience</term> + + <listitem> + <para> + + &SCons; tries to do as much for you out of the box as reasonable, + including detecting the right tools on your system + and using them correctly to build the software. + + </para> + </listitem> + </varlistentry> + + </variablelist> + + <para> + + In a nutshell, we try hard to make &SCons; just + "do the right thing" and build software correctly, + with a minimum of hassles. + + </para> + + </section> + + <section> + <title>History</title> + + <para> + + &SCons; originated with a design + that was submitted to the Software Carpentry + design competition in 2000. + + </para> + + <para> + + &SCons; is the direct descendant + of a Perl utility called &Cons;. + &Cons; in turn based some of its ideas on &Jam;, + a build tool from Perforce Systems. + + </para> + + <para> + + XXX + + </para> + + </section> + + <!-- + + <section> + <title>Conventions</title> + + <para> + + XXX + + </para> + + </section> + + --> + + <section> + <title>Acknowledgements</title> + + <para> + + &SCons; would not exist without a lot of help + from a lot of people, + many of whom may not even be aware + that they helped or served as inspiration. + So in no particular order, + and at the risk of leaving out someone: + + </para> + + <para> + + First and foremost, + &SCons; owes a tremendous debt to Bob Sidebotham, + the original author of the classic Perl-based &Cons; tool + which Bob first released to the world back around 1996. + Bob's work on Cons classic provided the underlying architecture + and model of specifying a build configuration + using a real scripting language. + My real-world experience working on Cons + informed many of the design decisions in SCons, + including the improved parallel build support, + making Builder objects easily definable by users, + and separating the build engine from the wrapping interface. + + </para> + + <para> + + Greg Wilson was instrumental in getting + &SCons; started as a real project + when he initiated the Software Carpentry design + competition in February 2000. + Without that nudge, + marrying the advantages of the Cons classic + architecture with the readability of Python + might have just stayed no more than a nice idea. + + </para> + + <para> + + The entire &SCons; team have been + absolutely wonderful to work with, + and &SCons; would be nowhere near as useful a + tool without the energy, enthusiasm + and time people have contributed over the past few years. + The "core team" + of Chad Austin, Anthony Roach, Charles Crain, + Steve Leblanc, Greg Spencer and Christoph Wiedemann + have been great about reviewing my (and other) changes + and catching problems before they get in the code base. + Of particular technical note: + Anthony's outstanding and innovative work on the tasking engine + has given &SCons; a vastly superior parallel build model; + Charles has been the master of the crucial Node infrastructure; + Christoph's work on the Configure infrastructure + has added crucial Autoconf-like functionality; + and Greg has provided excellent support + for Microsoft Visual Studio. + + </para> + + <para> + + Special thanks to David Snopek for contributing + his underlying "Autocons" code that formed + the basis of Christoph's work with the Configure functionality. + David was extremely generous in making + this code available to &SCons;, + given that he initially released it under the GPL + and &SCons; is released under a less-restrictive MIT-style license. + + </para> + + <para> + + &SCons; has received contributions + from many other people, of course: + Matt Balvin (extending long command-line support on Win32), + Allen Bierbaum (extensions and fixes to Options), + Steve Christensen (help text sorting and function action signature fixes), + Michael Cook (avoiding losing signal bits from executed commands), + Derrick 'dman' Hudson (), + Alex Jacques (work on the Win32 scons.bat file), + Stephen Kennedy (performance enhancements), + Lachlan O'Dea (SharedObject() support for masm + and normalized paths for the WhereIs() function), + Damyan Pepper (keeping output like Make), + Jeff Petkau (significant fixes for CacheDir and other areas), + Stefan Reichor (Ghostscript support), + Zed Shaw (Append() and Replace() environment methods), + Terrel Shumway (build and test fixes, as well as the SCons Wiki), + sam th (dynamic checks for utilities) + and Moshe Zadke (Debian packaging). + + </para> + + <para> + + Thanks to Peter Miller + for his splendid change management system, &Aegis;, + which has provided the &SCons; project + with a robust development methodology from day one, + and which showed me how you could + integrate incremental regression tests into + a practical development cycle + (years before eXtreme Programming arrived on the scene). + + </para> + + <para> + + And last, thanks to Guido van Rossum + for his elegant scripting language, + which is the basis not only for the &SCons; implementation, + but for the interface itself. + + </para> + + </section> + + <section> + <title>Contact</title> + + <para> + + The best way to contact people involved with SCons, + including the author, + is through the SCons mailing lists. + + </para> + + <para> + + If you want to ask general questions about how to use &SCons; + send email to &scons-users;. + + </para> + + <para> + + If you want to contact the &SCons; development community directly, + send email to &scons-devel;. + + </para> + + <para> + + If you want to receive announcements about &SCons, + join the low-volume &scons-announce; mailing list. + + </para> + + </section> diff --git a/doc/user/repositories.in b/doc/user/repositories.in new file mode 100644 index 0000000..c155617 --- /dev/null +++ b/doc/user/repositories.in @@ -0,0 +1,499 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + +<!-- + + +=head2 Repository dependency analysis + +Due to its built-in scanning, Cons will search the specified repository +trees for included F<.h> files. Unless the compiler also knows about the +repository trees, though, it will be unable to find F<.h> files that only +exist in a repository. If, for example, the F<hello.c> file includes the +F<hello.h> file in its current directory: + + % cons -R /usr/all/repository hello + gcc -c /usr/all/repository/hello.c -o hello.o + /usr/all/repository/hello.c:1: hello.h: No such file or directory + +Solving this problem forces some requirements onto the way construction +environments are defined and onto the way the C C<#include> preprocessor +directive is used to include files. + +In order to inform the compiler about the repository trees, Cons will add +appropriate C<-I> flags to the compilation commands. This means that the +C<CPPPATH> variable in the construction environment must explicitly specify +all subdirectories which are to be searched for included files, including the +current directory. Consequently, we can fix the above example by changing +the environment creation in the F<Construct> file as follows: + + $env = new cons( + CC => 'gcc', + CPPPATH => '.', + LIBS => 'libworld.a', + ); + +Due to the definition of the C<CPPPATH> variable, this yields, when we +re-execute the command: + + % cons -R /usr/all/repository hello + gcc -c -I. -I/usr/all/repository /usr/all/repository/hello.c -o hello.o + gcc -o hello hello.o /usr/all/repository/libworld.a + +The order of the C<-I> flags replicates, for the C preprocessor, the same +repository-directory search path that Cons uses for its own dependency +analysis. If there are multiple repositories and multiple C<CPPPATH> +directories, Cons will append the repository directories to the beginning of +each C<CPPPATH> directory, rapidly multiplying the number of C<-I> flags. +As an extreme example, a F<Construct> file containing: + + Repository qw( + /u1 + /u2 + ); + + $env = new cons( + CPPPATH => 'a:b:c', + ); + +Would yield a compilation command of: + + cc -Ia -I/u1/a -I/u2/a -Ib -I/u1/b -I/u2/b -Ic -I/u1/c -I/u2/c -c hello.c -o hello.o + +In order to shorten the command lines as much as possible, Cons will +remove C<-I> flags for any directories, locally or in the repositories, +which do not actually exist. (Note that the C<-I> flags are not included +in the MD5 signature calculation for the target file, so the target will +not be recompiled if the compilation command changes due to a directory +coming into existence.) + +Because Cons relies on the compiler's C<-I> flags to communicate the +order in which repository directories must be searched, Cons' handling +of repository directories is fundamentally incompatible with using +double-quotes on the C<#include> directives in any C source code that +you plan to modify: + + #include "file.h" /* DON'T USE DOUBLE-QUOTES LIKE THIS */ + +This is because most C preprocessors, when faced with such a directive, will +always first search the directory containing the source file. This +undermines the elaborate C<-I> options that Cons constructs to make the +preprocessor conform to its preferred search path. + +Consequently, when using repository trees in Cons, B<always> use +angle-brackets for included files in any C source (.c or .h) files that +you plan to modify locally: + + #include <file.h> /* USE ANGLE-BRACKETS INSTEAD */ + +Code that will not change can still safely use double quotes on #include +lines. + + +=head2 Repository_List + +Cons provides a C<Repository_List> command to return a list of all +repository directories in their current search order. This can be used for +debugging, or to do more complex Perl stuff: + + @list = Repository_List; + print join(' ', @list), "\n"; + + +=head2 Repository interaction with other Cons features + +Cons' handling of repository trees interacts correctly with other Cons +features, which is to say, it generally does what you would expect. + +Most notably, repository trees interact correctly, and rather powerfully, +with the 'Link' command. A repository tree may contain one or more +subdirectories for version builds established via C<Link> to a source +subdirectory. Cons will search for derived files in the appropriate build +subdirectories under the repository tree. + +--> + + <para> + + Often, a software project will have + one or more central repositories, + directory trees that contain + source code, or derived files, or both. + You can eliminate additional unnecessary + rebuilds of files by having &SCons; + use files from one or more code repositories + to build files in your local build tree. + + </para> + + <section> + <title>The &Repository; Method</title> + + <!-- + + The repository directories specified may contain source files, derived files + (objects, libraries and executables), or both. If there is no local file + (source or derived) under the directory in which Cons is executed, then the + first copy of a same-named file found under a repository directory will be + used to build any local derived files. + + --> + + <para> + + It's often useful to allow multiple programmers working + on a project to build software from + source files and/or derived files that + are stored in a centrally-accessible repository, + a directory copy of the source code tree. + (Note that this is not the sort of repository + maintained by a source code management system + like BitKeeper, CVS, or Subversion. + For information about using &SCons; + with these systems, see the section, + "Fetching Files From Source Code Management Systems," + below.) + You use the &Repository; method + to tell &SCons; to search one or more + central code repositories (in order) + for any source files and derived files + that are not present in the local build tree: + + </para> + + <scons_example name="ex1"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Program('hello.c') + Repository('/usr/repository1', '/usr/repository2') + </file> + <file name="hello.c"> + int main() { printf("Hello, world!\n"); } + </file> + </scons_example> + + <para> + + Multiple calls to the &Repository; method + will simply add repositories to the global list + that &SCons; maintains, + with the exception that &SCons; will automatically eliminate + the current directory and any non-existent + directories from the list. + + </para> + + </section> + + <section> + <title>Finding source files in repositories</title> + + <para> + + The above example + specifies that &SCons; + will first search for files under + the <filename>/usr/repository1</filename> tree + and next under the <filename>/usr/repository2</filename> tree. + &SCons; expects that any files it searches + for will be found in the same position + relative to the top-level directory XXX + In the above example, if the &hello_c; file is not + found in the local build tree, + &SCons; will search first for + a <filename>/usr/repository1/hello.c</filename> file + and then for a <filename>/usr/repository1/hello.c</filename> file + to use in its place. + + </para> + + <para> + + So given the &SConstruct; file above, + if the &hello_c; file exists in the local + build directory, + &SCons; will rebuild the &hello; program + as normal: + + </para> + + <scons_output example="ex1"> + <command>scons</command> + </scons_output> + + <para> + + If, however, there is no local &hello_c; file, + but one exists in <filename>/usr/repository1</filename>, + &SCons; will recompile the &hello; program + from the source file it finds in the repository: + + </para> + + <scons_example name="ex2"> + <file name="SConstruct"> + env = Environment() + env.Program('hello.c') + Repository('/usr/repository1', '/usr/repository2') + </file> + <file name="hello.c"> + int main() { printf("Hello, world!\n"); } + </file> + </scons_example> + + <scons_output example="ex2"> + <command>scons</command> + gcc -c /usr/repository1/hello.c -o hello.o + gcc -o hello hello.o + </scons_output> + + <para> + + And similarly, if there is no local &hello_c; file + and no <filename>/usr/repository1/hello.c</filename>, + but one exists in <filename>/usr/repository2</filename>: + + </para> + + <scons_example name="ex3"> + <file name="SConstruct"> + env = Environment() + env.Program('hello.c') + Repository('/usr/repository1', '/usr/repository2') + </file> + <file name="hello.c"> + int main() { printf("Hello, world!\n"); } + </file> + </scons_example> + + <scons_output example="ex3"> + <command>scons</command> + </scons_output> + + <para> + + </para> + + </section> + + <section> + <title>Finding the &SConstruct; file in repositories</title> + + <para> + + &SCons; will also search in repositories + for the &SConstruct; file and any specified &SConscript; files. + This poses a problem, though: how can &SCons; search a + repository tree for an &SConstruct; file + if the &SConstruct; file itself contains the information + about the pathname of the repository? + To solve this problem, &SCons; allows you + to specify repository directories + on the command line using the <literal>-Y</literal> option: + + </para> + + <literallayout> + % <userinput>scons -Y /usr/repository1 -Y /usr/repository2</userinput> + </literallayout> + + <para> + + When looking for source or derived files, + &SCons; will first search the repositories + specified on the command line, + and then search the repositories + specified in the &SConstruct; or &SConscript; files. + + </para> + + </section> + + <section> + <title>Finding derived files in repositories</title> + + <para> + + If a repository contains not only source files, + but also derived files (such as object files, + libraries, or executables), &SCons; will perform + its normal MD5 signature calculation to + decide if a derived file in a repository is up-to-date, + or the derived file must be rebuilt in the local build directory. + For the &SCons; signature calculation to work correctly, + a repository tree must contain the &sconsign; files + that &SCons; uses to keep track of signature information. + + </para> + + <para> + + Usually, this would be done by a build integrator + who would run &SCons; in the repository + to create all of its derived files and &sconsign; files, + or who would &SCons; in a separate build directory + and copying the resulting tree to the desired repository: + + </para> + + <scons_example name="ex4"> + <file name="SConstruct"> + env = Environment() + env.Program('hello.c') + Repository('/usr/repository1', '/usr/repository2') + </file> + <file name="hello.c"> + int main() { printf("Hello, world!\n"); } + </file> + </scons_example> + + <literallayout> + % <userinput>cd /usr/repository1</userinput> + % <userinput>scons</userinput> + gcc -c hello.c -o hello.o + gcc -o hello hello.o + </literallayout> + + <para> + + (Note that this is safe even if the &SConstruct; file + lists <filename>/usr/repository1</filename> as a repository, + because &SCons; will remove the current build directory + from its repository list for that invocation.) + + </para> + + <para> + + Now, with the repository populated, + we only need to create the one local source file + we're interested in working with at the moment, + and use the <literal>-Y</literal> option to + tell &SCons; to fetch any other files it needs + from the repository: + + </para> + + <literallayout> + % <userinput>cd $HOME/build</userinput> + % <userinput>edit hello.c</userinput> + % <userinput>scons -Y /usr/repository1</userinput> + gcc -c hello.c -o hello.o + gcc -o hello hello.o + XXXXXXX + </literallayout> + + <para> + + Notice that &SCons; realizes that it does not need to + rebuild a local XXX.o file, + but instead uses the already-compiled XXX.o file + from the repository. + + </para> + + </section> + + <section> + <title>Guaranteeing local copies of files</title> + + <para> + + If the repository tree contains the complete results of a build, + and we try to build from the repository + without any files in our local tree, + something moderately surprising happens: + + </para> + + <literallayout> + % <userinput>mkdir $HOME/build2</userinput> + % <userinput>cd $HOME/build2</userinput> + % <userinput>scons -Y /usr/all/repository hello</userinput> + scons: `hello' is up-to-date. + </literallayout> + + <para> + + Why does &SCons; say that the &hello; program + is up-to-date when there is no &hello; program + in the local build directory? + Because the repository (not the local directory) + contains the up-to-date &hello; program, + and &SCons; correctly determines that nothing + needs to be done to rebuild that + up-to-date copy of the file. + + </para> + + <para> + + There are, however, many times when you want to ensure that a + local copy of a file always exists. + A packaging or testing script, for example, + may assume that certain generated files exist locally. + To tell &SCons; to make a copy of any up-to-date repository + file in the local build directory, + use the &Local; function: + + </para> + + <scons_example name="ex1"> + <file name="SConstruct" printme="1"> + env = Environment() + hello = env.Program('hello.c') + Local(hello) + </file> + <file name="hello.c"> + int main() { printf("Hello, world!\n"); } + </file> + </scons_example> + + <para> + + If we then run the same command, + &SCons; will make a local copy of the program + from the repository copy, + and tell you that it is doing so: + + </para> + + <literallayout> + % scons -Y /usr/all/repository hello + Local copy of hello from /usr/all/repository/hello + scons: `hello' is up-to-date. + XXXXXX DO WE REALLY REPORT up-to-date, TOO? + </literallayout> + + <para> + + (Notice that, because the act of making the local copy + is not considered a "build" of the &hello; file, + &SCons; still reports that it is up-to-date.) + XXXXXX DO WE REALLY REPORT up-to-date, TOO? + + </para> + + </section> diff --git a/doc/user/run.in b/doc/user/run.in new file mode 100644 index 0000000..83ca3d2 --- /dev/null +++ b/doc/user/run.in @@ -0,0 +1,364 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + +<!-- + +=head1 Invoking Cons + +The C<cons> command is usually invoked from the root of the build tree. A +F<Construct> file must exist in that directory. If the C<-f> argument is +used, then an alternate F<Construct> file may be used (and, possibly, an +alternate root, since C<cons> will cd to F<Construct> file's containing +directory). + +If C<cons> is invoked from a child of the root of the build tree with +the C<-t> argument, it will walk up the directory hierarchy looking for a +F<Construct> file. (An alternate name may still be specified with C<-f>.) +The targets supplied on the command line will be modified to be relative +to the discovered F<Construct> file. For example, from a directory +containing a top-level F<Construct> file, the following invocation: + + % cd libfoo/subdir + % cons -t target + +is exactly equivalent to: + + % cons libfoo/subdir/target + +If there are any C<Default> targets specified in the directory hierarchy's +F<Construct> or F<Conscript> files, only the default targets at or below +the directory from which C<cons -t> was invoked will be built. + +The command is invoked as follows: + + cons <arguments> , <construct-args> + +where I<arguments> can be any of the following, in any order: + +=over 10 + +=item I<target> + +Build the specified target. If I<target> is a directory, then recursively +build everything within that directory. + +=item I<+pattern> + +Limit the F<Conscript> files considered to just those that match I<pattern>, +which is a Perl regular expression. Multiple C<+> arguments are accepted. + +=item I<name>=<val> + +Sets I<name> to value I<val> in the C<ARG> hash passed to the top-level +F<Construct> file. + +=item C<-cc> + +Show command that would have been executed, when retrieving from cache. No +indication that the file has been retrieved is given; this is useful for +generating build logs that can be compared with real build logs. + +=item C<-cd> + +Disable all caching. Do not retrieve from cache nor flush to cache. + +=item C<-cr> + +Build dependencies in random order. This is useful when building multiple +similar trees with caching enabled. + +=item C<-cs> + +Synchronize existing build targets that are found to be up-to-date with +cache. This is useful if caching has been disabled with -cc or just recently +enabled with UseCache. + +=item C<-d> + +Enable dependency debugging. + +=item C<-f> <file> + +Use the specified file instead of F<Construct> (but first change to +containing directory of I<file>). + +=item C<-h> + +Show a help message local to the current build if one such is defined, and +exit. + +=item C<-k> + +Keep going as far as possible after errors. + +=item C<-o> <file> + +Read override file I<file>. + +=item C<-p> + +Show construction products in specified trees. No build is attempted. + +=item C<-pa> + +Show construction products and associated actions. No build is attempted. + +=item C<-pw> + +Show products and where they are defined. No build is attempted. + +=item C<-q> + +Make the build quiet. Multiple C<-q> options may be specified. + +A single C<-q> options suppress messages about Installing and Removing +targets. + +Two C<-q> options suppress build command lines and target up-to-date +messages. + +=item C<-r> + +Remove construction products associated with <targets>. No build is +attempted. + +=item C<-R> <repos> + +Search for files in I<repos>. Multiple B<-R> I<repos> directories are +searched in the order specified. + +=item C<-S> <pkg> + +Use the sig::<pkg> package to calculate. Supported <pkg> values +include "md5" for MD5 signature calculation and "md5::debug" for debug +information about MD5 signature calculation. + +If the specified package ends in <::debug>, signature debug information +will be printed to the file name specified in the C<CONS_SIG_DEBUG> +environment variable, or to standard output if the environment variable +is not set. + +=item C<-t> + +Traverse up the directory hierarchy looking for a F<Construct> file, +if none exists in the current directory. Targets will be modified to +be relative to the F<Construct> file. + +Internally, C<cons> will change its working directory to the directory +which contains the top-level F<Construct> file and report: + + cons: Entering directory `top-level-directory' + +This message indicates to an invoking editor (such as emacs) or build +environment that Cons will now report all file names relative to the +top-level directory. This message can not be suppressed with the C<-q> +option. + +=item C<-v> + +Show C<cons> version and continue processing. + +=item C<-V> + +Show C<cons> version and exit. + +=item C<-wf> <file> + +Write all filenames considered into I<file>. + +=item C<-x> + +Show a help message similar to this one, and exit. + +=back + +And I<construct-args> can be any arguments that you wish to process in the +F<Construct> file. Note that there should be a B<-,-> separating the arguments +to cons and the arguments that you wish to process in the F<Construct> file. + +Processing of I<construct-args> can be done by any standard package like +B<Getopt> or its variants, or any user defined package. B<cons> will pass in +the I<construct-args> as B<@ARGV> and will not attempt to interpret anything +after the B<-,->. + + % cons -R /usr/local/repository -d os=solaris +driver -,- -c test -f DEBUG + +would pass the following to cons + + -R /usr/local/repository -d os=solaris +driver + +and the following, to the top level F<Construct> file as B<@ARGV> + + -c test -f DEBUG + +Note that C<cons -r .> is equivalent to a full recursive C<make clean>, +but requires no support in the F<Construct> file or any F<Conscript> +files. This is most useful if you are compiling files into source +directories (if you separate the F<build> and F<export> directories, +then you can just remove the directories). + +The options C<-p>, C<-pa>, and C<-pw> are extremely useful for use as an aid +in reading scripts or debugging them. If you want to know what script +installs F<export/include/foo.h>, for example, just type: + + % cons -pw export/include/foo.h + +=head1 Selective builds + +Cons provides two methods for reducing the size of given build. The first is +by specifying targets on the command line, and the second is a method for +pruning the build tree. We'll consider target specification first. + + +=head2 Selective targeting + +Like make, Cons allows the specification of ``targets'' on the command +line. Cons targets may be either files or directories. When a directory is +specified, this is simply a short-hand notation for every derivable +product-,-that Cons knows about-,-in the specified directory and below. For +example: + + % cons build/hello/hello.o + +means build F<hello.o> and everything that F<hello.o> might need. This is +from a previous version of the B<Hello, World!> program in which F<hello.o> +depended upon F<export/include/world.h>. If that file is not up-to-date +(because someone modified F<src/world/world.h)>, then it will be rebuilt, +even though it is in a directory remote from F<build/hello>. + +In this example: + + % cons build + +Everything in the F<build> directory is built, if necessary. Again, this may +cause more files to be built. In particular, both F<export/include/world.h> +and F<export/lib/libworld.a> are required by the F<build/hello> directory, +and so they will be built if they are out-of-date. + +If we do, instead: + + % cons export + +then only the files that should be installed in the export directory will be +rebuilt, if necessary, and then installed there. Note that C<cons build> +might build files that C<cons export> doesn't build, and vice-versa. + + +=head1 Build Pruning + +In conjunction with target selection, B<build pruning> can be used to reduce +the scope of the build. In the previous peAcH and baNaNa example, we have +already seen how script-driven build pruning can be used to make only half +of the potential build available for any given invocation of C<cons>. Cons +also provides, as a convenience, a command line convention that allows you +to specify which F<Conscript> files actually get ``built''-,-that is, +incorporated into the build tree. For example: + + % cons build +world + +The C<+> argument introduces a Perl regular expression. This must, of +course, be quoted at the shell level if there are any shell meta-characters +within the expression. The expression is matched against each F<Conscript> +file which has been mentioned in a C<Build> statement, and only those +scripts with matching names are actually incorporated into the build +tree. Multiple such arguments are allowed, in which case a match against any +of them is sufficient to cause a script to be included. + +In the example, above, the F<hello> program will not be built, since Cons +will have no knowledge of the script F<hello/Conscript>. The F<libworld.a> +archive will be built, however, if need be. + +There are a couple of uses for build pruning via the command line. Perhaps +the most useful is the ability to make local changes, and then, with +sufficient knowledge of the consequences of those changes, restrict the size +of the build tree in order to speed up the rebuild time. A second use for +build pruning is to actively prevent the recompilation of certain files that +you know will recompile due to, for example, a modified header file. You may +know that either the changes to the header file are immaterial, or that the +changes may be safely ignored for most of the tree, for testing +purposes.With Cons, the view is that it is pragmatic to admit this type of +behavior, with the understanding that on the next full build everything that +needs to be rebuilt will be. There is no equivalent to a ``make touch'' +command, to mark files as permanently up-to-date. So any risk that is +incurred by build pruning is mitigated. For release quality work, obviously, +we recommend that you do not use build pruning (it's perfectly OK to use +during integration, however, for checking compilation, etc. Just be sure to +do an unconstrained build before committing the integration). + +--> + + <para> + + XXX + + </para> + + <section> + <title>Selective Builds</title> + + <para> + + XXX + + </para> + + </section> + + <!-- + + <section> + <title>Build Pruning</title> + + <para> + + XXX + + </para> + + </section> + + --> + + <section> + <title>Overriding Construction Variables</title> + + <para> + + XXX + + </para> + + </section> + + <section> + <title>The &SCONSFLAGS; Environment Variable</title> + + <para> + + XXX + + </para> + + </section> diff --git a/doc/user/scanners.in b/doc/user/scanners.in new file mode 100644 index 0000000..76b2a1a --- /dev/null +++ b/doc/user/scanners.in @@ -0,0 +1,139 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + +<!-- + +=head1 Using and writing dependency scanners + +QuickScan allows simple target-independent scanners to be set up for +source files. Only one QuickScan scanner may be associated with any given +source file and environment, although the same scanner may (and should) +be used for multiple files of a given type. + +A QuickScan scanner is only ever invoked once for a given source file, +and it is only invoked if the file is used by some target in the tree +(i.e., there is a dependency on the source file). + +QuickScan is invoked as follows: + + QuickScan CONSENV CODEREF, FILENAME [, PATH] + +The subroutine referenced by CODEREF is expected to return a list of +filenames included directly by FILE. These filenames will, in turn, be +scanned. The optional PATH argument supplies a lookup path for finding +FILENAME and/or files returned by the user-supplied subroutine. The PATH +may be a reference to an array of lookup-directory names, or a string of +names separated by the system's separator character (':' on UNIX systems, +';' on Windows NT). + +The subroutine is called once for each line in the file, with $_ set to the +current line. If the subroutine needs to look at additional lines, or, for +that matter, the entire file, then it may read them itself, from the +filehandle SCAN. It may also terminate the loop, if it knows that no further +include information is available, by closing the filehandle. + +Whether or not a lookup path is provided, QuickScan first tries to lookup +the file relative to the current directory (for the top-level file +supplied directly to QuickScan), or from the directory containing the +file which referenced the file. This is not very general, but seems good +enough, especially if you have the luxury of writing your own utilities +and can control the use of the search path in a standard way. + +Here's a real example, taken from a F<Construct> file here: + + sub cons::SMFgen { + my($env, @tables) = @_; + foreach $t (@tables) { + $env->QuickScan(sub { /\b\S*?\.smf\b/g }, "$t.smf", + $env->{SMF_INCLUDE_PATH}); + $env->Command(["$t.smdb.cc","$t.smdb.h","$t.snmp.cc", + "$t.ami.cc", "$t.http.cc"], "$t.smf", + q(smfgen %( %SMF_INCLUDE_OPT %) %<)); + } + } + +The subroutine above finds all names of the form <name>.smf in the +file. It will return the names even if they're found within comments, +but that's OK (the mechanism is forgiving of extra files; they're just +ignored on the assumption that the missing file will be noticed when +the program, in this example, smfgen, is actually invoked). + +[NOTE that the form C<$env-E<gt>QuickScan ...> and C<$env-E<gt>Command +...> should not be necessary, but, for some reason, is required +for this particular invocation. This appears to be a bug in Perl or +a misunderstanding on my part; this invocation style does not always +appear to be necessary.] + +Here is another way to build the same scanner. This one uses an +explicit code reference, and also (unnecessarily, in this case) reads +the whole file itself: + + sub myscan { + my(@includes); + do { + push(@includes, /\b\S*?\.smf\b/g); + } while <SCAN>; + @includes + } + +Note that the order of the loop is reversed, with the loop test at the +end. This is because the first line is already read for you. This scanner +can be attached to a source file by: + + QuickScan $env \&myscan, "$_.smf"; + +This final example, which scans a different type of input file, takes +over the file scanning rather than being called for each input line: + + $env->QuickScan( + sub { my(@includes) = (); + do { + push(@includes, $3) + if /^(#include|import)\s+(\")(.+)(\")/ && $3 + } while <SCAN>; + @includes + }, + "$idlFileName", + "$env->{CPPPATH};$BUILD/ActiveContext/ACSCLientInterfaces" + ); + +--> + + <para> + + XXX + + </para> + + <section> + <title>XXX</title> + + <para> + + XXX + + </para> + + </section> diff --git a/doc/user/separate.in b/doc/user/separate.in new file mode 100644 index 0000000..c1b3c32 --- /dev/null +++ b/doc/user/separate.in @@ -0,0 +1,429 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + +<!-- + +=head1 Separating source and build trees + +It's often desirable to keep any derived files from the build completely +separate from the source files. This makes it much easier to keep track of +just what is a source file, and also makes it simpler to handle B<variant> +builds, especially if you want the variant builds to co-exist. + +=head2 Separating build and source directories using the Link command + +Cons provides a simple mechanism that handles all of these requirements. The +C<Link> command is invoked as in this example: + + Link 'build' => 'src'; + +The specified directories are ``linked'' to the specified source +directory. Let's suppose that you setup a source directory, F<src>, with the +sub-directories F<world> and F<hello> below it, as in the previous +example. You could then substitute for the original build lines the +following: + + Build qw( + build/world/Conscript + build/hello/Conscript + ); + +Notice that you treat the F<Conscript> file as if it existed in the build +directory. Now if you type the same command as before, you will get the +following results: + + % cons export + Install build/world/world.h as export/include/world.h + cc -Iexport/include -c build/hello/hello.c -o build/hello/hello.o + cc -Iexport/include -c build/world/world.c -o build/world/world.o + ar r build/world/libworld.a build/world/world.o + ar: creating build/world/libworld.a + ranlib build/world/libworld.a + Install build/world/libworld.a as export/lib/libworld.a + cc -o build/hello/hello build/hello/hello.o -Lexport/lib -lworld + Install build/hello/hello as export/bin/hello + +Again, Cons has taken care of the details for you. In particular, you will +notice that all the builds are done using source files and object files from +the build directory. For example, F<build/world/world.o> is compiled from +F<build/world/world.c>, and F<export/include/world.h> is installed from +F<build/world/world.h>. This is accomplished on most systems by the simple +expedient of ``hard'' linking the required files from each source directory +into the appropriate build directory. + +The links are maintained correctly by Cons, no matter what you do to the +source directory. If you modify a source file, your editor may do this ``in +place'' or it may rename it first and create a new file. In the latter case, +any hard link will be lost. Cons will detect this condition the next time +the source file is needed, and will relink it appropriately. + +You'll also notice, by the way, that B<no> changes were required to the +underlying F<Conscript> files. And we can go further, as we shall see in the +next section. + +=head2 Explicit references to the source directory + +When using the C<Link> command on some operating systems or with some +tool chains, it's sometimes useful to have a command actually use +the path name to the source directory, not the build directory. For +example, on systems that must copy, not "hard link," the F<src/> and +F<build/> copies of C<Linked> files, using the F<src/> path of a file +name might make an editor aware that a syntax error must be fixed in the +source directory, not the build directory. + +You can tell Cons that you want to use the "source path" for a file by +preceding the file name with a ``!'' (exclamation point). For example, +if we add a ``!'' to the beginning of a source file: + + Program $env "foo", "!foo.c"; # Notice initial ! on foo.c + +Cons will compile the target as follows: + + cc -c src/foo.c -o build/foo.o + cc -o build/foo build/foo.o + +Notice that Cons has compiled the program from the the F<src/foo.c> +source file. Without the initial ``!'', Cons would have compiled the +program using the F<build/foo.c> path name. + +--> + + <para> + + It's often useful to keep any built files completely + separate from the source files. + This is usually done by creating one or more separate + <emphasis>build directories</emphasis> + that are used to hold the built objects files, libraries, + and executable programs, etc. + for a specific flavor of build. + &SCons; provides two ways to do this, + one through the &SConscript; function that we've already seen, + and the second through a more flexible &BuildDir; function. + + </para> + + <section> + <title>Specifying a Build Directory as Part of an &SConscript; Call</title> + + <para> + + The most straightforward way to establish a build directory + uses the fact that the usual way to + set up a build hierarcy is to have an + &SConscript; file in the source subdirectory. + If you then pass a &build_dir; argument to the + &SConscript; function call: + + </para> + + <sconstruct> + SConscript('src/SConscript', build_dir='build') + </sconstruct> + + <para> + + &SCons; will then build all of the files in + the &build; subdirectory: + + </para> + + <scons_example name="ex1"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Program('hello.c') + </file> + <file name="hello.c"> + int main() { printf("Hello, world!\n"); } + </file> + </scons_example> + + <scons_output example="ex1"> + <command>ls -1 src</command> + <command>scons</command> + <command>ls -1 build</command> + </scons_output> + + <para> + + But wait a minute--what's going on here? + &SCons; created the object file + <filename>build/hello.o</filename> + in the &build; subdirectory, + as expected. + But even though our &hello_c; file lives in the &src; subdirectory, + &SCons; has actually compiled a + <filename>build/hello.c</filename> file + to create the object file. + + </para> + + <para> + + What's happened is that &SCons; has <emphasis>duplicated</emphasis> + the &hello_c; file from the &src; subdirectory + to the &build; subdirectory, + and built the program from there. + The next section explains why &SCons; does this. + + </para> + + </section> + + <section> + <title>Why &SCons; Duplicates Source Files in a Build Directory</title> + + <para> + + &SCons; duplicates source files in build directories + because it's the most straightforward way to guarantee a correct build + <emphasis>regardless of include-file directory paths</emphasis>, + and the &SCons; philosophy is to, by default, + guarantee a correct build in all cases. + Here is an example of an end case where duplicating + source files in a build directory + is necessary for a correct build: + + </para> + + <para> + + XXX + + </para> + + <sconstruct> + env = Environmnet() + </sconstruct> + + <para> + + XXX + + </para> + + <literallayout> + % <userinput>scons</userinput> + cc -c build/hello.c -o build/hello.o + cc -o build/hello build/hello.o + </literallayout> + + </section> + + <section> + <title>Telling &SCons; to Not Duplicate Source Files in the Build Directory</title> + + <para> + + In most cases, however, + having &SCons; place its target files in a build subdirectory + <emphasis>without</emphasis> + duplicating the source files works just fine. + You can disable the default &SCons; behavior + by specifying <literal>duplicate=0</literal> + when you call the &SConscript; function: + + </para> + + <sconstruct> + SConscript('src/SConscript', build_dir='build', duplicate=0) + </sconstruct> + + <para> + + When this flag is specified, + &SCons; uses the build directory + like most people expect--that is, + the output files are placed in the build directory + while the source files stay in the source directory: + + </para> + + <literallayout> + % <userinput>ls -1 src</userinput> + SConscript + hello.c + % <userinput>scons</userinput> + cc -c src/hello.c -o build/hello.o + cc -o build/hello build/hello.o + % <userinput>ls -1 build</userinput> + hello + hello.o + </literallayout> + + </section> + + <section> + <title>The &BuildDir; Function</title> + + <para> + + Use the &BuildDir; function to establish that target + files should be built in a separate directory + from the source files: + + </para> + + <scons_example name="ex_builddir"> + <file name="SConstruct" printme="1"> + BuildDir('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> + + Note that when you're not using + an &SConscript; file in the &src; subdirectory, + you must actually specify that + the program must be built from + the <filename>build/hello.c</filename> + file that &SCons; will duplicate in the + &build; subdirectory. + + </para> + + <para> + + XXX + + </para> + + <para> + + When using the &BuildDir; function directly, + &SCons; still duplicates the source files + in the build directory by default: + + </para> + + <scons_output example="ex_builddir"> + <command>ls src</command> + <command>scons</command> + <command>ls -1 build</command> + </scons_output> + + <para> + + You can specify the same <literal>duplicate=0</literal> argument + that you can specify for an &SConscript; call: + + </para> + + <scons_example name="ex_duplicate_0"> + <file name="SConstruct" printme="1"> + BuildDir('build', 'src', duplicate=0) + 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="ex_duplicate_0"> + <command>ls src</command> + <command>scons</command> + <command>ls -1 build</command> + </scons_output> + + </section> + + <section> + <title>Using &BuildDir; With an &SConscript; File</title> + + <para> + + Even when using the &BuildDir; function, + it's much more natural to use it with + a subsidiary &SConscript; file. + For example, if the + <filename>src/SConscript</filename> + looks like this: + + </para> + + <scons_example name="example_builddir_sconscript"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Program('hello.c') + </file> + <file name="SConscript"> + BuildDir('build', 'src') + SConscript('build/SConscript') + </scons_example> + + <para> + + Then our &SConscript; file could look like: + + </para> + + <scons_example_file example="example_builddir_sconscript" name="SConscript"> + </scons_example_file> + + <para> + + Yielding the following output: + + </para> + + <scons_output example="example_builddir_sconscript"> + <command>ls -1 src</command> + <command>scons</command> + <command>ls -1 build</command> + </scons_output> + + <para> + + Notice that this is completely equivalent + to the use of &SConscript; that we + learned about in the previous section. + + </para> + + </section> + + <section> + <title>Why You'd Want to Call &BuildDir; Instead of &SConscript;</title> + + <para> + + XXX + + </para> + + </section> diff --git a/doc/user/simple.in b/doc/user/simple.in new file mode 100644 index 0000000..df82ea2 --- /dev/null +++ b/doc/user/simple.in @@ -0,0 +1,522 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + + <para> + + Here's the famous "Hello, World!" program in C: + + </para> + + <programlisting> + int + main() + { + printf("Hello, world!\n"); + } + </programlisting> + + <para> + + And here's how to build it using &SCons;. + Enter the following into a file named &SConstruct;: + + </para> + + <scons_example name="ex1"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Program('hello.c') + </file> + <file name="hello.c"> + int main() { printf("Hello, world!\n"); } + </file> + </scons_example> + + <para> + + That's it. Now run the &scons; command to build the program. + On a POSIX-compliant system like Linux or UNIX, + you'll see something like: + + </para> + + <scons_output example="ex1" os="posix"> + <command>scons</command> + </scons_output> + + <para> + + On a Windows system with the Microsoft Visual C++ compiler, + you'll see something like: + + </para> + + <scons_output example="ex1" os="win32"> + <command>scons</command> + </scons_output> + + <para> + + First, notice that you only need + to specify the name of the source file, + and that &SCons; deduces the names of + the object and executable files + correctly from the base of the source file name. + + </para> + + <para> + + Second, notice that the same input &SConstruct; file, + without any changes, + generates the correct output file names on both systems: + <filename>hello.o</filename> and <filename>hello</filename> + on POSIX systems, + <filename>hello.obj</filename> and <filename>hello.exe</filename> + on Windows systems. + This is a simple example of how &SCons; + makes it extremely easy to + write portable software builds. + + </para> + + <para> + + (Note that we won't provide duplicate side-by-side + POSIX and Windows output for all of the examples in this guide; + just keep in mind that, unless otherwise specified, + any of the examples should work equally well on both types of systems.) + + </para> + + <section> + <title>The &SConstruct; File</title> + + <para> + + If you're used to build systems like &Make; + you've already figured out that the &SConstruct; file + is the &SCons; equivalent of a &Makefile;. + That is, the &SConstruct; file is the input file + that &SCons; reads to control the build. + + </para> + + <para> + + There is, however, an important difference between + an &SConstruct; file and a &Makefile;: + the &SConstruct; file is actually a Python script. + If you're not already familiar with Python, don't worry. + This User's Guide will introduce you step-by-step + to the relatively small amount of Python you'll + need to know to be able to use &SCons; effectively. + And Python is very easy to learn. + + </para> + + <para> + + One aspect of using Python as the + scripting language is that you can put comments + in your &SConstruct; file using Python's commenting convention; + that is, everything between a '#' and the end of the line + will be ignored: + + </para> + + <programlisting> + env = Environment() # Create an environment. + # Arrange to build the "hello" program. + env.Program('hello.c') + </programlisting> + + <para> + + You'll see throughout the remainder of this Guide + that being able to use the power of a + real scripting language + can greatly simplify the solutions + to complex requirements of real-world builds. + + </para> + + </section> + + <section> + <title>Compiling Multiple Source Files</title> + + <para> + + You've just seen how to configure &SCons; + to compile a program from a single source file. + It's more common, of course, + that you'll need to build a program from + many input source files, not just one. + To do this, you need to put the + source files in a Python list + (enclosed in square brackets), + like so: + + </para> + + <scons_example name="ex2"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Program(['prog.c', 'file1.c', 'file2.c']) + </file> + <file name="prog.c"> + int main() { printf("prog.c\n"); } + </file> + <file name="file1.c"> + void file1() { printf("file1.c\n"); } + </file> + <file name="file2.c"> + void file2() { printf("file2.c\n"); } + </file> + </scons_example> + + <para> + + A build of the above example would look like: + + </para> + + <scons_output example="ex2"> + <command>scons</command> + </scons_output> + + <para> + + Notice that &SCons; + deduces the output program name + from the first source file specified + in the list--that is, + because the first source file was &prog_c;, + &SCons; will name the resulting program &prog; + (or &prog_exe; on a Windows system). + If you want to specify a different program name, + then you slide the list of source files + over to the right + to make room for the output program file name. + (&SCons; puts the output file name to the left + of the source file names + so that the order mimics that of an + assignment statement: "program = source files".) + This makes our example: + + </para> + + <scons_example name="ex3"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Program('program', ['main.c', 'file1.c', 'file2.c']) + </file> + <file name="main.c"> + int main() { printf("prog.c\n"); } + </file> + <file name="file1.c"> + void file1() { printf("file1.c\n"); } + </file> + <file name="file2.c"> + void file2() { printf("file2.c\n"); } + </file> + </scons_example> + + <para> + + On Linux, a build of this example would look like: + + </para> + + <scons_output example="ex3" os="posix"> + <command>scons</command> + </scons_output> + + <para> + + Or on Windows: + + </para> + + <scons_output example="ex3" os="win32"> + <command>scons</command> + </scons_output> + + </section> + + <section> + <title>Keeping &SConstruct; Files Easy to Read</title> + + <para> + + One drawback to the use of a Python list + for source files is that + each file name must be enclosed in quotes + (either single quotes or double quotes). + This can get cumbersome and difficult to read + when the list of file names is long. + Fortunately, &SCons; and Python provide a number of ways + to make sure that + the &SConstruct; file stays easy to read. + + </para> + + <para> + + To make long lists of file names + easier to deal with, &SCons; provides a + &Split; function + that takes a quoted list of file names, + with the names separated by spaces or other white-space characters, + and turns it into a list of separate file names. + Using the &Split; function turns the + previous example into: + + </para> + + <programlisting> + env = Environment() + env.Program('program', Split('main.c file1.c file2.')) + </programlisting> + + <para> + + Putting the call to the &Split; function + inside the <function>env.Program</function> call + can also be a little unwieldy. + A more readable alternative is to + assign the output from the &Split; call + to a variable name, + and then use the variable when calling the + <function>env.Program</function> function: + + </para> + + <programlisting> + env = Environment() + list = Split('main.c file1.c file2.') + env.Program('program', list) + </programlisting> + + <para> + + Lastly, the &Split; function + doesn't care how much white space separates + the file names in the quoted string. + This allows you to create lists of file + names that span multiple lines, + which often makes for easier editing: + + </para> + + <programlisting> + env = Environment() + list = Split('main.c + file1.c + file2.c') + env.Program('program', list) + </programlisting> + + </section> + + <section> + <title>Keyword Arguments</title> + + <para> + + &SCons; also allows you to identify + the output file and input source files + using Python keyword arguments. + The output file is known as the + <emphasis>target</emphasis>, + and the source file(s) are known (logically enough) as the + <emphasis>source</emphasis>. + The Python syntax for this is: + + </para> + + <programlisting> + env = Environment() + list = Split('main.c file1.c file2.') + env.Program(target = 'program', source = list) + </programlisting> + + <para> + + Whether or not you choose to use keyword arguments + to identify the target and source files + is purely a personal choice; + &SCons; functions the same either way. + + </para> + + </section> + + <section> + <title>Compiling Multiple Programs</title> + + <para> + + In order to compile multiple programs + within the same &SConstruct; file, + simply call the <function>env.Program</function> method + multiple times, + once for each program you need to build: + + </para> + + <scons_example name="ex4"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Program('foo.c') + env.Program('bar', ['bar1.c', 'bar2.c']) + </file> + <file name="foo.c"> + int main() { printf("foo.c\n"); } + </file> + <file name="bar1.c"> + int main() { printf("bar1.c\n"); } + </file> + <file name="bar2.c"> + void bar2() { printf("bar2.c\n"); } + </file> + </scons_example> + + <para> + + &SCons; would then build the programs as follows: + + </para> + + <scons_output example="ex4"> + <command>scons</command> + </scons_output> + + <para> + + Notice that &SCons; does not necessarily build the + programs in the same order in which you specify + them in the &SConstruct; file. + &SCons; does, however, recognize that + the individual object files must be built + before the resulting program can be built. + We'll discuss this in greater detail in + the "Dependencies" section, below. + + </para> + + </section> + + <section> + <title>Sharing Source Files Between Multiple Programs</title> + + <para> + + It's common to re-use code by sharing source files + between multiple programs. + One way to do this is to create a library + from the common source files, + which can then be linked into resulting programs. + (Creating libraries is discussed in + section XXX, below.) + + </para> + + <para> + + A more straightforward, but perhaps less convenient, + way to share source files between multiple programs + is simply to include the common files + in the lists of source files for each program: + + </para> + + <scons_example name="ex5"> + <file name="SConstruct" printme="1"> + env = Environment() + env.Program(Split('foo.c common1.c common2.c')) + env.Program('bar', Split('bar1.c bar2.c common1.c common2.c')) + </file> + <file name="foo.c"> + int main() { printf("foo.c\n"); } + </file> + <file name="bar1.c"> + int main() { printf("bar1.c\n"); } + </file> + <file name="bar2.c"> + int bar2() { printf("bar2.c\n"); } + </file> + <file name="common1.c"> + void common1() { printf("common1.c\n"); } + </file> + <file name="common2.c"> + void common22() { printf("common2.c\n"); } + </file> + </scons_example> + + <para> + + &SCons; recognizes that the object files for + the &common1_c; and &common2_c; source files + each only need to be built once, + even though the files are listed multiple times: + + </para> + + <scons_output example="ex5"> + <command>scons</command> + </scons_output> + + <para> + + If two or more programs + share a lot of common source files, + repeating the common files in the list for each program + can be a maintenance problem when you need to change the + list of common files. + You can simplify this by creating a separate Python list + to hold the common file names, + and concatenating it with other lists + using the Python + operator: + + </para> + + <programlisting> + common = ['common1.c', 'common2.c'] + foo_files = ['foo.c'] + common + bar_files = ['bar1.c', 'bar2.c'] + common + env = Environment() + env.Program('foo', foo_files) + env.Program('bar', bar_files) + </programlisting> + + <para> + + This is functionally equivalent to the previous example. + + </para> + + </section> diff --git a/doc/user/sourcecode.in b/doc/user/sourcecode.in new file mode 100644 index 0000000..b794c09 --- /dev/null +++ b/doc/user/sourcecode.in @@ -0,0 +1,153 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + +<!-- + +--> + + <para> + + X + + </para> + + <section> + <title>Fetching Source Code From BitKeeper</title> + + <para> + + X + + </para> + + <scons_example name="ex_bitkeeper"> + <file name="SConstruct" printme="1"> + env = Environment() + env.SourceCode('.', env.BitKeeper('XXX')) + env.Program('hello.c') + </file> + </scons_example> + + <scons_output example="ex_bitkeeper"> + <userinput>scons</userinput> + </scons_output> + + </section> + + <section> + <title>Fetching Source Code From CVS</title> + + <para> + + X + + </para> + + <scons_example name="ex_cvs"> + <file name="SConstruct" printme="1"> + env = Environment() + env.SourceCode('.', env.CVS('XXX')) + env.Program('hello.c') + </file> + </scons_example> + + <scons_output example="ex_cvs"> + <userinput>scons</userinput> + </scons_output> + + </section> + + <section> + <title>Fetching Source Code From RCS</title> + + <para> + + X + + </para> + + <scons_example name="ex_rcs"> + <file name="SConstruct" printme="1"> + env = Environment() + env.SourceCode('.', env.RCS()) + env.Program('hello.c') + </file> + </scons_example> + + <scons_output example="ex_rcs"> + <userinput>scons</userinput> + </scons_output> + + </section> + + <section> + <title>Fetching Source Code From SCCS</title> + + <para> + + X + + </para> + + <scons_example name="ex_sccs"> + <file name="SConstruct" printme="1"> + env = Environment() + env.SourceCode('.', env.SCCS()) + env.Program('hello.c') + </file> + </scons_example> + + <scons_output example="ex_sccs"> + <userinput>scons</userinput> + </scons_output> + + </section> + + <!-- + + <section> + <title>Fetching Source Code From Subversion</title> + + <para> + + X + + </para> + + <scons_example name="ex_subversion"> + <file name="SConstruct" printme="1"> + env = Environment() + env.SourceCode('.', env.Subversion('XXX')) + env.Program('hello.c') + </file> + </scons_example> + + <scons_output example="ex_subversion"> + <userinput>scons</userinput> + </scons_output> + + </section> + + --> diff --git a/doc/user/troubleshoot.in b/doc/user/troubleshoot.in new file mode 100644 index 0000000..f83ab63 --- /dev/null +++ b/doc/user/troubleshoot.in @@ -0,0 +1,41 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + + <para> + + XXX + + </para> + + <section> + <title>XXX</title> + + <para> + + XXX + + </para> + + </section> diff --git a/doc/user/variants.in b/doc/user/variants.in new file mode 100644 index 0000000..1fb461a --- /dev/null +++ b/doc/user/variants.in @@ -0,0 +1,179 @@ +<!-- + + Copyright (c) 2001, 2002, 2003 Steven Knight + + Permission is hereby granted, free of charge, to any person obtaining + a copy of this software and associated documentation files (the + "Software"), to deal in the Software without restriction, including + without limitation the rights to use, copy, modify, merge, publish, + distribute, sublicense, and/or sell copies of the Software, and to + permit persons to whom the Software is furnished to do so, subject to + the following conditions: + + The above copyright notice and this permission notice shall be included + in all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY + KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE + WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND + NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE + LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION + OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +--> + +<!-- + +=head1 Variant builds + + +=head2 Hello, World! for baNaNa and peAcH OS's + +Variant builds require just another simple extension. Let's take as an +example a requirement to allow builds for both the baNaNa and peAcH +operating systems. In this case, we are using a distributed file system, +such as NFS to access the particular system, and only one or the other of +the systems has to be compiled for any given invocation of C<cons>. Here's +one way we could set up the F<Construct> file for our B<Hello, World!> +application: + + # Construct file for Hello, World! + + die qq(OS must be specified) unless $OS = $ARG{OS}; + die qq(OS must be "peach" or "banana") + if $OS ne "peach" && $OS ne "banana"; + + # Where to put all our shared products. + $EXPORT = "#export/$OS"; + + Export qw( CONS INCLUDE LIB BIN ); + + # Standard directories for sharing products. + $INCLUDE = "$EXPORT/include"; + $LIB = "$EXPORT/lib"; + $BIN = "$EXPORT/bin"; + + # A standard construction environment. + $CONS = new cons ( + CPPPATH => $INCLUDE, # Include path for C Compilations + LIBPATH => $LIB, # Library path for linking programs + LIBS => '-lworld', # List of standard libraries + ); + + # $BUILD is where we will derive everything. + $BUILD = "#build/$OS"; + + # Tell cons where the source files for $BUILD are. + Link $BUILD => 'src'; + + Build ( + "$BUILD/hello/Conscript", + "$BUILD/world/Conscript", + ); + +Now if we login to a peAcH system, we can build our B<Hello, World!> +application for that platform: + + % cons export OS=peach + Install build/peach/world/world.h as export/peach/include/world.h + cc -Iexport/peach/include -c build/peach/hello/hello.c -o build/peach/hello/hello.o + cc -Iexport/peach/include -c build/peach/world/world.c -o build/peach/world/world.o + ar r build/peach/world/libworld.a build/peach/world/world.o + ar: creating build/peach/world/libworld.a + ranlib build/peach/world/libworld.a + Install build/peach/world/libworld.a as export/peach/lib/libworld.a + cc -o build/peach/hello/hello build/peach/hello/hello.o -Lexport/peach/lib -lworld + Install build/peach/hello/hello as export/peach/bin/hello + + +=head2 Variations on a theme + +Other variations of this model are possible. For example, you might decide +that you want to separate out your include files into platform dependent and +platform independent files. In this case, you'd have to define an +alternative to C<$INCLUDE> for platform-dependent files. Most F<Conscript> +files, generating purely platform-independent include files, would not have +to change. + +You might also want to be able to compile your whole system with debugging +or profiling, for example, enabled. You could do this with appropriate +command line options, such as C<DEBUG=on>. This would then be translated +into the appropriate platform-specific requirements to enable debugging +(this might include turning off optimization, for example). You could +optionally vary the name space for these different types of systems, but, as +we'll see in the next section, it's not B<essential> to do this, since Cons +is pretty smart about rebuilding things when you change options. + +--> + + <para> + + The &BuildDir; function now gives us everything + we need to show how easy it is to create + variant builds using &SCons;. + Suppose, for example, that we want to + build a program for both Windows and Linux platforms, + but that we want to build it in a shared directory + with separate side-by-side build directories + for the Windows and Linux versions of the program. + + </para> + + <scons_example name="ex_variants"> + <file name="SConstruct" printme="1"> + platform = ARGUMENT.get('OS', Platform()) + + include = "#export/$PLATFORM/include" + lib = "#export/$PLATFORM/lib" + bin = "#export/$PLATFORM/bin" + + env = Environment(PLATFORM = platform, + CPPPATH = [include], + LIB = lib, + LIBS = '-lworld') + + Export('env') + + SConscript('src/SConscript', build_dir='build/$PLATFORM') + + # + #BuildDir("#build/$PLATFORM", 'src') + #SConscript("build/$PLATFORM/hello/SConscript") + #SConscript("build/$PLATFORM/world/SConscript") + </file> + </scons_example> + + <para> + + This SConstruct file, + when run on a Linux system, yields: + + </para> + + <scons_output example="ex_variants" os="posix"> + <command>scons OS=linux</command> + </scons_output> + + <para> + + The same SConstruct file on Windows would build: + + </para> + + <scons_output example="ex_variants" os="win32"> + <command>scons OS=windows</command> + </scons_output> + + <scons_example name="ex_var2"> + <programlisting> + <file name="SConstruct" printme="1"> + env = Environment(OS = ) + for os in ['newell', 'post']: + SConscript('src/SConscript', build_dir='build/' + os) + </file> + </scons_example> + + <scons_output example="ex_var2"> + % <userinput>scons</userinput> + </scons_output> diff --git a/src/engine/SCons/Defaults.py b/src/engine/SCons/Defaults.py index 87420aa..5ade3e0 100644 --- a/src/engine/SCons/Defaults.py +++ b/src/engine/SCons/Defaults.py @@ -257,7 +257,7 @@ class NullCmdGenerator: def __init__(self, cmd): self.cmd = cmd - def __call__(self, target, source, env): + def __call__(self, target, source, env, for_signature=None): return self.cmd ConstructionEnvironment = { diff --git a/src/engine/SCons/Environment.py b/src/engine/SCons/Environment.py index d0a22b5..fae86e4 100644 --- a/src/engine/SCons/Environment.py +++ b/src/engine/SCons/Environment.py @@ -165,7 +165,7 @@ class Environment: """ def __init__(self, - platform=SCons.Platform.Platform(), + platform=None, tools=None, options=None, **kw): @@ -174,6 +174,10 @@ class Environment: self._dict['BUILDERS'] = BuilderDict(self._dict['BUILDERS'], self) + if platform is None: + platform = self._dict.get('PLATFORM', None) + if platform is None: + platform = SCons.Platform.Platform() if SCons.Util.is_String(platform): platform = SCons.Platform.Platform(platform) self._dict['PLATFORM'] = str(platform) @@ -189,7 +193,9 @@ class Environment: options.Update(self) if tools is None: - tools = ['default'] + tools = self._dict.get('TOOLS', None) + if tools is None: + tools = ['default'] apply_tools(self, tools) # Reapply the passed in variables after calling the tools, diff --git a/src/engine/SCons/EnvironmentTests.py b/src/engine/SCons/EnvironmentTests.py index 5038419..e79ce7c 100644 --- a/src/engine/SCons/EnvironmentTests.py +++ b/src/engine/SCons/EnvironmentTests.py @@ -909,11 +909,37 @@ class EnvironmentTestCase(unittest.TestCase): def __call__(self, env): env['XYZZY'] = 777 def tool(env): + env['SET_TOOL'] = 'initialized' assert env['PLATFORM'] == "TestPlatform" env = Environment(platform = platform(), tools = [tool]) assert env['XYZZY'] == 777, env assert env['PLATFORM'] == "TestPlatform" + assert env['SET_TOOL'] == "initialized" + + def test_Default_PLATFORM(self): + """Test overriding the default PLATFORM variable""" + class platform: + def __str__(self): return "DefaultTestPlatform" + def __call__(self, env): env['XYZZY'] = 888 + + def tool(env): + env['SET_TOOL'] = 'abcde' + assert env['PLATFORM'] == "DefaultTestPlatform" + + import SCons.Defaults + save = SCons.Defaults.ConstructionEnvironment.copy() + try: + import SCons.Defaults + SCons.Defaults.ConstructionEnvironment.update({ + 'PLATFORM' : platform(), + }) + env = Environment(tools = [tool]) + assert env['XYZZY'] == 888, env + assert env['PLATFORM'] == "DefaultTestPlatform" + assert env['SET_TOOL'] == "abcde" + finally: + SCons.Defaults.ConstructionEnvironment = save def test_tools(self): """Test specifying a tool callable when instantiating.""" @@ -932,6 +958,31 @@ class EnvironmentTestCase(unittest.TestCase): t4(env) assert env['TOOL4'] == 444, env + def test_Default_TOOLS(self): + """Test overriding the default TOOLS variable""" + def t5(env): + env['TOOL5'] = 555 + def t6(env): + env['TOOL6'] = 666 + def t7(env): + env['BBB'] = env['XYZ'] + def t8(env): + env['TOOL8'] = 888 + + import SCons.Defaults + save = SCons.Defaults.ConstructionEnvironment.copy() + try: + SCons.Defaults.ConstructionEnvironment.update({ + 'TOOLS' : [t5, t6, t7], + }) + env = Environment(XYZ = 'bbb') + assert env['TOOL5'] == 555, env['TOOL5'] + assert env['TOOL6'] == 666, env + assert env['BBB'] == 'bbb', env + t8(env) + assert env['TOOL8'] == 888, env + finally: + SCons.Defaults.ConstructionEnvironment = save def test_get(self): """Test the get() method.""" diff --git a/test/ToolSurrogate.py b/test/ToolSurrogate.py new file mode 100644 index 0000000..f33d804 --- /dev/null +++ b/test/ToolSurrogate.py @@ -0,0 +1,101 @@ +#!/usr/bin/env python +# +# __COPYRIGHT__ +# +# Permission is hereby granted, free of charge, to any person obtaining +# a copy of this software and associated documentation files (the +# "Software"), to deal in the Software without restriction, including +# without limitation the rights to use, copy, modify, merge, publish, +# distribute, sublicense, and/or sell copies of the Software, and to +# permit persons to whom the Software is furnished to do so, subject to +# the following conditions: +# +# The above copyright notice and this permission notice shall be included +# in all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY +# KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE +# WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +# + +__revision__ = "__FILE__ __REVISION__ __DATE__ __DEVELOPER__" + +""" +Test that SCons supports use of a home-brew ToolSurrogate class +like we use in our bin/sconsexamples.py script. +""" + +import TestSCons + +test = TestSCons.TestSCons() + +test.write('SConstruct', """\ +class Curry: + def __init__(self, fun, *args, **kwargs): + self.fun = fun + self.pending = args[:] + self.kwargs = kwargs.copy() + + def __call__(self, *args, **kwargs): + if kwargs and self.kwargs: + kw = self.kwargs.copy() + kw.update(kwargs) + else: + kw = kwargs or self.kwargs + + return apply(self.fun, self.pending + args, kw) + +def Str(target, source, env, cmd=""): + return env.subst(cmd, target=target, source=source) + +class ToolSurrogate: + def __init__(self, tool, variable, func): + self.tool = tool + self.variable = variable + self.func = func + def __call__(self, env): + t = Tool(self.tool) + t.generate(env) + orig = env[self.variable] + env[self.variable] = Action(self.func, strfunction=Curry(Str, cmd=orig)) + +def Cat(target, source, env): + target = str(target[0]) + f = open(target, "wb") + for src in map(str, source): + f.write(open(src, "rb").read()) + f.close() + +ToolList = { + 'posix' : [('cc', 'CCCOM', Cat), + ('link', 'LINKCOM', Cat)], + 'win32' : [('msvc', 'CCCOM', Cat), + ('mslink', 'LINKCOM', Cat)] +} + +platform = ARGUMENTS['platform'] +tools = map(lambda t: apply(ToolSurrogate, t), ToolList[platform]) + +env = Environment(tools = tools) +env.Program('foo.c') +""") + +test.write('foo.c', "foo.c posix\n") + +test.run(arguments = '. platform=posix', stdout = test.wrap_stdout("""\ +cc -c -o foo.o foo.c +c++ -o foo foo.o +""")) + +test.write('foo.c', "foo.c win32\n") + +test.run(arguments = '. platform=win32', stdout = test.wrap_stdout("""\ +cl /nologo /c foo.c /Fofoo.o +link /nologo /OUT:foo foo.o +""")) + +test.pass_test() |