# Status: ported. # Base revision: 64488. # Copyright 2003 Dave Abrahams # Copyright 2002, 2003 Rene Rivera # Copyright 2002, 2003, 2004, 2005 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) # Defines the "symlink" special target. 'symlink' targets make symbolic links # to the sources. import b2.build.feature as feature import b2.build.targets as targets import b2.build.property_set as property_set import b2.build.virtual_target as virtual_target import b2.build.targets from b2.manager import get_manager import bjam import os feature.feature("symlink-location", ["project-relative", "build-relative"], ["incidental"]) class SymlinkTarget(targets.BasicTarget): _count = 0 def __init__(self, project, targets, sources): # Generate a fake name for now. Need unnamed targets eventually. fake_name = "symlink#%s" % SymlinkTarget._count SymlinkTarget._count = SymlinkTarget._count + 1 b2.build.targets.BasicTarget.__init__(self, fake_name, project, sources) # Remember the targets to map the sources onto. Pad or truncate # to fit the sources given. assert len(targets) <= len(sources) self.targets = targets[:] + sources[len(targets):] # The virtual targets corresponding to the given targets. self.virtual_targets = [] def construct(self, name, source_targets, ps): i = 0 for t in source_targets: s = self.targets[i] a = virtual_target.Action(self.manager(), [t], "symlink.ln", ps) vt = virtual_target.FileTarget(os.path.basename(s), t.type(), self.project(), a) # Place the symlink in the directory relative to the project # location, instead of placing it in the build directory. if not ps.get('symlink-location') == "project-relative": vt.set_path(os.path.join(self.project().get('location'), os.path.dirname(s))) vt = get_manager().virtual_targets().register(vt) self.virtual_targets.append(vt) i = i + 1 return (property_set.empty(), self.virtual_targets) # Creates a symbolic link from a set of targets to a set of sources. # The targets and sources map one to one. The symlinks generated are # limited to be the ones given as the sources. That is, the targets # are either padded or trimmed to equate to the sources. The padding # is done with the name of the corresponding source. For example:: # # symlink : one two ; # # Is equal to:: # # symlink one two : one two ; # # Names for symlink are relative to the project location. They cannot # include ".." path components. def symlink(targets, sources): from b2.manager import get_manager t = get_manager().targets() p = get_manager().projects().current() return t.main_target_alternative( SymlinkTarget(p, targets, # Note: inline targets are not supported for symlink, intentionally, # since it's used to linking existing non-local targets. sources)) def setup_ln(targets, sources, ps): source_path = bjam.call("get-target-variable", sources[0], "LOCATE")[0] target_path = bjam.call("get-target-variable", targets[0], "LOCATE")[0] rel = os.path.relpath(source_path, target_path) if rel == ".": bjam.call("set-target-variable", targets, "PATH_TO_SOURCE", "") else: bjam.call("set-target-variable", targets, "PATH_TO_SOURCE", rel) if os.name == 'nt': ln_action = """echo "NT symlinks not supported yet, making copy" del /f /q "$(<)" 2>nul >nul copy "$(>)" "$(<)" $(NULL_OUT)""" else: ln_action = "ln -f -s '$(>:D=:R=$(PATH_TO_SOURCE))' '$(<)'" get_manager().engine().register_action("symlink.ln", ln_action, function=setup_ln) get_manager().projects().add_rule("symlink", symlink)