mirror of
https://github.com/Relintai/scons_gd.git
synced 2025-02-06 16:25:59 +01:00
589 lines
15 KiB
XML
589 lines
15 KiB
XML
|
<?xml version='1.0'?>
|
||
|
<!DOCTYPE sconsdoc [
|
||
|
<!ENTITY % scons SYSTEM "../scons.mod">
|
||
|
%scons;
|
||
|
|
||
|
<!ENTITY % builders-mod SYSTEM "../generated/builders.mod">
|
||
|
%builders-mod;
|
||
|
<!ENTITY % functions-mod SYSTEM "../generated/functions.mod">
|
||
|
%functions-mod;
|
||
|
<!ENTITY % tools-mod SYSTEM "../generated/tools.mod">
|
||
|
%tools-mod;
|
||
|
<!ENTITY % variables-mod SYSTEM "../generated/variables.mod">
|
||
|
%variables-mod;
|
||
|
]>
|
||
|
|
||
|
<chapter id="chap-sconf"
|
||
|
xmlns="http://www.scons.org/dbxsd/v1.0"
|
||
|
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||
|
xsi:schemaLocation="http://www.scons.org/dbxsd/v1.0 http://www.scons.org/dbxsd/v1.0/scons.xsd">
|
||
|
<title>Multi-Platform Configuration (&Autoconf; Functionality)</title>
|
||
|
|
||
|
<!--
|
||
|
|
||
|
MIT License
|
||
|
|
||
|
Copyright The SCons Foundation
|
||
|
|
||
|
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; has integrated support for build configuration
|
||
|
similar in style to GNU &Autoconf;, but designed to be
|
||
|
transparently multi-platform. The configuration system
|
||
|
can help figure out if external build requirements such
|
||
|
as system libraries or header files
|
||
|
are available on the build system.
|
||
|
This section describes how to use
|
||
|
this &SCons; feature.
|
||
|
(See also the &SCons; man page for additional information).
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<section>
|
||
|
<title>&Configure_Contexts;</title>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
The basic framework for multi-platform build configuration
|
||
|
in &SCons; is to create a &configure_context; inside a
|
||
|
&consenv; by calling the &Configure; function,
|
||
|
perform the desired checks for
|
||
|
libraries, functions, header files, etc.,
|
||
|
and then call the configure context's &Finish; method
|
||
|
to finish off the configuration:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<sconstruct>
|
||
|
env = Environment()
|
||
|
conf = Configure(env)
|
||
|
# Checks for libraries, header files, etc. go here!
|
||
|
env = conf.Finish()
|
||
|
</sconstruct>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
The &Finish; call is required; if a new context is
|
||
|
created while a context is active, even in a different
|
||
|
&consenv;, &scons; will complain and exit.
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
&SCons; provides a number of pre-defined basic checks,
|
||
|
as well as a mechanism for adding your own custom checks.
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
There are a few possible strategies for failing
|
||
|
configure checks. Some checks may be for features without
|
||
|
which you cannot proceed. The simple approach here is
|
||
|
just to exit &SCons; at that point - a number of the
|
||
|
examples in this chapter are coded that way. If there
|
||
|
are multiple hard requirements, however, it may be
|
||
|
friendlier to the user to set a flag in case of any
|
||
|
fails of hard requirements and accumulate a record of them,
|
||
|
so that on the completion of the &configure_context; they can
|
||
|
all be listed prior to failing the build - as it can be frustrating
|
||
|
to have to iterate through the setup, fixing one new
|
||
|
requirement each iteration. Other checks may be for
|
||
|
features which you can do without, and here the strategy
|
||
|
will usually be to set a construction variable which the
|
||
|
rest of the build can examine for its absence/presence,
|
||
|
or to set particular compiler flags, library lists, etc.
|
||
|
as appropriate for the circumstances, so you can proceed
|
||
|
with the build appropriately based on available features.
|
||
|
|
||
|
</para>
|
||
|
|
||
|
|
||
|
<para>
|
||
|
|
||
|
Note that &SCons; uses its own dependency
|
||
|
mechanism to determine when a check
|
||
|
needs to be run--that is,
|
||
|
&SCons; does not run the checks
|
||
|
every time it is invoked,
|
||
|
but caches the values returned by previous checks
|
||
|
and uses the cached values unless something has changed.
|
||
|
This saves a tremendous amount
|
||
|
of developer time while working on
|
||
|
cross-platform build issues.
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
The next sections describe
|
||
|
the basic checks that &SCons; supports,
|
||
|
as well as how to add your own custom checks.
|
||
|
|
||
|
</para>
|
||
|
|
||
|
</section>
|
||
|
|
||
|
<section>
|
||
|
<title>Checking for the Existence of Header Files</title>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
Testing the existence of a header file
|
||
|
requires knowing what language the header file is.
|
||
|
This information is supplied in the <varname>language</varname>
|
||
|
keyword parameter to the &CheckHeader; method.
|
||
|
Since &scons; grew up in a world of C/C++ code,
|
||
|
a &configure_context; also has a &CheckCHeader; method
|
||
|
that specifically checks for the existence of a C header file:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<sconstruct>
|
||
|
env = Environment()
|
||
|
conf = Configure(env)
|
||
|
if not conf.CheckCHeader('math.h'):
|
||
|
print('Math.h must be installed!')
|
||
|
Exit(1)
|
||
|
if conf.CheckCHeader('foo.h'):
|
||
|
conf.env.Append(CPPDEFINES='HAS_FOO_H')
|
||
|
env = conf.Finish()
|
||
|
</sconstruct>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
As shown in the example, depending on the circumstances
|
||
|
you can choose to terminate
|
||
|
the build if a given header file doesn't exist,
|
||
|
or you can modify the construction environment
|
||
|
based on the presence or absence of a header file
|
||
|
(the same applies to any other check). If there are a
|
||
|
many elements to check for, it may be friendlier for
|
||
|
the user if you do not terminate on the first failure,
|
||
|
but track the problems found until the end and report on
|
||
|
all of them, that way the user does not have to iterate
|
||
|
multiple times, each time finding one new dependency that
|
||
|
needs to be installed.
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
If you need to check for the existence
|
||
|
a C++ header file,
|
||
|
use the &CheckCXXHeader; method:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<sconstruct>
|
||
|
env = Environment()
|
||
|
conf = Configure(env)
|
||
|
if not conf.CheckCXXHeader('vector.h'):
|
||
|
print('vector.h must be installed!')
|
||
|
Exit(1)
|
||
|
env = conf.Finish()
|
||
|
</sconstruct>
|
||
|
|
||
|
</section>
|
||
|
|
||
|
<section>
|
||
|
<title>Checking for the Availability of a Function</title>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
Check for the availability of a specific function
|
||
|
using the &CheckFunc; method:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<sconstruct>
|
||
|
env = Environment()
|
||
|
conf = Configure(env)
|
||
|
if not conf.CheckFunc('strcpy'):
|
||
|
print('Did not find strcpy(), using local version')
|
||
|
conf.env.Append(CPPDEFINES=('strcpy','my_local_strcpy'))
|
||
|
env = conf.Finish()
|
||
|
</sconstruct>
|
||
|
|
||
|
</section>
|
||
|
|
||
|
<section>
|
||
|
<title>Checking for the Availability of a Library</title>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
Check for the availability of a library
|
||
|
using the &CheckLib; method.
|
||
|
You only specify the base part of the library name,
|
||
|
you don't need to add a <literal>lib</literal>
|
||
|
prefix or a <literal>.a</literal> or <literal>.lib</literal> suffix:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<sconstruct>
|
||
|
env = Environment()
|
||
|
conf = Configure(env)
|
||
|
if not conf.CheckLib('m'):
|
||
|
print('Did not find libm.a or m.lib, exiting!')
|
||
|
Exit(1)
|
||
|
env = conf.Finish()
|
||
|
</sconstruct>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
Because the ability to use a library successfully
|
||
|
often depends on having access to a header file
|
||
|
that describes the library's interface,
|
||
|
you can check for a library
|
||
|
<emphasis>and</emphasis> a header file
|
||
|
at the same time by using the
|
||
|
&CheckLibWithHeader; method:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<sconstruct>
|
||
|
env = Environment()
|
||
|
conf = Configure(env)
|
||
|
if not conf.CheckLibWithHeader('m', 'math.h', language='c'):
|
||
|
print('Did not find libm.a or m.lib, exiting!')
|
||
|
Exit(1)
|
||
|
env = conf.Finish()
|
||
|
</sconstruct>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
This is essentially shorthand for
|
||
|
separate calls to the &CheckHeader; and &CheckLib;
|
||
|
functions.
|
||
|
|
||
|
</para>
|
||
|
|
||
|
</section>
|
||
|
|
||
|
<section>
|
||
|
<title>Checking for the Availability of a &typedef;</title>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
Check for the availability of a &typedef;
|
||
|
by using the &CheckType; method:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<sconstruct>
|
||
|
env = Environment()
|
||
|
conf = Configure(env)
|
||
|
if not conf.CheckType('off_t'):
|
||
|
print('Did not find off_t typedef, assuming int')
|
||
|
conf.env.Append(CPPDEFINES=('off_t','int'))
|
||
|
env = conf.Finish()
|
||
|
</sconstruct>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
You can also add a string that will be
|
||
|
placed at the beginning of the test file
|
||
|
that will be used to check for the &typedef;.
|
||
|
This provide a way to specify
|
||
|
files that must be included to find the &typedef;:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<sconstruct>
|
||
|
env = Environment()
|
||
|
conf = Configure(env)
|
||
|
if not conf.CheckType('off_t', '#include <sys/types.h>\n'):
|
||
|
print('Did not find off_t typedef, assuming int')
|
||
|
conf.env.Append(CPPDEFINES=('off_t','int'))
|
||
|
env = conf.Finish()
|
||
|
</sconstruct>
|
||
|
|
||
|
</section>
|
||
|
<section>
|
||
|
<title>Checking the size of a datatype</title>
|
||
|
<para>
|
||
|
Check the size of a datatype by using the &CheckTypeSize; method:
|
||
|
</para>
|
||
|
|
||
|
<sconstruct>
|
||
|
env = Environment()
|
||
|
conf = Configure(env)
|
||
|
int_size = conf.CheckTypeSize('unsigned int')
|
||
|
print('sizeof unsigned int is', int_size)
|
||
|
env = conf.Finish()
|
||
|
</sconstruct>
|
||
|
|
||
|
<screen>
|
||
|
% <userinput>scons -Q</userinput>
|
||
|
sizeof unsigned int is 4
|
||
|
scons: `.' is up to date.
|
||
|
</screen>
|
||
|
</section>
|
||
|
|
||
|
<section>
|
||
|
<title>Checking for the Presence of a program</title>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
Check for the presence of a program
|
||
|
by using the &CheckProg; method:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<sconstruct>
|
||
|
env = Environment()
|
||
|
conf = Configure(env)
|
||
|
if not conf.CheckProg('foobar'):
|
||
|
print('Unable to find the program foobar on the system')
|
||
|
Exit(1)
|
||
|
env = conf.Finish()
|
||
|
</sconstruct>
|
||
|
|
||
|
</section>
|
||
|
<section>
|
||
|
<title>Extending &SCons;: Adding Your Own Custom Checks</title>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
A custom check is a Python function
|
||
|
that checks for a certain condition to exist
|
||
|
on the running system,
|
||
|
usually using methods that &SCons;
|
||
|
supplies to take care of the details
|
||
|
of checking whether a compilation succeeds,
|
||
|
a link succeeds,
|
||
|
a program is runnable,
|
||
|
etc.
|
||
|
A simple custom check for the existence of
|
||
|
a specific library might look as follows:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<sconstruct>
|
||
|
mylib_test_source_file = """
|
||
|
#include <mylib.h>
|
||
|
int main(int argc, char **argv)
|
||
|
{
|
||
|
MyLibrary mylib(argc, argv);
|
||
|
return 0;
|
||
|
}
|
||
|
"""
|
||
|
|
||
|
def CheckMyLibrary(context):
|
||
|
context.Message('Checking for MyLibrary...')
|
||
|
result = context.TryLink(mylib_test_source_file, '.c')
|
||
|
context.Result(result)
|
||
|
return result
|
||
|
</sconstruct>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
The &Message; and &Result; methods
|
||
|
should typically begin and end a custom check to
|
||
|
let the user know what's going on:
|
||
|
the &Message; call prints the
|
||
|
specified message (with no trailing newline)
|
||
|
and the &Result; call prints
|
||
|
<literal>yes</literal> if the check succeeds and
|
||
|
<literal>no</literal> if it doesn't.
|
||
|
The &TryLink; method
|
||
|
actually tests for whether the
|
||
|
specified program text
|
||
|
will successfully link.
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
(Note that a custom check can modify
|
||
|
its check based on any arguments you
|
||
|
choose to pass it,
|
||
|
or by using or modifying the configure context environment
|
||
|
in the <literal>context.env</literal> attribute.)
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
This custom check function is
|
||
|
then attached to the &configure_context;
|
||
|
by passing a dictionary
|
||
|
to the &Configure; call
|
||
|
that maps a name of the check
|
||
|
to the underlying function:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<sconstruct>
|
||
|
env = Environment()
|
||
|
conf = Configure(env, custom_tests={'CheckMyLibrary': CheckMyLibrary})
|
||
|
</sconstruct>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
You'll typically want to make
|
||
|
the check and the function name the same,
|
||
|
as we've done here,
|
||
|
to avoid potential confusion.
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
We can then put these pieces together
|
||
|
and actually call the <literal>CheckMyLibrary</literal> check
|
||
|
as follows:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<sconstruct>
|
||
|
mylib_test_source_file = """
|
||
|
#include <mylib.h>
|
||
|
int main(int argc, char **argv)
|
||
|
{
|
||
|
MyLibrary mylib(argc, argv);
|
||
|
return 0;
|
||
|
}
|
||
|
"""
|
||
|
|
||
|
def CheckMyLibrary(context):
|
||
|
context.Message('Checking for MyLibrary... ')
|
||
|
result = context.TryLink(mylib_test_source_file, '.c')
|
||
|
context.Result(result)
|
||
|
return result
|
||
|
|
||
|
env = Environment()
|
||
|
conf = Configure(env, custom_tests={'CheckMyLibrary': CheckMyLibrary})
|
||
|
if not conf.CheckMyLibrary():
|
||
|
print('MyLibrary is not installed!')
|
||
|
Exit(1)
|
||
|
env = conf.Finish()
|
||
|
|
||
|
# We would then add actual calls like Program() to build
|
||
|
# something using the "env" construction environment.
|
||
|
</sconstruct>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
If MyLibrary is not installed on the system,
|
||
|
the output will look like:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<screen>
|
||
|
% <userinput>scons</userinput>
|
||
|
scons: Reading SConscript file ...
|
||
|
Checking for MyLibrary... no
|
||
|
MyLibrary is not installed!
|
||
|
</screen>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
If MyLibrary is installed,
|
||
|
the output will look like:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<screen>
|
||
|
% <userinput>scons</userinput>
|
||
|
scons: Reading SConscript file ...
|
||
|
Checking for MyLibrary... yes
|
||
|
scons: done reading SConscript
|
||
|
scons: Building targets ...
|
||
|
.
|
||
|
.
|
||
|
.
|
||
|
</screen>
|
||
|
|
||
|
</section>
|
||
|
|
||
|
<section>
|
||
|
<title>Not Configuring When Cleaning Targets</title>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
Using multi-platform configuration
|
||
|
as described in the previous sections
|
||
|
will run the configuration commands
|
||
|
even when invoking
|
||
|
<userinput>scons -c</userinput>
|
||
|
to clean targets:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<screen>
|
||
|
% <userinput>scons -Q -c</userinput>
|
||
|
Checking for MyLibrary... yes
|
||
|
Removed foo.o
|
||
|
Removed foo
|
||
|
</screen>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
Although running the platform checks
|
||
|
when removing targets doesn't hurt anything,
|
||
|
it's usually unnecessary.
|
||
|
You can avoid this by using the
|
||
|
&GetOption; method to
|
||
|
check whether the <option>-c</option> (clean)
|
||
|
option has been invoked on the command line:
|
||
|
|
||
|
</para>
|
||
|
|
||
|
<sconstruct>
|
||
|
env = Environment()
|
||
|
if not env.GetOption('clean'):
|
||
|
conf = Configure(env, custom_tests={'CheckMyLibrary': CheckMyLibrary})
|
||
|
if not conf.CheckMyLibrary():
|
||
|
print('MyLibrary is not installed!')
|
||
|
Exit(1)
|
||
|
env = conf.Finish()
|
||
|
</sconstruct>
|
||
|
|
||
|
<screen>
|
||
|
% <userinput>scons -Q -c</userinput>
|
||
|
Removed foo.o
|
||
|
Removed foo
|
||
|
</screen>
|
||
|
|
||
|
</section>
|
||
|
|
||
|
<!--
|
||
|
|
||
|
<section>
|
||
|
<title>Controlling Configuration: the &config; Option</title>
|
||
|
|
||
|
<para>
|
||
|
|
||
|
XXX -D, -u and -U
|
||
|
|
||
|
</para>
|
||
|
|
||
|
</section>
|
||
|
|
||
|
-->
|
||
|
|
||
|
</chapter>
|