2
0
mirror of https://github.com/boostorg/build.git synced 2026-02-15 00:52:16 +00:00

Merge Boost.Build from the trunk.

[SVN r86768]
This commit is contained in:
Steven Watanabe
2013-11-19 19:52:02 +00:00
parent 6f79500bc2
commit 50e09fb39a
42 changed files with 1883 additions and 269 deletions

View File

@@ -123,10 +123,71 @@ rule print-configure-checks-summary ( )
}
}
# Attempts to build a set of virtual targets
rule try-build ( targets * : ps : what : retry ? )
{
local cache-name = $(what) [ $(ps).raw ] ;
cache-name = $(cache-name:J=-) ;
local value = [ config-cache.get $(cache-name) ] ;
local result ;
local jam-targets ;
# Attempt to build a metatarget named by 'metatarget-reference' in context of
# 'project' with properties 'ps'. Returns non-empty value if build is OK.
#
for local t in $(targets)
{
jam-targets += [ $(t).actualize ] ;
}
if $(value)
{
local x = [ PAD " - $(what)" : $(.width) ] ;
if $(value) = true
{
.$(what)-supported.$(ps) = yes ;
result = true ;
log-check-result "$(x) : yes (cached)" ;
}
else
{
log-check-result "$(x) : no (cached)" ;
}
}
else if ! UPDATE_NOW in [ RULENAMES ]
{
# Cannot determine. Assume existance.
}
else
{
local x = [ PAD " - $(what)" : $(.width) ] ;
if [ UPDATE_NOW $(jam-targets) :
$(.log-fd) : ignore-minus-n : ignore-minus-q ]
{
.$(what)-supported.$(ps) = yes ;
result = true ;
log-check-result "$(x) : yes" ;
}
else
{
log-check-result "$(x) : no" ;
}
}
if ! $(value)
{
if $(result)
{
config-cache.set $(cache-name) : true ;
}
else
{
config-cache.set $(cache-name) : false ;
}
}
return $(result) ;
}
# Attempt to build a metatarget named by 'metatarget-reference'
# in context of 'project' with properties 'ps'.
# Returns non-empty value if build is OK.
rule builds-raw ( metatarget-reference : project : ps : what : retry ? )
{
local result ;
@@ -135,64 +196,14 @@ rule builds-raw ( metatarget-reference : project : ps : what : retry ? )
{
.$(what)-tested.$(ps) = true ;
local cache-name = $(what) [ $(ps).raw ] ;
cache-name = $(cache-name:J=-) ;
local value = [ config-cache.get $(cache-name) ] ;
local targets = [ targets.generate-from-reference
$(metatarget-reference) : $(project) : $(ps) ] ;
local jam-targets ;
for local t in $(targets[2-])
{
jam-targets += [ $(t).actualize ] ;
}
if $(value)
{
local x = [ PAD " - $(what)" : $(.width) ] ;
if $(value) = true
{
.$(what)-supported.$(ps) = yes ;
result = true ;
log-check-result "$(x) : yes (cached)" ;
}
else
{
log-check-result "$(x) : no (cached)" ;
}
}
else if ! UPDATE_NOW in [ RULENAMES ]
{
# Cannot determine. Assume existance.
}
else
{
local x = [ PAD " - $(what)" : $(.width) ] ;
if [ UPDATE_NOW $(jam-targets) :
$(.log-fd) : ignore-minus-n : ignore-minus-q ]
{
.$(what)-supported.$(ps) = yes ;
result = true ;
log-check-result "$(x) : yes" ;
}
else
{
log-check-result "$(x) : no" ;
}
}
if ! $(value)
{
if $(result)
{
config-cache.set $(cache-name) : true ;
}
else
{
config-cache.set $(cache-name) : false ;
}
}
result = [ try-build $(targets[2-]) : $(ps) : $(what) : $(retry) ] ;
.$(what)-supported.$(ps) = $(result) ;
return $(result) ;
}
else
{

View File

@@ -667,7 +667,7 @@ class generator
result += $(source) ;
}
}
return [ sequence.unique $(result) ] ;
return [ sequence.unique $(result) : stable ] ;
}
}

View File

@@ -6,8 +6,8 @@
import numbers ;
.major = "2011" ;
.minor = "12" ;
.major = "2013" ;
.minor = "05" ;
rule boost-build ( )

View File

@@ -1324,8 +1324,9 @@ class subvariant
for local t in $(self.created-targets)
{
# Skip targets of the wrong type.
local type = [ $(t).type ] ;
if ! $(target-type) ||
[ type.is-derived [ $(t).type ] $(target-type) ]
( $(type) && [ type.is-derived $(type) $(target-type) ] )
{
result = [ sequence.merge $(result) : [ $(t).path ] ] ;
}

View File

@@ -14,6 +14,9 @@
<section id="bbv2.arch.overview">
<title>Overview</title>
<!-- FIXME: the below does not mention engine at all, making rest of the
text confusing. Things like 'kernel' and 'util' don't have to be
mentioned at all. -->
<para>
Boost.Build implementation is structured in four different components:
"kernel", "util", "build" and "tools". The first two are relatively
@@ -58,6 +61,7 @@
from them.
</para></listitem>
</itemizedlist>
</para>
<para>
@@ -78,13 +82,14 @@
<listitem><para>
A generator appropriate for the build properties is selected and its
<code>run</code> method is called. The method returns a list of virtual
targets
targets.
</para></listitem>
<listitem><para>
The targets are returned to the top level code. They are converted
into bjam targets (via <code>virtual-target.actualize</code>) and passed
to bjam for building.
The virtual targets are returned to the top level code, and for each instance,
the <literal>actualize</literal> method is called to setup nodes and updating
actions in the depenendency graph kepts inside Boost.Build engine. This dependency
graph is then updated, which runs necessary commands.
</para></listitem>
</orderedlist>
</para>
@@ -163,11 +168,11 @@ lib a : a.cpp : &lt;toolset&gt;gcc ;
<title>Virtual targets</title>
<para>
Virtual targets correspond to atomic updatable entities. Each virtual
Virtual targets are atomic updatable entities. Each virtual
target can be assigned an updating action -- instance of the
<code>action</code> class. The action class, in turn, contains a list of
source targets, properties, and a name of an bjam action which should be
executed.
source targets, properties, and a name of an action which
should be executed.
</para>
<para>
@@ -178,12 +183,13 @@ lib a : a.cpp : &lt;toolset&gt;gcc ;
already been created. In that case, the preexisting target is returned.
</para>
<!-- FIXME: the below 2 para are rubbish, must be totally rewritten. -->
<para>
When all virtual targets are produced, they are "actualized". This means
that the real file names are computed, and the commands that should be run
are generated. This is done by the <code>virtual-target.actualize</code>
and <code>action.actualize</code> methods. The first is conceptually
simple, while the second needs additional explanation. Commands in bjam
simple, while the second needs additional explanation. Commands in Boost.Build
are generated in a two-stage process. First, a rule with an appropriate
name (for example "gcc.compile") is called and is given a list of target
names. The rule sets some variables, like "OPTIONS". After that, the
@@ -308,20 +314,14 @@ lib a : a.cpp : &lt;toolset&gt;gcc ;
<para>
As stated above, it is possible to compile a C++ file multiple times,
using different include paths. Therefore, include dependencies for those
compilations can be different. The problem is that bjam does not allow
multiple scans of the same target.
compilations can be different. The problem is that Boost.Build engine does
not allow multiple scans of the same target. To solve that, we pass the
scanner object when calling <literal>virtual-target.actualize</literal>
and it creates different engine targets for different scanners.
</para>
<para>
The solution in Boost.Build is straightforward. When a virtual target is
converted to a bjam target (via the
<literal>virtual-target.actualize</literal> method), we specify the
scanner object to be used. The actualize method will create different bjam
targets for different scanners.
</para>
<para>
For each Boost Jam target created with a scanner is created, a
For each engine target created with a specified scanner, a
corresponding one is created without it. The updating action is
associated with the scanner-less target, and the target with the scanner
is made to depend on it. That way if sources for that action are touched,
@@ -355,7 +355,7 @@ a.cpp (installed copy) &lt;--(copy) ----------------------- a.cpp (no scanner
<listitem><simpara>
If when compiling "a.cpp" there is an include of "a.h", the "dir"
directory is on the include path, and a target called "a.h" will be
generated in "dir", then bjam should discover the include, and create
generated in "dir", then Boost.Build should discover the include, and create
"a.h" before compiling "a.cpp".
</simpara></listitem>

View File

@@ -181,7 +181,7 @@ local t = [ new file-target $(name) : CPP : $(project) : $(a) ] ;
once with the same properties. Returning to different instance of <classname>file-target</classname>
that correspond to the same file clearly will result in problems. Therefore, whenever
returning targets you should pass them via the <code>virtual-target.register</code>
function, besides allowing Boost Build to track which virtual targets
function, besides allowing Boost.Build to track which virtual targets
got created for each metatarget, this will also replace targets with previously created identical
ones, as necessary.<footnote><para>This create-then-register pattern is caused by limitations
of the Boost.Jam language. Python port is likely to never create duplicate targets.</para></footnote>
@@ -613,7 +613,7 @@ actions inline-file
and the target type. When invoked, the generator will create a target
of type <literal>CPP</literal> with a source target of
type <literal>VERBATIM</literal> as the only source. But what command
will be used to actually generate the file? In bjam, actions are
will be used to actually generate the file? In Boost.Build, actions are
specified using named "actions" blocks and the name of the action
block should be specified when creating targets. By convention,
generators use the same name of the action block as their own id. So,
@@ -888,7 +888,7 @@ actions inline-file
<listitem><para>if a feature is used to refer to a path relative
to the Jamfile, it must be a “path” feature. Such features will
also get their values automatically converted to Boost Build's
also get their values automatically converted to Boost.Build's
internal path representation. For example, <code>include</code>
is a path feature.</para></listitem>
@@ -970,7 +970,7 @@ actions link bind DEF_FILE
<para> Note the <code>bind DEF_FILE</code> part. It tells
bjam to translate the internal target name in
Boost.Build to translate the internal target name in
<varname>DEF_FILE</varname> to a corresponding filename in
the <code>link</code> action. Without it the expansion of
<code>$(DEF_FILE)</code> would be a strange symbol that is
@@ -981,8 +981,7 @@ actions link bind DEF_FILE
targets in general, only source files." I'm not sure
what I meant by that; maybe you can figure it out. -->
<para>
We are almost done, but we should stop for a small workaround. Add the following
code to msvc.jam
We are almost done, except for adding the follwing code to <filename>msvc.jam</filename>:
<programlisting>
rule link
@@ -992,8 +991,8 @@ rule link
</programlisting>
<!-- You *must* explain the part in [...] above. It's completely opaque to the casual reader -->
This is needed to accomodate some bug in bjam, which hopefully
will be fixed one day.
This is a workaround for a bug in Boost.Build engine, which will hopefully
be fixed one day.
<!-- This is *NOT* a bug!! Anyway, BBv2 shouild handle this automatically. Why doesn't it? -->
</para></listitem>

View File

@@ -446,6 +446,37 @@ alias mylib ;
</programlisting>
</para>
</section>
<section id="bbv2.faq.names">
<title>
What is the difference between Boost.Build,
<filename>b2</filename>, <filename>bjam</filename> and Perforce Jam?
</title>
<para>
Boost.Build is the name of the complete build system. The executable that runs
it is <filename>b2</filename>. That executable is written in C and implements
performance-critical algorithms, like traversal of dependency graph and executing
commands. It also implements an interpreted language used to implement the rest of
Boost.Build. This executable is formally called "Boost.Build engine".
</para>
<para>
The Boost.Build engine is derived from an earlier build tool called Perforce Jam. Originally,
there were just minor changes, and the filename was <filename>bjam</filename>. Later on,
with more and more changes, the similarity of names because a disservice to users, and as of
Boost 1.47.0, the official name of the executable was changed to <filename>b2</filename>.
A copy named <filename>bjam</filename> is still created for compatibility,
but you are encouraged to use the new name in all cases.
</para>
<para>
Perforce Jam was an important foundation, and we gratefully acknowledge its influence,
but for users today, these tools share only some basics of the interpreted language.
</para>
</section>
</chapter>
<!--

View File

@@ -103,7 +103,7 @@
</para>
<para>Placing Boost.Build into <filename>/usr/share/boost-build</filename>
will make sure that <command>bjam</command> will find Boost.Build
will make sure that <command>b2</command> will find Boost.Build
without any additional setup.</para>
</listitem>
@@ -120,7 +120,7 @@ using gcc ;
</para>
<para>If those guidelines are met, users will be able to invoke
<command>bjam</command> without any explicit configuration.
<command>b2/command> without any explicit configuration.
</para>

View File

@@ -14,9 +14,9 @@
</para>
<para>
Boost.Build actually consists of two parts - Boost.Jam, a build engine
Boost.Build has two parts&emdash;a build engine
with its own interpreted language, and Boost.Build itself, implemented in
Boost.Jam's language. The chain of events when you type
that language. The chain of events when you type
<command>b2</command> on the command line is as follows:
<orderedlist>
<listitem>
@@ -1183,7 +1183,9 @@ obj main : main.cpp : &lt;optimization&gt;off ;
</programlisting>
</para>
<para id="bbv2.overview.targets.requirements.conditional">Sometimes, particular relationships need to be maintained
<para id="bbv2.overview.targets.requirements.conditional">
<indexterm><primary>requirements</primary><secondary>conditional</secondary></indexterm>
Sometimes, particular relationships need to be maintained
among a target's build properties. This can be achieved with
<firstterm>conditional
requirements</firstterm>. For example, you might want to set
@@ -1212,6 +1214,7 @@ lib network : network.cpp
</para>
<para id="bbv2.overview.targets.requirements.indirect">
<indexterm><primary>requirements</primary><secondary>indirect</secondary></indexterm>
A more powerful variant of conditional requirements
is <firstterm>indirect conditional requirements</firstterm>.
You can provide a rule that will be called with the current build properties and can compute additional properties

View File

@@ -6,6 +6,6 @@
future or FAQ completely supercedes it. -->
<appendix id="bbv2.recipies">
<title>Boost Build System V2 recipes</title>
<title>Boost.Build System V2 recipes</title>
</appendix>

View File

@@ -17,8 +17,9 @@
<section id="bbv2.reference.init">
<title>Initialization</title>
<para>bjam's first job upon startup is to load the Jam code that
implements the build system. To do this, it searches for a file
<para>
Immediately upon starting, the Boost.Build engine (<command>b2</command>)
loads the Jam code that implements the build system. To do this, it searches for a file
called <filename>boost-build.jam</filename>, first in the invocation directory, then
in its parent and so forth up to the filesystem root, and finally
in the directories specified by the environment variable
@@ -741,7 +742,7 @@ path-constant DATA : data/a.txt ;
<para>
<emphasis role="bold">Note:</emphasis> Due to some internal details
in the current Boost Build implementation it is not possible to have
in the current Boost.Build implementation it is not possible to have
features whose valid values are all positive integer. As a
workaround a large set of allowed values has been defined for this
feature and, if a different one is needed, user can easily add it by

View File

@@ -447,8 +447,8 @@ rule run ( sources + : args * : input-files * : requirements * : target-name ?
<varname>args</varname> and <varname>input-files</varname> as command-line
arguments. The <varname>args</varname> parameter is passed verbatim and
the values of the <varname>input-files</varname> parameter are treated as
paths relative to containing Jamfile, and are adjusted if <command>bjam
</command> is invoked from a different directory. The
paths relative to containing Jamfile, and are adjusted if <command>b2</command>
is invoked from a different directory. The
<code>run-fail</code> rule is identical to the <code>run</code> rule,
except that it expects that the run fails.
</para>

View File

@@ -29,6 +29,10 @@
#include <ctype.h>
#ifdef OS_NT
#include <windows.h>
#endif
#if defined(USE_EXECUNIX)
# include <sys/types.h>
# include <sys/wait.h>
@@ -425,6 +429,11 @@ void load_builtins()
char const * args [] = { "path", 0 };
bind_builtin( "MAKEDIR", builtin_makedir, 0, args );
}
{
const char * args [] = { "path", 0 };
bind_builtin( "READLINK", builtin_readlink, 0, args );
}
/* Initialize builtin modules. */
init_set();
@@ -1827,6 +1836,94 @@ LIST * builtin_makedir( FRAME * frame, int flags )
: list_new( object_copy( list_front( path ) ) );
}
LIST *builtin_readlink( FRAME * frame, int flags )
{
const char * path = object_str( list_front( lol_get( frame->args, 0 ) ) );
#ifdef OS_NT
/* This struct is declared in ntifs.h which is
* part of the Windows Driver Kit.
*/
typedef struct _REPARSE_DATA_BUFFER {
ULONG ReparseTag;
USHORT ReparseDataLength;
USHORT Reserved;
union {
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
ULONG Flags;
WCHAR PathBuffer[ 1 ];
} SymbolicLinkReparseBuffer;
struct {
USHORT SubstituteNameOffset;
USHORT SubstituteNameLength;
USHORT PrintNameOffset;
USHORT PrintNameLength;
WCHAR PathBuffer[ 1 ];
} MountPointReparseBuffer;
struct {
UCHAR DataBuffer[ 1 ];
} GenericReparseBuffer;
};
} REPARSE_DATA_BUFFER;
HANDLE hLink = CreateFileA( path, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS | FILE_FLAG_OPEN_REPARSE_POINT, NULL );
DWORD n;
union {
REPARSE_DATA_BUFFER reparse;
char data[MAXIMUM_REPARSE_DATA_BUFFER_SIZE];
} buf;
int okay = DeviceIoControl(hLink, FSCTL_GET_REPARSE_POINT, NULL, 0, &buf, sizeof(buf), &n, NULL);
CloseHandle( hLink );
if (okay && buf.reparse.ReparseTag == IO_REPARSE_TAG_SYMLINK )
{
int index = buf.reparse.SymbolicLinkReparseBuffer.SubstituteNameOffset / 2;
int length = buf.reparse.SymbolicLinkReparseBuffer.SubstituteNameLength / 2;
char cbuf[MAX_PATH + 1];
int numchars = WideCharToMultiByte( CP_ACP, 0, buf.reparse.SymbolicLinkReparseBuffer.PathBuffer + index, length, cbuf, sizeof(cbuf), NULL, NULL );
if( numchars >= sizeof(cbuf) )
{
return 0;
}
cbuf[numchars] = '\0';
return list_new( object_new( cbuf ) );
}
return 0;
#else
char static_buf[256];
char * buf = static_buf;
size_t bufsize = 256;
LIST * result = 0;
while (1) {
ssize_t len = readlink( path, buf, bufsize );
if ( len < 0 )
{
break;
}
else if ( len < bufsize )
{
buf[ len ] = '\0';
result = list_new( object_new( buf ) );
break;
}
if ( buf != static_buf )
BJAM_FREE( buf );
bufsize *= 2;
buf = BJAM_MALLOC( bufsize );
}
if ( buf != static_buf )
BJAM_FREE( buf );
return result;
#endif
}
#ifdef HAVE_PYTHON

View File

@@ -63,6 +63,7 @@ LIST *builtin_pad( FRAME * frame, int flags );
LIST *builtin_precious( FRAME * frame, int flags );
LIST *builtin_self_path( FRAME * frame, int flags );
LIST *builtin_makedir( FRAME * frame, int flags );
LIST *builtin_readlink( FRAME * frame, int flags );
void backtrace( FRAME *frame );
extern int last_update_now_status;

View File

@@ -23,6 +23,37 @@
#include <assert.h>
/*
* cmdlist_append_cmd
*/
CMDLIST * cmdlist_append_cmd( CMDLIST * l, CMD * cmd )
{
CMDLIST * result = (CMDLIST *)BJAM_MALLOC( sizeof( CMDLIST ) );
result->iscmd = 1;
result->next = l;
result->impl.cmd = cmd;
return result;
}
CMDLIST * cmdlist_append_target( CMDLIST * l, TARGET * t )
{
CMDLIST * result = (CMDLIST *)BJAM_MALLOC( sizeof( CMDLIST ) );
result->iscmd = 0;
result->next = l;
result->impl.t = t;
return result;
}
void cmdlist_free( CMDLIST * l )
{
while ( l )
{
CMDLIST * tmp = l->next;
BJAM_FREE( l );
l = tmp;
}
}
/*
* cmd_new() - return a new CMD.
*/
@@ -37,6 +68,10 @@ CMD * cmd_new( RULE * rule, LIST * targets, LIST * sources, LIST * shell )
cmd->shell = shell;
cmd->next = 0;
cmd->noop = 0;
cmd->asynccnt = 1;
cmd->status = 0;
cmd->lock = NULL;
cmd->unlock = NULL;
lol_init( &cmd->args );
lol_add( &cmd->args, targets );
@@ -62,9 +97,11 @@ CMD * cmd_new( RULE * rule, LIST * targets, LIST * sources, LIST * shell )
void cmd_free( CMD * cmd )
{
cmdlist_free( cmd->next );
lol_free( &cmd->args );
list_free( cmd->shell );
string_free( cmd->buf );
freetargets( cmd->unlock );
BJAM_FREE( (void *)cmd );
}

View File

@@ -46,14 +46,41 @@
typedef struct _cmd CMD;
/*
* A list whose elements are either TARGETS or CMDS.
* CMDLIST is used only by CMD. A TARGET means that
* the CMD is the last updating action required to
* build the target. A CMD is the next CMD required
* to build the same target. (Note that a single action
* can update more than one target, so the CMDs form
* a DAG, not a straight linear list.)
*/
typedef struct _cmdlist {
struct _cmdlist * next;
union {
CMD * cmd;
TARGET * t;
} impl;
char iscmd;
} CMDLIST;
CMDLIST * cmdlist_append_cmd( CMDLIST *, CMD * );
CMDLIST * cmdlist_append_target( CMDLIST *, TARGET * );
void cmd_list_free( CMDLIST * );
struct _cmd
{
CMD * next;
CMDLIST * next;
RULE * rule; /* rule->actions contains shell script */
LIST * shell; /* $(JAMSHELL) value */
LOL args; /* LISTs for $(<), $(>) */
string buf[ 1 ]; /* actual commands */
int noop; /* no-op commands should be faked instead of executed */
int asynccnt; /* number of outstanding dependencies */
TARGETS * lock; /* semaphores that are required by this cmd. */
TARGETS * unlock; /* semaphores that are released when this cmd finishes. */
char status; /* the command status */
};
CMD * cmd_new

View File

@@ -117,56 +117,17 @@ LIST * evaluate_rule( RULE * rule, OBJECT * rulename, FRAME * frame )
action->refs = 1;
/* If we have a group of targets all being built using the same action
* then we must not allow any of them to be used as sources unless they
* are all up to date and their action does not need to be run or their
* action has had a chance to finish its work and build all of them
* anew.
*
* Without this it might be possible, in case of a multi-process build,
* for their action, triggered to building one of the targets, to still
* be running when another target in the group reports as done in order
* to avoid triggering the same action again and gets used prematurely.
*
* As a quick-fix to achieve this effect we make all the targets list
* each other as 'included targets'. More precisely, we mark the first
* listed target as including all the other targets in the list and vice
* versa. This makes anyone depending on any of those targets implicitly
* depend on all of them, thus making sure none of those targets can be
* used as sources until all of them have been built. Note that direct
* dependencies could not have been used due to the 'circular
* dependency' issue.
*
* TODO: Although the current implementation solves the problem of one
* of the targets getting used before its action completes its work, it
* also forces the action to run whenever any of the targets in the
* group is not up to date even though some of them might not actually
* be used by the targets being built. We should see how we can
* correctly recognize such cases and use that to avoid running the
* action if possible and not rebuild targets not actually depending on
* targets that are not up to date.
*
* TODO: Current solution using fake INCLUDES relations may cause
* actions to be run when the affected targets are built by multiple
* actions. E.g. if we have the following actions registered in the
* order specified:
* (I) builds targets A & B
* (II) builds target B
* and we want to build a target depending on target A, then both
* actions (I) & (II) will be run, even though the second one does not
* have any direct relationship to target A. Consider whether this is
* desired behaviour or not. It could be that Boost Build should (or
* possibly already does) run all actions registered for a given target
* if any of them needs to be run in which case our INCLUDES relations
* are not actually causing any actions to be run that would not have
* been run without them.
* and any of these targets is updated, then we have to consider them
* all to be out-dated. We do this by adding a REBUILDS in both directions
* between the first target and all the other targets.
*/
if ( action->targets )
{
TARGET * const t0 = action->targets->target;
for ( t = action->targets->next; t; t = t->next )
{
target_include( t->target, t0 );
target_include( t0, t->target );
t->target->rebuilds = targetentry( t->target->rebuilds, t0 );
t0->rebuilds = targetentry( t0->rebuilds, t->target );
}
}

View File

@@ -122,6 +122,19 @@ int file_collect_dir_content_( file_info_t * const d )
ff->is_file = !ff->is_dir;
ff->exists = 1;
timestamp_from_filetime( &ff->time, &finfo.ftLastWriteTime );
// Use the timestamp of the link target, not the link itself
// (i.e. stat instead of lstat)
if ( finfo.dwFileAttributes & FILE_ATTRIBUTE_REPARSE_POINT )
{
HANDLE hLink = CreateFileA( pathname->value, 0, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, NULL );
BY_HANDLE_FILE_INFORMATION target_finfo[ 1 ];
if ( hLink != INVALID_HANDLE_VALUE && GetFileInformationByHandle( hLink, target_finfo ) )
{
ff->is_file = target_finfo->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? 0 : 1;
ff->is_dir = target_finfo->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ? 1 : 0;
timestamp_from_filetime( &ff->time, &target_finfo->ftLastWriteTime );
}
}
}
}
while ( FindNextFile( findHandle, &finfo ) );

View File

@@ -161,6 +161,8 @@ int make( LIST * targets, int anyhow )
* make0() to be updated.
*/
static void force_rebuilds( TARGET * t );
static void update_dependants( TARGET * t )
{
TARGETS * q;
@@ -190,6 +192,8 @@ static void update_dependants( TARGET * t )
update_dependants( p );
}
}
/* Make sure that rebuilds can be chained. */
force_rebuilds( t );
}
@@ -676,7 +680,30 @@ void make0
else
fate = t->fate;
/* Step 4g: If this target needs to be built, force rebuild everything in
/*
* Step 4g: If this target needs to be built, make0 all targets
* that are updated by the same actions used to update this target.
* These have already been marked as REBUILDS, and make1 has
* special handling for them. We just need to make sure that
* they get make0ed.
*/
if ( ( fate >= T_FATE_BUILD ) && ( fate < T_FATE_BROKEN ) )
{
ACTIONS * a;
TARGETS * c;
for ( a = t->actions; a; a = a->next )
{
for ( c = a->action->targets; c; c = c->next )
{
if ( c->target->fate == T_FATE_INIT )
{
make0( c->target, ptime, depth + 1, counts, anyhow, rescanning );
}
}
}
}
/* Step 4h: If this target needs to be built, force rebuild everything in
* its rebuilds list.
*/
if ( ( fate >= T_FATE_BUILD ) && ( fate < T_FATE_BROKEN ) )

View File

@@ -63,6 +63,12 @@ static SETTINGS * make1settings ( struct module_t *, LIST * vars );
static void make1bind ( TARGET * );
static TARGET * make1findcycle ( TARGET * );
static void make1breakcycle( TARGET *, TARGET * cycle_root );
static void push_cmds( CMDLIST * cmds, int status );
static int cmd_sem_lock( TARGET * t );
static void cmd_sem_unlock( TARGET * t );
static int targets_contains( TARGETS * l, TARGET * t );
static int targets_equal( TARGETS * l1, TARGETS * l2 );
/* Ugly static - it is too hard to carry it through the callbacks. */
@@ -372,33 +378,16 @@ static void make1b( state * const pState )
TARGET * failed = 0;
char const * failed_name = "dependencies";
pop_state( &state_stack );
/* If any dependencies are still outstanding, wait until they signal their
* completion by pushing this same state for their parent targets.
*/
if ( --t->asynccnt )
{
pop_state( &state_stack );
return;
}
/* Try to aquire a semaphore. If it is locked, wait until the target that
* locked it is built and signals completition.
*/
#ifdef OPT_SEMAPHORE
if ( t->semaphore && t->semaphore->asynccnt )
{
/* Append 't' to the list of targets waiting on semaphore. */
t->semaphore->parents = targetentry( t->semaphore->parents, t );
t->asynccnt++;
if ( DEBUG_EXECCMD )
printf( "SEM: %s is busy, delaying launch of %s\n",
object_str( t->semaphore->name ), object_str( t->name ) );
pop_state( &state_stack );
return;
}
#endif
/* Now ready to build target 't', if dependencies built OK. */
/* Collect status from dependencies. If -n was passed then act as though all
@@ -492,28 +481,19 @@ static void make1b( state * const pState )
abort();
}
#ifdef OPT_SEMAPHORE
/* If there is a semaphore, indicate that it is in use. */
if ( t->semaphore )
{
++t->semaphore->asynccnt;
if ( DEBUG_EXECCMD )
printf( "SEM: %s now used by %s\n", object_str( t->semaphore->name
), object_str( t->name ) );
}
#endif
/* Proceed to MAKE1C to begin executing the chain of commands prepared for
* building the target. If we are not going to build the target (e.g. due to
* dependency failures or no commands needing to be run) the chain will be
* empty and MAKE1C processing will directly signal the target's completion.
*/
/* Implementation note:
* Morfing the current state on the stack instead of popping it and
* pushing a new one is a slight optimization with no side-effects since we
* pushed no other states while processing this one.
*/
pState->curstate = T_STATE_MAKE1C;
if ( t->cmds == NULL || --( ( CMD * )t->cmds )->asynccnt == 0 )
push_state( &state_stack, t, NULL, T_STATE_MAKE1C );
else if ( DEBUG_EXECCMD )
{
CMD * cmd = ( CMD * )t->cmds;
printf( "Delaying %s %s: %d targets not ready\n", object_str( cmd->rule->name ), object_str( t->boundname ), cmd->asynccnt );
}
}
@@ -534,7 +514,7 @@ static void make1c( state const * const pState )
TARGET * const t = pState->t;
CMD * const cmd = (CMD *)t->cmds;
if ( cmd && t->status == EXEC_CMD_OK )
if ( cmd )
{
/* Pop state first in case something below (e.g. exec_cmd(), exec_wait()
* or make1c_closure()) pushes a new state. Note that we must not access
@@ -543,6 +523,21 @@ static void make1c( state const * const pState )
*/
pop_state( &state_stack );
if ( cmd->status != EXEC_CMD_OK )
{
t->cmds = NULL;
push_cmds( cmd->next, cmd->status );
cmd_free( cmd );
return;
}
#ifdef OPT_SEMAPHORE
if ( ! cmd_sem_lock( t ) )
{
return;
}
#endif
/* Increment the jobs running counter. */
++cmdsrunning;
@@ -575,14 +570,6 @@ static void make1c( state const * const pState )
{
ACTIONS * actions;
/* Collect status from actions, and distribute it as well. */
for ( actions = t->actions; actions; actions = actions->next )
if ( actions->action->status > t->status )
t->status = actions->action->status;
for ( actions = t->actions; actions; actions = actions->next )
if ( t->status > actions->action->status )
actions->action->status = t->status;
/* Tally success/failure for those we tried to update. */
if ( t->progress == T_MAKE_RUNNING )
switch ( t->status )
@@ -677,38 +664,6 @@ static void make1c( state const * const pState )
push_state( &temp_stack, c->target, NULL, T_STATE_MAKE1B );
}
#ifdef OPT_SEMAPHORE
/* If there is a semaphore, it is now free. */
if ( t->semaphore )
{
assert( t->semaphore->asynccnt == 1 );
--t->semaphore->asynccnt;
if ( DEBUG_EXECCMD )
printf( "SEM: %s is now free\n", object_str(
t->semaphore->name ) );
/* If anything is waiting, notify the next target. There is no
* point in notifying all waiting targets, since they will be
* notified again.
*/
if ( t->semaphore->parents )
{
TARGETS * first = t->semaphore->parents;
t->semaphore->parents = first->next;
if ( first->next )
first->next->tail = first->tail;
if ( DEBUG_EXECCMD )
printf( "SEM: placing %s on stack\n", object_str(
first->target->name ) );
push_state( &temp_stack, first->target, NULL, T_STATE_MAKE1B
);
BJAM_FREE( first );
}
}
#endif
/* Must pop state before pushing any more. */
pop_state( &state_stack );
@@ -945,12 +900,57 @@ static void make1c_closure
}
}
#ifdef OPT_SEMAPHORE
/* Release any semaphores used by this action. */
cmd_sem_unlock( t );
#endif
/* Free this command and push the MAKE1C state to execute the next one
* scheduled for building this same target.
*/
t->cmds = (char *)cmd_next( cmd );
t->cmds = NULL;
push_cmds( cmd->next, t->status );
cmd_free( cmd );
push_state( &state_stack, t, NULL, T_STATE_MAKE1C );
}
/* push the next MAKE1C state after a command is run. */
static void push_cmds( CMDLIST * cmds, int status )
{
CMDLIST * cmd_iter;
for( cmd_iter = cmds; cmd_iter; cmd_iter = cmd_iter->next )
{
if ( cmd_iter->iscmd )
{
CMD * next_cmd = cmd_iter->impl.cmd;
/* Propagate the command status. */
if ( next_cmd->status < status )
next_cmd->status = status;
if ( --next_cmd->asynccnt == 0 )
{
/* Select the first target associated with the action.
* This is safe because sibling CMDs cannot have targets
* in common.
*/
TARGET * first_target = bindtarget( list_front( lol_get( &next_cmd->args, 0 ) ) );
first_target->cmds = (char *)next_cmd;
push_state( &state_stack, first_target, NULL, T_STATE_MAKE1C );
}
else if ( DEBUG_EXECCMD )
{
TARGET * first_target = bindtarget( list_front( lol_get( &next_cmd->args, 0 ) ) );
printf( "Delaying %s %s: %d targets not ready\n", object_str( next_cmd->rule->name ), object_str( first_target->boundname ), next_cmd->asynccnt );
}
}
else
{
/* This is a target that we're finished updating */
TARGET * updated_target = cmd_iter->impl.t;
if ( updated_target->status < status )
updated_target->status = status;
updated_target->cmds = NULL;
push_state( &state_stack, updated_target, NULL, T_STATE_MAKE1C );
}
}
}
@@ -995,15 +995,14 @@ static void swap_settings
static CMD * make1cmds( TARGET * t )
{
CMD * cmds = 0;
CMD * * cmds_next = &cmds;
CMD * last_cmd;
LIST * shell = L0;
module_t * settings_module = 0;
TARGET * settings_target = 0;
ACTIONS * a0;
int const running_flag = globs.noexec ? A_RUNNING_NOEXEC : A_RUNNING;
/* Step through actions. Actions may be shared with other targets or grouped
* using RULE_TOGETHER, so actions already seen are skipped.
/* Step through actions.
*/
for ( a0 = t->actions; a0; a0 = a0->next )
{
@@ -1014,12 +1013,37 @@ static CMD * make1cmds( TARGET * t )
LIST * ns;
ACTIONS * a1;
/* Only do rules with commands to execute. If this action has already
* been executed, use saved status.
/* Only do rules with commands to execute.
*/
if ( !actions || a0->action->running >= running_flag )
if ( !actions )
continue;
if ( a0->action->running >= running_flag )
{
CMD * first;
/* If this action was skipped either because it was
* combined with another action by RULE_TOGETHER, or
* because all of its sources were filtered out,
* then we don't have anything to do here.
*/
if ( a0->action->first_cmd == NULL )
continue;
/* This action has already been processed for another target.
* Just set up the dependency graph correctly and move on.
*/
first = a0->action->first_cmd;
if( cmds )
{
last_cmd->next = cmdlist_append_cmd( last_cmd->next, first );
}
else
{
cmds = first;
}
last_cmd = a0->action->last_cmd;
continue;
}
a0->action->running = running_flag;
/* Make LISTS of targets and sources. If `execute together` has been
@@ -1031,7 +1055,8 @@ static CMD * make1cmds( TARGET * t )
if ( actions->flags & RULE_TOGETHER )
for ( a1 = a0->next; a1; a1 = a1->next )
if ( a1->action->rule == rule &&
a1->action->running < running_flag )
a1->action->running < running_flag &&
targets_equal( a0->action->targets, a1->action->targets ) )
{
ns = make1list( ns, a1->action->sources, actions->flags );
a1->action->running = running_flag;
@@ -1076,8 +1101,12 @@ static CMD * make1cmds( TARGET * t )
int const length = list_length( ns );
int start = 0;
int chunk = length;
int cmd_count = 0;
LIST * cmd_targets = L0;
LIST * cmd_shell = L0;
TARGETS * semaphores = NULL;
TARGETS * targets_iter;
int unique_targets;
do
{
CMD * cmd;
@@ -1138,8 +1167,20 @@ static CMD * make1cmds( TARGET * t )
if ( accept_command )
{
/* Chain it up. */
*cmds_next = cmd;
cmds_next = &cmd->next;
if ( cmds )
{
last_cmd->next = cmdlist_append_cmd( last_cmd->next, cmd );
last_cmd = cmd;
}
else
{
cmds = last_cmd = cmd;
}
if ( cmd_count++ == 0 )
{
a0->action->first_cmd = cmd;
}
/* Mark lists we need recreated for the next command since
* they got consumed by the cmd object.
@@ -1160,6 +1201,39 @@ static CMD * make1cmds( TARGET * t )
start += chunk;
}
while ( start < length );
/* Record the end of the actions cmds */
a0->action->last_cmd = last_cmd;
unique_targets = 0;
for ( targets_iter = a0->action->targets; targets_iter; targets_iter = targets_iter->next )
{
if ( targets_contains( targets_iter->next, targets_iter->target ) )
continue;
/* Add all targets produced by the action to the update list. */
push_state( &state_stack, targets_iter->target, NULL, T_STATE_MAKE1A );
++unique_targets;
}
/* We need to wait until all the targets agree that
* it's okay to run this action.
*/
( ( CMD * )a0->action->first_cmd )->asynccnt = unique_targets;
#if OPT_SEMAPHORE
/* Collect semaphores */
for ( targets_iter = a0->action->targets; targets_iter; targets_iter = targets_iter->next )
{
TARGET * sem = targets_iter->target->semaphore;
if ( sem )
{
TARGETS * semiter;
if ( ! targets_contains( semaphores, sem ) )
semaphores = targetentry( semaphores, sem );
}
}
( ( CMD * )a0->action->first_cmd )->lock = semaphores;
( ( CMD * )a0->action->last_cmd )->unlock = semaphores;
#endif
}
/* These were always copied when used. */
@@ -1171,6 +1245,11 @@ static CMD * make1cmds( TARGET * t )
freesettings( boundvars );
}
if ( cmds )
{
last_cmd->next = cmdlist_append_target( last_cmd->next, t );
}
swap_settings( &settings_module, &settings_target, 0, 0 );
return cmds;
}
@@ -1281,3 +1360,101 @@ static void make1bind( TARGET * t )
t->binding = timestamp_empty( &t->time ) ? T_BIND_MISSING : T_BIND_EXISTS;
popsettings( root_module(), t->settings );
}
static int targets_contains( TARGETS * l, TARGET * t )
{
for ( ; l; l = l->next )
{
if ( t == l->target )
{
return 1;
}
}
return 0;
}
static int targets_equal( TARGETS * l1, TARGETS * l2 )
{
for ( ; l1 && l2; l1 = l1->next, l2 = l2->next )
{
if ( l1->target != l2->target )
return 0;
}
return !l1 && !l2;
}
#ifdef OPT_SEMAPHORE
static int cmd_sem_lock( TARGET * t )
{
CMD * cmd = (CMD *)t->cmds;
TARGETS * iter;
/* Check whether all the semaphores required for updating
* this target are free.
*/
for ( iter = cmd->lock; iter; iter = iter->next )
{
if ( iter->target->asynccnt > 0 )
{
if ( DEBUG_EXECCMD )
printf( "SEM: %s is busy, delaying launch of %s\n",
object_str( iter->target->name ), object_str( t->name ) );
iter->target->parents = targetentry( iter->target->parents, t );
return 0;
}
}
/* Lock the semaphores. */
for ( iter = cmd->lock; iter; iter = iter->next )
{
++iter->target->asynccnt;
if ( DEBUG_EXECCMD )
printf( "SEM: %s now used by %s\n", object_str( iter->target->name
), object_str( t->name ) );
}
/* A cmd only needs to be locked around its execution.
* clearing cmd->lock here makes it safe to call cmd_sem_lock
* twice.
*/
cmd->lock = NULL;
return 1;
}
static void cmd_sem_unlock( TARGET * t )
{
CMD * cmd = ( CMD * )t->cmds;
TARGETS * iter;
/* Release the semaphores. */
for ( iter = cmd->unlock; iter; iter = iter->next )
{
if ( DEBUG_EXECCMD )
printf( "SEM: %s is now free\n", object_str(
iter->target->name ) );
--iter->target->asynccnt;
assert( iter->target->asynccnt <= 0 );
}
for ( iter = cmd->unlock; iter; iter = iter->next )
{
/* Find a waiting target that's ready */
while ( iter->target->parents )
{
TARGETS * first = iter->target->parents;
TARGET * t1 = first->target;
/* Pop the first waiting CMD */
if ( first->next )
first->next->tail = first->tail;
iter->target->parents = first->next;
BJAM_FREE( first );
if ( cmd_sem_lock( t1 ) )
{
push_state( &state_stack, t1, NULL, T_STATE_MAKE1C );
break;
}
}
}
}
#endif

View File

@@ -7,11 +7,11 @@
/* Keep JAMVERSYM in sync with VERSION. */
/* It can be accessed as $(JAMVERSION) in the Jamfile. */
#define VERSION_MAJOR 2011
#define VERSION_MAJOR 2013
#define VERSION_MINOR 12
#define VERSION_PATCH 1
#define VERSION_MAJOR_SYM "2011"
#define VERSION_MINOR_SYM "12"
#define VERSION_PATCH_SYM "01"
#define VERSION "2011.12.1"
#define JAMVERSYM "JAMVERSION=2011.12"
#define VERSION_PATCH 0
#define VERSION_MAJOR_SYM "2013"
#define VERSION_MINOR_SYM "05"
#define VERSION_PATCH_SYM "00"
#define VERSION "2013.05"
#define JAMVERSYM "JAMVERSION=2013.05"

View File

@@ -92,8 +92,13 @@ struct _action
#define A_INIT 0
#define A_RUNNING_NOEXEC 1
#define A_RUNNING 2
char status; /* see TARGET status */
int refs;
/* WARNING: These variables are used to pass state required by make1cmds and
* are not valid anywhere else.
*/
void * first_cmd; /* Pointer to the first CMD created by this action */
void * last_cmd; /* Pointer to the last CMD created by this action */
};
/* SETTINGS - variables to set when executing a TARGET's ACTIONS. */

View File

@@ -14,7 +14,6 @@
#include "object.h"
#ifdef OS_NT
# define WIN32_LEAN_AND_MEAN
# include <windows.h>
#endif

24
v2/test/builtin_readlink.py Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/python
# Copyright 2012 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
import BoostBuild
import os
t = BoostBuild.Tester(pass_toolset=0)
t.write("link-target", "")
os.symlink("link-target", "link")
t.write("file.jam", """
ECHO [ READLINK link ] ;
EXIT [ READLINK link-target ] : 0 ;
""")
t.run_build_system(["-ffile.jam"], stdout="""link-target
""")
t.cleanup()

202
v2/test/core_multifile_actions.py Executable file
View File

@@ -0,0 +1,202 @@
#!/usr/bin/python
# Copyright 2013 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
# Tests that actions that produce multiple targets are handled
# correctly. The rules are as follows:
#
# - If any action that updates a target is run, then the target
# is considered to be out-of-date and all of its updating actions
# are run in order.
# - A target is considered updated when all of its updating actions
# have completed successfully.
# - If any updating action for a target fails, then the remaining
# actions are skipped and the target is marked as failed.
#
# Note that this is a more thorough test case for the same
# problem that core_parallel_multifile_actions_N.py checks for.
import BoostBuild
t = BoostBuild.Tester(pass_toolset=0, pass_d0=False)
t.write("file.jam", """
actions update
{
echo updating $(<)
}
update x1 x2 ;
update x2 x3 ;
""")
# Updating x1 should force x2 to update as well.
t.run_build_system(["-ffile.jam", "x1"], stdout="""\
...found 3 targets...
...updating 3 targets...
update x1
updating x1 x2
update x2
updating x2 x3
...updated 3 targets...
""")
# If x1 is up-to-date, we don't need to update x2,
# even though x2 is missing.
t.write("x1", "")
t.run_build_system(["-ffile.jam", "x1"], stdout="""\
...found 1 target...
""")
# Building x3 should update x1 and x2, even though
# x1 would be considered up-to-date, taken alone.
t.run_build_system(["-ffile.jam", "x3"], stdout="""\
...found 3 targets...
...updating 2 targets...
update x1
updating x1 x2
update x2
updating x2 x3
...updated 3 targets...
""")
# Updating x2 should succeed, but x3 should be skipped
t.rm("x1")
t.write("file.jam", """\
actions update
{
echo updating $(<)
}
actions fail
{
echo failed $(<)
exit 1
}
update x1 x2 ;
fail x1 ;
update x1 x3 ;
update x2 ;
update x3 ;
""")
t.run_build_system(["-ffile.jam", "x3"], status=1, stdout="""\
...found 3 targets...
...updating 3 targets...
update x1
updating x1 x2
fail x1
failed x1
echo failed x1
exit 1
...failed fail x1...
update x2
updating x2
...failed updating 2 targets...
...updated 1 target...
""")
# Make sure that dependencies of targets that are
# updated as a result of a multifile action are
# processed correctly.
t.rm("x1")
t.write("file.jam", """\
actions update
{
echo updating $(<)
}
update x1 ;
update x2 ;
DEPENDS x2 : x1 ;
update x2 x3 ;
""")
t.run_build_system(["-ffile.jam", "x3"], stdout="""\
...found 3 targets...
...updating 3 targets...
update x1
updating x1
update x2
updating x2
update x2
updating x2 x3
...updated 3 targets...
""")
# JAM_SEMAPHORE rules:
#
# - if two updating actions have targets that share a semaphore,
# these actions cannot be run in parallel.
#
t.write("file.jam", """\
actions update
{
echo updating $(<)
}
targets = x1 x2 ;
JAM_SEMAPHORE on $(targets) = <s>update_sem ;
update x1 x2 ;
""")
t.run_build_system(["-ffile.jam", "x1"], stdout="""\
...found 2 targets...
...updating 2 targets...
update x1
updating x1 x2
...updated 2 targets...
""")
# A target can appear multiple times in an action
t.write("file.jam", """\
actions update
{
echo updating $(<)
}
update x1 x1 ;
""")
t.run_build_system(["-ffile.jam", "x1"], stdout="""\
...found 1 target...
...updating 1 target...
update x1
updating x1 x1
...updated 1 target...
""")
# Together actions should check that all the targets are the same
# before combining.
t.write("file.jam", """\
actions together update
{
echo updating $(<) : $(>)
}
update x1 x2 : s1 ;
update x1 x2 : s2 ;
update x3 : s3 ;
update x3 x4 : s4 ;
update x4 x3 : s5 ;
DEPENDS all : x1 x2 x3 x4 ;
""")
t.run_build_system(["-ffile.jam"], stdout="""\
...found 5 targets...
...updating 4 targets...
update x1
updating x1 x2 : s1 s2
update x3
updating x3 : s3
update x3
updating x3 x4 : s4
update x4
updating x4 x3 : s5
...updated 4 targets...
""")
t.cleanup()

119
v2/test/libjpeg.py Executable file
View File

@@ -0,0 +1,119 @@
#!/usr/bin/python
# Copyright (C) 2013 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
import BoostBuild
import MockToolset
t = BoostBuild.Tester(arguments=['toolset=mock', '--ignore-site-config', '--user-config='], pass_toolset=0)
MockToolset.create(t)
# Build from source
t.write("libjpeg/jpeg.h", 'libjpeg')
t.write("libjpeg/jpeg.c", 'jpeg')
t.write("Jamroot.jam", """
path-constant here : . ;
using libjpeg : : <source>$(here)/libjpeg ;
alias libjpeg : /libjpeg//libjpeg : : <link>static <link>shared ;
""")
MockToolset.set_expected(t, '''
source_file('jpeg.c', 'jpeg')
action('-c -x c -I./libjpeg -o $jpeg.o $jpeg.c')
action('--dll $jpeg.o -o $jpeg.so')
action('--archive $jpeg.o -o $jpeg.a')
''')
t.run_build_system()
t.expect_addition('bin/standalone/libjpeg/mock/debug/jpeg.dll')
t.expect_addition('bin/standalone/libjpeg/mock/debug/link-static/jpeg.lib')
t.rm('libjpeg')
# Generic definitions that aren't configuration specific
common_stuff = '''
source_file('test.cpp', 'test.cpp')
source_file('main.cpp', 'int main() {}')
source_file('jpeg.h.cpp', '#include <jpeg.h>')
action('-c -x c++ $main.cpp -o $main.o')
'''
t.write('test.cpp', 'test.cpp')
# Default initialization - static library
t.rm('bin')
t.write("Jamroot.jam", """
path-constant here : . ;
using libjpeg ;
exe test : test.cpp /libjpeg//libjpeg : : <link>static <link>shared ;
""")
MockToolset.set_expected(t, common_stuff + '''
action('$main.o --static-lib=jpeg -o $config.exe')
action('-c -x c++ $jpeg.h.cpp -o $jpeg.h.o')
action('-c -x c++ $test.cpp -o $test.o')
action('$test.o --static-lib=jpeg -o $test')
''')
t.run_build_system()
t.expect_addition('bin/mock/debug/test.exe')
t.expect_addition('bin/mock/debug/link-static/test.exe')
# Default initialization - shared library
t.rm('bin')
t.write("Jamroot.jam", """
path-constant here : . ;
using libjpeg ;
exe test : test.cpp /libjpeg//libjpeg : : <link>static <link>shared ;
""")
MockToolset.set_expected(t, common_stuff + '''
action('$main.o --shared-lib=jpeg -o $config.exe')
action('-c -x c++ $jpeg.h.cpp -o $jpeg.h.o')
action('-c -x c++ $test.cpp -o $test.o')
action('$test.o --shared-lib=jpeg -o $test')
''')
t.run_build_system()
t.expect_addition('bin/mock/debug/test.exe')
t.expect_addition('bin/mock/debug/link-static/test.exe')
# Initialization in explicit location - static library
t.rm('bin')
t.write("Jamroot.jam", """
path-constant here : . ;
using libjpeg : : <name>mylibjpeg <include>$(here)/libjpeg <search>$(here)/libjpeg ;
exe test : test.cpp /libjpeg//libjpeg : : <link>static <link>shared ;
""")
t.write('libjpeg/jpeg.h', 'libjpeg')
MockToolset.set_expected(t, common_stuff + '''
action('$main.o -L./libjpeg --static-lib=mylibjpeg -o $config.exe')
action('-c -x c++ $test.cpp -I./libjpeg -o $test.o')
action('$test.o -L./libjpeg --static-lib=mylibjpeg -o $test')
''')
t.run_build_system()
t.expect_addition('bin/mock/debug/test.exe')
t.expect_addition('bin/mock/debug/link-static/test.exe')
# Initialization in explicit location - shared library
t.rm('bin')
t.write("Jamroot.jam", """
path-constant here : . ;
using libjpeg : : <name>mylibjpeg <include>$(here)/libjpeg <search>$(here)/libjpeg ;
exe test : test.cpp /libjpeg//libjpeg : : <link>static <link>shared ;
""")
MockToolset.set_expected(t, common_stuff + '''
action('$main.o -L./libjpeg --shared-lib=mylibjpeg -o $config.exe')
action('-c -x c++ $test.cpp -I./libjpeg -o $test.o')
action('$test.o -L./libjpeg --shared-lib=mylibjpeg -o $test')
''')
t.run_build_system()
t.expect_addition('bin/mock/debug/test.exe')
t.expect_addition('bin/mock/debug/link-static/test.exe')
t.cleanup()

119
v2/test/libpng.py Executable file
View File

@@ -0,0 +1,119 @@
#!/usr/bin/python
# Copyright (C) 2013 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
import BoostBuild
import MockToolset
t = BoostBuild.Tester(arguments=['toolset=mock', '--ignore-site-config', '--user-config='], pass_toolset=0)
MockToolset.create(t)
# Build from source
t.write("libpng/png.h", 'libpng')
t.write("libpng/png.c", 'png')
t.write("Jamroot.jam", """
path-constant here : . ;
using libpng : : <source>$(here)/libpng ;
alias libpng : /libpng//libpng : : <link>static <link>shared ;
""")
MockToolset.set_expected(t, '''
source_file('png.c', 'png')
action('-c -x c -I./libpng -o $png.o $png.c')
action('--dll $png.o -o $png.so')
action('--archive $png.o -o $png.a')
''')
t.run_build_system()
t.expect_addition('bin/standalone/libpng/mock/debug/png.dll')
t.expect_addition('bin/standalone/libpng/mock/debug/link-static/png.lib')
t.rm('libpng')
# Generic definitions that aren't configuration specific
common_stuff = '''
source_file('test.cpp', 'test.cpp')
source_file('main.cpp', 'int main() {}')
source_file('png.h.cpp', '#include <png.h>')
action('-c -x c++ $main.cpp -o $main.o')
'''
t.write('test.cpp', 'test.cpp')
# Default initialization - static library
t.rm('bin')
t.write("Jamroot.jam", """
path-constant here : . ;
using libpng ;
exe test : test.cpp /libpng//libpng : : <link>static <link>shared ;
""")
MockToolset.set_expected(t, common_stuff + '''
action('$main.o --static-lib=png -o $config.exe')
action('-c -x c++ $png.h.cpp -o $png.h.o')
action('-c -x c++ $test.cpp -o $test.o')
action('$test.o --static-lib=png -o $test')
''')
t.run_build_system()
t.expect_addition('bin/mock/debug/test.exe')
t.expect_addition('bin/mock/debug/link-static/test.exe')
# Default initialization - shared library
t.rm('bin')
t.write("Jamroot.jam", """
path-constant here : . ;
using libpng ;
exe test : test.cpp /libpng//libpng : : <link>static <link>shared ;
""")
MockToolset.set_expected(t, common_stuff + '''
action('$main.o --shared-lib=png -o $config.exe')
action('-c -x c++ $png.h.cpp -o $png.h.o')
action('-c -x c++ $test.cpp -o $test.o')
action('$test.o --shared-lib=png -o $test')
''')
t.run_build_system()
t.expect_addition('bin/mock/debug/test.exe')
t.expect_addition('bin/mock/debug/link-static/test.exe')
# Initialization in explicit location - static library
t.rm('bin')
t.write("Jamroot.jam", """
path-constant here : . ;
using libpng : : <name>mylibpng <include>$(here)/libpng <search>$(here)/libpng ;
exe test : test.cpp /libpng//libpng : : <link>static <link>shared ;
""")
t.write('libpng/png.h', 'libpng')
MockToolset.set_expected(t, common_stuff + '''
action('$main.o -L./libpng --static-lib=mylibpng -o $config.exe')
action('-c -x c++ $test.cpp -I./libpng -o $test.o')
action('$test.o -L./libpng --static-lib=mylibpng -o $test')
''')
t.run_build_system()
t.expect_addition('bin/mock/debug/test.exe')
t.expect_addition('bin/mock/debug/link-static/test.exe')
# Initialization in explicit location - shared library
t.rm('bin')
t.write("Jamroot.jam", """
path-constant here : . ;
using libpng : : <name>mylibpng <include>$(here)/libpng <search>$(here)/libpng ;
exe test : test.cpp /libpng//libpng : : <link>static <link>shared ;
""")
MockToolset.set_expected(t, common_stuff + '''
action('$main.o -L./libpng --shared-lib=mylibpng -o $config.exe')
action('-c -x c++ $test.cpp -I./libpng -o $test.o')
action('$test.o -L./libpng --shared-lib=mylibpng -o $test')
''')
t.run_build_system()
t.expect_addition('bin/mock/debug/test.exe')
t.expect_addition('bin/mock/debug/link-static/test.exe')
t.cleanup()

119
v2/test/libtiff.py Executable file
View File

@@ -0,0 +1,119 @@
#!/usr/bin/python
# Copyright (C) 2013 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or copy at
# http://www.boost.org/LICENSE_1_0.txt)
import BoostBuild
import MockToolset
t = BoostBuild.Tester(arguments=['toolset=mock', '--ignore-site-config', '--user-config='], pass_toolset=0)
MockToolset.create(t)
# Build from source
t.write("libtiff/tiff.h", 'libtiff')
t.write("libtiff/tiff.c", 'tiff')
t.write("Jamroot.jam", """
path-constant here : . ;
using libtiff : : <source>$(here)/libtiff ;
alias libtiff : /libtiff//libtiff : : <link>static <link>shared ;
""")
MockToolset.set_expected(t, '''
source_file('tiff.c', 'tiff')
action('-c -x c -I./libtiff -o $tiff.o $tiff.c')
action('--dll $tiff.o -o $tiff.so')
action('--archive $tiff.o -o $tiff.a')
''')
t.run_build_system()
t.expect_addition('bin/standalone/libtiff/mock/debug/tiff.dll')
t.expect_addition('bin/standalone/libtiff/mock/debug/link-static/tiff.lib')
t.rm('libtiff')
# Generic definitions that aren't configuration specific
common_stuff = '''
source_file('test.cpp', 'test.cpp')
source_file('main.cpp', 'int main() {}')
source_file('tiff.h.cpp', '#include <tiff.h>')
action('-c -x c++ $main.cpp -o $main.o')
'''
t.write('test.cpp', 'test.cpp')
# Default initialization - static library
t.rm('bin')
t.write("Jamroot.jam", """
path-constant here : . ;
using libtiff ;
exe test : test.cpp /libtiff//libtiff : : <link>static <link>shared ;
""")
MockToolset.set_expected(t, common_stuff + '''
action('$main.o --static-lib=tiff -o $config.exe')
action('-c -x c++ $tiff.h.cpp -o $tiff.h.o')
action('-c -x c++ $test.cpp -o $test.o')
action('$test.o --static-lib=tiff -o $test')
''')
t.run_build_system()
t.expect_addition('bin/mock/debug/test.exe')
t.expect_addition('bin/mock/debug/link-static/test.exe')
# Default initialization - shared library
t.rm('bin')
t.write("Jamroot.jam", """
path-constant here : . ;
using libtiff ;
exe test : test.cpp /libtiff//libtiff : : <link>static <link>shared ;
""")
MockToolset.set_expected(t, common_stuff + '''
action('$main.o --shared-lib=tiff -o $config.exe')
action('-c -x c++ $tiff.h.cpp -o $tiff.h.o')
action('-c -x c++ $test.cpp -o $test.o')
action('$test.o --shared-lib=tiff -o $test')
''')
t.run_build_system()
t.expect_addition('bin/mock/debug/test.exe')
t.expect_addition('bin/mock/debug/link-static/test.exe')
# Initialization in explicit location - static library
t.rm('bin')
t.write("Jamroot.jam", """
path-constant here : . ;
using libtiff : : <name>mylibtiff <include>$(here)/libtiff <search>$(here)/libtiff ;
exe test : test.cpp /libtiff//libtiff : : <link>static <link>shared ;
""")
t.write('libtiff/tiff.h', 'libtiff')
MockToolset.set_expected(t, common_stuff + '''
action('$main.o -L./libtiff --static-lib=mylibtiff -o $config.exe')
action('-c -x c++ $test.cpp -I./libtiff -o $test.o')
action('$test.o -L./libtiff --static-lib=mylibtiff -o $test')
''')
t.run_build_system()
t.expect_addition('bin/mock/debug/test.exe')
t.expect_addition('bin/mock/debug/link-static/test.exe')
# Initialization in explicit location - shared library
t.rm('bin')
t.write("Jamroot.jam", """
path-constant here : . ;
using libtiff : : <name>mylibtiff <include>$(here)/libtiff <search>$(here)/libtiff ;
exe test : test.cpp /libtiff//libtiff : : <link>static <link>shared ;
""")
MockToolset.set_expected(t, common_stuff + '''
action('$main.o -L./libtiff --shared-lib=mylibtiff -o $config.exe')
action('-c -x c++ $test.cpp -I./libtiff -o $test.o')
action('$test.o -L./libtiff --shared-lib=mylibtiff -o $test')
''')
t.run_build_system()
t.expect_addition('bin/mock/debug/test.exe')
t.expect_addition('bin/mock/debug/link-static/test.exe')
t.cleanup()

154
v2/test/link.py Executable file
View File

@@ -0,0 +1,154 @@
#!/usr/bin/python
# Copyright 2004 Vladimir Prus
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
# Tests the link-directory rule used to create the
# common boost/ directory in the new git layout.
import BoostBuild
def ignore_config(t):
"""These files are created by the configuration logic in link.jam
They may or may not exist, depending on the system."""
t.ignore("bin/test-hardlink")
t.ignore("bin/test-hardlink-source")
t.ignore("bin/test-symlink")
t.ignore("bin/test-symlink-source")
def test_basic():
"""Test creation of a single link"""
t = BoostBuild.Tester()
t.write("jamroot.jam", """\
import link ;
link-directory dir1-link : src/dir1/include : <location>. ;
""")
t.write("src/dir1/include/file1.h", "file1")
t.run_build_system()
t.expect_addition("include/file1.h")
t.expect_content("include/file1.h", "file1")
ignore_config(t)
t.expect_nothing_more()
t.cleanup()
def test_merge_two():
"""Test merging two directories"""
t = BoostBuild.Tester()
t.write("jamroot.jam", """\
import link ;
link-directory dir1-link : src/dir1/include : <location>. ;
link-directory dir2-link : src/dir2/include : <location>. ;
""")
t.write("src/dir1/include/file1.h", "file1")
t.write("src/dir2/include/file2.h", "file2")
t.run_build_system()
t.expect_addition("include/file1.h")
t.expect_content("include/file1.h", "file1")
t.expect_addition("include/file2.h")
t.expect_content("include/file2.h", "file2")
ignore_config(t)
t.expect_nothing_more()
t.cleanup()
def test_merge_existing():
"""Test adding a link when a different symlink already exists"""
t = BoostBuild.Tester()
t.write("jamroot.jam", """\
import link ;
link-directory dir1-link : src/dir1/include : <location>. ;
link-directory dir2-link : src/dir2/include : <location>. ;
""")
t.write("src/dir1/include/file1.h", "file1")
t.write("src/dir2/include/file2.h", "file2")
t.run_build_system(["dir1-link"])
t.expect_addition("include/file1.h")
t.expect_content("include/file1.h", "file1")
ignore_config(t)
t.expect_nothing_more()
t.run_build_system(["dir2-link"])
t.expect_addition("include/file2.h")
t.expect_content("include/file2.h", "file2")
# If include is a symlink to src/dir1/include, then
# we have to delete it and add a directory.
t.ignore_removal("include/file1.h")
ignore_config(t)
t.expect_nothing_more()
t.cleanup()
def test_merge_recursive():
"Test merging several directories including common prefixes"
t = BoostBuild.Tester()
t.write("jamroot.jam", """\
import link ;
link-directory dir1-link : src/dir1/include : <location>. ;
link-directory dir2-link : src/dir2/include : <location>. ;
link-directory dir3-link : src/dir3/include : <location>. ;
""")
t.write("src/dir1/include/file1.h", "file1")
t.write("src/dir2/include/file2.h", "file2")
t.write("src/dir2/include/nested/file3.h", "file3")
t.write("src/dir3/include/nested/file4.h", "file4")
t.run_build_system()
t.expect_addition("include/file1.h")
t.expect_content("include/file1.h", "file1")
t.expect_addition("include/file2.h")
t.expect_content("include/file2.h", "file2")
t.expect_addition("include/nested/file3.h")
t.expect_content("include/nested/file3.h", "file3")
t.expect_addition("include/nested/file4.h")
t.expect_content("include/nested/file4.h", "file4")
ignore_config(t)
t.expect_nothing_more()
t.cleanup()
def test_include_scan():
"""Make sure that the #include scanner finds the headers"""
t = BoostBuild.Tester()
t.write("jamroot.jam", """\
import link ;
link-directory dir1-link : src/dir1/include : <location>. ;
link-directory dir2-link : src/dir2/include : <location>. ;
obj test : test.cpp :
<include>include
<implicit-dependency>dir1-link
<implicit-dependency>dir2-link ;
""")
t.write("src/dir1/include/file1.h", "#include <file2.h>\n")
t.write("src/dir2/include/file2.h", "int f();\n")
t.write("test.cpp", """\
#include <file1.h>
int main() { f(); }
""");
t.run_build_system(["test"])
t.expect_addition("bin/$toolset/debug/test.obj")
t.run_build_system()
t.expect_nothing_more()
t.cleanup()
test_basic()
test_merge_two()
test_merge_existing()
test_merge_recursive()
test_include_scan()

