diff --git a/Jamroot b/Jamroot index 3628208..a246623 100644 --- a/Jamroot +++ b/Jamroot @@ -128,6 +128,7 @@ import sequence ; import xsltproc ; import set ; import path ; +import link ; path-constant BOOST_ROOT : . ; constant BOOST_VERSION : 1.53.0 ; @@ -135,6 +136,23 @@ constant BOOST_JAMROOT_MODULE : $(__name__) ; boostcpp.set-version $(BOOST_VERSION) ; +local all-headers = + [ MATCH .*libs/(.*)/include/boost : [ glob libs/*/include/boost ] ] ; + +for dir in $(all-headers) +{ + link-directory $(dir)-headers : libs/$(dir)/include/boost : . ; +} + +local numeric-headers = + [ MATCH .*libs/numeric/(.*)/include/boost : [ glob libs/*/*/include/boost ] ] ; + +for dir in $(numeric-headers) +{ + link-directory numeric-$(dir)-headers : libs/numeric/$(dir)/include/boost : . ; +} + + project boost : requirements . # Disable auto-linking for all targets here, primarily because it caused @@ -213,7 +231,7 @@ for local l in $(all-libraries) } } -alias headers : : : : . ; +alias headers : $(all-headers)-headers numeric-$(numeric-headers)-headers : : : . ; explicit headers ; # Make project ids of all libraries known. diff --git a/tools/build/v2/build/configure.jam b/tools/build/v2/build/configure.jam index afba37f..586af01 100644 --- a/tools/build/v2/build/configure.jam +++ b/tools/build/v2/build/configure.jam @@ -122,10 +122,41 @@ rule print-configure-checks-summary ( ) } } +# Attempts to build a set of virtual targets +rule try-build ( targets * : what ) +{ + local result ; + local jam-targets ; + for local t in $(targets) + { + jam-targets += [ $(t).actualize ] ; + } + + 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 ] + { + result = true ; + log-check-result "$(x) : yes" ; + } + else + { + log-check-result "$(x) : no" ; + } + } + 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. -# 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 ; @@ -134,34 +165,12 @@ rule builds-raw ( metatarget-reference : project : ps : what : retry ? ) { .$(what)-tested.$(ps) = true ; - local targets = [ targets.generate-from-reference + local targets = [ targets.generate-from-reference $(metatarget-reference) : $(project) : $(ps) ] ; - - local jam-targets ; - for local t in $(targets[2-]) - { - jam-targets += [ $(t).actualize ] ; - } - - if ! UPDATE_NOW in [ RULENAMES ] - { - # Cannot determine. Assume existence. - } - 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" ; - } - } + + result = [ try-build $(targets) : $(what) ] ; + .$(what)-supported.$(ps) = $(result) ; + return $(result) ; } else diff --git a/tools/build/v2/build/virtual-target.jam b/tools/build/v2/build/virtual-target.jam index 4ac73de..c755823 100644 --- a/tools/build/v2/build/virtual-target.jam +++ b/tools/build/v2/build/virtual-target.jam @@ -1331,8 +1331,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/tools/build/v2/engine/builtins.c b/tools/build/v2/engine/builtins.c index dec2ef7..6c373dc 100644 --- a/tools/build/v2/engine/builtins.c +++ b/tools/build/v2/engine/builtins.c @@ -29,6 +29,10 @@ #include +#ifdef NT +#include +#endif + #if defined(USE_EXECUNIX) # include # include @@ -426,6 +430,11 @@ void load_builtins() bind_builtin( "MAKEDIR", builtin_makedir, 0, args ); } + { + const char * args [] = { "path", 0 }; + bind_builtin( "READLINK", builtin_readlink, 0, args ); + } + /* Initialize builtin modules. */ init_set(); init_path(); @@ -1831,6 +1840,94 @@ LIST * builtin_makedir( FRAME * frame, int flags ) } +LIST *builtin_readlink( FRAME * frame, int flags ) +{ + const char * path = object_str( list_front( lol_get( frame->args, 0 ) ) ); +#ifdef 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 LIST * builtin_python_import_rule( FRAME * frame, int flags ) diff --git a/tools/build/v2/engine/builtins.h b/tools/build/v2/engine/builtins.h index b8c086f..af35577 100644 --- a/tools/build/v2/engine/builtins.h +++ b/tools/build/v2/engine/builtins.h @@ -61,6 +61,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/tools/build/v2/tools/link.jam b/tools/build/v2/tools/link.jam new file mode 100644 index 0000000..9019e1e --- /dev/null +++ b/tools/build/v2/tools/link.jam @@ -0,0 +1,431 @@ +# 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 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 ) +{ + 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) : "symlinks supported" ] + { + .can-symlink = true ; + } + else + { + .can-symlink = false ; + } + } + if $(.can-symlink) = true + { + return true ; + } +} + + +rule can-hardlink ( project ) +{ + if ! $(.can-hardlink) + { + # Find the root project. + 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) : "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 ; + + if ! [ link.can-symlink $(self.project) ] + { + link.can-hardlink $(self.project) ; + } + + 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-symlink) = true + { + link.mklink $(target) : $(source) ; + } + else if $(.can-hardlink) = true + { + link.hardlink $(target) : $(source) ; + } + else + { + 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) ; + } + if ! [ on $(target) return $(RM) ] + { + NOUPDATE $(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) ; +} + +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) ; + } + 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 +{ + rmdir "$(<)" +} + +} + +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 ;