librpc/ndr: add helper functions to setup and free compression states.
[Samba.git] / buildtools / wafsamba / samba_deps.py
blob978a5e9afe6f28d3c8129c2910cfb02b0ac4d223
1 # Samba automatic dependency handling and project rules
3 import os, sys, re, time
5 import Build, Environment, Options, Logs, Utils
6 from Logs import debug
7 from Configure import conf
9 from samba_bundled import BUILTIN_LIBRARY
10 from samba_utils import LOCAL_CACHE, TO_LIST, get_tgt_list, unique_list, os_path_relpath
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', '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 (ccflags, 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']:
105 # this is needed for the ccflags 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):
114 up_list = []
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=
131 attribute
134 if getattr(self, 'samba_includes', None) is None:
135 return
137 bld = self.bld
139 inc_deps = includes_objects(bld, self, set(), {})
141 includes = []
143 # maybe add local includes
144 if getattr(self, 'local_include', True) and getattr(self, 'local_include_first', True):
145 includes.append('.')
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'])
152 includes.append('#')
154 inc_set = set()
155 inc_abs = []
157 for d in inc_deps:
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):
162 inclist.append('.')
163 if inclist == []:
164 continue
165 tpath = t.samba_abspath
166 for inc in inclist:
167 npath = tpath + '/' + inc
168 if not npath in inc_set:
169 inc_abs.append(npath)
170 inc_set.add(npath)
172 mypath = self.path.abspath(bld.env)
173 for inc in inc_abs:
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):
178 includes.append('.')
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
183 includes_top = []
184 for i in includes:
185 if i[0] == '#':
186 # some are already top based
187 includes_top.append(i)
188 continue
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'''
201 bld = self.bld
203 subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
205 # cope with the separated object lists from BINARY and LIBRARY targets
206 sname = self.sname
207 if sname.endswith('.objlist'):
208 sname = sname[0:-8]
210 modules = []
211 if sname in subsystems:
212 modules.append(sname)
214 m = getattr(self, 'samba_modules', None)
215 if m is not None:
216 modules.extend(TO_LIST(m))
218 m = getattr(self, 'samba_subsystem', None)
219 if m is not None:
220 modules.append(m)
222 if 'pyembed' in self.features:
223 return
225 sentinel = getattr(self, 'init_function_sentinel', 'NULL')
227 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
228 cflags = getattr(self, 'samba_cflags', [])[:]
230 if modules == []:
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))
238 self.ccflags = cflags
239 return
241 for m in modules:
242 bld.ASSERT(m in subsystems,
243 "No init_function defined for module '%s' in target '%s'" % (m, self.sname))
244 init_fn_list = []
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))
253 else:
254 cflags.append('-DSTATIC_%s_MODULES=%s' % (m, ','.join(init_fn_list) + ',' + sentinel))
255 proto=''
256 for f in init_fn_list:
257 proto += '_MODULE_PROTO(%s)' % f
258 proto += "extern void __%s_dummy_module_proto(void)" % (m)
259 cflags.append('-DSTATIC_%s_MODULES_PROTO=%s' % (m, proto))
260 self.ccflags = cflags
263 def check_duplicate_sources(bld, tgt_list):
264 '''see if we are compiling the same source file more than once'''
266 debug('deps: checking for duplicate sources')
267 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
269 for t in tgt_list:
270 source_list = TO_LIST(getattr(t, 'source', ''))
271 tpath = os.path.normpath(os_path_relpath(t.path.abspath(bld.env), t.env.BUILD_DIRECTORY + '/default'))
272 obj_sources = set()
273 for s in source_list:
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', '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 Utils.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', 'BINARY', 'PYTHON', 'SUBSYSTEM']:
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 a 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.ccflags = getattr(t, 'samba_cflags', '')
377 def replace_grouping_libraries(bld, tgt_list):
378 '''replace dependencies based on grouping libraries
380 If a library is marked as a grouping library, then any target that
381 depends on a subsystem that is part of that grouping library gets
382 that dependency replaced with a dependency on the grouping library
385 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
387 grouping = {}
389 # find our list of grouping libraries, mapped from the subsystems they depend on
390 for t in tgt_list:
391 if not getattr(t, 'grouping_library', False):
392 continue
393 for dep in t.samba_deps_extended:
394 bld.ASSERT(dep in targets, "grouping library target %s not declared in %s" % (dep, t.sname))
395 if targets[dep] == 'SUBSYSTEM':
396 grouping[dep] = t.sname
398 # now replace any dependencies on elements of grouping libraries
399 for t in tgt_list:
400 for i in range(len(t.samba_deps_extended)):
401 dep = t.samba_deps_extended[i]
402 if dep in grouping:
403 if t.sname != grouping[dep]:
404 debug("deps: target %s: replacing dependency %s with grouping library %s" % (t.sname, dep, grouping[dep]))
405 t.samba_deps_extended[i] = grouping[dep]
409 def build_direct_deps(bld, tgt_list):
410 '''build the direct_objects and direct_libs sets for each target'''
412 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
413 syslib_deps = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
415 global_deps = bld.env.GLOBAL_DEPENDENCIES
416 global_deps_exclude = set()
417 for dep in global_deps:
418 t = bld.get_tgen_by_name(dep)
419 for d in t.samba_deps:
420 # prevent loops from the global dependencies list
421 global_deps_exclude.add(d)
422 global_deps_exclude.add(d + '.objlist')
424 for t in tgt_list:
425 t.direct_objects = set()
426 t.direct_libs = set()
427 t.direct_syslibs = set()
428 deps = t.samba_deps_extended[:]
429 if getattr(t, 'samba_use_global_deps', False) and not t.sname in global_deps_exclude:
430 deps.extend(global_deps)
431 for d in deps:
432 if d == t.sname: continue
433 if not d in targets:
434 Logs.error("Unknown dependency '%s' in '%s'" % (d, t.sname))
435 sys.exit(1)
436 if targets[d] in [ 'EMPTY', 'DISABLED' ]:
437 continue
438 if targets[d] == 'PYTHON' and targets[t.sname] != 'PYTHON' and t.sname.find('.objlist') == -1:
439 # this check should be more restrictive, but for now we have pidl-generated python
440 # code that directly depends on other python modules
441 Logs.error('ERROR: Target %s has dependency on python module %s' % (t.sname, d))
442 sys.exit(1)
443 if targets[d] == 'SYSLIB':
444 t.direct_syslibs.add(d)
445 if d in syslib_deps:
446 for implied in TO_LIST(syslib_deps[d]):
447 if BUILTIN_LIBRARY(bld, implied):
448 t.direct_objects.add(implied)
449 elif targets[implied] == 'SYSLIB':
450 t.direct_syslibs.add(implied)
451 elif targets[implied] in ['LIBRARY', 'MODULE']:
452 t.direct_libs.add(implied)
453 else:
454 Logs.error('Implied dependency %s in %s is of type %s' % (
455 implied, t.sname, targets[implied]))
456 sys.exit(1)
457 continue
458 t2 = bld.get_tgen_by_name(d)
459 if t2 is None:
460 Logs.error("no task %s of type %s in %s" % (d, targets[d], t.sname))
461 sys.exit(1)
462 if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
463 t.direct_libs.add(d)
464 elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
465 t.direct_objects.add(d)
466 debug('deps: built direct dependencies')
469 def dependency_loop(loops, t, target):
470 '''add a dependency loop to the loops dictionary'''
471 if t.sname == target:
472 return
473 if not target in loops:
474 loops[target] = set()
475 if not t.sname in loops[target]:
476 loops[target].add(t.sname)
479 def indirect_libs(bld, t, chain, loops):
480 '''recursively calculate the indirect library dependencies for a target
482 An indirect library is a library that results from a dependency on
483 a subsystem
486 ret = getattr(t, 'indirect_libs', None)
487 if ret is not None:
488 return ret
490 ret = set()
491 for obj in t.direct_objects:
492 if obj in chain:
493 dependency_loop(loops, t, obj)
494 continue
495 chain.add(obj)
496 t2 = bld.get_tgen_by_name(obj)
497 r2 = indirect_libs(bld, t2, chain, loops)
498 chain.remove(obj)
499 ret = ret.union(t2.direct_libs)
500 ret = ret.union(r2)
502 for obj in indirect_objects(bld, t, set(), loops):
503 if obj in chain:
504 dependency_loop(loops, t, obj)
505 continue
506 chain.add(obj)
507 t2 = bld.get_tgen_by_name(obj)
508 r2 = indirect_libs(bld, t2, chain, loops)
509 chain.remove(obj)
510 ret = ret.union(t2.direct_libs)
511 ret = ret.union(r2)
513 t.indirect_libs = ret
515 return ret
518 def indirect_objects(bld, t, chain, loops):
519 '''recursively calculate the indirect object dependencies for a target
521 indirect objects are the set of objects from expanding the
522 subsystem dependencies
525 ret = getattr(t, 'indirect_objects', None)
526 if ret is not None: return ret
528 ret = set()
529 for lib in t.direct_objects:
530 if lib in chain:
531 dependency_loop(loops, t, lib)
532 continue
533 chain.add(lib)
534 t2 = bld.get_tgen_by_name(lib)
535 r2 = indirect_objects(bld, t2, chain, loops)
536 chain.remove(lib)
537 ret = ret.union(t2.direct_objects)
538 ret = ret.union(r2)
540 t.indirect_objects = ret
541 return ret
544 def extended_objects(bld, t, chain):
545 '''recursively calculate the extended object dependencies for a target
547 extended objects are the union of:
548 - direct objects
549 - indirect objects
550 - direct and indirect objects of all direct and indirect libraries
553 ret = getattr(t, 'extended_objects', None)
554 if ret is not None: return ret
556 ret = set()
557 ret = ret.union(t.final_objects)
559 for lib in t.final_libs:
560 if lib in chain:
561 continue
562 t2 = bld.get_tgen_by_name(lib)
563 chain.add(lib)
564 r2 = extended_objects(bld, t2, chain)
565 chain.remove(lib)
566 ret = ret.union(t2.final_objects)
567 ret = ret.union(r2)
569 t.extended_objects = ret
570 return ret
573 def includes_objects(bld, t, chain, inc_loops):
574 '''recursively calculate the includes object dependencies for a target
576 includes dependencies come from either library or object dependencies
578 ret = getattr(t, 'includes_objects', None)
579 if ret is not None:
580 return ret
582 ret = t.direct_objects.copy()
583 ret = ret.union(t.direct_libs)
585 for obj in t.direct_objects:
586 if obj in chain:
587 dependency_loop(inc_loops, t, obj)
588 continue
589 chain.add(obj)
590 t2 = bld.get_tgen_by_name(obj)
591 r2 = includes_objects(bld, t2, chain, inc_loops)
592 chain.remove(obj)
593 ret = ret.union(t2.direct_objects)
594 ret = ret.union(r2)
596 for lib in t.direct_libs:
597 if lib in chain:
598 dependency_loop(inc_loops, t, lib)
599 continue
600 chain.add(lib)
601 t2 = bld.get_tgen_by_name(lib)
602 if t2 is None:
603 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
604 Logs.error('Target %s of type %s not found in direct_libs for %s' % (
605 lib, targets[lib], t.sname))
606 sys.exit(1)
607 r2 = includes_objects(bld, t2, chain, inc_loops)
608 chain.remove(lib)
609 ret = ret.union(t2.direct_objects)
610 ret = ret.union(r2)
612 t.includes_objects = ret
613 return ret
616 def break_dependency_loops(bld, tgt_list):
617 '''find and break dependency loops'''
618 loops = {}
619 inc_loops = {}
621 # build up the list of loops
622 for t in tgt_list:
623 indirect_objects(bld, t, set(), loops)
624 indirect_libs(bld, t, set(), loops)
625 includes_objects(bld, t, set(), inc_loops)
627 # break the loops
628 for t in tgt_list:
629 if t.sname in loops:
630 for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
631 objs = getattr(t, attr, set())
632 setattr(t, attr, objs.difference(loops[t.sname]))
634 for loop in loops:
635 debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
637 for loop in inc_loops:
638 debug('deps: Found include loops for target %s : %s', loop, inc_loops[loop])
640 # expand the loops mapping by one level
641 for loop in loops.copy():
642 for tgt in loops[loop]:
643 if tgt in loops:
644 loops[loop] = loops[loop].union(loops[tgt])
646 for loop in inc_loops.copy():
647 for tgt in inc_loops[loop]:
648 if tgt in inc_loops:
649 inc_loops[loop] = inc_loops[loop].union(inc_loops[tgt])
652 # expand indirect subsystem and library loops
653 for loop in loops.copy():
654 t = bld.get_tgen_by_name(loop)
655 if t.samba_type in ['SUBSYSTEM']:
656 loops[loop] = loops[loop].union(t.indirect_objects)
657 loops[loop] = loops[loop].union(t.direct_objects)
658 if t.samba_type in ['LIBRARY','PYTHON']:
659 loops[loop] = loops[loop].union(t.indirect_libs)
660 loops[loop] = loops[loop].union(t.direct_libs)
661 if loop in loops[loop]:
662 loops[loop].remove(loop)
664 # expand indirect includes loops
665 for loop in inc_loops.copy():
666 t = bld.get_tgen_by_name(loop)
667 inc_loops[loop] = inc_loops[loop].union(t.includes_objects)
668 if loop in inc_loops[loop]:
669 inc_loops[loop].remove(loop)
671 # add in the replacement dependencies
672 for t in tgt_list:
673 for loop in loops:
674 for attr in ['indirect_objects', 'indirect_libs']:
675 objs = getattr(t, attr, set())
676 if loop in objs:
677 diff = loops[loop].difference(objs)
678 if t.sname in diff:
679 diff.remove(t.sname)
680 if diff:
681 debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
682 objs = objs.union(diff)
683 setattr(t, attr, objs)
685 for loop in inc_loops:
686 objs = getattr(t, 'includes_objects', set())
687 if loop in objs:
688 diff = inc_loops[loop].difference(objs)
689 if t.sname in diff:
690 diff.remove(t.sname)
691 if diff:
692 debug('deps: Expanded target %s includes of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
693 objs = objs.union(diff)
694 setattr(t, 'includes_objects', objs)
697 def reduce_objects(bld, tgt_list):
698 '''reduce objects by looking for indirect object dependencies'''
699 rely_on = {}
701 for t in tgt_list:
702 t.extended_objects = None
704 changed = False
706 for type in ['BINARY', 'PYTHON', 'LIBRARY']:
707 for t in tgt_list:
708 if t.samba_type != type: continue
709 # if we will indirectly link to a target then we don't need it
710 new = t.final_objects.copy()
711 for l in t.final_libs:
712 t2 = bld.get_tgen_by_name(l)
713 t2_obj = extended_objects(bld, t2, set())
714 dup = new.intersection(t2_obj)
715 if t.sname in rely_on:
716 dup = dup.difference(rely_on[t.sname])
717 if dup:
718 debug('deps: removing dups from %s of type %s: %s also in %s %s',
719 t.sname, t.samba_type, dup, t2.samba_type, l)
720 new = new.difference(dup)
721 changed = True
722 if not l in rely_on:
723 rely_on[l] = set()
724 rely_on[l] = rely_on[l].union(dup)
725 t.final_objects = new
727 if not changed:
728 return False
730 # add back in any objects that were relied upon by the reduction rules
731 for r in rely_on:
732 t = bld.get_tgen_by_name(r)
733 t.final_objects = t.final_objects.union(rely_on[r])
735 return True
738 def show_library_loop(bld, lib1, lib2, path, seen):
739 '''show the detailed path of a library loop between lib1 and lib2'''
741 t = bld.get_tgen_by_name(lib1)
742 if not lib2 in getattr(t, 'final_libs', set()):
743 return
745 for d in t.samba_deps_extended:
746 if d in seen:
747 continue
748 seen.add(d)
749 path2 = path + '=>' + d
750 if d == lib2:
751 Logs.warn('library loop path: ' + path2)
752 return
753 show_library_loop(bld, d, lib2, path2, seen)
754 seen.remove(d)
757 def calculate_final_deps(bld, tgt_list, loops):
758 '''calculate the final library and object dependencies'''
759 for t in tgt_list:
760 # start with the maximum possible list
761 t.final_libs = t.direct_libs.union(indirect_libs(bld, t, set(), loops))
762 t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops))
764 for t in tgt_list:
765 # don't depend on ourselves
766 if t.sname in t.final_libs:
767 t.final_libs.remove(t.sname)
768 if t.sname in t.final_objects:
769 t.final_objects.remove(t.sname)
771 # handle any non-shared binaries
772 for t in tgt_list:
773 if t.samba_type == 'BINARY' and bld.NONSHARED_BINARY(t.sname):
774 subsystem_list = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
775 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
777 # replace lib deps with objlist deps
778 for l in t.final_libs:
779 objname = l + '.objlist'
780 t2 = bld.get_tgen_by_name(objname)
781 if t2 is None:
782 Logs.error('ERROR: subsystem %s not found' % objname)
783 sys.exit(1)
784 t.final_objects.add(objname)
785 t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
786 if l in subsystem_list:
787 # its a subsystem - we also need the contents of any modules
788 for d in subsystem_list[l]:
789 module_name = d['TARGET']
790 if targets[module_name] == 'LIBRARY':
791 objname = module_name + '.objlist'
792 elif targets[module_name] == 'SUBSYSTEM':
793 objname = module_name
794 else:
795 continue
796 t2 = bld.get_tgen_by_name(objname)
797 if t2 is None:
798 Logs.error('ERROR: subsystem %s not found' % objname)
799 sys.exit(1)
800 t.final_objects.add(objname)
801 t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
802 t.final_libs = set()
804 # find any library loops
805 for t in tgt_list:
806 if t.samba_type in ['LIBRARY', 'PYTHON']:
807 for l in t.final_libs.copy():
808 t2 = bld.get_tgen_by_name(l)
809 if t.sname in t2.final_libs:
810 if getattr(bld.env, "ALLOW_CIRCULAR_LIB_DEPENDENCIES", False):
811 # we could break this in either direction. If one of the libraries
812 # has a version number, and will this be distributed publicly, then
813 # we should make it the lower level library in the DAG
814 Logs.warn('deps: removing library loop %s from %s' % (t.sname, t2.sname))
815 dependency_loop(loops, t, t2.sname)
816 t2.final_libs.remove(t.sname)
817 else:
818 Logs.error('ERROR: circular library dependency between %s and %s'
819 % (t.sname, t2.sname))
820 show_library_loop(bld, t.sname, t2.sname, t.sname, set())
821 show_library_loop(bld, t2.sname, t.sname, t2.sname, set())
822 sys.exit(1)
824 for loop in loops:
825 debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
827 # we now need to make corrections for any library loops we broke up
828 # any target that depended on the target of the loop and doesn't
829 # depend on the source of the loop needs to get the loop source added
830 for type in ['BINARY','PYTHON','LIBRARY','BINARY']:
831 for t in tgt_list:
832 if t.samba_type != type: continue
833 for loop in loops:
834 if loop in t.final_libs:
835 diff = loops[loop].difference(t.final_libs)
836 if t.sname in diff:
837 diff.remove(t.sname)
838 if t.sname in diff:
839 diff.remove(t.sname)
840 # make sure we don't recreate the loop again!
841 for d in diff.copy():
842 t2 = bld.get_tgen_by_name(d)
843 if t2.samba_type == 'LIBRARY':
844 if t.sname in t2.final_libs:
845 debug('deps: removing expansion %s from %s', d, t.sname)
846 diff.remove(d)
847 if diff:
848 debug('deps: Expanded target %s by loop %s libraries (loop %s) %s', t.sname, loop,
849 loops[loop], diff)
850 t.final_libs = t.final_libs.union(diff)
852 # remove objects that are also available in linked libs
853 count = 0
854 while reduce_objects(bld, tgt_list):
855 count += 1
856 if count > 100:
857 Logs.warn("WARNING: Unable to remove all inter-target object duplicates")
858 break
859 debug('deps: Object reduction took %u iterations', count)
861 # add in any syslib dependencies
862 for t in tgt_list:
863 if not t.samba_type in ['BINARY','PYTHON','LIBRARY','SUBSYSTEM']:
864 continue
865 syslibs = set()
866 for d in t.final_objects:
867 t2 = bld.get_tgen_by_name(d)
868 syslibs = syslibs.union(t2.direct_syslibs)
869 # this adds the indirect syslibs as well, which may not be needed
870 # depending on the linker flags
871 for d in t.final_libs:
872 t2 = bld.get_tgen_by_name(d)
873 syslibs = syslibs.union(t2.direct_syslibs)
874 t.final_syslibs = syslibs
877 # find any unresolved library loops
878 lib_loop_error = False
879 for t in tgt_list:
880 if t.samba_type in ['LIBRARY', 'PYTHON']:
881 for l in t.final_libs.copy():
882 t2 = bld.get_tgen_by_name(l)
883 if t.sname in t2.final_libs:
884 Logs.error('ERROR: Unresolved library loop %s from %s' % (t.sname, t2.sname))
885 lib_loop_error = True
886 if lib_loop_error:
887 sys.exit(1)
889 debug('deps: removed duplicate dependencies')
892 def show_dependencies(bld, target, seen):
893 '''recursively show the dependencies of target'''
895 if target in seen:
896 return
898 t = bld.get_tgen_by_name(target)
899 if t is None:
900 Logs.error("ERROR: Unable to find target '%s'" % target)
901 sys.exit(1)
903 Logs.info('%s(OBJECTS): %s' % (target, t.direct_objects))
904 Logs.info('%s(LIBS): %s' % (target, t.direct_libs))
905 Logs.info('%s(SYSLIBS): %s' % (target, t.direct_syslibs))
907 seen.add(target)
909 for t2 in t.direct_objects:
910 show_dependencies(bld, t2, seen)
913 def show_object_duplicates(bld, tgt_list):
914 '''show a list of object files that are included in more than
915 one library or binary'''
917 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
919 used_by = {}
921 Logs.info("showing duplicate objects")
923 for t in tgt_list:
924 if not targets[t.sname] in [ 'LIBRARY', 'PYTHON' ]:
925 continue
926 for n in getattr(t, 'final_objects', set()):
927 t2 = bld.get_tgen_by_name(n)
928 if not n in used_by:
929 used_by[n] = set()
930 used_by[n].add(t.sname)
932 for n in used_by:
933 if len(used_by[n]) > 1:
934 Logs.info("target '%s' is used by %s" % (n, used_by[n]))
936 Logs.info("showing indirect dependency counts (sorted by count)")
938 def indirect_count(t1, t2):
939 return len(t2.indirect_objects) - len(t1.indirect_objects)
941 sorted_list = sorted(tgt_list, cmp=indirect_count)
942 for t in sorted_list:
943 if len(t.indirect_objects) > 1:
944 Logs.info("%s depends on %u indirect objects" % (t.sname, len(t.indirect_objects)))
947 ######################################################################
948 # this provides a way to save our dependency calculations between runs
949 savedeps_version = 3
950 savedeps_inputs = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags',
951 'source', 'grouping_library', 'samba_ldflags', 'allow_undefined_symbols',
952 'use_global_deps', 'global_include' ]
953 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes',
954 'ccflags', 'ldflags', 'samba_deps_extended', 'final_libs']
955 savedeps_outenv = ['INC_PATHS']
956 savedeps_envvars = ['NONSHARED_BINARIES', 'GLOBAL_DEPENDENCIES', 'EXTRA_CFLAGS', 'EXTRA_LDFLAGS', 'EXTRA_INCLUDES' ]
957 savedeps_caches = ['GLOBAL_DEPENDENCIES', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
958 savedeps_files = ['buildtools/wafsamba/samba_deps.py']
960 def save_samba_deps(bld, tgt_list):
961 '''save the dependency calculations between builds, to make
962 further builds faster'''
963 denv = Environment.Environment()
965 denv.version = savedeps_version
966 denv.savedeps_inputs = savedeps_inputs
967 denv.savedeps_outputs = savedeps_outputs
968 denv.input = {}
969 denv.output = {}
970 denv.outenv = {}
971 denv.caches = {}
972 denv.envvar = {}
973 denv.files = {}
975 for f in savedeps_files:
976 denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
978 for c in savedeps_caches:
979 denv.caches[c] = LOCAL_CACHE(bld, c)
981 for e in savedeps_envvars:
982 denv.envvar[e] = bld.env[e]
984 for t in tgt_list:
985 # save all the input attributes for each target
986 tdeps = {}
987 for attr in savedeps_inputs:
988 v = getattr(t, attr, None)
989 if v is not None:
990 tdeps[attr] = v
991 if tdeps != {}:
992 denv.input[t.sname] = tdeps
994 # save all the output attributes for each target
995 tdeps = {}
996 for attr in savedeps_outputs:
997 v = getattr(t, attr, None)
998 if v is not None:
999 tdeps[attr] = v
1000 if tdeps != {}:
1001 denv.output[t.sname] = tdeps
1003 tdeps = {}
1004 for attr in savedeps_outenv:
1005 if attr in t.env:
1006 tdeps[attr] = t.env[attr]
1007 if tdeps != {}:
1008 denv.outenv[t.sname] = tdeps
1010 depsfile = os.path.join(bld.bdir, "sambadeps")
1011 denv.store_fast(depsfile)
1015 def load_samba_deps(bld, tgt_list):
1016 '''load a previous set of build dependencies if possible'''
1017 depsfile = os.path.join(bld.bdir, "sambadeps")
1018 denv = Environment.Environment()
1019 try:
1020 debug('deps: checking saved dependencies')
1021 denv.load_fast(depsfile)
1022 if (denv.version != savedeps_version or
1023 denv.savedeps_inputs != savedeps_inputs or
1024 denv.savedeps_outputs != savedeps_outputs):
1025 return False
1026 except Exception:
1027 return False
1029 # check if critical files have changed
1030 for f in savedeps_files:
1031 if f not in denv.files:
1032 return False
1033 if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
1034 return False
1036 # check if caches are the same
1037 for c in savedeps_caches:
1038 if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
1039 return False
1041 # check if caches are the same
1042 for e in savedeps_envvars:
1043 if e not in denv.envvar or denv.envvar[e] != bld.env[e]:
1044 return False
1046 # check inputs are the same
1047 for t in tgt_list:
1048 tdeps = {}
1049 for attr in savedeps_inputs:
1050 v = getattr(t, attr, None)
1051 if v is not None:
1052 tdeps[attr] = v
1053 if t.sname in denv.input:
1054 olddeps = denv.input[t.sname]
1055 else:
1056 olddeps = {}
1057 if tdeps != olddeps:
1058 #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
1059 return False
1061 # put outputs in place
1062 for t in tgt_list:
1063 if not t.sname in denv.output: continue
1064 tdeps = denv.output[t.sname]
1065 for a in tdeps:
1066 setattr(t, a, tdeps[a])
1068 # put output env vars in place
1069 for t in tgt_list:
1070 if not t.sname in denv.outenv: continue
1071 tdeps = denv.outenv[t.sname]
1072 for a in tdeps:
1073 t.env[a] = tdeps[a]
1075 debug('deps: loaded saved dependencies')
1076 return True
1080 def check_project_rules(bld):
1081 '''check the project rules - ensuring the targets are sane'''
1083 loops = {}
1084 inc_loops = {}
1086 tgt_list = get_tgt_list(bld)
1088 add_samba_attributes(bld, tgt_list)
1090 force_project_rules = (Options.options.SHOWDEPS or
1091 Options.options.SHOW_DUPLICATES)
1093 if not force_project_rules and load_samba_deps(bld, tgt_list):
1094 return
1096 global tstart
1097 tstart = time.clock()
1099 bld.new_rules = True
1100 Logs.info("Checking project rules ...")
1102 debug('deps: project rules checking started')
1104 expand_subsystem_deps(bld)
1106 debug("deps: expand_subsystem_deps: %f" % (time.clock() - tstart))
1108 replace_grouping_libraries(bld, tgt_list)
1110 debug("deps: replace_grouping_libraries: %f" % (time.clock() - tstart))
1112 build_direct_deps(bld, tgt_list)
1114 debug("deps: build_direct_deps: %f" % (time.clock() - tstart))
1116 break_dependency_loops(bld, tgt_list)
1118 debug("deps: break_dependency_loops: %f" % (time.clock() - tstart))
1120 if Options.options.SHOWDEPS:
1121 show_dependencies(bld, Options.options.SHOWDEPS, set())
1123 calculate_final_deps(bld, tgt_list, loops)
1125 debug("deps: calculate_final_deps: %f" % (time.clock() - tstart))
1127 if Options.options.SHOW_DUPLICATES:
1128 show_object_duplicates(bld, tgt_list)
1130 # run the various attribute generators
1131 for f in [ build_dependencies, build_includes, add_init_functions ]:
1132 debug('deps: project rules checking %s', f)
1133 for t in tgt_list: f(t)
1134 debug("deps: %s: %f" % (f, time.clock() - tstart))
1136 debug('deps: project rules stage1 completed')
1138 if not check_duplicate_sources(bld, tgt_list):
1139 Logs.error("Duplicate sources present - aborting")
1140 sys.exit(1)
1142 debug("deps: check_duplicate_sources: %f" % (time.clock() - tstart))
1144 if not bld.check_group_ordering(tgt_list):
1145 Logs.error("Bad group ordering - aborting")
1146 sys.exit(1)
1148 debug("deps: check_group_ordering: %f" % (time.clock() - tstart))
1150 show_final_deps(bld, tgt_list)
1152 debug("deps: show_final_deps: %f" % (time.clock() - tstart))
1154 debug('deps: project rules checking completed - %u targets checked',
1155 len(tgt_list))
1157 if not bld.is_install:
1158 save_samba_deps(bld, tgt_list)
1160 debug("deps: save_samba_deps: %f" % (time.clock() - tstart))
1162 Logs.info("Project rules pass")
1165 def CHECK_PROJECT_RULES(bld):
1166 '''enable checking of project targets for sanity'''
1167 if bld.env.added_project_rules:
1168 return
1169 bld.env.added_project_rules = True
1170 bld.add_pre_fun(check_project_rules)
1171 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES