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