netcmd:domain:policy: Fix missing conversion from tgt_lifetime minutes to 10^(-7...
[Samba.git] / buildtools / wafsamba / samba_deps.py
blob6db44cc3a612bd064ae6640a432130472f40ab57
1 # Samba automatic dependency handling and project rules
3 import os, sys, re
5 from waflib import Build, Options, Logs, Utils, Errors, Task
6 from waflib.Logs import debug
7 from waflib.Configure import conf
8 from waflib import ConfigSet
10 from samba_utils import LOCAL_CACHE, TO_LIST, get_tgt_list, unique_list
11 from samba_autoconf import library_flags
13 @conf
14 def ADD_GLOBAL_DEPENDENCY(ctx, dep):
15 '''add a dependency for all binaries and libraries'''
16 if not 'GLOBAL_DEPENDENCIES' in ctx.env:
17 ctx.env.GLOBAL_DEPENDENCIES = []
18 ctx.env.GLOBAL_DEPENDENCIES.append(dep)
21 @conf
22 def BREAK_CIRCULAR_LIBRARY_DEPENDENCIES(ctx):
23 '''indicate that circular dependencies between libraries should be broken.'''
24 ctx.env.ALLOW_CIRCULAR_LIB_DEPENDENCIES = True
27 @conf
28 def SET_SYSLIB_DEPS(conf, target, deps):
29 '''setup some implied dependencies for a SYSLIB'''
30 cache = LOCAL_CACHE(conf, 'SYSLIB_DEPS')
31 cache[target] = deps
34 def expand_subsystem_deps(bld):
35 '''expand the reverse dependencies resulting from subsystem
36 attributes of modules. This is walking over the complete list
37 of declared subsystems, and expands the samba_deps_extended list for any
38 module<->subsystem dependencies'''
40 subsystem_list = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
41 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
43 for subsystem_name in subsystem_list:
44 bld.ASSERT(subsystem_name in targets, "Subsystem target %s not declared" % subsystem_name)
45 type = targets[subsystem_name]
46 if type == 'DISABLED' or type == 'EMPTY':
47 continue
49 # for example,
50 # subsystem_name = dcerpc_server (a subsystem)
51 # subsystem = dcerpc_server (a subsystem object)
52 # module_name = rpc_epmapper (a module within the dcerpc_server subsystem)
53 # module = rpc_epmapper (a module object within the dcerpc_server subsystem)
55 subsystem = bld.get_tgen_by_name(subsystem_name)
56 bld.ASSERT(subsystem is not None, "Unable to find subsystem %s" % subsystem_name)
57 for d in subsystem_list[subsystem_name]:
58 module_name = d['TARGET']
59 module_type = targets[module_name]
60 if module_type in ['DISABLED', 'EMPTY']:
61 continue
62 bld.ASSERT(subsystem is not None,
63 "Subsystem target %s for %s (%s) not found" % (subsystem_name, module_name, module_type))
64 if module_type in ['SUBSYSTEM']:
65 # if a module is a plain object type (not a library) then the
66 # subsystem it is part of needs to have it as a dependency, so targets
67 # that depend on this subsystem get the modules of that subsystem
68 subsystem.samba_deps_extended.append(module_name)
69 subsystem.samba_deps_extended = unique_list(subsystem.samba_deps_extended)
73 def build_dependencies(self):
74 '''This builds the dependency list for a target. It runs after all the targets are declared
76 The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
77 the full dependency list for a target until we have all of the targets declared.
78 '''
80 if self.samba_type in ['LIBRARY', 'PLUGIN', 'BINARY', 'PYTHON']:
81 self.uselib = list(self.final_syslibs)
82 self.uselib_local = list(self.final_libs)
83 self.add_objects = list(self.final_objects)
85 # extra link flags from pkg_config
86 (cflags, ldflags, cpppath, libs) = library_flags(
87 self, list(self.final_syslibs.copy()))
88 new_ldflags = getattr(self, 'samba_ldflags', [])[:]
89 new_ldflags.extend(ldflags)
90 self.ldflags = new_ldflags
92 if getattr(self, 'allow_undefined_symbols', False) and self.env.undefined_ldflags:
93 for f in self.env.undefined_ldflags:
94 self.ldflags.remove(f)
96 if getattr(self, 'allow_undefined_symbols', False) and self.env.undefined_ignore_ldflags:
97 for f in self.env.undefined_ignore_ldflags:
98 self.ldflags.append(f)
100 debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
101 self.sname, self.uselib, self.uselib_local, self.add_objects)
103 if self.samba_type in ['SUBSYSTEM', 'BUILTIN']:
104 # this is needed for the cflags of libs that come from pkg_config
105 self.uselib = list(self.final_syslibs)
106 self.uselib.extend(list(self.direct_syslibs))
107 for lib in self.final_libs:
108 t = self.bld.get_tgen_by_name(lib)
109 self.uselib.extend(list(t.final_syslibs))
110 self.uselib = unique_list(self.uselib)
112 if getattr(self, 'uselib', None):
113 up_list = []
114 for l in self.uselib:
115 up_list.append(l.upper())
116 self.uselib = up_list
119 def build_includes(self):
120 '''This builds the right set of includes for a target.
122 One tricky part of this is that the includes= attribute for a
123 target needs to use paths which are relative to that targets
124 declaration directory (which we can get at via t.path).
126 The way this works is the includes list gets added as
127 samba_includes in the main build task declaration. Then this
128 function runs after all of the tasks are declared, and it
129 processes the samba_includes attribute to produce a includes=
130 attribute
133 if getattr(self, 'samba_includes', None) is None:
134 return
136 bld = self.bld
138 inc_deps = includes_objects(bld, self, set(), {})
140 includes = []
142 # maybe add local includes
143 if getattr(self, 'local_include', True) and getattr(self, 'local_include_first', True):
144 includes.append('.')
146 includes.extend(self.samba_includes_extended)
148 if 'EXTRA_INCLUDES' in bld.env and getattr(self, 'global_include', True):
149 includes.extend(bld.env['EXTRA_INCLUDES'])
151 includes.append('#')
153 inc_set = set()
154 inc_abs = []
156 for d in inc_deps:
157 t = bld.get_tgen_by_name(d)
158 bld.ASSERT(t is not None, "Unable to find dependency %s for %s" % (d, self.sname))
159 inclist = getattr(t, 'samba_includes_extended', [])[:]
160 if getattr(t, 'local_include', True):
161 inclist.append('.')
162 if inclist == []:
163 continue
164 tpath = t.samba_abspath
165 for inc in inclist:
166 npath = tpath + '/' + inc
167 if not npath in inc_set:
168 inc_abs.append(npath)
169 inc_set.add(npath)
171 mypath = self.path.abspath(bld.env)
172 for inc in inc_abs:
173 relpath = os.path.relpath(inc, mypath)
174 includes.append(relpath)
176 if getattr(self, 'local_include', True) and not getattr(self, 'local_include_first', True):
177 includes.append('.')
179 # now transform the includes list to be relative to the top directory
180 # which is represented by '#' in waf. This allows waf to cache the
181 # includes lists more efficiently
182 includes_top = []
183 for i in includes:
184 if i[0] == '#':
185 # some are already top based
186 includes_top.append(i)
187 continue
188 absinc = os.path.join(self.path.abspath(), i)
189 relinc = os.path.relpath(absinc, self.bld.srcnode.abspath())
190 includes_top.append('#' + relinc)
192 self.includes = unique_list(includes_top)
193 debug('deps: includes for target %s: includes=%s',
194 self.sname, self.includes)
197 def add_init_functions(self):
198 '''This builds the right set of init functions'''
200 bld = self.bld
202 subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
204 # cope with the separated object lists from BINARY and LIBRARY targets
205 sname = self.sname
206 if sname.endswith('.objlist'):
207 sname = sname[0:-8]
209 modules = []
210 if sname in subsystems:
211 modules.append(sname)
213 m = getattr(self, 'samba_modules', None)
214 if m is not None:
215 modules.extend(TO_LIST(m))
217 m = getattr(self, 'samba_subsystem', None)
218 if m is not None:
219 modules.append(m)
221 if 'pyembed' in self.features:
222 return
224 sentinel = getattr(self, 'init_function_sentinel', 'NULL')
226 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
227 cflags = getattr(self, 'samba_cflags', [])[:]
229 if modules == []:
230 sname = sname.replace('-','_')
231 sname = sname.replace('.','_')
232 sname = sname.replace('/','_')
233 cflags.append('-DSTATIC_%s_MODULES=%s' % (sname, sentinel))
234 if sentinel == 'NULL':
235 proto = "extern void __%s_dummy_module_proto(void)" % (sname)
236 cflags.append('-DSTATIC_%s_MODULES_PROTO=%s' % (sname, proto))
237 self.cflags = cflags
238 return
240 for m in modules:
241 bld.ASSERT(m in subsystems,
242 "No init_function defined for module '%s' in target '%s'" % (m, self.sname))
243 init_fn_list = []
244 for d in subsystems[m]:
245 if targets[d['TARGET']] != 'DISABLED':
246 init_fn_list.append(d['INIT_FUNCTION'])
247 if init_fn_list == []:
248 cflags.append('-DSTATIC_%s_MODULES=%s' % (m, sentinel))
249 if sentinel == 'NULL':
250 proto = "extern void __%s_dummy_module_proto(void)" % (m)
251 cflags.append('-DSTATIC_%s_MODULES_PROTO=%s' % (m, proto))
252 else:
253 cflags.append('-DSTATIC_%s_MODULES=%s' % (m, ','.join(init_fn_list) + ',' + sentinel))
254 proto = "".join('_MODULE_PROTO(%s)' % f for f in init_fn_list) +\
255 "extern void __%s_dummy_module_proto(void)" % (m)
256 cflags.append('-DSTATIC_%s_MODULES_PROTO=%s' % (m, proto))
257 self.cflags = cflags
260 def check_duplicate_sources(bld, tgt_list):
261 '''see if we are compiling the same source file more than once'''
263 debug('deps: checking for duplicate sources')
264 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
266 for t in tgt_list:
267 source_list = TO_LIST(getattr(t, 'source', ''))
268 tpath = os.path.normpath(os.path.relpath(t.path.abspath(bld.env), t.env.BUILD_DIRECTORY + '/default'))
269 obj_sources = set()
270 for s in source_list:
271 if not isinstance(s, str):
272 print('strange path in check_duplicate_sources %r' % s)
273 s = s.abspath()
274 p = os.path.normpath(os.path.join(tpath, s))
275 if p in obj_sources:
276 Logs.error("ERROR: source %s appears twice in target '%s'" % (p, t.sname))
277 sys.exit(1)
278 obj_sources.add(p)
279 t.samba_source_set = obj_sources
281 subsystems = {}
283 # build a list of targets that each source file is part of
284 for t in tgt_list:
285 if not targets[t.sname] in [ 'LIBRARY', 'PLUGIN', 'BINARY', 'PYTHON' ]:
286 continue
287 for obj in t.add_objects:
288 t2 = t.bld.get_tgen_by_name(obj)
289 source_set = getattr(t2, 'samba_source_set', set())
290 for s in source_set:
291 if not s in subsystems:
292 subsystems[s] = {}
293 if not t.sname in subsystems[s]:
294 subsystems[s][t.sname] = []
295 subsystems[s][t.sname].append(t2.sname)
297 for s in subsystems:
298 if len(subsystems[s]) > 1 and Options.options.SHOW_DUPLICATES:
299 Logs.warn("WARNING: source %s is in more than one target: %s" % (s, subsystems[s].keys()))
300 for tname in subsystems[s]:
301 if len(subsystems[s][tname]) > 1:
302 raise Errors.WafError("ERROR: source %s is in more than one subsystem of target '%s': %s" % (s, tname, subsystems[s][tname]))
304 return True
306 def check_group_ordering(bld, tgt_list):
307 '''see if we have any dependencies that violate the group ordering
309 It is an error for a target to depend on a target from a later
310 build group
313 def group_name(g):
314 tm = bld.task_manager
315 return [x for x in tm.groups_names if id(tm.groups_names[x]) == id(g)][0]
317 for g in bld.task_manager.groups:
318 gname = group_name(g)
319 for t in g.tasks_gen:
320 t.samba_group = gname
322 grp_map = {}
323 idx = 0
324 for g in bld.task_manager.groups:
325 name = group_name(g)
326 grp_map[name] = idx
327 idx += 1
329 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
331 ret = True
332 for t in tgt_list:
333 tdeps = getattr(t, 'add_objects', []) + getattr(t, 'uselib_local', [])
334 for d in tdeps:
335 t2 = bld.get_tgen_by_name(d)
336 if t2 is None:
337 continue
338 map1 = grp_map[t.samba_group]
339 map2 = grp_map[t2.samba_group]
341 if map2 > map1:
342 Logs.error("Target %r in build group %r depends on target %r from later build group %r" % (
343 t.sname, t.samba_group, t2.sname, t2.samba_group))
344 ret = False
346 return ret
347 Build.BuildContext.check_group_ordering = check_group_ordering
349 def show_final_deps(bld, tgt_list):
350 '''show the final dependencies for all targets'''
352 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
354 for t in tgt_list:
355 if not targets[t.sname] in ['LIBRARY', 'PLUGIN', 'BINARY', 'PYTHON', 'SUBSYSTEM', 'BUILTIN']:
356 continue
357 debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
358 t.sname, t.uselib, getattr(t, 'uselib_local', []), getattr(t, 'add_objects', []))
361 def add_samba_attributes(bld, tgt_list):
362 '''ensure a target has the required samba attributes'''
364 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
366 for t in tgt_list:
367 if t.name != '':
368 t.sname = t.name
369 else:
370 t.sname = t.target
371 t.samba_type = targets[t.sname]
372 t.samba_abspath = t.path.abspath(bld.env)
373 t.samba_deps_extended = t.samba_deps[:]
374 t.samba_includes_extended = TO_LIST(t.samba_includes)[:]
375 t.cflags = getattr(t, 'samba_cflags', '')
377 def replace_builtin_subsystem_deps(bld, tgt_list):
378 '''replace dependencies based on builtin subsystems/libraries
382 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
384 # If either the target or the dependency require builtin linking
385 # we should replace the dependency
386 for t in tgt_list:
387 t_require_builtin_deps = getattr(t, 'samba_require_builtin_deps', False)
388 if t_require_builtin_deps:
389 debug("deps: target %s: requires builtin dependencies..." % (t.sname))
390 else:
391 debug("deps: target %s: does not require builtin dependencies..." % (t.sname))
393 replacing = {}
395 for dep in t.samba_deps_extended:
396 bld.ASSERT(dep in targets, "target %s: dependency target %s not declared" % (t.sname, dep))
397 dtype = targets[dep]
398 bld.ASSERT(dtype != 'BUILTIN', "target %s: dependency target %s is BUILTIN" % (t.sname, dep))
399 bld.ASSERT(dtype != 'PLUGIN', "target %s: dependency target %s is PLUGIN" % (t.sname, dep))
400 if dtype not in ['SUBSYSTEM', 'LIBRARY']:
401 debug("deps: target %s: keep %s dependency %s" % (t.sname, dtype, dep))
402 continue
403 dt = bld.get_tgen_by_name(dep)
404 bld.ASSERT(dt is not None, "target %s: dependency target %s not found by name" % (t.sname, dep))
405 dt_require_builtin_deps = getattr(dt, 'samba_require_builtin_deps', False)
406 if not dt_require_builtin_deps and not t_require_builtin_deps:
407 # both target and dependency don't require builtin linking
408 continue
409 sdt = getattr(dt, 'samba_builtin_subsystem', None)
410 if not t_require_builtin_deps:
411 if sdt is None:
412 debug("deps: target %s: dependency %s requires builtin deps only" % (t.sname, dep))
413 continue
414 debug("deps: target %s: dependency %s requires builtin linking" % (t.sname, dep))
415 bld.ASSERT(sdt is not None, "target %s: dependency target %s is missing samba_builtin_subsystem" % (t.sname, dep))
416 sdep = sdt.sname
417 bld.ASSERT(sdep in targets, "target %s: builtin dependency target %s (from %s) not declared" % (t.sname, sdep, dep))
418 sdt = targets[sdep]
419 bld.ASSERT(sdt == 'BUILTIN', "target %s: builtin dependency target %s (from %s) is not BUILTIN" % (t.sname, sdep, dep))
420 replacing[dep] = sdep
422 for i in range(len(t.samba_deps_extended)):
423 dep = t.samba_deps_extended[i]
424 if dep in replacing:
425 sdep = replacing[dep]
426 debug("deps: target %s: replacing dependency %s with builtin subsystem %s" % (t.sname, dep, sdep))
427 t.samba_deps_extended[i] = sdep
429 def replace_grouping_libraries(bld, tgt_list):
430 '''replace dependencies based on grouping libraries
432 If a library is marked as a grouping library, then any target that
433 depends on a subsystem that is part of that grouping library gets
434 that dependency replaced with a dependency on the grouping library
437 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
439 grouping = {}
441 # find our list of grouping libraries, mapped from the subsystems they depend on
442 for t in tgt_list:
443 if not getattr(t, 'grouping_library', False):
444 continue
445 for dep in t.samba_deps_extended:
446 bld.ASSERT(dep in targets, "grouping library target %s not declared in %s" % (dep, t.sname))
447 if targets[dep] == 'SUBSYSTEM':
448 grouping[dep] = t.sname
450 # now replace any dependencies on elements of grouping libraries
451 for t in tgt_list:
452 for i in range(len(t.samba_deps_extended)):
453 dep = t.samba_deps_extended[i]
454 if dep in grouping:
455 if t.sname != grouping[dep]:
456 debug("deps: target %s: replacing dependency %s with grouping library %s" % (t.sname, dep, grouping[dep]))
457 t.samba_deps_extended[i] = grouping[dep]
461 def build_direct_deps(bld, tgt_list):
462 '''build the direct_objects and direct_libs sets for each target'''
464 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
465 syslib_deps = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
467 global_deps = bld.env.GLOBAL_DEPENDENCIES
468 global_deps_exclude = set()
469 for dep in global_deps:
470 t = bld.get_tgen_by_name(dep)
471 for d in t.samba_deps:
472 # prevent loops from the global dependencies list
473 global_deps_exclude.add(d)
474 global_deps_exclude.add(d + '.objlist')
476 for t in tgt_list:
477 t.direct_objects = set()
478 t.direct_libs = set()
479 t.direct_syslibs = set()
480 deps = t.samba_deps_extended[:]
481 if getattr(t, 'samba_use_global_deps', False) and not t.sname in global_deps_exclude:
482 deps.extend(global_deps)
483 for d in deps:
484 if d == t.sname: continue
485 if not d in targets:
486 Logs.error("Unknown dependency '%s' in '%s'" % (d, t.sname))
487 sys.exit(1)
488 if targets[d] in [ 'EMPTY', 'DISABLED' ]:
489 continue
490 if targets[d] == 'PYTHON' and targets[t.sname] != 'PYTHON' and t.sname.find('.objlist') == -1:
491 # this check should be more restrictive, but for now we have pidl-generated python
492 # code that directly depends on other python modules
493 Logs.error('ERROR: Target %s has dependency on python module %s' % (t.sname, d))
494 sys.exit(1)
495 if targets[d] == 'SYSLIB':
496 t.direct_syslibs.add(d)
497 if d in syslib_deps:
498 for implied in TO_LIST(syslib_deps[d]):
499 if targets[implied] == 'SUBSYSTEM':
500 it = bld.get_tgen_by_name(implied)
501 sit = getattr(it, 'samba_builtin_subsystem', None)
502 if sit:
503 implied = sit.sname
504 if targets[implied] == 'BUILTIN':
505 t.direct_objects.add(implied)
506 elif targets[implied] == 'SYSLIB':
507 t.direct_syslibs.add(implied)
508 elif targets[implied] in ['LIBRARY', 'MODULE']:
509 t.direct_libs.add(implied)
510 else:
511 Logs.error('Implied dependency %s in %s is of type %s' % (
512 implied, t.sname, targets[implied]))
513 sys.exit(1)
514 continue
515 t2 = bld.get_tgen_by_name(d)
516 if t2 is None:
517 Logs.error("no task %s of type %s in %s" % (d, targets[d], t.sname))
518 sys.exit(1)
519 if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
520 t.direct_libs.add(d)
521 elif t2.samba_type in [ 'SUBSYSTEM', 'BUILTIN', 'ASN1', 'PYTHON' ]:
522 t.direct_objects.add(d)
523 elif t2.samba_type in [ 'PLUGIN' ]:
524 Logs.error('Implicit dependency %s in %s is of type %s' % (
525 d, t.sname, t2.samba_type))
526 sys.exit(1)
528 debug('deps: built direct dependencies')
531 def dependency_loop(loops, t, target):
532 '''add a dependency loop to the loops dictionary'''
533 if t.sname == target:
534 return
535 if not target in loops:
536 loops[target] = set()
537 if not t.sname in loops[target]:
538 loops[target].add(t.sname)
541 def indirect_libs(bld, t, chain, loops):
542 '''recursively calculate the indirect library dependencies for a target
544 An indirect library is a library that results from a dependency on
545 a subsystem
548 ret = getattr(t, 'indirect_libs', None)
549 if ret is not None:
550 return ret
552 ret = set()
553 for obj in t.direct_objects:
554 if obj in chain:
555 dependency_loop(loops, t, obj)
556 continue
557 chain.add(obj)
558 t2 = bld.get_tgen_by_name(obj)
559 r2 = indirect_libs(bld, t2, chain, loops)
560 chain.remove(obj)
561 ret = ret.union(t2.direct_libs)
562 ret = ret.union(r2)
564 for obj in indirect_objects(bld, t, set(), loops):
565 if obj in chain:
566 dependency_loop(loops, t, obj)
567 continue
568 chain.add(obj)
569 t2 = bld.get_tgen_by_name(obj)
570 r2 = indirect_libs(bld, t2, chain, loops)
571 chain.remove(obj)
572 ret = ret.union(t2.direct_libs)
573 ret = ret.union(r2)
575 t.indirect_libs = ret
577 return ret
580 def indirect_objects(bld, t, chain, loops):
581 '''recursively calculate the indirect object dependencies for a target
583 indirect objects are the set of objects from expanding the
584 subsystem dependencies
587 ret = getattr(t, 'indirect_objects', None)
588 if ret is not None: return ret
590 ret = set()
591 for lib in t.direct_objects:
592 if lib in chain:
593 dependency_loop(loops, t, lib)
594 continue
595 chain.add(lib)
596 t2 = bld.get_tgen_by_name(lib)
597 r2 = indirect_objects(bld, t2, chain, loops)
598 chain.remove(lib)
599 ret = ret.union(t2.direct_objects)
600 ret = ret.union(r2)
602 t.indirect_objects = ret
603 return ret
606 def extended_objects(bld, t, chain):
607 '''recursively calculate the extended object dependencies for a target
609 extended objects are the union of:
610 - direct objects
611 - indirect objects
612 - direct and indirect objects of all direct and indirect libraries
615 ret = getattr(t, 'extended_objects', None)
616 if ret is not None: return ret
618 ret = set()
619 ret = ret.union(t.final_objects)
621 for lib in t.final_libs:
622 if lib in chain:
623 continue
624 t2 = bld.get_tgen_by_name(lib)
625 chain.add(lib)
626 r2 = extended_objects(bld, t2, chain)
627 chain.remove(lib)
628 ret = ret.union(t2.final_objects)
629 ret = ret.union(r2)
631 t.extended_objects = ret
632 return ret
635 def includes_objects(bld, t, chain, inc_loops):
636 '''recursively calculate the includes object dependencies for a target
638 includes dependencies come from either library or object dependencies
640 ret = getattr(t, 'includes_objects', None)
641 if ret is not None:
642 return ret
644 ret = t.direct_objects.copy()
645 ret = ret.union(t.direct_libs)
647 for obj in t.direct_objects:
648 if obj in chain:
649 dependency_loop(inc_loops, t, obj)
650 continue
651 chain.add(obj)
652 t2 = bld.get_tgen_by_name(obj)
653 r2 = includes_objects(bld, t2, chain, inc_loops)
654 chain.remove(obj)
655 ret = ret.union(t2.direct_objects)
656 ret = ret.union(r2)
658 for lib in t.direct_libs:
659 if lib in chain:
660 dependency_loop(inc_loops, t, lib)
661 continue
662 chain.add(lib)
663 t2 = bld.get_tgen_by_name(lib)
664 if t2 is None:
665 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
666 Logs.error('Target %s of type %s not found in direct_libs for %s' % (
667 lib, targets[lib], t.sname))
668 sys.exit(1)
669 r2 = includes_objects(bld, t2, chain, inc_loops)
670 chain.remove(lib)
671 ret = ret.union(t2.direct_objects)
672 ret = ret.union(r2)
674 t.includes_objects = ret
675 return ret
678 def break_dependency_loops(bld, tgt_list):
679 '''find and break dependency loops'''
680 loops = {}
681 inc_loops = {}
683 # build up the list of loops
684 for t in tgt_list:
685 indirect_objects(bld, t, set(), loops)
686 indirect_libs(bld, t, set(), loops)
687 includes_objects(bld, t, set(), inc_loops)
689 # break the loops
690 for t in tgt_list:
691 if t.sname in loops:
692 for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
693 objs = getattr(t, attr, set())
694 setattr(t, attr, objs.difference(loops[t.sname]))
696 for loop in loops:
697 debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
699 for loop in inc_loops:
700 debug('deps: Found include loops for target %s : %s', loop, inc_loops[loop])
702 # expand the loops mapping by one level
703 for loop in loops.copy():
704 for tgt in loops[loop]:
705 if tgt in loops:
706 loops[loop] = loops[loop].union(loops[tgt])
708 for loop in inc_loops.copy():
709 for tgt in inc_loops[loop]:
710 if tgt in inc_loops:
711 inc_loops[loop] = inc_loops[loop].union(inc_loops[tgt])
714 # expand indirect subsystem and library loops
715 for loop in loops.copy():
716 t = bld.get_tgen_by_name(loop)
717 if t.samba_type in ['SUBSYSTEM', 'BUILTIN']:
718 loops[loop] = loops[loop].union(t.indirect_objects)
719 loops[loop] = loops[loop].union(t.direct_objects)
720 if t.samba_type in ['LIBRARY', 'PLUGIN', 'PYTHON']:
721 loops[loop] = loops[loop].union(t.indirect_libs)
722 loops[loop] = loops[loop].union(t.direct_libs)
723 if loop in loops[loop]:
724 loops[loop].remove(loop)
726 # expand indirect includes loops
727 for loop in inc_loops.copy():
728 t = bld.get_tgen_by_name(loop)
729 inc_loops[loop] = inc_loops[loop].union(t.includes_objects)
730 if loop in inc_loops[loop]:
731 inc_loops[loop].remove(loop)
733 # add in the replacement dependencies
734 for t in tgt_list:
735 for loop in loops:
736 for attr in ['indirect_objects', 'indirect_libs']:
737 objs = getattr(t, attr, set())
738 if loop in objs:
739 diff = loops[loop].difference(objs)
740 if t.sname in diff:
741 diff.remove(t.sname)
742 if diff:
743 debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
744 objs = objs.union(diff)
745 setattr(t, attr, objs)
747 for loop in inc_loops:
748 objs = getattr(t, 'includes_objects', set())
749 if loop in objs:
750 diff = inc_loops[loop].difference(objs)
751 if t.sname in diff:
752 diff.remove(t.sname)
753 if diff:
754 debug('deps: Expanded target %s includes of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
755 objs = objs.union(diff)
756 setattr(t, 'includes_objects', objs)
759 def reduce_objects(bld, tgt_list):
760 '''reduce objects by looking for indirect object dependencies'''
761 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
763 rely_on = {}
765 for t in tgt_list:
766 t.extended_objects = None
768 changed = False
770 for type in ['BINARY', 'PYTHON', 'LIBRARY', 'PLUGIN']:
771 for t in tgt_list:
772 if t.samba_type != type: continue
773 # if we will indirectly link to a target then we don't need it
774 new = t.final_objects.copy()
775 for l in t.final_libs:
776 t2 = bld.get_tgen_by_name(l)
777 t2_obj = extended_objects(bld, t2, set())
778 dup = new.intersection(t2_obj)
779 if t.sname in rely_on:
780 dup = dup.difference(rely_on[t.sname])
781 if dup:
782 # Do not remove duplicates of BUILTINS
783 for d in iter(dup.copy()):
784 dtype = targets[d]
785 if dtype == 'BUILTIN':
786 debug('deps: BUILTIN SKIP: removing dups from %s of type %s: %s also in %s %s',
787 t.sname, t.samba_type, d, t2.samba_type, l)
788 dup.remove(d)
789 if len(dup) == 0:
790 continue
792 debug('deps: removing dups from %s of type %s: %s also in %s %s',
793 t.sname, t.samba_type, dup, t2.samba_type, l)
794 new = new.difference(dup)
795 changed = True
796 if not l in rely_on:
797 rely_on[l] = set()
798 rely_on[l] = rely_on[l].union(dup)
799 for n in iter(new.copy()):
800 # if we got the builtin version as well
801 # as the native one, we keep using the
802 # builtin one and remove the rest.
803 # Otherwise our check_duplicate_sources()
804 # checks would trigger!
805 if n.endswith('.builtin.objlist'):
806 unused = n.replace('.builtin.objlist', '.objlist')
807 if unused in new:
808 new.remove(unused)
809 unused = n.replace('.builtin.objlist', '')
810 if unused in new:
811 new.remove(unused)
812 t.final_objects = new
814 if not changed:
815 return False
817 # add back in any objects that were relied upon by the reduction rules
818 for r in rely_on:
819 t = bld.get_tgen_by_name(r)
820 t.final_objects = t.final_objects.union(rely_on[r])
822 return True
825 def show_library_loop(bld, lib1, lib2, path, seen):
826 '''show the detailed path of a library loop between lib1 and lib2'''
828 t = bld.get_tgen_by_name(lib1)
829 if not lib2 in getattr(t, 'final_libs', set()):
830 return
832 for d in t.samba_deps_extended:
833 if d in seen:
834 continue
835 seen.add(d)
836 path2 = path + '=>' + d
837 if d == lib2:
838 Logs.warn('library loop path: ' + path2)
839 return
840 show_library_loop(bld, d, lib2, path2, seen)
841 seen.remove(d)
844 def calculate_final_deps(bld, tgt_list, loops):
845 '''calculate the final library and object dependencies'''
846 for t in tgt_list:
847 # start with the maximum possible list
848 t.final_libs = t.direct_libs.union(indirect_libs(bld, t, set(), loops))
849 t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops))
851 for t in tgt_list:
852 # don't depend on ourselves
853 if t.sname in t.final_libs:
854 t.final_libs.remove(t.sname)
855 if t.sname in t.final_objects:
856 t.final_objects.remove(t.sname)
858 # handle any non-shared binaries
859 for t in tgt_list:
860 if t.samba_type == 'BINARY' and bld.NONSHARED_BINARY(t.sname):
861 subsystem_list = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
862 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
864 # replace lib deps with objlist deps
865 for l in t.final_libs:
866 objname = l + '.objlist'
867 t2 = bld.get_tgen_by_name(objname)
868 if t2 is None:
869 Logs.error('ERROR: subsystem %s not found' % objname)
870 sys.exit(1)
871 t.final_objects.add(objname)
872 t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
873 if l in subsystem_list:
874 # its a subsystem - we also need the contents of any modules
875 for d in subsystem_list[l]:
876 module_name = d['TARGET']
877 if targets[module_name] == 'LIBRARY':
878 objname = module_name + '.objlist'
879 elif targets[module_name] == 'SUBSYSTEM':
880 objname = module_name
881 else:
882 continue
883 t2 = bld.get_tgen_by_name(objname)
884 if t2 is None:
885 Logs.error('ERROR: subsystem %s not found' % objname)
886 sys.exit(1)
887 t.final_objects.add(objname)
888 t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
889 t.final_libs = set()
891 # find any library loops
892 for t in tgt_list:
893 if t.samba_type in ['LIBRARY', 'PYTHON']:
894 for l in t.final_libs.copy():
895 t2 = bld.get_tgen_by_name(l)
896 if t.sname in t2.final_libs:
897 if getattr(bld.env, "ALLOW_CIRCULAR_LIB_DEPENDENCIES", False):
898 # we could break this in either direction. If one of the libraries
899 # has a version number, and will this be distributed publicly, then
900 # we should make it the lower level library in the DAG
901 Logs.warn('deps: removing library loop %s from %s' % (t.sname, t2.sname))
902 dependency_loop(loops, t, t2.sname)
903 t2.final_libs.remove(t.sname)
904 else:
905 Logs.error('ERROR: circular library dependency between %s and %s'
906 % (t.sname, t2.sname))
907 show_library_loop(bld, t.sname, t2.sname, t.sname, set())
908 show_library_loop(bld, t2.sname, t.sname, t2.sname, set())
909 sys.exit(1)
911 for loop in loops:
912 debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
914 # we now need to make corrections for any library loops we broke up
915 # any target that depended on the target of the loop and doesn't
916 # depend on the source of the loop needs to get the loop source added
917 for type in ['BINARY','PYTHON','LIBRARY','PLUGIN','BINARY']:
918 for t in tgt_list:
919 if t.samba_type != type: continue
920 for loop in loops:
921 if loop in t.final_libs:
922 diff = loops[loop].difference(t.final_libs)
923 if t.sname in diff:
924 diff.remove(t.sname)
925 if t.sname in diff:
926 diff.remove(t.sname)
927 # make sure we don't recreate the loop again!
928 for d in diff.copy():
929 t2 = bld.get_tgen_by_name(d)
930 if t2.samba_type == 'LIBRARY':
931 if t.sname in t2.final_libs:
932 debug('deps: removing expansion %s from %s', d, t.sname)
933 diff.remove(d)
934 if diff:
935 debug('deps: Expanded target %s by loop %s libraries (loop %s) %s', t.sname, loop,
936 loops[loop], diff)
937 t.final_libs = t.final_libs.union(diff)
939 # remove objects that are also available in linked libs
940 count = 0
941 while reduce_objects(bld, tgt_list):
942 count += 1
943 if count > 100:
944 Logs.warn("WARNING: Unable to remove all inter-target object duplicates")
945 break
946 debug('deps: Object reduction took %u iterations', count)
948 # add in any syslib dependencies
949 for t in tgt_list:
950 if not t.samba_type in ['BINARY','PYTHON','LIBRARY','PLUGIN','SUBSYSTEM','BUILTIN']:
951 continue
952 syslibs = set()
953 for d in t.final_objects:
954 t2 = bld.get_tgen_by_name(d)
955 syslibs = syslibs.union(t2.direct_syslibs)
956 # this adds the indirect syslibs as well, which may not be needed
957 # depending on the linker flags
958 for d in t.final_libs:
959 t2 = bld.get_tgen_by_name(d)
960 syslibs = syslibs.union(t2.direct_syslibs)
961 t.final_syslibs = syslibs
964 # find any unresolved library loops
965 lib_loop_error = False
966 for t in tgt_list:
967 if t.samba_type in ['LIBRARY', 'PLUGIN', 'PYTHON']:
968 for l in t.final_libs.copy():
969 t2 = bld.get_tgen_by_name(l)
970 if t.sname in t2.final_libs:
971 Logs.error('ERROR: Unresolved library loop %s from %s' % (t.sname, t2.sname))
972 lib_loop_error = True
973 if lib_loop_error:
974 sys.exit(1)
976 debug('deps: removed duplicate dependencies')
979 def show_dependencies(bld, target, seen):
980 '''recursively show the dependencies of target'''
982 if target in seen:
983 return
985 t = bld.get_tgen_by_name(target)
986 if t is None:
987 Logs.error("ERROR: Unable to find target '%s'" % target)
988 sys.exit(1)
990 Logs.info('%s(OBJECTS): %s' % (target, t.direct_objects))
991 Logs.info('%s(LIBS): %s' % (target, t.direct_libs))
992 Logs.info('%s(SYSLIBS): %s' % (target, t.direct_syslibs))
994 seen.add(target)
996 for t2 in t.direct_objects:
997 show_dependencies(bld, t2, seen)
1000 def show_object_duplicates(bld, tgt_list):
1001 '''show a list of object files that are included in more than
1002 one library or binary'''
1004 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
1006 used_by = {}
1008 Logs.info("showing duplicate objects")
1010 for t in tgt_list:
1011 if not targets[t.sname] in [ 'LIBRARY', 'PYTHON' ]:
1012 continue
1013 for n in getattr(t, 'final_objects', set()):
1014 t2 = bld.get_tgen_by_name(n)
1015 if not n in used_by:
1016 used_by[n] = set()
1017 used_by[n].add(t.sname)
1019 for n in used_by:
1020 if len(used_by[n]) > 1:
1021 Logs.info("target '%s' is used by %s" % (n, used_by[n]))
1023 Logs.info("showing indirect dependency counts (sorted by count)")
1025 def indirect_count(t):
1026 return len(t.indirect_objects)
1028 sorted_list = sorted(tgt_list, key=indirect_count, reverse=True)
1029 for t in sorted_list:
1030 if len(t.indirect_objects) > 1:
1031 Logs.info("%s depends on %u indirect objects" % (t.sname, len(t.indirect_objects)))
1034 ######################################################################
1035 # this provides a way to save our dependency calculations between runs
1036 savedeps_version = 3
1037 savedeps_inputs = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags',
1038 'source', 'grouping_library', 'samba_ldflags', 'allow_undefined_symbols',
1039 'use_global_deps', 'global_include' ]
1040 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes',
1041 'cflags', 'ldflags', 'samba_deps_extended', 'final_libs']
1042 savedeps_outenv = ['INC_PATHS']
1043 savedeps_envvars = ['NONSHARED_BINARIES', 'GLOBAL_DEPENDENCIES', 'EXTRA_CFLAGS', 'EXTRA_LDFLAGS', 'EXTRA_INCLUDES' ]
1044 savedeps_caches = ['GLOBAL_DEPENDENCIES', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
1045 savedeps_files = ['buildtools/wafsamba/samba_deps.py']
1047 def save_samba_deps(bld, tgt_list):
1048 '''save the dependency calculations between builds, to make
1049 further builds faster'''
1050 denv = ConfigSet.ConfigSet()
1052 denv.version = savedeps_version
1053 denv.savedeps_inputs = savedeps_inputs
1054 denv.savedeps_outputs = savedeps_outputs
1055 denv.input = {}
1056 denv.output = {}
1057 denv.outenv = {}
1058 denv.caches = {}
1059 denv.envvar = {}
1060 denv.files = {}
1062 for f in savedeps_files:
1063 denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
1065 for c in savedeps_caches:
1066 denv.caches[c] = LOCAL_CACHE(bld, c)
1068 for e in savedeps_envvars:
1069 denv.envvar[e] = bld.env[e]
1071 for t in tgt_list:
1072 # save all the input attributes for each target
1073 tdeps = {}
1074 for attr in savedeps_inputs:
1075 v = getattr(t, attr, None)
1076 if v is not None:
1077 tdeps[attr] = v
1078 if tdeps != {}:
1079 denv.input[t.sname] = tdeps
1081 # save all the output attributes for each target
1082 tdeps = {}
1083 for attr in savedeps_outputs:
1084 v = getattr(t, attr, None)
1085 if v is not None:
1086 tdeps[attr] = v
1087 if tdeps != {}:
1088 denv.output[t.sname] = tdeps
1090 tdeps = {}
1091 for attr in savedeps_outenv:
1092 if attr in t.env:
1093 tdeps[attr] = t.env[attr]
1094 if tdeps != {}:
1095 denv.outenv[t.sname] = tdeps
1097 depsfile = os.path.join(bld.cache_dir, "sambadeps")
1098 denv.store_fast(depsfile)
1102 def load_samba_deps(bld, tgt_list):
1103 '''load a previous set of build dependencies if possible'''
1104 depsfile = os.path.join(bld.cache_dir, "sambadeps")
1105 denv = ConfigSet.ConfigSet()
1106 try:
1107 debug('deps: checking saved dependencies')
1108 denv.load_fast(depsfile)
1109 if (denv.version != savedeps_version or
1110 denv.savedeps_inputs != savedeps_inputs or
1111 denv.savedeps_outputs != savedeps_outputs):
1112 return False
1113 except Exception:
1114 return False
1116 # check if critical files have changed
1117 for f in savedeps_files:
1118 if f not in denv.files:
1119 return False
1120 if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
1121 return False
1123 # check if caches are the same
1124 for c in savedeps_caches:
1125 if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
1126 return False
1128 # check if caches are the same
1129 for e in savedeps_envvars:
1130 if e not in denv.envvar or denv.envvar[e] != bld.env[e]:
1131 return False
1133 # check inputs are the same
1134 for t in tgt_list:
1135 tdeps = {}
1136 for attr in savedeps_inputs:
1137 v = getattr(t, attr, None)
1138 if v is not None:
1139 tdeps[attr] = v
1140 if t.sname in denv.input:
1141 olddeps = denv.input[t.sname]
1142 else:
1143 olddeps = {}
1144 if tdeps != olddeps:
1145 #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
1146 return False
1148 # put outputs in place
1149 for t in tgt_list:
1150 if not t.sname in denv.output: continue
1151 tdeps = denv.output[t.sname]
1152 for a in tdeps:
1153 setattr(t, a, tdeps[a])
1155 # put output env vars in place
1156 for t in tgt_list:
1157 if not t.sname in denv.outenv: continue
1158 tdeps = denv.outenv[t.sname]
1159 for a in tdeps:
1160 t.env[a] = tdeps[a]
1162 debug('deps: loaded saved dependencies')
1163 return True
1166 def generate_clangdb(bld):
1167 classes = []
1168 for x in ('c', 'cxx'):
1169 cls = Task.classes.get(x)
1170 if cls:
1171 classes.append(cls)
1172 task_classes = tuple(classes)
1174 tasks = []
1175 for g in bld.groups:
1176 for tg in g:
1177 if isinstance(tg, Task.Task):
1178 lst = [tg]
1179 else:
1180 lst = tg.tasks
1181 for task in lst:
1182 try:
1183 task.last_cmd
1184 except AttributeError:
1185 continue
1186 if isinstance(task, task_classes):
1187 tasks.append(task)
1188 if len(tasks) == 0:
1189 return
1191 database_file = bld.bldnode.make_node('compile_commands.json')
1192 Logs.info('Build commands will be stored in %s',
1193 database_file.path_from(bld.path))
1194 try:
1195 root = database_file.read_json()
1196 except IOError:
1197 root = []
1198 clang_db = dict((x['file'], x) for x in root)
1199 for task in tasks:
1200 f_node = task.inputs[0]
1201 cmd = task.last_cmd
1202 filename = f_node.path_from(task.get_cwd())
1203 entry = {
1204 "directory": task.get_cwd().abspath(),
1205 "arguments": cmd,
1206 "file": filename,
1208 clang_db[filename] = entry
1209 root = list(clang_db.values())
1210 database_file.write_json(root)
1213 def check_project_rules(bld):
1214 '''check the project rules - ensuring the targets are sane'''
1216 loops = {}
1217 inc_loops = {}
1219 tgt_list = get_tgt_list(bld)
1221 add_samba_attributes(bld, tgt_list)
1223 force_project_rules = (Options.options.SHOWDEPS or
1224 Options.options.SHOW_DUPLICATES)
1226 if not force_project_rules and load_samba_deps(bld, tgt_list):
1227 return
1229 timer = Utils.Timer()
1231 bld.new_rules = True
1232 Logs.info("Checking project rules ...")
1234 debug('deps: project rules checking started')
1236 replace_builtin_subsystem_deps(bld, tgt_list)
1238 debug("deps: replace_builtin_subsystem_deps: %s" % str(timer))
1240 expand_subsystem_deps(bld)
1242 debug("deps: expand_subsystem_deps: %s" % str(timer))
1244 replace_grouping_libraries(bld, tgt_list)
1246 debug("deps: replace_grouping_libraries: %s" % str(timer))
1248 build_direct_deps(bld, tgt_list)
1250 debug("deps: build_direct_deps: %s" % str(timer))
1252 break_dependency_loops(bld, tgt_list)
1254 debug("deps: break_dependency_loops: %s" % str(timer))
1256 if Options.options.SHOWDEPS:
1257 show_dependencies(bld, Options.options.SHOWDEPS, set())
1259 calculate_final_deps(bld, tgt_list, loops)
1261 debug("deps: calculate_final_deps: %s" % str(timer))
1263 if Options.options.SHOW_DUPLICATES:
1264 show_object_duplicates(bld, tgt_list)
1266 # run the various attribute generators
1267 for f in [ build_dependencies, build_includes, add_init_functions ]:
1268 debug('deps: project rules checking %s', f)
1269 for t in tgt_list: f(t)
1270 debug("deps: %s: %s" % (f, str(timer)))
1272 debug('deps: project rules stage1 completed')
1274 if not check_duplicate_sources(bld, tgt_list):
1275 Logs.error("Duplicate sources present - aborting")
1276 sys.exit(1)
1278 debug("deps: check_duplicate_sources: %s" % str(timer))
1280 if not bld.check_group_ordering(tgt_list):
1281 Logs.error("Bad group ordering - aborting")
1282 sys.exit(1)
1284 debug("deps: check_group_ordering: %s" % str(timer))
1286 show_final_deps(bld, tgt_list)
1288 debug("deps: show_final_deps: %s" % str(timer))
1290 debug('deps: project rules checking completed - %u targets checked',
1291 len(tgt_list))
1293 if not bld.is_install:
1294 save_samba_deps(bld, tgt_list)
1296 debug("deps: save_samba_deps: %s" % str(timer))
1298 Logs.info("Project rules pass")
1300 if bld.cmd == 'build':
1301 Task.Task.keep_last_cmd = True
1302 bld.add_post_fun(generate_clangdb)
1305 def CHECK_PROJECT_RULES(bld):
1306 '''enable checking of project targets for sanity'''
1307 if bld.env.added_project_rules:
1308 return
1309 bld.env.added_project_rules = True
1310 bld.add_pre_fun(check_project_rules)
1311 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES