From 50e09fb39af8c59bfdb8272001c439bed8899bf3 Mon Sep 17 00:00:00 2001 From: Steven Watanabe Date: Tue, 19 Nov 2013 19:52:02 +0000 Subject: [PATCH] Merge Boost.Build from the trunk. [SVN r86768] --- v2/build/configure.jam | 125 +++++---- v2/build/generators.jam | 2 +- v2/build/version.jam | 4 +- v2/build/virtual-target.jam | 3 +- v2/doc/src/architecture.xml | 40 +-- v2/doc/src/extending.xml | 15 +- v2/doc/src/faq.xml | 31 +++ v2/doc/src/install.xml | 4 +- v2/doc/src/overview.xml | 9 +- v2/doc/src/recipes.xml | 2 +- v2/doc/src/reference.xml | 7 +- v2/doc/src/tasks.xml | 4 +- v2/engine/builtins.c | 97 +++++++ v2/engine/builtins.h | 1 + v2/engine/command.c | 37 +++ v2/engine/command.h | 29 +- v2/engine/compile.c | 49 +--- v2/engine/filent.c | 13 + v2/engine/make.c | 29 +- v2/engine/make1.c | 353 +++++++++++++++++------ v2/engine/patchlevel.h | 14 +- v2/engine/rules.h | 7 +- v2/engine/timestamp.h | 1 - v2/test/builtin_readlink.py | 24 ++ v2/test/core_multifile_actions.py | 202 ++++++++++++++ v2/test/libjpeg.py | 119 ++++++++ v2/test/libpng.py | 119 ++++++++ v2/test/libtiff.py | 119 ++++++++ v2/test/link.py | 154 ++++++++++ v2/test/qt5/jamroot.jam | 2 +- v2/test/source_order.py | 53 ++++ v2/test/test_all.py | 3 + v2/tools/boostbook.jam | 4 +- v2/tools/{jpeg.jam => libjpeg.jam} | 7 +- v2/tools/{png.jam => libpng.jam} | 10 +- v2/tools/{tiff.jam => libtiff.jam} | 7 +- v2/tools/link.jam | 433 +++++++++++++++++++++++++++++ v2/tools/mpi.jam | 2 +- v2/tools/msvc.jam | 12 +- v2/tools/msvc.py | 2 +- v2/tools/python.jam | 2 +- v2/tools/qt5.jam | 2 +- 42 files changed, 1883 insertions(+), 269 deletions(-) create mode 100755 v2/test/builtin_readlink.py create mode 100755 v2/test/core_multifile_actions.py create mode 100755 v2/test/libjpeg.py create mode 100755 v2/test/libpng.py create mode 100755 v2/test/libtiff.py create mode 100755 v2/test/link.py create mode 100755 v2/test/source_order.py rename v2/tools/{jpeg.jam => libjpeg.jam} (97%) rename v2/tools/{png.jam => libpng.jam} (97%) rename v2/tools/{tiff.jam => libtiff.jam} (98%) create mode 100644 v2/tools/link.jam diff --git a/v2/build/configure.jam b/v2/build/configure.jam index 66b81b349..543bade35 100644 --- a/v2/build/configure.jam +++ b/v2/build/configure.jam @@ -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 { diff --git a/v2/build/generators.jam b/v2/build/generators.jam index ec7183a32..27fb22487 100644 --- a/v2/build/generators.jam +++ b/v2/build/generators.jam @@ -667,7 +667,7 @@ class generator result += $(source) ; } } - return [ sequence.unique $(result) ] ; + return [ sequence.unique $(result) : stable ] ; } } diff --git a/v2/build/version.jam b/v2/build/version.jam index d4b787d6d..76d48fd2f 100644 --- a/v2/build/version.jam +++ b/v2/build/version.jam @@ -6,8 +6,8 @@ import numbers ; -.major = "2011" ; -.minor = "12" ; +.major = "2013" ; +.minor = "05" ; rule boost-build ( ) diff --git a/v2/build/virtual-target.jam b/v2/build/virtual-target.jam index f62eadbe1..44a706db3 100644 --- a/v2/build/virtual-target.jam +++ b/v2/build/virtual-target.jam @@ -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 ] ] ; } diff --git a/v2/doc/src/architecture.xml b/v2/doc/src/architecture.xml index c03575bdc..0b22defef 100644 --- a/v2/doc/src/architecture.xml +++ b/v2/doc/src/architecture.xml @@ -14,6 +14,9 @@
Overview + 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. + @@ -78,13 +82,14 @@ A generator appropriate for the build properties is selected and its run method is called. The method returns a list of virtual - targets + targets. - The targets are returned to the top level code. They are converted - into bjam targets (via virtual-target.actualize) and passed - to bjam for building. + The virtual targets are returned to the top level code, and for each instance, + the actualize 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. @@ -163,11 +168,11 @@ lib a : a.cpp : <toolset>gcc ; Virtual targets - 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 action 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. @@ -178,12 +183,13 @@ lib a : a.cpp : <toolset>gcc ; already been created. In that case, the preexisting target is returned. + 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 virtual-target.actualize and action.actualize 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 : <toolset>gcc ; 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 virtual-target.actualize + and it creates different engine targets for different scanners. - The solution in Boost.Build is straightforward. When a virtual target is - converted to a bjam target (via the - virtual-target.actualize method), we specify the - scanner object to be used. The actualize method will create different bjam - targets for different scanners. - - - - 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) <--(copy) ----------------------- a.cpp (no scanner 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". diff --git a/v2/doc/src/extending.xml b/v2/doc/src/extending.xml index f5665b45a..8b7811dc7 100644 --- a/v2/doc/src/extending.xml +++ b/v2/doc/src/extending.xml @@ -181,7 +181,7 @@ local t = [ new file-target $(name) : CPP : $(project) : $(a) ] ; once with the same properties. Returning to different instance of file-target that correspond to the same file clearly will result in problems. Therefore, whenever returning targets you should pass them via the virtual-target.register - 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.This create-then-register pattern is caused by limitations of the Boost.Jam language. Python port is likely to never create duplicate targets. @@ -613,7 +613,7 @@ actions inline-file and the target type. When invoked, the generator will create a target of type CPP with a source target of type VERBATIM 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 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, include is a path feature. @@ -970,7 +970,7 @@ actions link bind DEF_FILE Note the bind DEF_FILE part. It tells - bjam to translate the internal target name in + Boost.Build to translate the internal target name in DEF_FILE to a corresponding filename in the link action. Without it the expansion of $(DEF_FILE) 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. --> - 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 msvc.jam: rule link @@ -992,8 +991,8 @@ rule link - 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. diff --git a/v2/doc/src/faq.xml b/v2/doc/src/faq.xml index a648a3761..9a7f9c458 100644 --- a/v2/doc/src/faq.xml +++ b/v2/doc/src/faq.xml @@ -446,6 +446,37 @@ alias mylib ;
+ +
+ + What is the difference between Boost.Build, + <filename>b2</filename>, <filename>bjam</filename> and Perforce Jam? + + + + Boost.Build is the name of the complete build system. The executable that runs + it is b2. 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". + + + + The Boost.Build engine is derived from an earlier build tool called Perforce Jam. Originally, + there were just minor changes, and the filename was bjam. 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 b2. + A copy named bjam is still created for compatibility, + but you are encouraged to use the new name in all cases. + + + + 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. + + +
+ - Boost Build System V2 recipes + Boost.Build System V2 recipes diff --git a/v2/doc/src/reference.xml b/v2/doc/src/reference.xml index 9d6fdf4c1..3d4681184 100644 --- a/v2/doc/src/reference.xml +++ b/v2/doc/src/reference.xml @@ -17,8 +17,9 @@
Initialization - 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 + + Immediately upon starting, the Boost.Build engine (b2) + loads the Jam code that implements the build system. To do this, it searches for a file called boost-build.jam, 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 ; Note: 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 diff --git a/v2/doc/src/tasks.xml b/v2/doc/src/tasks.xml index d6419d4d8..c95a426c0 100644 --- a/v2/doc/src/tasks.xml +++ b/v2/doc/src/tasks.xml @@ -447,8 +447,8 @@ rule run ( sources + : args * : input-files * : requirements * : target-name ? args and input-files as command-line arguments. The args parameter is passed verbatim and the values of the input-files parameter are treated as - paths relative to containing Jamfile, and are adjusted if bjam - is invoked from a different directory. The + paths relative to containing Jamfile, and are adjusted if b2 + is invoked from a different directory. The run-fail rule is identical to the run rule, except that it expects that the run fails. diff --git a/v2/engine/builtins.c b/v2/engine/builtins.c index e4130bb92..3aa8e8122 100644 --- a/v2/engine/builtins.c +++ b/v2/engine/builtins.c @@ -29,6 +29,10 @@ #include +#ifdef OS_NT +#include +#endif + #if defined(USE_EXECUNIX) # include # include @@ -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 diff --git a/v2/engine/builtins.h b/v2/engine/builtins.h index b7a967ce0..6d0c87367 100644 --- a/v2/engine/builtins.h +++ b/v2/engine/builtins.h @@ -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; diff --git a/v2/engine/command.c b/v2/engine/command.c index 571b8b6d0..31141fc97 100644 --- a/v2/engine/command.c +++ b/v2/engine/command.c @@ -23,6 +23,37 @@ #include +/* + * 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 ); } diff --git a/v2/engine/command.h b/v2/engine/command.h index ba6557710..fd59ed11d 100644 --- a/v2/engine/command.h +++ b/v2/engine/command.h @@ -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 diff --git a/v2/engine/compile.c b/v2/engine/compile.c index db46937ca..a690b9fa5 100644 --- a/v2/engine/compile.c +++ b/v2/engine/compile.c @@ -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 ); } } diff --git a/v2/engine/filent.c b/v2/engine/filent.c index e4ac3319b..00dcc49b3 100644 --- a/v2/engine/filent.c +++ b/v2/engine/filent.c @@ -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 ) ); diff --git a/v2/engine/make.c b/v2/engine/make.c index afc8bb938..c83f525c8 100644 --- a/v2/engine/make.c +++ b/v2/engine/make.c @@ -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 ) ) diff --git a/v2/engine/make1.c b/v2/engine/make1.c index 71eee1231..5a96dc4e5 100644 --- a/v2/engine/make1.c +++ b/v2/engine/make1.c @@ -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 diff --git a/v2/engine/patchlevel.h b/v2/engine/patchlevel.h index 60b0d61d7..73236a5d8 100644 --- a/v2/engine/patchlevel.h +++ b/v2/engine/patchlevel.h @@ -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" diff --git a/v2/engine/rules.h b/v2/engine/rules.h index fe2792f43..f3a020bb8 100644 --- a/v2/engine/rules.h +++ b/v2/engine/rules.h @@ -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. */ diff --git a/v2/engine/timestamp.h b/v2/engine/timestamp.h index aaf1310fe..ecedb5f92 100644 --- a/v2/engine/timestamp.h +++ b/v2/engine/timestamp.h @@ -14,7 +14,6 @@ #include "object.h" #ifdef OS_NT -# define WIN32_LEAN_AND_MEAN # include #endif diff --git a/v2/test/builtin_readlink.py b/v2/test/builtin_readlink.py new file mode 100755 index 000000000..e57d7286a --- /dev/null +++ b/v2/test/builtin_readlink.py @@ -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() diff --git a/v2/test/core_multifile_actions.py b/v2/test/core_multifile_actions.py new file mode 100755 index 000000000..50bfe8339 --- /dev/null +++ b/v2/test/core_multifile_actions.py @@ -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) = 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() diff --git a/v2/test/libjpeg.py b/v2/test/libjpeg.py new file mode 100755 index 000000000..dcf81c9b0 --- /dev/null +++ b/v2/test/libjpeg.py @@ -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 : : $(here)/libjpeg ; +alias libjpeg : /libjpeg//libjpeg : : static 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 ') +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 : : static 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 : : static 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 : : mylibjpeg $(here)/libjpeg $(here)/libjpeg ; +exe test : test.cpp /libjpeg//libjpeg : : static 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 : : mylibjpeg $(here)/libjpeg $(here)/libjpeg ; +exe test : test.cpp /libjpeg//libjpeg : : static 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() diff --git a/v2/test/libpng.py b/v2/test/libpng.py new file mode 100755 index 000000000..3e7e5cd2d --- /dev/null +++ b/v2/test/libpng.py @@ -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 : : $(here)/libpng ; +alias libpng : /libpng//libpng : : static 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 ') +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 : : static 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 : : static 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 : : mylibpng $(here)/libpng $(here)/libpng ; +exe test : test.cpp /libpng//libpng : : static 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 : : mylibpng $(here)/libpng $(here)/libpng ; +exe test : test.cpp /libpng//libpng : : static 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() diff --git a/v2/test/libtiff.py b/v2/test/libtiff.py new file mode 100755 index 000000000..cb0d07b0f --- /dev/null +++ b/v2/test/libtiff.py @@ -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 : : $(here)/libtiff ; +alias libtiff : /libtiff//libtiff : : static 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 ') +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 : : static 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 : : static 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 : : mylibtiff $(here)/libtiff $(here)/libtiff ; +exe test : test.cpp /libtiff//libtiff : : static 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 : : mylibtiff $(here)/libtiff $(here)/libtiff ; +exe test : test.cpp /libtiff//libtiff : : static 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() diff --git a/v2/test/link.py b/v2/test/link.py new file mode 100755 index 000000000..849442119 --- /dev/null +++ b/v2/test/link.py @@ -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 : . ; + """) + + 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 : . ; + link-directory dir2-link : src/dir2/include : . ; + """) + + 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 : . ; + link-directory dir2-link : src/dir2/include : . ; + """) + + 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 : . ; + link-directory dir2-link : src/dir2/include : . ; + link-directory dir3-link : src/dir3/include : . ; + """) + + 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 : . ; + link-directory dir2-link : src/dir2/include : . ; + obj test : test.cpp : + include + dir1-link + dir2-link ; + """) + + t.write("src/dir1/include/file1.h", "#include \n") + t.write("src/dir2/include/file2.h", "int f();\n") + t.write("test.cpp", """\ + #include + 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() diff --git a/v2/test/qt5/jamroot.jam b/v2/test/qt5/jamroot.jam index b9585e3ef..c4e26c1f0 100644 --- a/v2/test/qt5/jamroot.jam +++ b/v2/test/qt5/jamroot.jam @@ -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 ] diff --git a/v2/test/source_order.py b/v2/test/source_order.py new file mode 100755 index 000000000..b53d1087d --- /dev/null +++ b/v2/test/source_order.py @@ -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() diff --git a/v2/test/test_all.py b/v2/test/test_all.py index cddd48263..290fe0dfa 100644 --- a/v2/test/test_all.py +++ b/v2/test/test_all.py @@ -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", diff --git a/v2/tools/boostbook.jam b/v2/tools/boostbook.jam index de83dec64..42342d9bb 100644 --- a/v2/tools/boostbook.jam +++ b/v2/tools/boostbook.jam @@ -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) ] ] ; } diff --git a/v2/tools/jpeg.jam b/v2/tools/libjpeg.jam similarity index 97% rename from v2/tools/jpeg.jam rename to v2/tools/libjpeg.jam index 128ab6353..309b59df6 100644 --- a/v2/tools/jpeg.jam +++ b/v2/tools/libjpeg.jam @@ -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) ] diff --git a/v2/tools/png.jam b/v2/tools/libpng.jam similarity index 97% rename from v2/tools/png.jam rename to v2/tools/libpng.jam index 0544fe905..46e3cc9ae 100644 --- a/v2/tools/png.jam +++ b/v2/tools/libpng.jam @@ -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 ( $(source-path) msvc:_CRT_SECURE_NO_DEPRECATE msvc:_SCL_SECURE_NO_DEPRECATE - shared:ZLIB_DLL + shared:LIBPNG_DLL : : $(source-path) ] ; } diff --git a/v2/tools/tiff.jam b/v2/tools/libtiff.jam similarity index 98% rename from v2/tools/tiff.jam rename to v2/tools/libtiff.jam index 14f235e3f..cbd8ad015 100644 --- a/v2/tools/tiff.jam +++ b/v2/tools/libtiff.jam @@ -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) ] diff --git a/v2/tools/link.jam b/v2/tools/link.jam new file mode 100644 index 000000000..8a1431b46 --- /dev/null +++ b/v2/tools/link.jam @@ -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 ] + { + property-set = [ property-set.create + [ property.select : [ $(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 $(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 : $(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 ; diff --git a/v2/tools/mpi.jam b/v2/tools/mpi.jam index 0fe490bec..4ba6aec6b 100644 --- a/v2/tools/mpi.jam +++ b/v2/tools/mpi.jam @@ -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 diff --git a/v2/tools/msvc.jam b/v2/tools/msvc.jam index 6ecd033ad..bab9a9fb6 100644 --- a/v2/tools/msvc.jam +++ b/v2/tools/msvc.jam @@ -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 : $(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 ; diff --git a/v2/tools/msvc.py b/v2/tools/msvc.py index 855b0c369..f1cecb6b3 100644 --- a/v2/tools/msvc.py +++ b/v2/tools/msvc.py @@ -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 ; # diff --git a/v2/tools/python.jam b/v2/tools/python.jam index d48bb7838..2681bb960 100644 --- a/v2/tools/python.jam +++ b/v2/tools/python.jam @@ -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) ] ; diff --git a/v2/tools/qt5.jam b/v2/tools/qt5.jam index 5c3500f60..09c3f3532 100644 --- a/v2/tools/qt5.jam +++ b/v2/tools/qt5.jam @@ -680,7 +680,7 @@ flags qt5.moc DEFINES ; # 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