]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/tools/build/src/tools/stage.py
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / boost / tools / build / src / tools / stage.py
CommitLineData
7c673cae
FG
1# Status: ported.
2# Base revision 64444.
3#
4# Copyright 2003 Dave Abrahams
5# Copyright 2005, 2006 Rene Rivera
6# Copyright 2002, 2003, 2004, 2005, 2006, 2010 Vladimir Prus
7# Distributed under the Boost Software License, Version 1.0.
1e59de90 8# (See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt)
7c673cae
FG
9
10# This module defines the 'install' rule, used to copy a set of targets to a
11# single location.
12
13import b2.build.feature as feature
14import b2.build.targets as targets
15import b2.build.property as property
16import b2.build.property_set as property_set
17import b2.build.generators as generators
18import b2.build.virtual_target as virtual_target
19
20from b2.manager import get_manager
21from b2.util.sequence import unique
22from b2.util import bjam_signature
23
24import b2.build.type
25
26import os.path
27import re
28import types
29
30feature.feature('install-dependencies', ['off', 'on'], ['incidental'])
31feature.feature('install-type', [], ['free', 'incidental'])
32feature.feature('install-source-root', [], ['free', 'path'])
33feature.feature('so-version', [], ['free', 'incidental'])
34
35# If 'on', version symlinks for shared libraries will not be created. Affects
36# Unix builds only.
37feature.feature('install-no-version-symlinks', ['on'], ['optional', 'incidental'])
38
39class InstallTargetClass(targets.BasicTarget):
40
41 def update_location(self, ps):
42 """If <location> is not set, sets it based on the project data."""
43
44 loc = ps.get('location')
45 if not loc:
46 loc = os.path.join(self.project().get('location'), self.name())
47 ps = ps.add_raw(["<location>" + loc])
48
49 return ps
50
51 def adjust_properties(self, target, build_ps):
52 a = target.action()
53 properties = []
54 if a:
55 ps = a.properties()
56 properties = ps.all()
57
58 # Unless <hardcode-dll-paths>true is in properties, which can happen
59 # only if the user has explicitly requested it, nuke all <dll-path>
60 # properties.
61
62 if build_ps.get('hardcode-dll-paths') != ['true']:
63 properties = [p for p in properties if p.feature.name != 'dll-path']
64
65 # If any <dll-path> properties were specified for installing, add
66 # them.
67 properties.extend(build_ps.get_properties('dll-path'))
68
69 # Also copy <linkflags> feature from current build set, to be used
70 # for relinking.
71 properties.extend(build_ps.get_properties('linkflags'))
72
73 # Remove the <tag> feature on original targets.
74 # And <location>. If stage target has another stage target in
75 # sources, then we shall get virtual targets with the <location>
76 # property set.
77 properties = [p for p in properties
78 if not p.feature.name in ['tag', 'location']]
79
80 properties.extend(build_ps.get_properties('dependency'))
81
82 properties.extend(build_ps.get_properties('location'))
83
84
85 properties.extend(build_ps.get_properties('install-no-version-symlinks'))
86
87 d = build_ps.get_properties('install-source-root')
88
89 # Make the path absolute: we shall use it to compute relative paths and
90 # making the path absolute will help.
91 if d:
92 p = d[0]
93 properties.append(property.Property(p.feature, os.path.abspath(p.value)))
94
95 return property_set.create(properties)
96
97
98 def construct(self, name, source_targets, ps):
99
100 source_targets = self.targets_to_stage(source_targets, ps)
101 ps = self.update_location(ps)
102
103 ename = ps.get('name')
104 if ename:
105 ename = ename[0]
106 if ename and len(source_targets) > 1:
107 get_manager().errors()("When <name> property is used in 'install', only one source is allowed")
108
109 result = []
110
111 for i in source_targets:
112
113 staged_targets = []
114 new_ps = self.adjust_properties(i, ps)
115
116 # See if something special should be done when staging this type. It
117 # is indicated by the presence of a special "INSTALLED_" type.
118 t = i.type()
119 if t and b2.build.type.registered("INSTALLED_" + t):
120
121 if ename:
122 get_manager().errors()("In 'install': <name> property specified with target that requires relinking.")
123 else:
124 (r, targets) = generators.construct(self.project(), name, "INSTALLED_" + t,
125 new_ps, [i])
126 assert isinstance(r, property_set.PropertySet)
127 staged_targets.extend(targets)
128
129 else:
130 staged_targets.append(copy_file(self.project(), ename, i, new_ps))
131
132 if not staged_targets:
133 get_manager().errors()("Unable to generate staged version of " + i)
134
135 result.extend(get_manager().virtual_targets().register(t) for t in staged_targets)
136
137 return (property_set.empty(), result)
138
139 def targets_to_stage(self, source_targets, ps):
140 """Given the list of source targets explicitly passed to 'stage', returns the
141 list of targets which must be staged."""
142
143 result = []
144
145 # Traverse the dependencies, if needed.
146 if ps.get('install-dependencies') == ['on']:
147 source_targets = self.collect_targets(source_targets)
148
149 # Filter the target types, if needed.
150 included_types = ps.get('install-type')
151 for r in source_targets:
152 ty = r.type()
153 if ty:
154 # Do not stage searched libs.
155 if ty != "SEARCHED_LIB":
156 if included_types:
157 if self.include_type(ty, included_types):
158 result.append(r)
159 else:
160 result.append(r)
161 elif not included_types:
162 # Don't install typeless target if there is an explicit list of
163 # allowed types.
164 result.append(r)
165
166 return result
167
168 # CONSIDER: figure out why we can not use virtual-target.traverse here.
169 #
170 def collect_targets(self, targets):
171
172 s = [t.creating_subvariant() for t in targets]
173 s = unique(filter(lambda l: l != None,s))
174
175 result = set(targets)
176 for i in s:
177 i.all_referenced_targets(result)
178
179 result2 = []
180 for r in result:
181 if isinstance(r, property.Property):
182
183 if r.feature.name != 'use':
184 result2.append(r.value)
185 else:
186 result2.append(r)
187 result2 = unique(result2)
188 return result2
189
190 # Returns true iff 'type' is subtype of some element of 'types-to-include'.
191 #
192 def include_type(self, type, types_to_include):
193 return any(b2.build.type.is_subtype(type, ti) for ti in types_to_include)
194
195# Creates a copy of target 'source'. The 'properties' object should have a
196# <location> property which specifies where the target must be placed.
197#
198def copy_file(project, name, source, ps):
199
200 if not name:
201 name = source.name()
202
203 relative = ""
204
205 new_a = virtual_target.NonScanningAction([source], "common.copy", ps)
206 source_root = ps.get('install-source-root')
207 if source_root:
208 source_root = source_root[0]
209 # Get the real path of the target. We probably need to strip relative
210 # path from the target name at construction.
211 path = os.path.join(source.path(), os.path.dirname(name))
212 # Make the path absolute. Otherwise, it would be hard to compute the
213 # relative path. The 'source-root' is already absolute, see the
214 # 'adjust-properties' method above.
215 path = os.path.abspath(path)
216
217 relative = os.path.relpath(path, source_root)
218
219 name = os.path.join(relative, os.path.basename(name))
220 return virtual_target.FileTarget(name, source.type(), project, new_a, exact=True)
221
222def symlink(name, project, source, ps):
223 a = virtual_target.Action([source], "symlink.ln", ps)
224 return virtual_target.FileTarget(name, source.type(), project, a, exact=True)
225
226def relink_file(project, source, ps):
227 action = source[0].action()
228 cloned_action = virtual_target.clone_action(action, project, "", ps)
229 targets = cloned_action.targets()
230 # We relink only on Unix, where exe or shared lib is always a single file.
231 assert len(targets) == 1
232 return targets[0]
233
234
235# Declare installed version of the EXE type. Generator for this type will cause
236# relinking to the new location.
237b2.build.type.register('INSTALLED_EXE', [], 'EXE')
238
239class InstalledExeGenerator(generators.Generator):
240
241 def __init__(self):
242 generators.Generator.__init__(self, "install-exe", False, ['EXE'], ['INSTALLED_EXE'])
243
244 def run(self, project, name, ps, source):
245
246 need_relink = False;
247
248 if ps.get('os') in ['NT', 'CYGWIN'] or ps.get('target-os') in ['windows', 'cygwin']:
249 # Never relink
250 pass
251 else:
252 # See if the dll-path properties are not changed during
253 # install. If so, copy, don't relink.
254 need_relink = source[0].action() and ps.get('dll-path') != source[0].action().properties().get('dll-path')
255
256 if need_relink:
257 return [relink_file(project, source, ps)]
258 else:
259 return [copy_file(project, None, source[0], ps)]
260
261generators.register(InstalledExeGenerator())
262
263
264# Installing a shared link on Unix might cause a creation of versioned symbolic
265# links.
266b2.build.type.register('INSTALLED_SHARED_LIB', [], 'SHARED_LIB')
267
268class InstalledSharedLibGenerator(generators.Generator):
269
270 def __init__(self):
271 generators.Generator.__init__(self, 'install-shared-lib', False, ['SHARED_LIB'], ['INSTALLED_SHARED_LIB'])
272
273 def run(self, project, name, ps, source):
274
275 source = source[0]
276 if ps.get('os') in ['NT', 'CYGWIN'] or ps.get('target-os') in ['windows', 'cygwin']:
277 copied = copy_file(project, None, source, ps)
278 return [get_manager().virtual_targets().register(copied)]
279 else:
280 a = source.action()
281 if not a:
282 # Non-derived file, just copy.
283 copied = copy_file(project, None, source, ps)
284 else:
285
286 need_relink = ps.get('dll-path') != source.action().properties().get('dll-path')
287
288 if need_relink:
289 # Rpath changed, need to relink.
290 copied = relink_file(project, source, ps)
291 else:
292 copied = copy_file(project, None, source, ps)
293
294 result = [get_manager().virtual_targets().register(copied)]
295 # If the name is in the form NNN.XXX.YYY.ZZZ, where all 'X', 'Y' and
296 # 'Z' are numbers, we need to create NNN.XXX and NNN.XXX.YYY
297 # symbolic links.
298 m = re.match("(.*)\\.([0123456789]+)\\.([0123456789]+)\\.([0123456789]+)$",
299 copied.name());
300 if m:
301 # Symlink without version at all is used to make
302 # -lsome_library work.
303 result.append(symlink(m.group(1), project, copied, ps))
304
305 # Symlinks of some libfoo.N and libfoo.N.M are used so that
306 # library can found at runtime, if libfoo.N.M.X has soname of
307 # libfoo.N. That happens when the library makes some binary
308 # compatibility guarantees. If not, it is possible to skip those
309 # symlinks.
310 if ps.get('install-no-version-symlinks') != ['on']:
311
312 result.append(symlink(m.group(1) + '.' + m.group(2), project, copied, ps))
313 result.append(symlink(m.group(1) + '.' + m.group(2) + '.' + m.group(3),
314 project, copied, ps))
315
316 return result
317
318generators.register(InstalledSharedLibGenerator())
319
320
321# Main target rule for 'install'.
322#
323@bjam_signature((["name"], ["sources", "*"], ["requirements", "*"],
324 ["default_build", "*"], ["usage_requirements", "*"]))
325def install(name, sources, requirements=[], default_build=[], usage_requirements=[]):
326
327 requirements = requirements[:]
328 # Unless the user has explicitly asked us to hardcode dll paths, add
329 # <hardcode-dll-paths>false in requirements, to override default value.
330 if not '<hardcode-dll-paths>true' in requirements:
331 requirements.append('<hardcode-dll-paths>false')
332
333 if any(r.startswith('<tag>') for r in requirements):
334 get_manager().errors()("The <tag> property is not allowed for the 'install' rule")
335
336 from b2.manager import get_manager
337 t = get_manager().targets()
338
339 project = get_manager().projects().current()
340
341 return t.main_target_alternative(
342 InstallTargetClass(name, project,
343 t.main_target_sources(sources, name),
344 t.main_target_requirements(requirements, project),
345 t.main_target_default_build(default_build, project),
346 t.main_target_usage_requirements(usage_requirements, project)))
347
348get_manager().projects().add_rule("install", install)
349get_manager().projects().add_rule("stage", install)
350