View File

@@ -44,7 +44,7 @@ if [ qt5.initialized ]
[ link qtdeclarative.cpp /qt5//QtDeclarative ]
# QtQuick version2
[ run qtquick.cpp /qt5//QtQuick : : $(CWD)/qtquick.qml ]
[ run qtquick.cpp /qt5//QtQuick : -platform offscreen : $(CWD)/qtquick.qml ]
# Help systems.
[ link qthelp.cpp /qt5//QtHelp ]

53
v2/test/source_order.py Executable file
View File

@@ -0,0 +1,53 @@
#!/usr/bin/python
# Copyright 2013 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
# Tests that action sources are not reordered
import BoostBuild
t = BoostBuild.Tester()
t.write("check-order.jam", """\
import type ;
import generators ;
type.register ORDER_TEST : order-test ;
SPACE = " " ;
nl = "\n" ;
actions check-order
{
echo$(SPACE)$(>[1])$(SPACE)>$(<[1])
echo$(SPACE)$(>[2-])$(SPACE)>>$(<[1])$(nl)
}
generators.register-composing check-order.check-order : C : ORDER_TEST ;
""")
# The aliases are necessary for this test, since
# the targets were sorted by virtual target
# id, not by file name.
t.write("jamroot.jam", """\
import check-order ;
alias file1 : file1.c ;
alias file2 : file2.c ;
alias file3 : file3.c ;
order-test check : file2 file1 file3 ;
""")
t.write("file1.c", "")
t.write("file2.c", "")
t.write("file3.c", "")
t.run_build_system()
t.expect_addition("bin/$toolset/debug/check.order-test")
t.expect_content("bin/$toolset/debug/check.order-test", """\
file2.c
file1.c
file3.c
""", True)
t.cleanup()

View File

@@ -182,6 +182,7 @@ tests = ["absolute_sources",
"core_actions_quietly",
"core_at_file",
"core_bindrule",
"core_multifile_actions",
"core_nt_cmd_line",
"core_option_d2",
"core_option_l",
@@ -220,6 +221,7 @@ tests = ["absolute_sources",
"lib_source_property",
"library_chain",
"library_property",
"link",
"load_order",
"loop",
"make_rule",
@@ -251,6 +253,7 @@ tests = ["absolute_sources",
"skipping",
"sort_rule",
"source_locations",
"source_order",
"space_in_path",
"stage",
"standalone",

View File

@@ -178,6 +178,7 @@ rule print-error ( location message * )
rule make-error ( message * )
{
import errors ;
return [ errors.nearest-user-location ] $(message) ;
}
@@ -204,7 +205,6 @@ rule check-docbook-xsl-dir ( )
{
if ! [ path.glob $(.docbook-xsl-dir) : common/common.xsl ]
{
import errors ;
.error-message = [ make-error BoostBook: could not find docbook XSL stylesheets
in: [ path.native $(.docbook-xsl-dir) ] ] ;
}
@@ -223,7 +223,6 @@ rule check-docbook-dtd-dir ( )
{
if ! [ path.glob $(.docbook-dtd-dir) : docbookx.dtd ]
{
import errors ;
.error-message = [ make-error BoostBook: could not find docbook DTD in: [
path.native $(.docbook-dtd-dir) ] ] ;
}
@@ -278,7 +277,6 @@ rule check-boostbook-dir ( boostbook-dir ? )
{
if $(boostbook-dir) && ! [ path.glob $(boostbook-dir) : xsl ]
{
import errors ;
.error-message = [ make-error BoostBook: could not find boostbook in: [ path.native
$(boostbook-dir) ] ] ;
}

View File

@@ -23,10 +23,9 @@ import indirect ;
import property ;
import property-set ;
header = jconfig.h jdct.h jerror.h jinclude.h jmemsys.h jmorecfg.h jpegint.h jpeglib.h
jversion.h ;
header = jpeglib.h ;
names = libjpeg ;
names = jpeg ;
sources = jaricom.c jcapimin.c jcapistd.c jcarith.c jccoefct.c jccolor.c
jcdctmgr.c jchuff.c jcinit.c jcmainct.c jcmarker.c jcmaster.c
@@ -166,7 +165,7 @@ rule init (
}
else if $(source-path) && ! $(no-build-from-source)
{
build-name ?= z ;
build-name ?= jpeg ;
library-id = [ CALC $(library-id) + 1 ] ;
tag = [ MATCH ^@?(.*)$ : $(tag) ] ;
if $(tag) && ! [ MATCH ^([^%]*)%([^%]+)$ : $(tag) ]

View File

@@ -24,7 +24,7 @@ import property ;
import property-set ;
header = png.h ;
names = libpng ;
names = png ;
sources = png.c pngerror.c pngget.c pngmem.c pngpread.c pngread.c pngrio.c pngrtran.c pngrutil.c
pngset.c pngtrans.c pngwio.c pngwrite.c pngwtran.c pngwutil.c ;
@@ -124,7 +124,7 @@ rule init (
condition = [ property-set.create [ $(condition).base ] ] ;
local no-build-from-source ;
# Ignore environmental ZLIB_SOURCE if this initialization
# Ignore environmental LIBPNG_SOURCE if this initialization
# requested to search for a specific pre-built library.
if $(library-path) || $(include-path) || $(library-name)
{
@@ -140,7 +140,7 @@ rule init (
}
}
source-path ?= [ modules.peek : ZLIB_SOURCE ] ;
source-path ?= [ modules.peek : LIBPNG_SOURCE ] ;
if $(.configured.$(condition))
{
@@ -159,7 +159,7 @@ rule init (
}
else if $(source-path) && ! $(no-build-from-source)
{
build-name ?= z ;
build-name ?= png ;
library-id = [ CALC $(library-id) + 1 ] ;
tag = [ MATCH ^@?(.*)$ : $(tag) ] ;
if $(tag) && ! [ MATCH ^([^%]*)%([^%]+)$ : $(tag) ]
@@ -193,7 +193,7 @@ rule init (
<include>$(source-path)
<toolset>msvc:<define>_CRT_SECURE_NO_DEPRECATE
<toolset>msvc:<define>_SCL_SECURE_NO_DEPRECATE
<link>shared:<define>ZLIB_DLL
<link>shared:<define>LIBPNG_DLL
:
: <include>$(source-path) ] ;
}

View File

@@ -23,9 +23,8 @@ import indirect ;
import property ;
import property-set ;
header = tiff.h tiffio.hxx ;
names = libtiff ;
header = tiff.h ;
names = tiff ;
sources = tif_aux.c tif_close.c tif_codec.c tif_color.c tif_compress.c tif_dir.c tif_dirinfo.c
tif_dirread.c tif_dirwrite.c tif_dumpmode.c tif_error.c tif_extension.c tif_fax3.c tif_fax3sm.c
@@ -163,7 +162,7 @@ rule init (
}
else if $(source-path) && ! $(no-build-from-source)
{
build-name ?= z ;
build-name ?= tiff ;
library-id = [ CALC $(library-id) + 1 ] ;
tag = [ MATCH ^@?(.*)$ : $(tag) ] ;
if $(tag) && ! [ MATCH ^([^%]*)%([^%]+)$ : $(tag) ]

433
v2/tools/link.jam Normal file
View File

@@ -0,0 +1,433 @@
# Copyright 2012 Steven Watanabe
# Distributed under the Boost Software License, Version 1.0.
# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
import os ;
import targets ;
import project ;
import "class" : new ;
import virtual-target ;
import configure ;
import path ;
import property ;
import property-set ;
import common ;
rule get-root-project ( project )
{
# Find the root project.
local root-project = $(project) ;
root-project = [ $(root-project).project-module ] ;
while
[ project.attribute $(root-project) parent-module ] &&
[ project.attribute $(root-project) parent-module ] != user-config &&
[ project.attribute $(root-project) parent-module ] != project-config
{
root-project = [ project.attribute $(root-project) parent-module ] ;
}
return $(root-project) ;
}
TOUCH = [ common.file-touch-command ] ;
actions touch {
$(TOUCH) "$(<)"
}
rule can-symlink ( project : ps )
{
if ! $(.can-symlink)
{
local root-project = [ get-root-project $(project) ] ;
local source-target = [ new file-target test-symlink-source : :
$(project) : [ new action : link.touch ] ] ;
local target = [ new file-target test-symlink : :
$(project) : [ new action $(source-target) : link.mklink ] ] ;
if [ configure.try-build $(target) : $(ps) : "symlinks supported" ]
{
.can-symlink = true ;
}
else
{
.can-symlink = false ;
}
}
if $(.can-symlink) = true
{
return true ;
}
}
rule can-hardlink ( project : ps )
{
if ! $(.can-hardlink)
{
local root-project = [ get-root-project $(project) ] ;
local source-target = [ new file-target test-hardlink-source : :
$(project) : [ new action : link.touch ] ] ;
local target = [ new file-target test-hardlink : :
$(project) : [ new action $(source-target) : link.hardlink ] ] ;
if [ configure.try-build $(target) : $(ps) : "hardlinks supported" ]
{
.can-hardlink = true ;
}
else
{
.can-hardlink = false ;
}
}
if $(.can-hardlink) = true
{
return true ;
}
}
class file-or-directory-reference : basic-target
{
import virtual-target ;
import property-set ;
import path ;
rule construct ( name : source-targets * : property-set )
{
return [ property-set.empty ] [ virtual-target.from-file $(self.name) :
[ location ] : $(self.project) ] ;
}
# Returns true if the referred file really exists.
rule exists ( )
{
location ;
return $(self.file-path) ;
}
# Returns the location of target. Needed by 'testing.jam'.
rule location ( )
{
if ! $(self.file-location)
{
local source-location = [ $(self.project).get source-location ] ;
for local src-dir in $(source-location)
{
if ! $(self.file-location)
{
local location = [ path.root $(self.name) $(src-dir) ] ;
if [ path.exists [ path.native $(location) ] ]
{
self.file-location = $(src-dir) ;
self.file-path = $(location) ;
}
}
}
}
return $(self.file-location) ;
}
}
class symlink-target-class : basic-target
{
import path ;
import virtual-target ;
import link ;
import os ;
import type ;
rule construct ( name : source-target : property-set )
{
local location = [ path.join
[ $(source-target).path ] [ $(source-target).name ] ] ;
local files = [ path.glob-tree $(location) : * ] ;
local targets ;
link.can-symlink $(self.project) : $(property-set) ;
link.can-hardlink $(self.project) : $(property-set) ;
if [ $(property-set).get <location> ]
{
property-set = [ property-set.create
[ property.select <location> : [ $(property-set).raw ] ] ] ;
}
else
{
local path,relative-to-build-dir = [ $(property-set).target-path ] ;
local path = $(path,relative-to-build-dir[1]) ;
local relative-to-build-dir = $(path,relative-to-build-dir[2]) ;
if $(relative-to-build-dir)
{
path = [ path.join [ $(self.project).build-dir ] $(path) ] ;
}
property-set = [ property-set.create <location>$(path) ] ;
}
local a = [ new non-scanning-action $(source-target) :
link.do-link-recursively : $(property-set) ] ;
local t = [ new notfile-target $(name)
: $(self.project) : $(a) ] ;
return [ property-set.empty ] [ virtual-target.register $(t) ] ;
}
}
rule do-file-link
{
local target = [ path.native [ path.relative-to [ path.pwd ] $(<) ] ] ;
local source = [ path.native [ path.relative-to [ path.pwd ] $(>) ] ] ;
LOCATE on $(target) = . ;
DEPENDS $(.current-target) : $(target) ;
if $(.can-hardlink) = true
{
DEPENDS $(target) : $(source) ;
link.hardlink $(target) : $(source) ;
}
else if $(.can-symlink) = true
{
link.mklink $(target) : $(source) ;
}
else
{
DEPENDS $(target) : $(source) ;
common.copy $(target) : $(source) ;
}
}
rule do-link
{
local target = [ path.native [ path.relative-to [ path.pwd ] $(<) ] ] ;
local source = [ path.native [ path.relative-to [ path.pwd ] $(>) ] ] ;
local relative = [ path.native [ path.relative-to [ path.parent $(<) ] $(>) ] ] ;
if ! [ on $(target) return $(MKLINK_OR_DIR) ]
{
LOCATE on $(target) = . ;
DEPENDS $(.current-target) : $(target) ;
mklink-or-dir $(target) : $(source) ;
}
if [ os.name ] = NT
{
MKLINK_OR_DIR on $(target) = mklink /D \"$(target)\" \"$(relative)\" ;
}
else
{
MKLINK_OR_DIR on $(target) = ln -s $(relative) $(target) ;
}
}
rule do-split
{
local target = [ path.native [ path.relative-to [ path.pwd ] $(<) ] ] ;
if ! [ on $(target) return $(MKLINK_OR_DIR) ]
{
LOCATE on $(target) = . ;
DEPENDS $(.current-target) : $(target) ;
common.mkdir $(target) ;
}
MKLINK_OR_DIR on $(target) = mkdir \"$(target)\" ;
}
rule do-rm
{
local target = [ path.native [ path.relative-to [ path.pwd ] $(<) ] ] ;
ALWAYS $(target) ;
RM on $(target) = rmdir ;
link.rm $(target) ;
}
rule mklink-or-dir
{
NOUPDATE $(<) ;
}
actions mklink-or-dir
{
$(MKLINK_OR_DIR)
}
rule link-entries ( target : files * : split ? )
{
for local s in $(files)
{
local t = [ path.join $(target) [ path.basename $(s) ] ] ;
if ! $(.known-dirs.$(t))
{
local t = [ path.native [ path.relative-to [ path.pwd ] $(t) ] ] ;
local s = [ path.native [ path.relative-to [ path.pwd ] $(target) ] ] ;
LOCATE on $(t) = . ;
DEPENDS $(t) : $(s) ;
NOUPDATE $(s) ;
}
if $(split)
{
link-recursively $(t) : $(s) ;
}
else
{
link-entries $(t) : [ path.glob $(s) : * ] ;
}
}
if ! $(.known-dirs.$(target))
{
.known-dirs.$(target) += $(files) ;
.known-dirs.base.$(target) = $(.current-target) ;
}
}
rule link-recursively ( target : source : no-recurse ? )
{
local split ;
if [ CHECK_IF_FILE [ path.native $(source) ] ]
{
do-file-link $(target) : $(source) ;
}
else if $(.known-dirs.$(target)) && ! $(no-recurse)
{
split = true ;
if ! $(.split-dirs.$(target))
{
local .current-target = $(.known-dirs.base.$(target)) ;
for local s in $(.known-dirs.$(target))
{
local t = [ path.join $(target) [ path.basename $(s) ] ] ;
link-recursively $(t) : $(s) : flat ;
}
if [ READLINK [ path.native $(target) ] ]
{
do-rm $(target) ;
}
do-split $(target) ;
.split-dirs.$(target) = true ;
}
}
else if [ path.exists [ path.native $(target) ] ]
{
local link-target = [ READLINK [ path.native $(target) ] ] ;
if $(link-target)
{
local full-path =
[ path.root [ path.make $(link-target) ] [ path.parent $(target) ] ] ;
if $(full-path) != $(source)
{
do-rm $(target) ;
do-split $(target) ;
split = true ;
}
}
else
{
do-split $(target) ;
split = true ;
}
}
else if $(.can-symlink) = false
{
if [ READLINK [ path.native $(target) ] ]
{
do-rm $(target) ;
}
do-split $(target) ;
split = true ;
}
else
{
do-link $(target) : $(source) ;
}
if ! $(no-recurse)
{
link-entries $(target) : [ path.glob $(source) : * ] : $(split) ;
}
}
rule do-link-recursively ( target : source : properties * )
{
local target-path = [ property.select <location> : $(properties) ] ;
local source-path = [ on $(source) return $(LOCATE) ] [ on $(source) return $(SEARCH) ] ;
local absolute-target = [ path.root
[ path.join [ path.make $(target-path[1]:G=) ]
[ path.basename [ path.make $(source:G=) ] ] ]
[ path.pwd ] ] ;
local absolute-source = [ path.root
[ path.root [ path.make $(source:G=) ]
[ path.make $(source-path[1]) ] ]
[ path.pwd ] ] ;
local .current-target = $(target) ;
link-recursively $(absolute-target) : $(absolute-source) ;
}
rule mklink
{
local target-path = [ on $(<) return $(LOCATE) ] [ on $(<) return $(SEARCH) ] . ;
local source-path = [ on $(>) return $(LOCATE) ] [ on $(>) return $(SEARCH) ] . ;
local relative-path = [ path.relative-to
[ path.parent [ path.join [ path.root [ path.make $(target-path[1]) ] [ path.pwd ] ] [ path.make $(<:G=) ] ] ]
[ path.join [ path.root [ path.make $(source-path[1]) ] [ path.pwd ] ] [ path.make $(>:G=) ] ] ] ;
PATH_TO_SOURCE on $(<) = [ path.native $(relative-path) ] ;
NOUPDATE $(<) ;
}
if [ os.name ] = NT
{
actions mklink
{
if exist "$(<)" del "$(<)"
mklink "$(<)" "$(PATH_TO_SOURCE)"
}
actions hardlink
{
if exist "$(<)" del "$(<)"
mklink /H "$(<)" "$(>)"
}
actions rm
{
rmdir "$(<)"
}
}
else
{
actions mklink
{
ln -f -s "$(PATH_TO_SOURCE)" "$(<)"
}
actions hardlink
{
ln -f "$(>)" "$(<)"
}
actions rm
{
rm "$(<)"
}
}
rule link-directory ( name : sources : requirements * : default-build * : usage-requirements * )
{
local project = [ project.current ] ;
sources = [ new file-or-directory-reference $(sources) : $(project) ] ;
targets.main-target-alternative $(sources) ;
return [ targets.main-target-alternative
[ new symlink-target-class $(name) : $(project)
: [ targets.main-target-sources $(sources) : $(name) : no-renaming ]
: [ targets.main-target-requirements $(requirements) : $(project) ]
: [ targets.main-target-default-build : $(project) ]
: [ targets.main-target-usage-requirements $(usage-requirements) :
$(project) ] ] ] ;
}
IMPORT $(__name__) : link-directory : : link-directory ;

View File

@@ -314,7 +314,7 @@ rule init ( mpicxx ? : options * : mpirun-with-options * )
# Prepend COMPILER as the executable name, to match the format of
# other compilation commands.
compile_flags = "COMPILER $(compile_flags)" ;
compile_flags = "COMPILER $(compile_flags) -DOMPI_SKIP_MPICXX " ;
link_flags = "COMPILER $(link_flags)" ;
}
# Look for LAM-MPI's -showme

View File

@@ -55,7 +55,7 @@ type.register PDB : pdb ;
# using msvc : 6.5 : cl.exe ;
# using msvc : 7.0 : Y:/foo/bar/cl.exe ;
#
# The version parameter may be ommited:
# The version parameter may be omitted:
#
# using msvc : : Z:/foo/bar/cl.exe ;
#
@@ -901,9 +901,9 @@ local rule configure-really ( version ? : options * )
setup-$(c) = [ feature.get-values <setup-$(c)> : $(options) ] ;
if ! $(setup-$(c))-is-not-empty
if ! $(setup-$(c))-is-defined
{
if $(global-setup)-is-not-empty
if $(global-setup)-is-defined
{
setup-$(c) = $(global-setup) ;
@@ -916,7 +916,8 @@ local rule configure-really ( version ? : options * )
}
else
{
setup-$(c) = [ locate-default-setup $(command) : $(parent) : $(default-setup-$(c)) ] ;
setup-$(c) = [ locate-default-setup $(command) :
$(parent) : $(default-setup-$(c)) ] ;
}
}
@@ -1381,7 +1382,8 @@ if [ MATCH (--debug-configuration) : [ modules.peek : ARGV ] ]
merom-xe kentsfield kentsfield-xe penryn wolfdale
yorksfield nehalem sandy-bridge ivy-bridge haswell ;
.cpu-type-amd64 = k8 opteron athlon64 athlon-fx k8-sse3 opteron-sse3
athlon64-sse3 amdfam10 barcelona bdver1 bdver2 bdver3 btver1 btver2 ;
athlon64-sse3 amdfam10 barcelona bdver1 bdver2 bdver3
btver1 btver2 ;
.cpu-type-g7 = pentium4 pentium4m athlon athlon-tbird athlon-4 athlon-xp
athlon-mp $(.cpu-type-em64t) $(.cpu-type-amd64) ;
.cpu-type-itanium = itanium itanium1 merced ;

View File

@@ -71,7 +71,7 @@ type.register('PDB',['pdb'])
# using msvc : 6.5 : cl.exe ;
# using msvc : 7.0 : Y:/foo/bar/cl.exe ;
#
# The version parameter may be ommited:
# The version parameter may be omitted:
#
# using msvc : : Z:/foo/bar/cl.exe ;
#

View File

@@ -1234,7 +1234,7 @@ rule capture-output ( target : sources * : properties * )
# Oddly, host-os is not in properties, so grab the default value.
local host-os = [ feature.defaults host-os ] ;
host-os = $(host-os:G=) ;
if $(target-os) != $(host-os)
if $(target-os) != $(host-os) && $(target-os) in windows cygwin && $(host-os) in windows cygwin
{
PYTHONPATH = [ sequence.transform $(host-os)-to-$(target-os)-path :
$(PYTHONPATH) ] ;

View File

@@ -680,7 +680,7 @@ flags qt5.moc DEFINES <define> ;
#
actions moc
{
$(.BINPREFIX[-1])/moc -f $(>) -o $(<) @"@($(<).rsp:E=-D$(DEFINES)$(.nl) -I$(INCLUDES:T)$(.nl))"
$(.BINPREFIX[-1])/moc $(>) -o $(<) @"@($(<).rsp:E=-D$(DEFINES)$(.nl) -I$(INCLUDES:T)$(.nl))"
}
# When moccing files for include only, we don't need -f, otherwise the generated