1 # Samba automatic dependency handling and project rules
5 from waflib import Build, Options, Logs, Utils, Errors, Scripting
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
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)
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
28 def SET_SYSLIB_DEPS(conf, target, deps):
29 '''setup some implied dependencies for a SYSLIB'''
30 cache = LOCAL_CACHE(conf, 'SYSLIB_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':
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']:
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.
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 libs = self.final_syslibs.copy()
88 (cflags, ldflags, cpppath) = library_flags(self, list(libs))
89 new_ldflags = getattr(self, 'samba_ldflags', [])[:]
90 new_ldflags.extend(ldflags)
91 self.ldflags = new_ldflags
93 if getattr(self, 'allow_undefined_symbols', False) and self.env.undefined_ldflags:
94 for f in self.env.undefined_ldflags:
95 self.ldflags.remove(f)
97 if getattr(self, 'allow_undefined_symbols', False) and self.env.undefined_ignore_ldflags:
98 for f in self.env.undefined_ignore_ldflags:
99 self.ldflags.append(f)
101 debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
102 self.sname, self.uselib, self.uselib_local, self.add_objects)
104 if self.samba_type in ['SUBSYSTEM', 'BUILTIN']:
105 # this is needed for the cflags of libs that come from pkg_config
106 self.uselib = list(self.final_syslibs)
107 self.uselib.extend(list(self.direct_syslibs))
108 for lib in self.final_libs:
109 t = self.bld.get_tgen_by_name(lib)
110 self.uselib.extend(list(t.final_syslibs))
111 self.uselib = unique_list(self.uselib)
113 if getattr(self, 'uselib', None):
115 for l in self.uselib:
116 up_list.append(l.upper())
117 self.uselib = up_list
120 def build_includes(self):
121 '''This builds the right set of includes for a target.
123 One tricky part of this is that the includes= attribute for a
124 target needs to use paths which are relative to that targets
125 declaration directory (which we can get at via t.path).
127 The way this works is the includes list gets added as
128 samba_includes in the main build task declaration. Then this
129 function runs after all of the tasks are declared, and it
130 processes the samba_includes attribute to produce a includes=
134 if getattr(self, 'samba_includes', None) is None:
139 inc_deps = includes_objects(bld, self, set(), {})
143 # maybe add local includes
144 if getattr(self, 'local_include', True) and getattr(self, 'local_include_first', True):
147 includes.extend(self.samba_includes_extended)
149 if 'EXTRA_INCLUDES' in bld.env and getattr(self, 'global_include', True):
150 includes.extend(bld.env['EXTRA_INCLUDES'])
158 t = bld.get_tgen_by_name(d)
159 bld.ASSERT(t is not None, "Unable to find dependency %s for %s" % (d, self.sname))
160 inclist = getattr(t, 'samba_includes_extended', [])[:]
161 if getattr(t, 'local_include', True):
165 tpath = t.samba_abspath
167 npath = tpath + '/' + inc
168 if not npath in inc_set:
169 inc_abs.append(npath)
172 mypath = self.path.abspath(bld.env)
174 relpath = os.path.relpath(inc, mypath)
175 includes.append(relpath)
177 if getattr(self, 'local_include', True) and not getattr(self, 'local_include_first', True):
180 # now transform the includes list to be relative to the top directory
181 # which is represented by '#' in waf. This allows waf to cache the
182 # includes lists more efficiently
186 # some are already top based
187 includes_top.append(i)
189 absinc = os.path.join(self.path.abspath(), i)
190 relinc = os.path.relpath(absinc, self.bld.srcnode.abspath())
191 includes_top.append('#' + relinc)
193 self.includes = unique_list(includes_top)
194 debug('deps: includes for target %s: includes=%s',
195 self.sname, self.includes)
198 def add_init_functions(self):
199 '''This builds the right set of init functions'''
203 subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
205 # cope with the separated object lists from BINARY and LIBRARY targets
207 if sname.endswith('.objlist'):
211 if sname in subsystems:
212 modules.append(sname)
214 m = getattr(self, 'samba_modules', None)
216 modules.extend(TO_LIST(m))
218 m = getattr(self, 'samba_subsystem', None)
222 if 'pyembed' in self.features:
225 sentinel = getattr(self, 'init_function_sentinel', 'NULL')
227 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
228 cflags = getattr(self, 'samba_cflags', [])[:]
231 sname = sname.replace('-','_')
232 sname = sname.replace('.','_')
233 sname = sname.replace('/','_')
234 cflags.append('-DSTATIC_%s_MODULES=%s' % (sname, sentinel))
235 if sentinel == 'NULL':
236 proto = "extern void __%s_dummy_module_proto(void)" % (sname)
237 cflags.append('-DSTATIC_%s_MODULES_PROTO=%s' % (sname, proto))
242 bld.ASSERT(m in subsystems,
243 "No init_function defined for module '%s' in target '%s'" % (m, self.sname))
245 for d in subsystems[m]:
246 if targets[d['TARGET']] != 'DISABLED':
247 init_fn_list.append(d['INIT_FUNCTION'])
248 if init_fn_list == []:
249 cflags.append('-DSTATIC_%s_MODULES=%s' % (m, sentinel))
250 if sentinel == 'NULL':
251 proto = "extern void __%s_dummy_module_proto(void)" % (m)
252 cflags.append('-DSTATIC_%s_MODULES_PROTO=%s' % (m, proto))
254 cflags.append('-DSTATIC_%s_MODULES=%s' % (m, ','.join(init_fn_list) + ',' + sentinel))
255 proto = "".join('_MODULE_PROTO(%s)' % f for f in init_fn_list) +\
256 "extern void __%s_dummy_module_proto(void)" % (m)
257 cflags.append('-DSTATIC_%s_MODULES_PROTO=%s' % (m, proto))
261 def check_duplicate_sources(bld, tgt_list):
262 '''see if we are compiling the same source file more than once'''
264 debug('deps: checking for duplicate sources')
265 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
268 source_list = TO_LIST(getattr(t, 'source', ''))
269 tpath = os.path.normpath(os.path.relpath(t.path.abspath(bld.env), t.env.BUILD_DIRECTORY + '/default'))
271 for s in source_list:
272 if not isinstance(s, str):
273 print('strange path in check_duplicate_sources %r' % s)
275 p = os.path.normpath(os.path.join(tpath, s))
277 Logs.error("ERROR: source %s appears twice in target '%s'" % (p, t.sname))
280 t.samba_source_set = obj_sources
284 # build a list of targets that each source file is part of
286 if not targets[t.sname] in [ 'LIBRARY', 'PLUGIN', 'BINARY', 'PYTHON' ]:
288 for obj in t.add_objects:
289 t2 = t.bld.get_tgen_by_name(obj)
290 source_set = getattr(t2, 'samba_source_set', set())
292 if not s in subsystems:
294 if not t.sname in subsystems[s]:
295 subsystems[s][t.sname] = []
296 subsystems[s][t.sname].append(t2.sname)
299 if len(subsystems[s]) > 1 and Options.options.SHOW_DUPLICATES:
300 Logs.warn("WARNING: source %s is in more than one target: %s" % (s, subsystems[s].keys()))
301 for tname in subsystems[s]:
302 if len(subsystems[s][tname]) > 1:
303 raise Errors.WafError("ERROR: source %s is in more than one subsystem of target '%s': %s" % (s, tname, subsystems[s][tname]))
307 def check_group_ordering(bld, tgt_list):
308 '''see if we have any dependencies that violate the group ordering
310 It is an error for a target to depend on a target from a later
315 tm = bld.task_manager
316 return [x for x in tm.groups_names if id(tm.groups_names[x]) == id(g)][0]
318 for g in bld.task_manager.groups:
319 gname = group_name(g)
320 for t in g.tasks_gen:
321 t.samba_group = gname
325 for g in bld.task_manager.groups:
330 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
334 tdeps = getattr(t, 'add_objects', []) + getattr(t, 'uselib_local', [])
336 t2 = bld.get_tgen_by_name(d)
339 map1 = grp_map[t.samba_group]
340 map2 = grp_map[t2.samba_group]
343 Logs.error("Target %r in build group %r depends on target %r from later build group %r" % (
344 t.sname, t.samba_group, t2.sname, t2.samba_group))
348 Build.BuildContext.check_group_ordering = check_group_ordering
350 def show_final_deps(bld, tgt_list):
351 '''show the final dependencies for all targets'''
353 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
356 if not targets[t.sname] in ['LIBRARY', 'PLUGIN', 'BINARY', 'PYTHON', 'SUBSYSTEM', 'BUILTIN']:
358 debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
359 t.sname, t.uselib, getattr(t, 'uselib_local', []), getattr(t, 'add_objects', []))
362 def add_samba_attributes(bld, tgt_list):
363 '''ensure a target has a the required samba attributes'''
365 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
372 t.samba_type = targets[t.sname]
373 t.samba_abspath = t.path.abspath(bld.env)
374 t.samba_deps_extended = t.samba_deps[:]
375 t.samba_includes_extended = TO_LIST(t.samba_includes)[:]
376 t.cflags = getattr(t, 'samba_cflags', '')
378 def replace_builtin_subsystem_deps(bld, tgt_list):
379 '''replace dependencies based on builtin subsystems/libraries
383 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
385 # If either the target or the dependency require builtin linking
386 # we should replace the dependency
388 t_require_builtin_deps = getattr(t, 'samba_require_builtin_deps', False)
389 if t_require_builtin_deps:
390 debug("deps: target %s: requires builtin dependencies..." % (t.sname))
392 debug("deps: target %s: does not require builtin dependencies..." % (t.sname))
396 for dep in t.samba_deps_extended:
397 bld.ASSERT(dep in targets, "target %s: dependency target %s not declared" % (t.sname, dep))
399 bld.ASSERT(dtype != 'BUILTIN', "target %s: dependency target %s is BUILTIN" % (t.sname, dep))
400 bld.ASSERT(dtype != 'PLUGIN', "target %s: dependency target %s is PLUGIN" % (t.sname, dep))
401 if dtype not in ['SUBSYSTEM', 'LIBRARY']:
402 debug("deps: target %s: keep %s dependency %s" % (t.sname, dtype, dep))
404 dt = bld.get_tgen_by_name(dep)
405 bld.ASSERT(dt is not None, "target %s: dependency target %s not found by name" % (t.sname, dep))
406 dt_require_builtin_deps = getattr(dt, 'samba_require_builtin_deps', False)
407 if not dt_require_builtin_deps and not t_require_builtin_deps:
408 # both target and dependency don't require builtin linking
410 sdt = getattr(dt, 'samba_builtin_subsystem', None)
411 if not t_require_builtin_deps:
413 debug("deps: target %s: dependency %s requires builtin deps only" % (t.sname, dep))
415 debug("deps: target %s: dependency %s requires builtin linking" % (t.sname, dep))
416 bld.ASSERT(sdt is not None, "target %s: dependency target %s is missing samba_builtin_subsystem" % (t.sname, dep))
418 bld.ASSERT(sdep in targets, "target %s: builtin dependency target %s (from %s) not declared" % (t.sname, sdep, dep))
420 bld.ASSERT(sdt == 'BUILTIN', "target %s: builtin dependency target %s (from %s) is not BUILTIN" % (t.sname, sdep, dep))
421 replacing[dep] = sdep
423 for i in range(len(t.samba_deps_extended)):
424 dep = t.samba_deps_extended[i]
426 sdep = replacing[dep]
427 debug("deps: target %s: replacing dependency %s with builtin subsystem %s" % (t.sname, dep, sdep))
428 t.samba_deps_extended[i] = sdep
430 def replace_grouping_libraries(bld, tgt_list):
431 '''replace dependencies based on grouping libraries
433 If a library is marked as a grouping library, then any target that
434 depends on a subsystem that is part of that grouping library gets
435 that dependency replaced with a dependency on the grouping library
438 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
442 # find our list of grouping libraries, mapped from the subsystems they depend on
444 if not getattr(t, 'grouping_library', False):
446 for dep in t.samba_deps_extended:
447 bld.ASSERT(dep in targets, "grouping library target %s not declared in %s" % (dep, t.sname))
448 if targets[dep] == 'SUBSYSTEM':
449 grouping[dep] = t.sname
451 # now replace any dependencies on elements of grouping libraries
453 for i in range(len(t.samba_deps_extended)):
454 dep = t.samba_deps_extended[i]
456 if t.sname != grouping[dep]:
457 debug("deps: target %s: replacing dependency %s with grouping library %s" % (t.sname, dep, grouping[dep]))
458 t.samba_deps_extended[i] = grouping[dep]
462 def build_direct_deps(bld, tgt_list):
463 '''build the direct_objects and direct_libs sets for each target'''
465 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
466 syslib_deps = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
468 global_deps = bld.env.GLOBAL_DEPENDENCIES
469 global_deps_exclude = set()
470 for dep in global_deps:
471 t = bld.get_tgen_by_name(dep)
472 for d in t.samba_deps:
473 # prevent loops from the global dependencies list
474 global_deps_exclude.add(d)
475 global_deps_exclude.add(d + '.objlist')
478 t.direct_objects = set()
479 t.direct_libs = set()
480 t.direct_syslibs = set()
481 deps = t.samba_deps_extended[:]
482 if getattr(t, 'samba_use_global_deps', False) and not t.sname in global_deps_exclude:
483 deps.extend(global_deps)
485 if d == t.sname: continue
487 Logs.error("Unknown dependency '%s' in '%s'" % (d, t.sname))
489 if targets[d] in [ 'EMPTY', 'DISABLED' ]:
491 if targets[d] == 'PYTHON' and targets[t.sname] != 'PYTHON' and t.sname.find('.objlist') == -1:
492 # this check should be more restrictive, but for now we have pidl-generated python
493 # code that directly depends on other python modules
494 Logs.error('ERROR: Target %s has dependency on python module %s' % (t.sname, d))
496 if targets[d] == 'SYSLIB':
497 t.direct_syslibs.add(d)
499 for implied in TO_LIST(syslib_deps[d]):
500 if targets[implied] == 'SUBSYSTEM':
501 it = bld.get_tgen_by_name(implied)
502 sit = getattr(it, 'samba_builtin_subsystem', None)
505 if targets[implied] == 'BUILTIN':
506 t.direct_objects.add(implied)
507 elif targets[implied] == 'SYSLIB':
508 t.direct_syslibs.add(implied)
509 elif targets[implied] in ['LIBRARY', 'MODULE']:
510 t.direct_libs.add(implied)
512 Logs.error('Implied dependency %s in %s is of type %s' % (
513 implied, t.sname, targets[implied]))
516 t2 = bld.get_tgen_by_name(d)
518 Logs.error("no task %s of type %s in %s" % (d, targets[d], t.sname))
520 if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
522 elif t2.samba_type in [ 'SUBSYSTEM', 'BUILTIN', 'ASN1', 'PYTHON' ]:
523 t.direct_objects.add(d)
524 elif t2.samba_type in [ 'PLUGIN' ]:
525 Logs.error('Implicit dependency %s in %s is of type %s' % (
526 d, t.sname, t2.samba_type))
529 debug('deps: built direct dependencies')
532 def dependency_loop(loops, t, target):
533 '''add a dependency loop to the loops dictionary'''
534 if t.sname == target:
536 if not target in loops:
537 loops[target] = set()
538 if not t.sname in loops[target]:
539 loops[target].add(t.sname)
542 def indirect_libs(bld, t, chain, loops):
543 '''recursively calculate the indirect library dependencies for a target
545 An indirect library is a library that results from a dependency on
549 ret = getattr(t, 'indirect_libs', None)
554 for obj in t.direct_objects:
556 dependency_loop(loops, t, obj)
559 t2 = bld.get_tgen_by_name(obj)
560 r2 = indirect_libs(bld, t2, chain, loops)
562 ret = ret.union(t2.direct_libs)
565 for obj in indirect_objects(bld, t, set(), loops):
567 dependency_loop(loops, t, obj)
570 t2 = bld.get_tgen_by_name(obj)
571 r2 = indirect_libs(bld, t2, chain, loops)
573 ret = ret.union(t2.direct_libs)
576 t.indirect_libs = ret
581 def indirect_objects(bld, t, chain, loops):
582 '''recursively calculate the indirect object dependencies for a target
584 indirect objects are the set of objects from expanding the
585 subsystem dependencies
588 ret = getattr(t, 'indirect_objects', None)
589 if ret is not None: return ret
592 for lib in t.direct_objects:
594 dependency_loop(loops, t, lib)
597 t2 = bld.get_tgen_by_name(lib)
598 r2 = indirect_objects(bld, t2, chain, loops)
600 ret = ret.union(t2.direct_objects)
603 t.indirect_objects = ret
607 def extended_objects(bld, t, chain):
608 '''recursively calculate the extended object dependencies for a target
610 extended objects are the union of:
613 - direct and indirect objects of all direct and indirect libraries
616 ret = getattr(t, 'extended_objects', None)
617 if ret is not None: return ret
620 ret = ret.union(t.final_objects)
622 for lib in t.final_libs:
625 t2 = bld.get_tgen_by_name(lib)
627 r2 = extended_objects(bld, t2, chain)
629 ret = ret.union(t2.final_objects)
632 t.extended_objects = ret
636 def includes_objects(bld, t, chain, inc_loops):
637 '''recursively calculate the includes object dependencies for a target
639 includes dependencies come from either library or object dependencies
641 ret = getattr(t, 'includes_objects', None)
645 ret = t.direct_objects.copy()
646 ret = ret.union(t.direct_libs)
648 for obj in t.direct_objects:
650 dependency_loop(inc_loops, t, obj)
653 t2 = bld.get_tgen_by_name(obj)
654 r2 = includes_objects(bld, t2, chain, inc_loops)
656 ret = ret.union(t2.direct_objects)
659 for lib in t.direct_libs:
661 dependency_loop(inc_loops, t, lib)
664 t2 = bld.get_tgen_by_name(lib)
666 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
667 Logs.error('Target %s of type %s not found in direct_libs for %s' % (
668 lib, targets[lib], t.sname))
670 r2 = includes_objects(bld, t2, chain, inc_loops)
672 ret = ret.union(t2.direct_objects)
675 t.includes_objects = ret
679 def break_dependency_loops(bld, tgt_list):
680 '''find and break dependency loops'''
684 # build up the list of loops
686 indirect_objects(bld, t, set(), loops)
687 indirect_libs(bld, t, set(), loops)
688 includes_objects(bld, t, set(), inc_loops)
693 for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
694 objs = getattr(t, attr, set())
695 setattr(t, attr, objs.difference(loops[t.sname]))
698 debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
700 for loop in inc_loops:
701 debug('deps: Found include loops for target %s : %s', loop, inc_loops[loop])
703 # expand the loops mapping by one level
704 for loop in loops.copy():
705 for tgt in loops[loop]:
707 loops[loop] = loops[loop].union(loops[tgt])
709 for loop in inc_loops.copy():
710 for tgt in inc_loops[loop]:
712 inc_loops[loop] = inc_loops[loop].union(inc_loops[tgt])
715 # expand indirect subsystem and library loops
716 for loop in loops.copy():
717 t = bld.get_tgen_by_name(loop)
718 if t.samba_type in ['SUBSYSTEM', 'BUILTIN']:
719 loops[loop] = loops[loop].union(t.indirect_objects)
720 loops[loop] = loops[loop].union(t.direct_objects)
721 if t.samba_type in ['LIBRARY', 'PLUGIN', 'PYTHON']:
722 loops[loop] = loops[loop].union(t.indirect_libs)
723 loops[loop] = loops[loop].union(t.direct_libs)
724 if loop in loops[loop]:
725 loops[loop].remove(loop)
727 # expand indirect includes loops
728 for loop in inc_loops.copy():
729 t = bld.get_tgen_by_name(loop)
730 inc_loops[loop] = inc_loops[loop].union(t.includes_objects)
731 if loop in inc_loops[loop]:
732 inc_loops[loop].remove(loop)
734 # add in the replacement dependencies
737 for attr in ['indirect_objects', 'indirect_libs']:
738 objs = getattr(t, attr, set())
740 diff = loops[loop].difference(objs)
744 debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
745 objs = objs.union(diff)
746 setattr(t, attr, objs)
748 for loop in inc_loops:
749 objs = getattr(t, 'includes_objects', set())
751 diff = inc_loops[loop].difference(objs)
755 debug('deps: Expanded target %s includes of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
756 objs = objs.union(diff)
757 setattr(t, 'includes_objects', objs)
760 def reduce_objects(bld, tgt_list):
761 '''reduce objects by looking for indirect object dependencies'''
762 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
767 t.extended_objects = None
771 for type in ['BINARY', 'PYTHON', 'LIBRARY', 'PLUGIN']:
773 if t.samba_type != type: continue
774 # if we will indirectly link to a target then we don't need it
775 new = t.final_objects.copy()
776 for l in t.final_libs:
777 t2 = bld.get_tgen_by_name(l)
778 t2_obj = extended_objects(bld, t2, set())
779 dup = new.intersection(t2_obj)
780 if t.sname in rely_on:
781 dup = dup.difference(rely_on[t.sname])
783 # Do not remove duplicates of BUILTINS
784 for d in iter(dup.copy()):
786 if dtype == 'BUILTIN':
787 debug('deps: BUILTIN SKIP: removing dups from %s of type %s: %s also in %s %s',
788 t.sname, t.samba_type, d, t2.samba_type, l)
793 debug('deps: removing dups from %s of type %s: %s also in %s %s',
794 t.sname, t.samba_type, dup, t2.samba_type, l)
795 new = new.difference(dup)
799 rely_on[l] = rely_on[l].union(dup)
800 for n in iter(new.copy()):
801 # if we got the builtin version as well
802 # as the native one, we keep using the
803 # builtin one and remove the rest.
804 # Otherwise our check_duplicate_sources()
805 # checks would trigger!
806 if n.endswith('.builtin.objlist'):
807 unused = n.replace('.builtin.objlist', '.objlist')
810 unused = n.replace('.builtin.objlist', '')
813 t.final_objects = new
818 # add back in any objects that were relied upon by the reduction rules
820 t = bld.get_tgen_by_name(r)
821 t.final_objects = t.final_objects.union(rely_on[r])
826 def show_library_loop(bld, lib1, lib2, path, seen):
827 '''show the detailed path of a library loop between lib1 and lib2'''
829 t = bld.get_tgen_by_name(lib1)
830 if not lib2 in getattr(t, 'final_libs', set()):
833 for d in t.samba_deps_extended:
837 path2 = path + '=>' + d
839 Logs.warn('library loop path: ' + path2)
841 show_library_loop(bld, d, lib2, path2, seen)
845 def calculate_final_deps(bld, tgt_list, loops):
846 '''calculate the final library and object dependencies'''
848 # start with the maximum possible list
849 t.final_libs = t.direct_libs.union(indirect_libs(bld, t, set(), loops))
850 t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops))
853 # don't depend on ourselves
854 if t.sname in t.final_libs:
855 t.final_libs.remove(t.sname)
856 if t.sname in t.final_objects:
857 t.final_objects.remove(t.sname)
859 # handle any non-shared binaries
861 if t.samba_type == 'BINARY' and bld.NONSHARED_BINARY(t.sname):
862 subsystem_list = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
863 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
865 # replace lib deps with objlist deps
866 for l in t.final_libs:
867 objname = l + '.objlist'
868 t2 = bld.get_tgen_by_name(objname)
870 Logs.error('ERROR: subsystem %s not found' % objname)
872 t.final_objects.add(objname)
873 t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
874 if l in subsystem_list:
875 # its a subsystem - we also need the contents of any modules
876 for d in subsystem_list[l]:
877 module_name = d['TARGET']
878 if targets[module_name] == 'LIBRARY':
879 objname = module_name + '.objlist'
880 elif targets[module_name] == 'SUBSYSTEM':
881 objname = module_name
884 t2 = bld.get_tgen_by_name(objname)
886 Logs.error('ERROR: subsystem %s not found' % objname)
888 t.final_objects.add(objname)
889 t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
892 # find any library loops
894 if t.samba_type in ['LIBRARY', 'PYTHON']:
895 for l in t.final_libs.copy():
896 t2 = bld.get_tgen_by_name(l)
897 if t.sname in t2.final_libs:
898 if getattr(bld.env, "ALLOW_CIRCULAR_LIB_DEPENDENCIES", False):
899 # we could break this in either direction. If one of the libraries
900 # has a version number, and will this be distributed publicly, then
901 # we should make it the lower level library in the DAG
902 Logs.warn('deps: removing library loop %s from %s' % (t.sname, t2.sname))
903 dependency_loop(loops, t, t2.sname)
904 t2.final_libs.remove(t.sname)
906 Logs.error('ERROR: circular library dependency between %s and %s'
907 % (t.sname, t2.sname))
908 show_library_loop(bld, t.sname, t2.sname, t.sname, set())
909 show_library_loop(bld, t2.sname, t.sname, t2.sname, set())
913 debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
915 # we now need to make corrections for any library loops we broke up
916 # any target that depended on the target of the loop and doesn't
917 # depend on the source of the loop needs to get the loop source added
918 for type in ['BINARY','PYTHON','LIBRARY','PLUGIN','BINARY']:
920 if t.samba_type != type: continue
922 if loop in t.final_libs:
923 diff = loops[loop].difference(t.final_libs)
928 # make sure we don't recreate the loop again!
929 for d in diff.copy():
930 t2 = bld.get_tgen_by_name(d)
931 if t2.samba_type == 'LIBRARY':
932 if t.sname in t2.final_libs:
933 debug('deps: removing expansion %s from %s', d, t.sname)
936 debug('deps: Expanded target %s by loop %s libraries (loop %s) %s', t.sname, loop,
938 t.final_libs = t.final_libs.union(diff)
940 # remove objects that are also available in linked libs
942 while reduce_objects(bld, tgt_list):
945 Logs.warn("WARNING: Unable to remove all inter-target object duplicates")
947 debug('deps: Object reduction took %u iterations', count)
949 # add in any syslib dependencies
951 if not t.samba_type in ['BINARY','PYTHON','LIBRARY','PLUGIN','SUBSYSTEM','BUILTIN']:
954 for d in t.final_objects:
955 t2 = bld.get_tgen_by_name(d)
956 syslibs = syslibs.union(t2.direct_syslibs)
957 # this adds the indirect syslibs as well, which may not be needed
958 # depending on the linker flags
959 for d in t.final_libs:
960 t2 = bld.get_tgen_by_name(d)
961 syslibs = syslibs.union(t2.direct_syslibs)
962 t.final_syslibs = syslibs
965 # find any unresolved library loops
966 lib_loop_error = False
968 if t.samba_type in ['LIBRARY', 'PLUGIN', 'PYTHON']:
969 for l in t.final_libs.copy():
970 t2 = bld.get_tgen_by_name(l)
971 if t.sname in t2.final_libs:
972 Logs.error('ERROR: Unresolved library loop %s from %s' % (t.sname, t2.sname))
973 lib_loop_error = True
977 debug('deps: removed duplicate dependencies')
980 def show_dependencies(bld, target, seen):
981 '''recursively show the dependencies of target'''
986 t = bld.get_tgen_by_name(target)
988 Logs.error("ERROR: Unable to find target '%s'" % target)
991 Logs.info('%s(OBJECTS): %s' % (target, t.direct_objects))
992 Logs.info('%s(LIBS): %s' % (target, t.direct_libs))
993 Logs.info('%s(SYSLIBS): %s' % (target, t.direct_syslibs))
997 for t2 in t.direct_objects:
998 show_dependencies(bld, t2, seen)
1001 def show_object_duplicates(bld, tgt_list):
1002 '''show a list of object files that are included in more than
1003 one library or binary'''
1005 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
1009 Logs.info("showing duplicate objects")
1012 if not targets[t.sname] in [ 'LIBRARY', 'PYTHON' ]:
1014 for n in getattr(t, 'final_objects', set()):
1015 t2 = bld.get_tgen_by_name(n)
1016 if not n in used_by:
1018 used_by[n].add(t.sname)
1021 if len(used_by[n]) > 1:
1022 Logs.info("target '%s' is used by %s" % (n, used_by[n]))
1024 Logs.info("showing indirect dependency counts (sorted by count)")
1026 def indirect_count(t1, t2):
1027 return len(t2.indirect_objects) - len(t1.indirect_objects)
1029 sorted_list = sorted(tgt_list, cmp=indirect_count)
1030 for t in sorted_list:
1031 if len(t.indirect_objects) > 1:
1032 Logs.info("%s depends on %u indirect objects" % (t.sname, len(t.indirect_objects)))
1035 ######################################################################
1036 # this provides a way to save our dependency calculations between runs
1037 savedeps_version = 3
1038 savedeps_inputs = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags',
1039 'source', 'grouping_library', 'samba_ldflags', 'allow_undefined_symbols',
1040 'use_global_deps', 'global_include' ]
1041 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes',
1042 'cflags', 'ldflags', 'samba_deps_extended', 'final_libs']
1043 savedeps_outenv = ['INC_PATHS']
1044 savedeps_envvars = ['NONSHARED_BINARIES', 'GLOBAL_DEPENDENCIES', 'EXTRA_CFLAGS', 'EXTRA_LDFLAGS', 'EXTRA_INCLUDES' ]
1045 savedeps_caches = ['GLOBAL_DEPENDENCIES', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
1046 savedeps_files = ['buildtools/wafsamba/samba_deps.py']
1048 def save_samba_deps(bld, tgt_list):
1049 '''save the dependency calculations between builds, to make
1050 further builds faster'''
1051 denv = ConfigSet.ConfigSet()
1053 denv.version = savedeps_version
1054 denv.savedeps_inputs = savedeps_inputs
1055 denv.savedeps_outputs = savedeps_outputs
1063 for f in savedeps_files:
1064 denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
1066 for c in savedeps_caches:
1067 denv.caches[c] = LOCAL_CACHE(bld, c)
1069 for e in savedeps_envvars:
1070 denv.envvar[e] = bld.env[e]
1073 # save all the input attributes for each target
1075 for attr in savedeps_inputs:
1076 v = getattr(t, attr, None)
1080 denv.input[t.sname] = tdeps
1082 # save all the output attributes for each target
1084 for attr in savedeps_outputs:
1085 v = getattr(t, attr, None)
1089 denv.output[t.sname] = tdeps
1092 for attr in savedeps_outenv:
1094 tdeps[attr] = t.env[attr]
1096 denv.outenv[t.sname] = tdeps
1098 depsfile = os.path.join(bld.cache_dir, "sambadeps")
1099 denv.store_fast(depsfile)
1103 def load_samba_deps(bld, tgt_list):
1104 '''load a previous set of build dependencies if possible'''
1105 depsfile = os.path.join(bld.cache_dir, "sambadeps")
1106 denv = ConfigSet.ConfigSet()
1108 debug('deps: checking saved dependencies')
1109 denv.load_fast(depsfile)
1110 if (denv.version != savedeps_version or
1111 denv.savedeps_inputs != savedeps_inputs or
1112 denv.savedeps_outputs != savedeps_outputs):
1117 # check if critical files have changed
1118 for f in savedeps_files:
1119 if f not in denv.files:
1121 if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
1124 # check if caches are the same
1125 for c in savedeps_caches:
1126 if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
1129 # check if caches are the same
1130 for e in savedeps_envvars:
1131 if e not in denv.envvar or denv.envvar[e] != bld.env[e]:
1134 # check inputs are the same
1137 for attr in savedeps_inputs:
1138 v = getattr(t, attr, None)
1141 if t.sname in denv.input:
1142 olddeps = denv.input[t.sname]
1145 if tdeps != olddeps:
1146 #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
1149 # put outputs in place
1151 if not t.sname in denv.output: continue
1152 tdeps = denv.output[t.sname]
1154 setattr(t, a, tdeps[a])
1156 # put output env vars in place
1158 if not t.sname in denv.outenv: continue
1159 tdeps = denv.outenv[t.sname]
1163 debug('deps: loaded saved dependencies')
1168 def check_project_rules(bld):
1169 '''check the project rules - ensuring the targets are sane'''
1171 if bld.__class__.__name__ == "ClangDbContext":
1177 tgt_list = get_tgt_list(bld)
1179 add_samba_attributes(bld, tgt_list)
1181 force_project_rules = (Options.options.SHOWDEPS or
1182 Options.options.SHOW_DUPLICATES)
1184 if not force_project_rules and load_samba_deps(bld, tgt_list):
1187 timer = Utils.Timer()
1189 bld.new_rules = True
1190 Logs.info("Checking project rules ...")
1192 debug('deps: project rules checking started')
1194 replace_builtin_subsystem_deps(bld, tgt_list)
1196 debug("deps: replace_builtin_subsystem_deps: %s" % str(timer))
1198 expand_subsystem_deps(bld)
1200 debug("deps: expand_subsystem_deps: %s" % str(timer))
1202 replace_grouping_libraries(bld, tgt_list)
1204 debug("deps: replace_grouping_libraries: %s" % str(timer))
1206 build_direct_deps(bld, tgt_list)
1208 debug("deps: build_direct_deps: %s" % str(timer))
1210 break_dependency_loops(bld, tgt_list)
1212 debug("deps: break_dependency_loops: %s" % str(timer))
1214 if Options.options.SHOWDEPS:
1215 show_dependencies(bld, Options.options.SHOWDEPS, set())
1217 calculate_final_deps(bld, tgt_list, loops)
1219 debug("deps: calculate_final_deps: %s" % str(timer))
1221 if Options.options.SHOW_DUPLICATES:
1222 show_object_duplicates(bld, tgt_list)
1224 # run the various attribute generators
1225 for f in [ build_dependencies, build_includes, add_init_functions ]:
1226 debug('deps: project rules checking %s', f)
1227 for t in tgt_list: f(t)
1228 debug("deps: %s: %s" % (f, str(timer)))
1230 debug('deps: project rules stage1 completed')
1232 if not check_duplicate_sources(bld, tgt_list):
1233 Logs.error("Duplicate sources present - aborting")
1236 debug("deps: check_duplicate_sources: %s" % str(timer))
1238 if not bld.check_group_ordering(tgt_list):
1239 Logs.error("Bad group ordering - aborting")
1242 debug("deps: check_group_ordering: %s" % str(timer))
1244 show_final_deps(bld, tgt_list)
1246 debug("deps: show_final_deps: %s" % str(timer))
1248 debug('deps: project rules checking completed - %u targets checked',
1251 if not bld.is_install:
1252 save_samba_deps(bld, tgt_list)
1254 debug("deps: save_samba_deps: %s" % str(timer))
1256 Logs.info("Project rules pass")
1258 timer = Utils.Timer()
1260 bld.load('clang_compilation_database')
1261 Scripting.run_command('clangdb')
1263 debug("deps: clang_compilation_database: %s" % str(timer))
1266 def CHECK_PROJECT_RULES(bld):
1267 '''enable checking of project targets for sanity'''
1268 if bld.env.added_project_rules:
1270 bld.env.added_project_rules = True
1271 bld.add_pre_fun(check_project_rules)
1272 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES