1 # Copyright 2012 Steven Watanabe
2 # Distributed under the Boost Software License, Version 1.0.
3 # (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt)
9 import virtual-target ;
16 rule get-root-project ( project )
18 # Find the root project.
19 local root-project = $(project) ;
20 root-project = [ $(root-project).project-module ] ;
22 [ project.attribute $(root-project) parent-module ] &&
23 [ project.attribute $(root-project) parent-module ] != user-config &&
24 [ project.attribute $(root-project) parent-module ] != project-config
26 root-project = [ project.attribute $(root-project) parent-module ] ;
28 return $(root-project) ;
31 TOUCH = [ common.file-touch-command ] ;
37 rule can-symlink ( project : ps )
41 local root-project = [ get-root-project $(project) ] ;
43 local source-target = [ new file-target test-symlink-source : :
44 $(project) : [ new action : link.touch ] ] ;
45 local target = [ new file-target test-symlink : :
46 $(project) : [ new action $(source-target) : link.mklink ] ] ;
48 if [ configure.try-build $(target) : $(ps) : "symlinks supported" ]
54 .can-symlink = false ;
57 if $(.can-symlink) = true
66 # Test for Windows junctions (mklink /J)
67 rule can-junction ( project : ps )
71 local root-project = [ get-root-project $(project) ] ;
73 local source-target = [ new file-target test-junction-source : :
74 $(project) : [ new action : common.mkdir ] ] ;
75 local target = [ new file-target test-junction : :
76 $(project) : [ new action $(source-target) : link.junction ] ] ;
78 if [ configure.try-build $(target) : $(ps) : "junctions supported" ]
80 .can-junction = true ;
84 .can-junction = false ;
87 if $(.can-junction) = true
97 .can-junction = false ;
99 rule can-junction ( project : ps )
105 rule can-hardlink ( project : ps )
107 if ! $(.can-hardlink)
109 local root-project = [ get-root-project $(project) ] ;
111 local source-target = [ new file-target test-hardlink-source : :
112 $(project) : [ new action : link.touch ] ] ;
113 # Use <location-prefix> so that the destination link is created
114 # in a different directory. AFS refuses to make hard links
115 # between files in different directories, so we want to check
117 local target = [ new file-target test-hardlink : :
118 $(project) : [ new action $(source-target) : link.hardlink
119 : [ new property-set <location-prefix>symlink ]
122 if [ configure.try-build $(target) : $(ps) : "hardlinks supported" ]
124 .can-hardlink = true ;
128 .can-hardlink = false ;
131 if $(.can-hardlink) = true
137 class file-or-directory-reference : basic-target
139 import virtual-target ;
140 import property-set ;
143 rule construct ( name : source-targets * : property-set )
145 return [ property-set.empty ] [ virtual-target.from-file $(self.name) :
146 [ location ] : $(self.project) ] ;
149 # Returns true if the referred file really exists.
153 return $(self.file-path) ;
156 # Returns the location of target. Needed by 'testing.jam'.
159 if ! $(self.file-location)
161 local source-location = [ $(self.project).get source-location ] ;
162 for local src-dir in $(source-location)
164 if ! $(self.file-location)
166 local location = [ path.root $(self.name) $(src-dir) ] ;
167 if [ path.exists [ path.native $(location) ] ]
169 self.file-location = $(src-dir) ;
170 self.file-path = $(location) ;
175 return $(self.file-location) ;
179 class symlink-target-class : basic-target
182 import virtual-target ;
186 rule construct ( name : source-target : property-set )
188 local location = [ path.join
189 [ $(source-target).path ] [ $(source-target).name ] ] ;
190 local files = [ path.glob-tree $(location) : * ] ;
193 # If we have symlinks, don't bother checking
194 # for hardlinks and junctions.
195 if ! [ link.can-symlink $(self.project) : $(property-set) ]
197 link.can-junction $(self.project) : $(property-set) ;
198 link.can-hardlink $(self.project) : $(property-set) ;
201 if [ $(property-set).get <location> ]
203 property-set = [ property-set.create
204 [ property.select <location> : [ $(property-set).raw ] ] ] ;
208 local path,relative-to-build-dir = [ $(property-set).target-path ] ;
209 local path = $(path,relative-to-build-dir[1]) ;
210 local relative-to-build-dir = $(path,relative-to-build-dir[2]) ;
212 if $(relative-to-build-dir)
214 path = [ path.join [ $(self.project).build-dir ] $(path) ] ;
217 property-set = [ property-set.create <location>$(path) ] ;
220 local a = [ new non-scanning-action $(source-target) :
221 link.do-link-recursively : $(property-set) ] ;
223 local t = [ new notfile-target $(name)
224 : $(self.project) : $(a) ] ;
226 return [ property-set.empty ] [ virtual-target.register $(t) ] ;
232 local target = [ path.native [ path.relative-to [ path.pwd ] $(<) ] ] ;
233 local source = [ path.native [ path.relative-to [ path.pwd ] $(>) ] ] ;
234 local old-source = [ on $(target) return $(LINK-SOURCE) ] ;
239 Cannot create link $(target) to $(source). :
240 Link previously defined to another file, $(old-source[1]). ;
242 LINK-SOURCE on $(target) = $(source) $(.current-target) ;
243 LOCATE on $(target) = . ;
244 DEPENDS $(.current-target) : $(target) ;
245 if $(.can-symlink) = true
247 DEPENDS $(target) : $(source) ;
248 link.mklink $(target) : $(source) ;
250 else if $(.can-hardlink) = true
252 DEPENDS $(target) : $(source) ;
253 link.hardlink $(target) : $(source) ;
257 DEPENDS $(target) : $(source) ;
258 common.copy $(target) : $(source) ;
264 local target = [ path.native [ path.relative-to [ path.pwd ] $(<) ] ] ;
265 local source = [ path.native [ path.relative-to [ path.pwd ] $(>) ] ] ;
266 local relative = [ path.native [ path.relative-to [ path.parent $(<) ] $(>) ] ] ;
267 if ! [ on $(target) return $(MKLINK_OR_DIR) ]
269 LOCATE on $(target) = . ;
270 DEPENDS $(.current-target) : $(target) ;
271 mklink-or-dir $(target) : $(source) ;
275 if $(.can-symlink) = true
277 MKLINK_OR_DIR on $(target) = mklink /D \"$(target)\" \"$(relative)\" ;
281 # This function should only be called
282 # if either symlinks or junctions are supported.
283 # To get here $(.can-junction) must be true.
285 MKLINK_OR_DIR on $(target) = mklink /J \"$(target)\" \"$(source)\" ;
290 MKLINK_OR_DIR on $(target) = ln -s $(relative) $(target) ;
296 local target = [ path.native [ path.relative-to [ path.pwd ] $(<) ] ] ;
302 local target = [ path.native [ path.relative-to [ path.pwd ] $(<) ] ] ;
303 if ! [ on $(target) return $(MKLINK_OR_DIR) ]
305 LOCATE on $(target) = . ;
306 DEPENDS $(.current-target) : $(target) ;
307 common.mkdir $(target) ;
309 MKLINK_OR_DIR on $(target) = mkdir \"$(target)\" ;
314 local target = [ path.native [ path.relative-to [ path.pwd ] $(<) ] ] ;
316 RM on $(target) = rmdir ;
325 actions mklink-or-dir
330 rule link-entries ( target : files * : split ? : deleted ? )
332 for local s in $(files)
334 local t = [ path.join $(target) [ path.basename $(s) ] ] ;
335 if ! $(.known-dirs.$(t))
337 local t = [ path.native [ path.relative-to [ path.pwd ] $(t) ] ] ;
338 local s = [ path.native [ path.relative-to [ path.pwd ] $(target) ] ] ;
340 DEPENDS $(t) : $(s) ;
345 link-recursively $(t) : $(s) : : $(deleted) ;
349 link-entries $(t) : [ path.glob $(s) : * ] ;
352 if ! $(.known-dirs.$(target))
354 .known-dirs.$(target) += $(files) ;
355 .known-dirs.base.$(target) = $(.current-target) ;
359 rule link-recursively ( target : source : no-recurse ? : deleted ? )
362 force-update $(target) ;
366 if [ CHECK_IF_FILE [ path.native $(source) ] ]
368 do-file-link $(target) : $(source) ;
370 else if $(.known-dirs.$(target)) && ! $(no-recurse)
373 if ! $(.split-dirs.$(target))
375 if [ READLINK [ path.native $(target) ] ]
380 .deleted-dirs.$(target) = true ;
383 local .current-target = $(.known-dirs.base.$(target)) ;
384 for local s in $(.known-dirs.$(target))
386 local t = [ path.join $(target) [ path.basename $(s) ] ] ;
387 link-recursively $(t) : $(s) : flat : $(deleted) ;
391 else if $(.deleted-dirs.$(target))
396 else if [ path.exists [ path.native $(target) ] ] && ! $(deleted)
398 local link-target = [ READLINK [ path.native $(target) ] ] ;
402 [ path.root [ path.make $(link-target) ] [ path.parent $(target) ] ] ;
403 # HACK: Take advantage of the fact that path.glob
404 # normalizes its arguments. If full-path and
405 # source are different, but both are empty, they
406 # will compare equal, but that's okay because
407 # for the purposes of this module, empty directories
409 if [ path.glob $(full-path) : * ] != [ path.glob $(source) : * ]
414 .deleted-dirs.$(target) = true ;
426 else if $(.can-symlink) = false && $(.can-junction) = false
428 if [ READLINK [ path.native $(target) ] ]
433 .deleted-dirs.$(target) = true ;
441 do-link $(target) : $(source) ;
446 .split-dirs.$(target) = true ;
451 link-entries $(target) : [ path.glob $(source) : * ] : $(split) : $(deleted) ;
455 rule do-link-recursively ( target : source : properties * )
457 local target-path = [ property.select <location> : $(properties) ] ;
458 local source-path = [ on $(source) return $(LOCATE) ] [ on $(source) return $(SEARCH) ] ;
460 local absolute-target = [ path.root
461 [ path.join [ path.make $(target-path[1]:G=) ]
462 [ path.basename [ path.make $(source:G=) ] ] ]
465 local absolute-source = [ path.root
466 [ path.root [ path.make $(source:G=) ]
467 [ path.make $(source-path[1]) ] ]
470 local .current-target = $(target) ;
472 link-recursively $(absolute-target) : $(absolute-source) ;
477 local target-path = [ on $(<) return $(LOCATE) ] [ on $(<) return $(SEARCH) ] . ;
478 local source-path = [ on $(>) return $(LOCATE) ] [ on $(>) return $(SEARCH) ] . ;
479 local relative-path = [ path.relative-to
480 [ path.parent [ path.join [ path.root [ path.make $(target-path[1]) ] [ path.pwd ] ] [ path.make $(<:G=) ] ] ]
481 [ path.join [ path.root [ path.make $(source-path[1]) ] [ path.pwd ] ] [ path.make $(>:G=) ] ] ] ;
483 PATH_TO_SOURCE on $(<) = [ path.native $(relative-path) ] ;
491 if exist "$(<)" rmdir "$(<)"
492 mklink /J "$(<)" "$(>)"
497 if exist "$(<)" del "$(<)"
498 mklink "$(<)" "$(PATH_TO_SOURCE)"
503 if exist "$(<)" del "$(<)"
504 mklink /H "$(<)" "$(>)"
518 ln -f -s "$(PATH_TO_SOURCE)" "$(<)"
533 rule link-directory ( name : sources : requirements * : default-build * : usage-requirements * )
535 local project = [ project.current ] ;
536 sources = [ new file-or-directory-reference $(sources) : $(project) ] ;
537 targets.main-target-alternative $(sources) ;
538 return [ targets.main-target-alternative
539 [ new symlink-target-class $(name) : $(project)
540 : [ targets.main-target-sources $(sources) : $(name) : no-renaming ]
541 : [ targets.main-target-requirements $(requirements) : $(project) ]
542 : [ targets.main-target-default-build : $(project) ]
543 : [ targets.main-target-usage-requirements $(usage-requirements) :
547 IMPORT $(__name__) : link-directory : : link-directory ;