waf: loosen the restriction on depending on python libs
[Samba/gebeck_regimport.git] / buildtools / wafsamba / samba_deps.py
bloba89c3e8ca3542c07bfd2598fcdb55b308e03c18d
1 # Samba automatic dependency handling and project rules
3 import Build, os, re, Environment, Logs
4 from samba_utils import *
5 from samba_autoconf import *
6 from samba_bundled import BUILTIN_LIBRARY
8 @conf
9 def ADD_GLOBAL_DEPENDENCY(ctx, dep):
10 '''add a dependency for all binaries and libraries'''
11 if not 'GLOBAL_DEPENDENCIES' in ctx.env:
12 ctx.env.GLOBAL_DEPENDENCIES = []
13 ctx.env.GLOBAL_DEPENDENCIES.append(dep)
16 @conf
17 def BREAK_CIRCULAR_LIBRARY_DEPENDENCIES(ctx):
18 '''indicate that circular dependencies between libraries should be broken.'''
19 ctx.env.ALLOW_CIRCULAR_LIB_DEPENDENCIES = True
22 def TARGET_ALIAS(bld, target, alias):
23 '''define an alias for a target name'''
24 cache = LOCAL_CACHE(bld, 'TARGET_ALIAS')
25 if alias in cache:
26 Logs.error("Target alias %s already set to %s : newalias %s" % (alias, cache[alias], target))
27 sys.exit(1)
28 cache[alias] = target
29 Build.BuildContext.TARGET_ALIAS = TARGET_ALIAS
32 @conf
33 def SET_SYSLIB_DEPS(conf, target, deps):
34 '''setup some implied dependencies for a SYSLIB'''
35 cache = LOCAL_CACHE(conf, 'SYSLIB_DEPS')
36 cache[target] = deps
39 def EXPAND_ALIAS(bld, target):
40 '''expand a target name via an alias'''
41 aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS')
42 if target in aliases:
43 return aliases[target]
44 return target
45 Build.BuildContext.EXPAND_ALIAS = EXPAND_ALIAS
48 def expand_subsystem_deps(bld):
49 '''expand the reverse dependencies resulting from subsystem
50 attributes of modules. This is walking over the complete list
51 of declared subsystems, and expands the samba_deps_extended list for any
52 module<->subsystem dependencies'''
54 subsystem_list = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
55 aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS')
56 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
58 for subsystem_name in subsystem_list:
59 if subsystem_name in aliases:
60 subsystem_name = aliases[subsystem_name]
61 bld.ASSERT(subsystem_name in targets, "Subsystem target %s not declared" % subsystem_name)
62 type = targets[subsystem_name]
63 if type == 'DISABLED' or type == 'EMPTY':
64 continue
66 # for example,
67 # subsystem_name = dcerpc_server (a subsystem)
68 # subsystem = dcerpc_server (a subsystem object)
69 # module_name = rpc_epmapper (a module within the dcerpc_server subsystem)
70 # module = rpc_epmapper (a module object within the dcerpc_server subsystem)
72 subsystem = bld.name_to_obj(subsystem_name, bld.env)
73 for d in subsystem_list[subsystem_name]:
74 module_name = d['TARGET']
75 module_type = targets[module_name]
76 if module_type in ['DISABLED', 'EMPTY']:
77 continue
78 bld.ASSERT(subsystem is not None,
79 "Subsystem target %s for %s (%s) not found" % (subsystem_name, module_name, module_type))
80 if module_type in ['SUBSYSTEM']:
81 # if a module is a plain object type (not a library) then the
82 # subsystem it is part of needs to have it as a dependency, so targets
83 # that depend on this subsystem get the modules of that subsystem
84 subsystem.samba_deps_extended.append(module_name)
85 module = bld.name_to_obj(module_name, bld.env)
86 module.samba_includes_extended.extend(subsystem.samba_includes_extended)
87 if targets[subsystem_name] in ['SUBSYSTEM']:
88 # if a subsystem is a plain object type (not a library) then any modules
89 # in that subsystem need to depend on the subsystem
90 module.samba_deps_extended.extend(subsystem.samba_deps_extended)
91 subsystem.samba_deps_extended = unique_list(subsystem.samba_deps_extended)
95 def build_dependencies(self):
96 '''This builds the dependency list for a target. It runs after all the targets are declared
98 The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
99 the full dependency list for a target until we have all of the targets declared.
102 if self.samba_type in ['LIBRARY', 'BINARY', 'PYTHON']:
103 self.uselib = list(self.final_syslibs)
104 self.uselib_local = list(self.final_libs)
105 self.add_objects = list(self.final_objects)
107 # extra link flags from pkg_config
108 libs = self.final_syslibs.copy()
110 (ccflags, ldflags) = library_flags(self, list(libs))
111 new_ldflags = getattr(self, 'ldflags', [])
112 new_ldflags.extend(ldflags)
113 self.ldflags = new_ldflags
115 debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
116 self.sname, self.uselib, self.uselib_local, self.add_objects)
118 if self.samba_type in ['SUBSYSTEM']:
119 # this is needed for the ccflags of libs that come from pkg_config
120 self.uselib = list(self.direct_syslibs)
122 if getattr(self, 'uselib', None):
123 up_list = []
124 for l in self.uselib:
125 up_list.append(l.upper())
126 self.uselib = up_list
128 def build_includes(self):
129 '''This builds the right set of includes for a target.
131 One tricky part of this is that the includes= attribute for a
132 target needs to use paths which are relative to that targets
133 declaration directory (which we can get at via t.path).
135 The way this works is the includes list gets added as
136 samba_includes in the main build task declaration. Then this
137 function runs after all of the tasks are declared, and it
138 processes the samba_includes attribute to produce a includes=
139 attribute
142 if getattr(self, 'samba_includes', None) is None:
143 return
145 bld = self.bld
147 inc_deps = includes_objects(bld, self, set(), {})
149 includes = []
151 # maybe add local includes
152 if getattr(self, 'local_include', True) == True and getattr(self, 'local_include_first', True):
153 includes.append('.')
155 includes.extend(self.samba_includes_extended)
157 if 'EXTRA_INCLUDES' in bld.env:
158 includes.extend(bld.env['EXTRA_INCLUDES'])
160 includes.append('#')
162 inc_set = set()
163 inc_abs = []
165 for d in inc_deps:
166 t = bld.name_to_obj(d, bld.env)
167 bld.ASSERT(t is not None, "Unable to find dependency %s for %s" % (d, self.sname))
168 inclist = getattr(t, 'samba_includes_extended', [])
169 if getattr(t, 'local_include', True) == True:
170 inclist.append('.')
171 if inclist == []:
172 continue
173 tpath = t.samba_abspath
174 for inc in inclist:
175 npath = tpath + '/' + inc
176 if not npath in inc_set:
177 inc_abs.append(npath)
178 inc_set.add(npath)
180 mypath = self.path.abspath(bld.env)
181 for inc in inc_abs:
182 relpath = os_path_relpath(inc, mypath)
183 includes.append(relpath)
185 if getattr(self, 'local_include', True) == True and not getattr(self, 'local_include_first', True):
186 includes.append('.')
188 # now transform the includes list to be relative to the top directory
189 # which is represented by '#' in waf. This allows waf to cache the
190 # includes lists more efficiently
191 includes_top = []
192 for i in includes:
193 if i[0] == '#':
194 # some are already top based
195 includes_top.append(i)
196 continue
197 absinc = os.path.join(self.path.abspath(), i)
198 relinc = os_path_relpath(absinc, self.bld.srcnode.abspath())
199 includes_top.append('#' + relinc)
201 self.includes = unique_list(includes_top)
202 debug('deps: includes for target %s: includes=%s',
203 self.sname, self.includes)
208 def add_init_functions(self):
209 '''This builds the right set of init functions'''
211 bld = self.bld
213 subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
215 # cope with the separated object lists from BINARY and LIBRARY targets
216 sname = self.sname
217 if sname.endswith('.objlist'):
218 sname = sname[0:-8]
220 modules = []
221 if sname in subsystems:
222 modules.append(sname)
224 m = getattr(self, 'samba_modules', None)
225 if m is not None:
226 modules.extend(TO_LIST(m))
228 m = getattr(self, 'samba_subsystem', None)
229 if m is not None:
230 modules.append(m)
232 if modules == []:
233 return
235 sentinal = getattr(self, 'init_function_sentinal', 'NULL')
237 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
239 cflags = getattr(self, 'samba_cflags', [])[:]
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, sentinal))
249 else:
250 cflags.append('-DSTATIC_%s_MODULES=%s' % (m, ','.join(init_fn_list) + ',' + sentinal))
251 self.ccflags = cflags
255 def check_duplicate_sources(bld, tgt_list):
256 '''see if we are compiling the same source file into multiple
257 subsystem targets for the same library or binary'''
259 debug('deps: checking for duplicate sources')
261 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
262 ret = True
264 seen = set()
266 for t in tgt_list:
267 obj_sources = getattr(t, 'source', '')
268 tpath = os.path.normpath(os_path_relpath(t.path.abspath(bld.env), t.env.BUILD_DIRECTORY + '/default'))
269 obj_sources = bld.SUBDIR(tpath, obj_sources)
270 t.samba_source_set = set(TO_LIST(obj_sources))
272 for t in tgt_list:
273 if not targets[t.sname] in [ 'LIBRARY', 'BINARY', 'PYTHON' ]:
274 continue
276 sources = []
277 for obj in t.add_objects:
278 t2 = t.bld.name_to_obj(obj, bld.env)
279 source_set = getattr(t2, 'samba_source_set', set())
280 sources.append( { 'dep':obj, 'src':source_set} )
281 for s in sources:
282 for s2 in sources:
283 if s['dep'] == s2['dep']: continue
284 common = s['src'].intersection(s2['src'])
285 if common.difference(seen):
286 Logs.error("Target %s has duplicate source files in %s and %s : %s" % (t.sname,
287 s['dep'], s2['dep'],
288 common))
289 seen = seen.union(common)
290 ret = False
291 return ret
294 def check_orpaned_targets(bld, tgt_list):
295 '''check if any build targets are orphaned'''
297 target_dict = LOCAL_CACHE(bld, 'TARGET_TYPE')
299 debug('deps: checking for orphaned targets')
301 for t in tgt_list:
302 if getattr(t, 'samba_used', False) == True:
303 continue
304 type = target_dict[t.sname]
305 if not type in ['BINARY', 'LIBRARY', 'MODULE', 'ET', 'PYTHON']:
306 if re.search('^PIDL_', t.sname) is None:
307 Logs.warn("Target %s of type %s is unused by any other target" % (t.sname, type))
310 def check_group_ordering(bld, tgt_list):
311 '''see if we have any dependencies that violate the group ordering
313 It is an error for a target to depend on a target from a later
314 build group
317 def group_name(g):
318 tm = bld.task_manager
319 return [x for x in tm.groups_names if id(tm.groups_names[x]) == id(g)][0]
321 for g in bld.task_manager.groups:
322 gname = group_name(g)
323 for t in g.tasks_gen:
324 t.samba_group = gname
326 grp_map = {}
327 idx = 0
328 for g in bld.task_manager.groups:
329 name = group_name(g)
330 grp_map[name] = idx
331 idx += 1
333 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
335 ret = True
336 for t in tgt_list:
337 tdeps = getattr(t, 'add_objects', []) + getattr(t, 'uselib_local', [])
338 for d in tdeps:
339 t2 = bld.name_to_obj(d, bld.env)
340 if t2 is None:
341 continue
342 map1 = grp_map[t.samba_group]
343 map2 = grp_map[t2.samba_group]
345 if map2 > map1:
346 Logs.error("Target %r in build group %r depends on target %r from later build group %r" % (
347 t.sname, t.samba_group, t2.sname, t2.samba_group))
348 ret = False
350 return ret
353 def show_final_deps(bld, tgt_list):
354 '''show the final dependencies for all targets'''
356 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
358 for t in tgt_list:
359 if not targets[t.sname] in ['LIBRARY', 'BINARY', 'PYTHON']:
360 continue
361 debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
362 t.sname, t.uselib, t.uselib_local, t.add_objects)
365 def add_samba_attributes(bld, tgt_list):
366 '''ensure a target has a the required samba attributes'''
368 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
370 for t in tgt_list:
371 if t.name != '':
372 t.sname = t.name
373 else:
374 t.sname = t.target
375 t.samba_type = targets[t.sname]
376 t.samba_abspath = t.path.abspath(bld.env)
377 t.samba_deps_extended = t.samba_deps[:]
378 t.samba_includes_extended = TO_LIST(t.samba_includes)[:]
379 t.ccflags = getattr(t, 'samba_cflags', '')
381 def replace_grouping_libraries(bld, tgt_list):
382 '''replace dependencies based on grouping libraries
384 If a library is marked as a grouping library, then any target that
385 depends on a subsystem that is part of that grouping library gets
386 that dependency replaced with a dependency on the grouping library
389 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
391 grouping = {}
393 # find our list of grouping libraries, mapped from the subsystems they depend on
394 for t in tgt_list:
395 if not getattr(t, 'grouping_library', False):
396 continue
397 for dep in t.samba_deps_extended:
398 if targets[dep] == 'SUBSYSTEM':
399 grouping[dep] = t.sname
401 # now replace any dependencies on elements of grouping libraries
402 for t in tgt_list:
403 for i in range(len(t.samba_deps_extended)):
404 dep = t.samba_deps_extended[i]
405 if dep in grouping:
406 if t.sname != grouping[dep]:
407 debug("deps: target %s: replacing dependency %s with grouping library %s" % (t.sname, dep, grouping[dep]))
408 t.samba_deps_extended[i] = grouping[dep]
412 def build_direct_deps(bld, tgt_list):
413 '''build the direct_objects and direct_libs sets for each target'''
415 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
416 syslib_deps = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
418 global_deps = bld.env.GLOBAL_DEPENDENCIES
419 global_deps_exclude = set()
420 for dep in global_deps:
421 t = bld.name_to_obj(dep, bld.env)
422 for d in t.samba_deps:
423 # prevent loops from the global dependencies list
424 global_deps_exclude.add(d)
425 global_deps_exclude.add(d + '.objlist')
427 for t in tgt_list:
428 t.direct_objects = set()
429 t.direct_libs = set()
430 t.direct_syslibs = set()
431 deps = t.samba_deps_extended[:]
432 if getattr(t, 'samba_use_global_deps', False) and not t.sname in global_deps_exclude:
433 deps.extend(global_deps)
434 for d in deps:
435 d = EXPAND_ALIAS(bld, d)
436 if d == t.sname: continue
437 if not d in targets:
438 Logs.error("Unknown dependency %s in %s" % (d, t.sname))
439 sys.exit(1)
440 if targets[d] in [ 'EMPTY', 'DISABLED' ]:
441 continue
442 if targets[d] == 'PYTHON' and targets[t.sname] != 'PYTHON' and t.sname.find('.objlist') == -1:
443 # this check should be more restrictive, but for now we have pidl-generated python
444 # code that directly depends on other python modules
445 Logs.error('ERROR: Target %s has dependency on python module %s' % (t.sname, d))
446 sys.exit(1)
447 if targets[d] == 'SYSLIB':
448 t.direct_syslibs.add(d)
449 if d in syslib_deps:
450 for implied in TO_LIST(syslib_deps[d]):
451 if BUILTIN_LIBRARY(bld, implied):
452 t.direct_objects.add(implied)
453 elif targets[implied] == 'SYSLIB':
454 t.direct_syslibs.add(implied)
455 elif targets[implied] in ['LIBRARY', 'MODULE']:
456 t.direct_libs.add(implied)
457 else:
458 Logs.error('Implied dependency %s in %s is of type %s' % (
459 implied, t.sname, targets[implied]))
460 sys.exit(1)
461 continue
462 t2 = bld.name_to_obj(d, bld.env)
463 if t2 is None:
464 Logs.error("no task %s of type %s in %s" % (d, targets[d], t.sname))
465 sys.exit(1)
466 if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
467 t.direct_libs.add(d)
468 elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
469 t.direct_objects.add(d)
470 debug('deps: built direct dependencies')
473 def dependency_loop(loops, t, target):
474 '''add a dependency loop to the loops dictionary'''
475 if t.sname == target:
476 return
477 if not target in loops:
478 loops[target] = set()
479 if not t.sname in loops[target]:
480 loops[target].add(t.sname)
483 def indirect_libs(bld, t, chain, loops):
484 '''recursively calculate the indirect library dependencies for a target
486 An indirect library is a library that results from a dependency on
487 a subsystem
490 ret = getattr(t, 'indirect_libs', None)
491 if ret is not None:
492 return ret
494 ret = set()
495 for obj in t.direct_objects:
496 if obj in chain:
497 dependency_loop(loops, t, obj)
498 continue
499 chain.add(obj)
500 t2 = bld.name_to_obj(obj, bld.env)
501 r2 = indirect_libs(bld, t2, chain, loops)
502 chain.remove(obj)
503 ret = ret.union(t2.direct_libs)
504 ret = ret.union(r2)
506 for obj in indirect_objects(bld, t, set(), loops):
507 if obj in chain:
508 dependency_loop(loops, t, obj)
509 continue
510 chain.add(obj)
511 t2 = bld.name_to_obj(obj, bld.env)
512 r2 = indirect_libs(bld, t2, chain, loops)
513 chain.remove(obj)
514 ret = ret.union(t2.direct_libs)
515 ret = ret.union(r2)
517 t.indirect_libs = ret
519 return ret
522 def indirect_objects(bld, t, chain, loops):
523 '''recursively calculate the indirect object dependencies for a target
525 indirect objects are the set of objects from expanding the
526 subsystem dependencies
529 ret = getattr(t, 'indirect_objects', None)
530 if ret is not None: return ret
532 ret = set()
533 for lib in t.direct_objects:
534 if lib in chain:
535 dependency_loop(loops, t, lib)
536 continue
537 chain.add(lib)
538 t2 = bld.name_to_obj(lib, bld.env)
539 r2 = indirect_objects(bld, t2, chain, loops)
540 chain.remove(lib)
541 ret = ret.union(t2.direct_objects)
542 ret = ret.union(r2)
544 t.indirect_objects = ret
545 return ret
548 def extended_objects(bld, t, chain):
549 '''recursively calculate the extended object dependencies for a target
551 extended objects are the union of:
552 - direct objects
553 - indirect objects
554 - direct and indirect objects of all direct and indirect libraries
557 ret = getattr(t, 'extended_objects', None)
558 if ret is not None: return ret
560 ret = set()
561 ret = ret.union(t.final_objects)
563 for lib in t.final_libs:
564 if lib in chain:
565 continue
566 t2 = bld.name_to_obj(lib, bld.env)
567 chain.add(lib)
568 r2 = extended_objects(bld, t2, chain)
569 chain.remove(lib)
570 ret = ret.union(t2.final_objects)
571 ret = ret.union(r2)
573 t.extended_objects = ret
574 return ret
577 def includes_objects(bld, t, chain, inc_loops):
578 '''recursively calculate the includes object dependencies for a target
580 includes dependencies come from either library or object dependencies
582 ret = getattr(t, 'includes_objects', None)
583 if ret is not None:
584 return ret
586 ret = t.direct_objects.copy()
587 ret = ret.union(t.direct_libs)
589 for obj in t.direct_objects:
590 if obj in chain:
591 dependency_loop(inc_loops, t, obj)
592 continue
593 chain.add(obj)
594 t2 = bld.name_to_obj(obj, bld.env)
595 r2 = includes_objects(bld, t2, chain, inc_loops)
596 chain.remove(obj)
597 ret = ret.union(t2.direct_objects)
598 ret = ret.union(r2)
600 for lib in t.direct_libs:
601 if lib in chain:
602 dependency_loop(inc_loops, t, lib)
603 continue
604 chain.add(lib)
605 t2 = bld.name_to_obj(lib, bld.env)
606 if t2 is None:
607 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
608 Logs.error('Target %s of type %s not found in direct_libs for %s' % (
609 lib, targets[lib], t.sname))
610 sys.exit(1)
611 r2 = includes_objects(bld, t2, chain, inc_loops)
612 chain.remove(lib)
613 ret = ret.union(t2.direct_objects)
614 ret = ret.union(r2)
616 t.includes_objects = ret
617 return ret
620 def break_dependency_loops(bld, tgt_list):
621 '''find and break dependency loops'''
622 loops = {}
623 inc_loops = {}
625 # build up the list of loops
626 for t in tgt_list:
627 indirect_objects(bld, t, set(), loops)
628 indirect_libs(bld, t, set(), loops)
629 includes_objects(bld, t, set(), inc_loops)
631 # break the loops
632 for t in tgt_list:
633 if t.sname in loops:
634 for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
635 objs = getattr(t, attr, set())
636 setattr(t, attr, objs.difference(loops[t.sname]))
638 for loop in loops:
639 debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
641 for loop in inc_loops:
642 debug('deps: Found include loops for target %s : %s', loop, inc_loops[loop])
644 # expand the loops mapping by one level
645 for loop in loops.copy():
646 for tgt in loops[loop]:
647 if tgt in loops:
648 loops[loop] = loops[loop].union(loops[tgt])
650 for loop in inc_loops.copy():
651 for tgt in inc_loops[loop]:
652 if tgt in inc_loops:
653 inc_loops[loop] = inc_loops[loop].union(inc_loops[tgt])
656 # expand indirect subsystem and library loops
657 for loop in loops.copy():
658 t = bld.name_to_obj(loop, bld.env)
659 if t.samba_type in ['SUBSYSTEM']:
660 loops[loop] = loops[loop].union(t.indirect_objects)
661 loops[loop] = loops[loop].union(t.direct_objects)
662 if t.samba_type in ['LIBRARY','PYTHON']:
663 loops[loop] = loops[loop].union(t.indirect_libs)
664 loops[loop] = loops[loop].union(t.direct_libs)
665 if loop in loops[loop]:
666 loops[loop].remove(loop)
668 # expand indirect includes loops
669 for loop in inc_loops.copy():
670 t = bld.name_to_obj(loop, bld.env)
671 inc_loops[loop] = inc_loops[loop].union(t.includes_objects)
672 if loop in inc_loops[loop]:
673 inc_loops[loop].remove(loop)
675 # add in the replacement dependencies
676 for t in tgt_list:
677 for loop in loops:
678 for attr in ['indirect_objects', 'indirect_libs']:
679 objs = getattr(t, attr, set())
680 if loop in objs:
681 diff = loops[loop].difference(objs)
682 if t.sname in diff:
683 diff.remove(t.sname)
684 if diff:
685 debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
686 objs = objs.union(diff)
687 setattr(t, attr, objs)
689 for loop in inc_loops:
690 objs = getattr(t, 'includes_objects', set())
691 if loop in objs:
692 diff = inc_loops[loop].difference(objs)
693 if t.sname in diff:
694 diff.remove(t.sname)
695 if diff:
696 debug('deps: Expanded target %s includes of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
697 objs = objs.union(diff)
698 setattr(t, 'includes_objects', objs)
701 def reduce_objects(bld, tgt_list):
702 '''reduce objects by looking for indirect object dependencies'''
703 rely_on = {}
705 for t in tgt_list:
706 t.extended_objects = None
708 changed = False
710 for type in ['BINARY', 'PYTHON', 'LIBRARY']:
711 for t in tgt_list:
712 if t.samba_type != type: continue
713 # if we will indirectly link to a target then we don't need it
714 new = t.final_objects.copy()
715 for l in t.final_libs:
716 t2 = bld.name_to_obj(l, bld.env)
717 t2_obj = extended_objects(bld, t2, set())
718 dup = new.intersection(t2_obj)
719 if t.sname in rely_on:
720 dup = dup.difference(rely_on[t.sname])
721 if dup:
722 debug('deps: removing dups from %s of type %s: %s also in %s %s',
723 t.sname, t.samba_type, dup, t2.samba_type, l)
724 new = new.difference(dup)
725 changed = True
726 if not l in rely_on:
727 rely_on[l] = set()
728 rely_on[l] = rely_on[l].union(dup)
729 t.final_objects = new
731 if not changed:
732 return False
734 # add back in any objects that were relied upon by the reduction rules
735 for r in rely_on:
736 t = bld.name_to_obj(r, bld.env)
737 t.final_objects = t.final_objects.union(rely_on[r])
739 return True
742 def calculate_final_deps(bld, tgt_list, loops):
743 '''calculate the final library and object dependencies'''
744 for t in tgt_list:
745 # start with the maximum possible list
746 t.final_libs = t.direct_libs.union(indirect_libs(bld, t, set(), loops))
747 t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops))
749 for t in tgt_list:
750 # don't depend on ourselves
751 if t.sname in t.final_libs:
752 t.final_libs.remove(t.sname)
753 if t.sname in t.final_objects:
754 t.final_objects.remove(t.sname)
756 # handle any non-shared binaries
757 for t in tgt_list:
758 if t.samba_type == 'BINARY' and bld.NONSHARED_BINARY(t.sname):
759 # replace lib deps with objlist deps
760 for l in t.final_libs:
761 objname = l + '.objlist'
762 t2 = bld.name_to_obj(objname, bld.env)
763 if t2 is None:
764 Logs.error('ERROR: subsystem %s not found' % objname)
765 sys.exit(1)
766 t.final_objects.add(objname)
767 t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
768 t.final_libs = set()
770 # find any library loops
771 for t in tgt_list:
772 if t.samba_type in ['LIBRARY', 'PYTHON']:
773 for l in t.final_libs.copy():
774 t2 = bld.name_to_obj(l, bld.env)
775 if t.sname in t2.final_libs:
776 if getattr(bld.env, "ALLOW_CIRCULAR_LIB_DEPENDENCIES", False):
777 # we could break this in either direction. If one of the libraries
778 # has a version number, and will this be distributed publicly, then
779 # we should make it the lower level library in the DAG
780 Logs.warn('deps: removing library loop %s from %s' % (t.sname, t2.sname))
781 dependency_loop(loops, t, t2.sname)
782 t2.final_libs.remove(t.sname)
783 else:
784 Logs.error('ERROR: circular library dependency between %s and %s'
785 % (t.sname, t2.sname))
786 sys.exit(1)
788 for loop in loops:
789 debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
791 # we now need to make corrections for any library loops we broke up
792 # any target that depended on the target of the loop and doesn't
793 # depend on the source of the loop needs to get the loop source added
794 for type in ['BINARY','PYTHON','LIBRARY','BINARY']:
795 for t in tgt_list:
796 if t.samba_type != type: continue
797 for loop in loops:
798 if loop in t.final_libs:
799 diff = loops[loop].difference(t.final_libs)
800 if t.sname in diff:
801 diff.remove(t.sname)
802 if t.sname in diff:
803 diff.remove(t.sname)
804 # make sure we don't recreate the loop again!
805 for d in diff.copy():
806 t2 = bld.name_to_obj(d, bld.env)
807 if t2.samba_type == 'LIBRARY':
808 if t.sname in t2.final_libs:
809 debug('deps: removing expansion %s from %s', d, t.sname)
810 diff.remove(d)
811 if diff:
812 debug('deps: Expanded target %s by loop %s libraries (loop %s) %s', t.sname, loop,
813 loops[loop], diff)
814 t.final_libs = t.final_libs.union(diff)
816 # remove objects that are also available in linked libs
817 count = 0
818 while reduce_objects(bld, tgt_list):
819 count += 1
820 if count > 100:
821 Logs.warn("WARNING: Unable to remove all inter-target object duplicates")
822 break
823 debug('deps: Object reduction took %u iterations', count)
825 # add in any syslib dependencies
826 for t in tgt_list:
827 if not t.samba_type in ['BINARY','PYTHON','LIBRARY']:
828 continue
829 syslibs = set()
830 for d in t.final_objects:
831 t2 = bld.name_to_obj(d, bld.env)
832 syslibs = syslibs.union(t2.direct_syslibs)
833 # this adds the indirect syslibs as well, which may not be needed
834 # depending on the linker flags
835 for d in t.final_libs:
836 t2 = bld.name_to_obj(d, bld.env)
837 syslibs = syslibs.union(t2.direct_syslibs)
838 t.final_syslibs = syslibs
841 # find any unresolved library loops
842 lib_loop_error = False
843 for t in tgt_list:
844 if t.samba_type in ['LIBRARY', 'PYTHON']:
845 for l in t.final_libs.copy():
846 t2 = bld.name_to_obj(l, bld.env)
847 if t.sname in t2.final_libs:
848 Logs.error('ERROR: Unresolved library loop %s from %s' % (t.sname, t2.sname))
849 lib_loop_error = True
850 if lib_loop_error:
851 sys.exit(1)
853 debug('deps: removed duplicate dependencies')
856 def show_dependencies(bld, target, seen):
857 '''recursively show the dependencies of target'''
859 if target in seen:
860 return
862 t = bld.name_to_obj(target, bld.env)
863 if t is None:
864 Logs.error("ERROR: Unable to find target '%s'" % target)
865 sys.exit(1)
867 Logs.info('%s(OBJECTS): %s' % (target, t.direct_objects))
868 Logs.info('%s(LIBS): %s' % (target, t.direct_libs))
869 Logs.info('%s(SYSLIBS): %s' % (target, t.direct_syslibs))
871 seen.add(target)
873 for t2 in t.direct_objects:
874 show_dependencies(bld, t2, seen)
877 def show_object_duplicates(bld, tgt_list):
878 '''show a list of object files that are included in more than
879 one library or binary'''
881 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
883 used_by = {}
885 Logs.info("showing duplicate objects")
887 for t in tgt_list:
888 if not targets[t.sname] in [ 'LIBRARY', 'PYTHON' ]:
889 continue
890 for n in getattr(t, 'final_objects', set()):
891 t2 = bld.name_to_obj(n, bld.env)
892 if not n in used_by:
893 used_by[n] = set()
894 used_by[n].add(t.sname)
896 for n in used_by:
897 if len(used_by[n]) > 1:
898 Logs.info("target '%s' is used by %s" % (n, used_by[n]))
900 Logs.info("showing indirect dependency counts (sorted by count)")
902 def indirect_count(t1, t2):
903 return len(t2.indirect_objects) - len(t1.indirect_objects)
905 sorted_list = sorted(tgt_list, cmp=indirect_count)
906 for t in sorted_list:
907 if len(t.indirect_objects) > 1:
908 Logs.info("%s depends on %u indirect objects" % (t.sname, len(t.indirect_objects)))
911 ######################################################################
912 # this provides a way to save our dependency calculations between runs
913 savedeps_version = 3
914 savedeps_inputs = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags', 'source', 'grouping_library']
915 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags', 'ldflags']
916 savedeps_outenv = ['INC_PATHS']
917 savedeps_envvars = ['NONSHARED_BINARIES', 'GLOBAL_DEPENDENCIES']
918 savedeps_caches = ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
919 savedeps_files = ['buildtools/wafsamba/samba_deps.py']
921 def save_samba_deps(bld, tgt_list):
922 '''save the dependency calculations between builds, to make
923 further builds faster'''
924 denv = Environment.Environment()
926 denv.version = savedeps_version
927 denv.savedeps_inputs = savedeps_inputs
928 denv.savedeps_outputs = savedeps_outputs
929 denv.input = {}
930 denv.output = {}
931 denv.outenv = {}
932 denv.caches = {}
933 denv.envvar = {}
934 denv.files = {}
936 for f in savedeps_files:
937 denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
939 for c in savedeps_caches:
940 denv.caches[c] = LOCAL_CACHE(bld, c)
942 for e in savedeps_envvars:
943 denv.envvar[e] = bld.env[e]
945 for t in tgt_list:
946 # save all the input attributes for each target
947 tdeps = {}
948 for attr in savedeps_inputs:
949 v = getattr(t, attr, None)
950 if v is not None:
951 tdeps[attr] = v
952 if tdeps != {}:
953 denv.input[t.sname] = tdeps
955 # save all the output attributes for each target
956 tdeps = {}
957 for attr in savedeps_outputs:
958 v = getattr(t, attr, None)
959 if v is not None:
960 tdeps[attr] = v
961 if tdeps != {}:
962 denv.output[t.sname] = tdeps
964 tdeps = {}
965 for attr in savedeps_outenv:
966 if attr in t.env:
967 tdeps[attr] = t.env[attr]
968 if tdeps != {}:
969 denv.outenv[t.sname] = tdeps
971 depsfile = os.path.join(bld.bdir, "sambadeps")
972 denv.store(depsfile)
976 def load_samba_deps(bld, tgt_list):
977 '''load a previous set of build dependencies if possible'''
978 depsfile = os.path.join(bld.bdir, "sambadeps")
979 denv = Environment.Environment()
980 try:
981 debug('deps: checking saved dependencies')
982 denv.load(depsfile)
983 if (denv.version != savedeps_version or
984 denv.savedeps_inputs != savedeps_inputs or
985 denv.savedeps_outputs != savedeps_outputs):
986 return False
987 except:
988 return False
990 # check if critical files have changed
991 for f in savedeps_files:
992 if f not in denv.files:
993 return False
994 if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
995 return False
997 # check if caches are the same
998 for c in savedeps_caches:
999 if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
1000 return False
1002 # check if caches are the same
1003 for e in savedeps_envvars:
1004 if e not in denv.envvar or denv.envvar[e] != bld.env[e]:
1005 return False
1007 # check inputs are the same
1008 for t in tgt_list:
1009 tdeps = {}
1010 for attr in savedeps_inputs:
1011 v = getattr(t, attr, None)
1012 if v is not None:
1013 tdeps[attr] = v
1014 if t.sname in denv.input:
1015 olddeps = denv.input[t.sname]
1016 else:
1017 olddeps = {}
1018 if tdeps != olddeps:
1019 #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
1020 return False
1022 # put outputs in place
1023 for t in tgt_list:
1024 if not t.sname in denv.output: continue
1025 tdeps = denv.output[t.sname]
1026 for a in tdeps:
1027 setattr(t, a, tdeps[a])
1029 # put output env vars in place
1030 for t in tgt_list:
1031 if not t.sname in denv.outenv: continue
1032 tdeps = denv.outenv[t.sname]
1033 for a in tdeps:
1034 t.env[a] = tdeps[a]
1036 debug('deps: loaded saved dependencies')
1037 return True
1041 def check_project_rules(bld):
1042 '''check the project rules - ensuring the targets are sane'''
1044 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
1045 loops = {}
1046 inc_loops = {}
1048 # build a list of task generators we are interested in
1049 tgt_list = []
1050 for tgt in targets:
1051 type = targets[tgt]
1052 if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']:
1053 continue
1054 t = bld.name_to_obj(tgt, bld.env)
1055 if t is None:
1056 Logs.error("Target %s of type %s has no task generator" % (tgt, type))
1057 sys.exit(1)
1058 tgt_list.append(t)
1060 add_samba_attributes(bld, tgt_list)
1062 force_project_rules = (Options.options.SHOWDEPS or
1063 Options.options.SHOW_DUPLICATES)
1065 if not force_project_rules and load_samba_deps(bld, tgt_list):
1066 return
1068 bld.new_rules = True
1069 Logs.info("Checking project rules ...")
1071 debug('deps: project rules checking started')
1073 expand_subsystem_deps(bld)
1074 replace_grouping_libraries(bld, tgt_list)
1075 build_direct_deps(bld, tgt_list)
1077 break_dependency_loops(bld, tgt_list)
1079 if Options.options.SHOWDEPS:
1080 show_dependencies(bld, Options.options.SHOWDEPS, set())
1082 calculate_final_deps(bld, tgt_list, loops)
1084 if Options.options.SHOW_DUPLICATES:
1085 show_object_duplicates(bld, tgt_list)
1087 # run the various attribute generators
1088 for f in [ build_dependencies, build_includes, add_init_functions ]:
1089 debug('deps: project rules checking %s', f)
1090 for t in tgt_list: f(t)
1092 debug('deps: project rules stage1 completed')
1094 #check_orpaned_targets(bld, tgt_list)
1096 if not check_duplicate_sources(bld, tgt_list):
1097 Logs.error("Duplicate sources present - aborting")
1098 sys.exit(1)
1100 if not check_group_ordering(bld, tgt_list):
1101 Logs.error("Bad group ordering - aborting")
1102 sys.exit(1)
1104 show_final_deps(bld, tgt_list)
1106 debug('deps: project rules checking completed - %u targets checked',
1107 len(tgt_list))
1109 if not bld.is_install:
1110 save_samba_deps(bld, tgt_list)
1112 Logs.info("Project rules pass")
1115 def CHECK_PROJECT_RULES(bld):
1116 '''enable checking of project targets for sanity'''
1117 if bld.env.added_project_rules:
1118 return
1119 bld.env.added_project_rules = True
1120 bld.add_pre_fun(check_project_rules)
1121 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES