1# Status: ported. 2# Base revision: 64488. 3 4# Copyright 2003 Dave Abrahams 5# Copyright 2002, 2003 Rene Rivera 6# Copyright 2002, 2003, 2004, 2005 Vladimir Prus 7# Distributed under the Boost Software License, Version 1.0. 8# (See accompanying file LICENSE_1_0.txt or http://www.boost.org/LICENSE_1_0.txt) 9 10# Defines the "symlink" special target. 'symlink' targets make symbolic links 11# to the sources. 12 13import b2.build.feature as feature 14import b2.build.targets as targets 15import b2.build.property_set as property_set 16import b2.build.virtual_target as virtual_target 17import b2.build.targets 18 19from b2.manager import get_manager 20 21import bjam 22 23import os 24 25 26feature.feature("symlink-location", ["project-relative", "build-relative"], ["incidental"]) 27 28class SymlinkTarget(targets.BasicTarget): 29 30 _count = 0 31 32 def __init__(self, project, targets, sources): 33 34 # Generate a fake name for now. Need unnamed targets eventually. 35 fake_name = "symlink#%s" % SymlinkTarget._count 36 SymlinkTarget._count = SymlinkTarget._count + 1 37 38 b2.build.targets.BasicTarget.__init__(self, fake_name, project, sources) 39 40 # Remember the targets to map the sources onto. Pad or truncate 41 # to fit the sources given. 42 assert len(targets) <= len(sources) 43 self.targets = targets[:] + sources[len(targets):] 44 45 # The virtual targets corresponding to the given targets. 46 self.virtual_targets = [] 47 48 def construct(self, name, source_targets, ps): 49 i = 0 50 for t in source_targets: 51 s = self.targets[i] 52 a = virtual_target.Action(self.manager(), [t], "symlink.ln", ps) 53 vt = virtual_target.FileTarget(os.path.basename(s), t.type(), self.project(), a) 54 55 # Place the symlink in the directory relative to the project 56 # location, instead of placing it in the build directory. 57 if not ps.get('symlink-location') == "project-relative": 58 vt.set_path(os.path.join(self.project().get('location'), os.path.dirname(s))) 59 60 vt = get_manager().virtual_targets().register(vt) 61 self.virtual_targets.append(vt) 62 i = i + 1 63 64 return (property_set.empty(), self.virtual_targets) 65 66# Creates a symbolic link from a set of targets to a set of sources. 67# The targets and sources map one to one. The symlinks generated are 68# limited to be the ones given as the sources. That is, the targets 69# are either padded or trimmed to equate to the sources. The padding 70# is done with the name of the corresponding source. For example:: 71# 72# symlink : one two ; 73# 74# Is equal to:: 75# 76# symlink one two : one two ; 77# 78# Names for symlink are relative to the project location. They cannot 79# include ".." path components. 80def symlink(targets, sources): 81 82 from b2.manager import get_manager 83 t = get_manager().targets() 84 p = get_manager().projects().current() 85 86 return t.main_target_alternative( 87 SymlinkTarget(p, targets, 88 # Note: inline targets are not supported for symlink, intentionally, 89 # since it's used to linking existing non-local targets. 90 sources)) 91 92 93def setup_ln(targets, sources, ps): 94 95 source_path = bjam.call("get-target-variable", sources[0], "LOCATE")[0] 96 target_path = bjam.call("get-target-variable", targets[0], "LOCATE")[0] 97 rel = os.path.relpath(source_path, target_path) 98 if rel == ".": 99 bjam.call("set-target-variable", targets, "PATH_TO_SOURCE", "") 100 else: 101 bjam.call("set-target-variable", targets, "PATH_TO_SOURCE", rel) 102 103if os.name == 'nt': 104 ln_action = """echo "NT symlinks not supported yet, making copy" 105del /f /q "$(<)" 2>nul >nul 106copy "$(>)" "$(<)" $(NULL_OUT)""" 107else: 108 ln_action = "ln -f -s '$(>:D=:R=$(PATH_TO_SOURCE))' '$(<)'" 109 110get_manager().engine().register_action("symlink.ln", ln_action, function=setup_ln) 111 112get_manager().projects().add_rule("symlink", symlink) 113