Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r84524 - in trunk/tools/build/v2: build engine test tools
From: steven_at_[hidden]
Date: 2013-05-26 20:02:45


Author: steven_watanabe
Date: 2013-05-26 20:02:43 EDT (Sun, 26 May 2013)
New Revision: 84524
URL: http://svn.boost.org/trac/boost/changeset/84524

Log:
Initial supprort for creating symlinks in the git layout.
Added:
   trunk/tools/build/v2/test/builtin_readlink.py (contents, props changed)
   trunk/tools/build/v2/tools/link.jam (contents, props changed)
Text files modified:
   trunk/tools/build/v2/build/virtual-target.jam | 3
   trunk/tools/build/v2/engine/builtins.c | 97 ++++++++++++++++++++++++++++++++++++++++
   trunk/tools/build/v2/engine/builtins.h | 1
   trunk/tools/build/v2/engine/filent.c | 13 +++++
   trunk/tools/build/v2/engine/timestamp.h | 1
   5 files changed, 113 insertions(+), 2 deletions(-)

Modified: trunk/tools/build/v2/build/virtual-target.jam
==============================================================================
--- trunk/tools/build/v2/build/virtual-target.jam (original)
+++ trunk/tools/build/v2/build/virtual-target.jam 2013-05-26 20:02:43 EDT (Sun, 26 May 2013)
@@ -1324,8 +1324,9 @@
         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 ] ] ;
             }

Modified: trunk/tools/build/v2/engine/builtins.c
==============================================================================
--- trunk/tools/build/v2/engine/builtins.c (original)
+++ trunk/tools/build/v2/engine/builtins.c 2013-05-26 20:02:43 EDT (Sun, 26 May 2013)
@@ -29,6 +29,10 @@
 
 #include <ctype.h>
 
+#ifdef OS_NT
+#include <windows.h>
+#endif
+
 #if defined(USE_EXECUNIX)
 # include <sys/types.h>
 # include <sys/wait.h>
@@ -425,6 +429,11 @@
         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_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
 

Modified: trunk/tools/build/v2/engine/builtins.h
==============================================================================
--- trunk/tools/build/v2/engine/builtins.h (original)
+++ trunk/tools/build/v2/engine/builtins.h 2013-05-26 20:02:43 EDT (Sun, 26 May 2013)
@@ -63,6 +63,7 @@
 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;

Modified: trunk/tools/build/v2/engine/filent.c
==============================================================================
--- trunk/tools/build/v2/engine/filent.c (original)
+++ trunk/tools/build/v2/engine/filent.c 2013-05-26 20:02:43 EDT (Sun, 26 May 2013)
@@ -122,6 +122,19 @@
                 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 ) );

Modified: trunk/tools/build/v2/engine/timestamp.h
==============================================================================
--- trunk/tools/build/v2/engine/timestamp.h (original)
+++ trunk/tools/build/v2/engine/timestamp.h 2013-05-26 20:02:43 EDT (Sun, 26 May 2013)
@@ -14,7 +14,6 @@
 #include "object.h"
 
 #ifdef OS_NT
-# define WIN32_LEAN_AND_MEAN
 # include <windows.h>
 #endif
 

Added: trunk/tools/build/v2/test/builtin_readlink.py
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/test/builtin_readlink.py 2013-05-26 20:02:43 EDT (Sun, 26 May 2013)
@@ -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()

Added: trunk/tools/build/v2/tools/link.jam
==============================================================================
--- (empty file)
+++ trunk/tools/build/v2/tools/link.jam 2013-05-26 20:02:43 EDT (Sun, 26 May 2013)
@@ -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 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 <location> ]
+ {
+ property-set = [ property-set.create
+ [ property.select <location> : [ $(property-set).raw ] ] ] ;
+ }
+ else
+ {
+ local path,relative-to-build-dir = [ $(property-set).target-path ] ;
+ local path = $(path,relative-to-build-dir[1]) ;
+ local relative-to-build-dir = $(path,relative-to-build-dir[2]) ;
+
+ if $(relative-to-build-dir)
+ {
+ path = [ path.join [ $(self.project).build-dir ] $(path) ] ;
+ }
+
+ property-set = [ property-set.create <location>$(path) ] ;
+ }
+
+ local a = [ new non-scanning-action $(source-target) :
+ link.do-link-recursively : $(property-set) ] ;
+
+ local t = [ new notfile-target $(name)
+ : $(self.project) : $(a) ] ;
+
+ return [ property-set.empty ] [ virtual-target.register $(t) ] ;
+ }
+}
+
+rule do-file-link
+{
+ local target = [ path.native [ path.relative-to [ path.pwd ] $(<) ] ] ;
+ local source = [ path.native [ path.relative-to [ path.pwd ] $(>) ] ] ;
+ LOCATE on $(target) = . ;
+ DEPENDS $(.current-target) : $(target) ;
+ if $(.can-symlink) = true
+ {
+ link.mklink $(target) : $(source) ;
+ }
+ else if $(.can-hardlink) = true
+ {
+ DEPENDS $(target) : $(source) ;
+ link.hardlink $(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) ;
+ }
+ 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 <location> : $(properties) ] ;
+ local source-path = [ on $(source) return $(LOCATE) ] [ on $(source) return $(SEARCH) ] ;
+
+ local absolute-target = [ path.root
+ [ path.join [ path.make $(target-path[1]:G=) ]
+ [ path.basename [ path.make $(source:G=) ] ] ]
+ [ path.pwd ] ] ;
+
+ local absolute-source = [ path.root
+ [ path.root [ path.make $(source:G=) ]
+ [ path.make $(source-path[1]) ] ]
+ [ path.pwd ] ] ;
+
+ local .current-target = $(target) ;
+
+ link-recursively $(absolute-target) : $(absolute-source) ;
+}
+
+rule mklink
+{
+ local target-path = [ on $(<) return $(LOCATE) ] [ on $(<) return $(SEARCH) ] . ;
+ local source-path = [ on $(>) return $(LOCATE) ] [ on $(>) return $(SEARCH) ] . ;
+ local relative-path = [ path.relative-to
+ [ path.parent [ path.join [ path.root [ path.make $(target-path[1]) ] [ path.pwd ] ] [ path.make $(<:G=) ] ] ]
+ [ path.join [ path.root [ path.make $(source-path[1]) ] [ path.pwd ] ] [ path.make $(>:G=) ] ] ] ;
+
+ PATH_TO_SOURCE on $(<) = [ path.native $(relative-path) ] ;
+ NOUPDATE $(<) ;
+}
+
+if [ os.name ] = NT
+{
+
+actions mklink
+{
+ if exist "$(<)" del "$(<)"
+ mklink "$(<)" "$(PATH_TO_SOURCE)"
+}
+
+actions hardlink
+{
+ if exist "$(<)" del "$(<)"
+ mklink /H "$(<)" "$(>)"
+}
+
+actions rm
+{
+ rmdir "$(<)"
+}
+
+}
+else
+{
+
+actions mklink
+{
+ ln -f -s "$(PATH_TO_SOURCE)" "$(<)"
+}
+
+actions hardlink
+{
+ ln -f "$(>)" "$(<)"
+}
+
+actions rm
+{
+ rm "$(<)"
+}
+
+}
+
+rule link-directory ( name : sources : requirements * : default-build * : usage-requirements * )
+{
+ local project = [ project.current ] ;
+ sources = [ new file-or-directory-reference $(sources) : $(project) ] ;
+ targets.main-target-alternative $(sources) ;
+ return [ targets.main-target-alternative
+ [ new symlink-target-class $(name) : $(project)
+ : [ targets.main-target-sources $(sources) : $(name) : no-renaming ]
+ : [ targets.main-target-requirements $(requirements) : $(project) ]
+ : [ targets.main-target-default-build : $(project) ]
+ : [ targets.main-target-usage-requirements $(usage-requirements) :
+ $(project) ] ] ] ;
+}
+
+IMPORT $(__name__) : link-directory : : link-directory ;


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk