s4-heimdal: Fix typo in comment.
[Samba/gbeck.git] / buildtools / wafsamba / samba_deps.py
blobd00fe7f4e56a650ff82c8283ca6d955c79110b2a
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 def TARGET_ALIAS(bld, target, alias):
17 '''define an alias for a target name'''
18 cache = LOCAL_CACHE(bld, 'TARGET_ALIAS')
19 if alias in cache:
20 Logs.error("Target alias %s already set to %s : newalias %s" % (alias, cache[alias], target))
21 sys.exit(1)
22 cache[alias] = target
23 Build.BuildContext.TARGET_ALIAS = TARGET_ALIAS
26 @conf
27 def SET_SYSLIB_DEPS(conf, target, deps):
28 '''setup some implied dependencies for a SYSLIB'''
29 cache = LOCAL_CACHE(conf, 'SYSLIB_DEPS')
30 cache[target] = deps
33 def EXPAND_ALIAS(bld, target):
34 '''expand a target name via an alias'''
35 aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS')
36 if target in aliases:
37 return aliases[target]
38 return target
39 Build.BuildContext.EXPAND_ALIAS = EXPAND_ALIAS
42 def expand_subsystem_deps(bld):
43 '''expand the reverse dependencies resulting from subsystem
44 attributes of modules'''
45 subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
46 aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS')
47 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
49 for s in subsystems:
50 if s in aliases:
51 s = aliases[s]
52 bld.ASSERT(s in targets, "Subsystem target %s not declared" % s)
53 type = targets[s]
54 if type == 'DISABLED' or type == 'EMPTY':
55 continue
57 t = bld.name_to_obj(s, bld.env)
58 bld.ASSERT(t is not None, "Subsystem target %s not found" % s)
59 for d in subsystems[s]:
60 type = targets[d['TARGET']]
61 if type != 'DISABLED' and type != 'EMPTY':
62 t.samba_deps_extended.append(d['TARGET'])
63 t2 = bld.name_to_obj(d['TARGET'], bld.env)
64 t2.samba_includes_extended.extend(t.samba_includes_extended)
65 t2.samba_deps_extended.extend(t.samba_deps_extended)
66 t.samba_deps_extended = unique_list(t.samba_deps_extended)
70 def build_dependencies(self):
71 '''This builds the dependency list for a target. It runs after all the targets are declared
73 The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
74 the full dependency list for a target until we have all of the targets declared.
75 '''
77 if self.samba_type in ['LIBRARY', 'BINARY', 'PYTHON']:
78 self.uselib = list(self.final_syslibs)
79 self.uselib_local = list(self.final_libs)
80 self.add_objects = list(self.final_objects)
82 # extra link flags from pkg_config
83 libs = self.final_syslibs.copy()
85 (ccflags, ldflags) = library_flags(self, list(libs))
86 new_ldflags = getattr(self, 'ldflags', [])
87 new_ldflags.extend(ldflags)
88 self.ldflags = new_ldflags
90 debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
91 self.sname, self.uselib, self.uselib_local, self.add_objects)
93 if self.samba_type in ['SUBSYSTEM']:
94 # this is needed for the ccflags of libs that come from pkg_config
95 self.uselib = list(self.direct_syslibs)
97 if getattr(self, 'uselib', None):
98 up_list = []
99 for l in self.uselib:
100 up_list.append(l.upper())
101 self.uselib = up_list
103 def build_includes(self):
104 '''This builds the right set of includes for a target.
106 One tricky part of this is that the includes= attribute for a
107 target needs to use paths which are relative to that targets
108 declaration directory (which we can get at via t.path).
110 The way this works is the includes list gets added as
111 samba_includes in the main build task declaration. Then this
112 function runs after all of the tasks are declared, and it
113 processes the samba_includes attribute to produce a includes=
114 attribute
117 if getattr(self, 'samba_includes', None) is None:
118 return
120 bld = self.bld
122 inc_deps = includes_objects(bld, self, set(), {})
124 includes = []
126 # maybe add local includes
127 if getattr(self, 'local_include', True) == True and getattr(self, 'local_include_first', True):
128 includes.append('.')
130 includes.extend(self.samba_includes_extended)
132 if 'EXTRA_INCLUDES' in bld.env:
133 includes.extend(bld.env['EXTRA_INCLUDES'])
135 includes.append('#')
137 inc_set = set()
138 inc_abs = []
140 for d in inc_deps:
141 t = bld.name_to_obj(d, bld.env)
142 bld.ASSERT(t is not None, "Unable to find dependency %s for %s" % (d, self.sname))
143 inclist = getattr(t, 'samba_includes_extended', [])
144 if getattr(t, 'local_include', True) == True:
145 inclist.append('.')
146 if inclist == []:
147 continue
148 tpath = t.samba_abspath
149 for inc in inclist:
150 npath = tpath + '/' + inc
151 if not npath in inc_set:
152 inc_abs.append(npath)
153 inc_set.add(npath)
155 mypath = self.path.abspath(bld.env)
156 for inc in inc_abs:
157 relpath = os_path_relpath(inc, mypath)
158 includes.append(relpath)
160 if getattr(self, 'local_include', True) == True and not getattr(self, 'local_include_first', True):
161 includes.append('.')
163 # now transform the includes list to be relative to the top directory
164 # which is represented by '#' in waf. This allows waf to cache the
165 # includes lists more efficiently
166 includes_top = []
167 for i in includes:
168 if i[0] == '#':
169 # some are already top based
170 includes_top.append(i)
171 continue
172 absinc = os.path.join(self.path.abspath(), i)
173 relinc = os_path_relpath(absinc, self.bld.srcnode.abspath())
174 includes_top.append('#' + relinc)
176 self.includes = unique_list(includes_top)
177 debug('deps: includes for target %s: includes=%s',
178 self.sname, self.includes)
183 def add_init_functions(self):
184 '''This builds the right set of init functions'''
186 bld = self.bld
188 subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
190 # cope with the separated object lists from BINARY and LIBRARY targets
191 sname = self.sname
192 if sname.endswith('.objlist'):
193 sname = sname[0:-8]
195 modules = []
196 if sname in subsystems:
197 modules.append(sname)
199 m = getattr(self, 'samba_modules', None)
200 if m is not None:
201 modules.extend(TO_LIST(m))
203 m = getattr(self, 'samba_subsystem', None)
204 if m is not None:
205 modules.append(m)
207 if modules == []:
208 return
210 sentinal = getattr(self, 'init_function_sentinal', 'NULL')
212 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
214 cflags = getattr(self, 'samba_cflags', [])[:]
215 for m in modules:
216 bld.ASSERT(m in subsystems,
217 "No init_function defined for module '%s' in target '%s'" % (m, self.sname))
218 init_fn_list = []
219 for d in subsystems[m]:
220 if targets[d['TARGET']] != 'DISABLED':
221 init_fn_list.append(d['INIT_FUNCTION'])
222 if init_fn_list == []:
223 cflags.append('-DSTATIC_%s_MODULES=%s' % (m, sentinal))
224 else:
225 cflags.append('-DSTATIC_%s_MODULES=%s' % (m, ','.join(init_fn_list) + ',' + sentinal))
226 self.ccflags = cflags
230 def check_duplicate_sources(bld, tgt_list):
231 '''see if we are compiling the same source file into multiple
232 subsystem targets for the same library or binary'''
234 debug('deps: checking for duplicate sources')
236 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
237 ret = True
239 seen = set()
241 for t in tgt_list:
242 obj_sources = getattr(t, 'source', '')
243 tpath = os.path.normpath(os_path_relpath(t.path.abspath(bld.env), t.env.BUILD_DIRECTORY + '/default'))
244 obj_sources = bld.SUBDIR(tpath, obj_sources)
245 t.samba_source_set = set(TO_LIST(obj_sources))
247 for t in tgt_list:
248 if not targets[t.sname] in [ 'LIBRARY', 'BINARY', 'PYTHON' ]:
249 continue
251 sources = []
252 for obj in t.add_objects:
253 t2 = t.bld.name_to_obj(obj, bld.env)
254 source_set = getattr(t2, 'samba_source_set', set())
255 sources.append( { 'dep':obj, 'src':source_set} )
256 for s in sources:
257 for s2 in sources:
258 if s['dep'] == s2['dep']: continue
259 common = s['src'].intersection(s2['src'])
260 if common.difference(seen):
261 Logs.error("Target %s has duplicate source files in %s and %s : %s" % (t.sname,
262 s['dep'], s2['dep'],
263 common))
264 seen = seen.union(common)
265 ret = False
266 return ret
269 def check_orpaned_targets(bld, tgt_list):
270 '''check if any build targets are orphaned'''
272 target_dict = LOCAL_CACHE(bld, 'TARGET_TYPE')
274 debug('deps: checking for orphaned targets')
276 for t in tgt_list:
277 if getattr(t, 'samba_used', False) == True:
278 continue
279 type = target_dict[t.sname]
280 if not type in ['BINARY', 'LIBRARY', 'MODULE', 'ET', 'PYTHON']:
281 if re.search('^PIDL_', t.sname) is None:
282 Logs.warn("Target %s of type %s is unused by any other target" % (t.sname, type))
285 def check_group_ordering(bld, tgt_list):
286 '''see if we have any dependencies that violate the group ordering
288 It is an error for a target to depend on a target from a later
289 build group
292 def group_name(g):
293 tm = bld.task_manager
294 return [x for x in tm.groups_names if id(tm.groups_names[x]) == id(g)][0]
296 for g in bld.task_manager.groups:
297 gname = group_name(g)
298 for t in g.tasks_gen:
299 t.samba_group = gname
301 grp_map = {}
302 idx = 0
303 for g in bld.task_manager.groups:
304 name = group_name(g)
305 grp_map[name] = idx
306 idx += 1
308 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
310 ret = True
311 for t in tgt_list:
312 tdeps = getattr(t, 'add_objects', []) + getattr(t, 'uselib_local', [])
313 for d in tdeps:
314 t2 = bld.name_to_obj(d, bld.env)
315 if t2 is None:
316 continue
317 map1 = grp_map[t.samba_group]
318 map2 = grp_map[t2.samba_group]
320 if map2 > map1:
321 Logs.error("Target %r in build group %r depends on target %r from later build group %r" % (
322 t.sname, t.samba_group, t2.sname, t2.samba_group))
323 ret = False
325 return ret
328 def show_final_deps(bld, tgt_list):
329 '''show the final dependencies for all targets'''
331 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
333 for t in tgt_list:
334 if not targets[t.sname] in ['LIBRARY', 'BINARY', 'PYTHON']:
335 continue
336 debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
337 t.sname, t.uselib, t.uselib_local, t.add_objects)
340 def add_samba_attributes(bld, tgt_list):
341 '''ensure a target has a the required samba attributes'''
343 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
345 for t in tgt_list:
346 if t.name != '':
347 t.sname = t.name
348 else:
349 t.sname = t.target
350 t.samba_type = targets[t.sname]
351 t.samba_abspath = t.path.abspath(bld.env)
352 t.samba_deps_extended = t.samba_deps[:]
353 t.samba_includes_extended = TO_LIST(t.samba_includes)[:]
354 t.ccflags = getattr(t, 'samba_cflags', '')
357 def build_direct_deps(bld, tgt_list):
358 '''build the direct_objects and direct_libs sets for each target'''
360 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
361 syslib_deps = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
362 global_deps = bld.env.GLOBAL_DEPENDENCIES
364 for t in tgt_list:
365 t.direct_objects = set()
366 t.direct_libs = set()
367 t.direct_syslibs = set()
368 deps = t.samba_deps_extended
369 if getattr(t, 'samba_use_global_deps', False):
370 deps.extend(global_deps)
371 for d in deps:
372 d = EXPAND_ALIAS(bld, d)
373 if d == t.sname: continue
374 if not d in targets:
375 Logs.error("Unknown dependency %s in %s" % (d, t.sname))
376 sys.exit(1)
377 if targets[d] in [ 'EMPTY', 'DISABLED' ]:
378 continue
379 if targets[d] == 'SYSLIB':
380 t.direct_syslibs.add(d)
381 if d in syslib_deps:
382 for implied in TO_LIST(syslib_deps[d]):
383 if BUILTIN_LIBRARY(bld, implied):
384 t.direct_objects.add(implied)
385 elif targets[implied] == 'SYSLIB':
386 t.direct_syslibs.add(implied)
387 elif targets[implied] in ['LIBRARY', 'MODULE']:
388 t.direct_libs.add(implied)
389 else:
390 Logs.error('Implied dependency %s in %s is of type %s' % (
391 implied, t.sname, targets[implied]))
392 sys.exit(1)
393 continue
394 t2 = bld.name_to_obj(d, bld.env)
395 if t2 is None:
396 Logs.error("no task %s of type %s in %s" % (d, targets[d], t.sname))
397 sys.exit(1)
398 if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
399 t.direct_libs.add(d)
400 elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
401 t.direct_objects.add(d)
402 debug('deps: built direct dependencies')
405 def dependency_loop(loops, t, target):
406 '''add a dependency loop to the loops dictionary'''
407 if t.sname == target:
408 return
409 if not target in loops:
410 loops[target] = set()
411 if not t.sname in loops[target]:
412 loops[target].add(t.sname)
415 def indirect_libs(bld, t, chain, loops):
416 '''recursively calculate the indirect library dependencies for a target
418 An indirect library is a library that results from a dependency on
419 a subsystem
422 ret = getattr(t, 'indirect_libs', None)
423 if ret is not None:
424 return ret
426 ret = set()
427 for obj in t.direct_objects:
428 if obj in chain:
429 dependency_loop(loops, t, obj)
430 continue
431 chain.add(obj)
432 t2 = bld.name_to_obj(obj, bld.env)
433 r2 = indirect_libs(bld, t2, chain, loops)
434 chain.remove(obj)
435 ret = ret.union(t2.direct_libs)
436 ret = ret.union(r2)
438 for obj in indirect_objects(bld, t, set(), loops):
439 if obj in chain:
440 dependency_loop(loops, t, obj)
441 continue
442 chain.add(obj)
443 t2 = bld.name_to_obj(obj, bld.env)
444 r2 = indirect_libs(bld, t2, chain, loops)
445 chain.remove(obj)
446 ret = ret.union(t2.direct_libs)
447 ret = ret.union(r2)
449 t.indirect_libs = ret
451 return ret
454 def indirect_objects(bld, t, chain, loops):
455 '''recursively calculate the indirect object dependencies for a target
457 indirect objects are the set of objects from expanding the
458 subsystem dependencies
461 ret = getattr(t, 'indirect_objects', None)
462 if ret is not None: return ret
464 ret = set()
465 for lib in t.direct_objects:
466 if lib in chain:
467 dependency_loop(loops, t, lib)
468 continue
469 chain.add(lib)
470 t2 = bld.name_to_obj(lib, bld.env)
471 r2 = indirect_objects(bld, t2, chain, loops)
472 chain.remove(lib)
473 ret = ret.union(t2.direct_objects)
474 ret = ret.union(r2)
476 t.indirect_objects = ret
477 return ret
480 def extended_objects(bld, t, chain):
481 '''recursively calculate the extended object dependencies for a target
483 extended objects are the union of:
484 - direct objects
485 - indirect objects
486 - direct and indirect objects of all direct and indirect libraries
489 ret = getattr(t, 'extended_objects', None)
490 if ret is not None: return ret
492 ret = set()
493 ret = ret.union(t.final_objects)
495 for lib in t.final_libs:
496 if lib in chain:
497 continue
498 t2 = bld.name_to_obj(lib, bld.env)
499 chain.add(lib)
500 r2 = extended_objects(bld, t2, chain)
501 chain.remove(lib)
502 ret = ret.union(t2.final_objects)
503 ret = ret.union(r2)
505 t.extended_objects = ret
506 return ret
509 def includes_objects(bld, t, chain, inc_loops):
510 '''recursively calculate the includes object dependencies for a target
512 includes dependencies come from either library or object dependencies
514 ret = getattr(t, 'includes_objects', None)
515 if ret is not None:
516 return ret
518 ret = t.direct_objects.copy()
519 ret = ret.union(t.direct_libs)
521 for obj in t.direct_objects:
522 if obj in chain:
523 dependency_loop(inc_loops, t, obj)
524 continue
525 chain.add(obj)
526 t2 = bld.name_to_obj(obj, bld.env)
527 r2 = includes_objects(bld, t2, chain, inc_loops)
528 chain.remove(obj)
529 ret = ret.union(t2.direct_objects)
530 ret = ret.union(r2)
532 for lib in t.direct_libs:
533 if lib in chain:
534 dependency_loop(inc_loops, t, lib)
535 continue
536 chain.add(lib)
537 t2 = bld.name_to_obj(lib, bld.env)
538 if t2 is None:
539 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
540 Logs.error('Target %s of type %s not found in direct_libs for %s' % (
541 lib, targets[lib], t.sname))
542 sys.exit(1)
543 r2 = includes_objects(bld, t2, chain, inc_loops)
544 chain.remove(lib)
545 ret = ret.union(t2.direct_objects)
546 ret = ret.union(r2)
548 t.includes_objects = ret
549 return ret
552 def break_dependency_loops(bld, tgt_list):
553 '''find and break dependency loops'''
554 loops = {}
555 inc_loops = {}
557 # build up the list of loops
558 for t in tgt_list:
559 indirect_objects(bld, t, set(), loops)
560 indirect_libs(bld, t, set(), loops)
561 includes_objects(bld, t, set(), inc_loops)
563 # break the loops
564 for t in tgt_list:
565 if t.sname in loops:
566 for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
567 objs = getattr(t, attr, set())
568 setattr(t, attr, objs.difference(loops[t.sname]))
570 for loop in loops:
571 debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
573 for loop in inc_loops:
574 debug('deps: Found include loops for target %s : %s', loop, inc_loops[loop])
576 # expand the loops mapping by one level
577 for loop in loops.copy():
578 for tgt in loops[loop]:
579 if tgt in loops:
580 loops[loop] = loops[loop].union(loops[tgt])
582 for loop in inc_loops.copy():
583 for tgt in inc_loops[loop]:
584 if tgt in inc_loops:
585 inc_loops[loop] = inc_loops[loop].union(inc_loops[tgt])
588 # expand indirect subsystem and library loops
589 for loop in loops.copy():
590 t = bld.name_to_obj(loop, bld.env)
591 if t.samba_type in ['SUBSYSTEM']:
592 loops[loop] = loops[loop].union(t.indirect_objects)
593 loops[loop] = loops[loop].union(t.direct_objects)
594 if t.samba_type in ['LIBRARY','PYTHON']:
595 loops[loop] = loops[loop].union(t.indirect_libs)
596 loops[loop] = loops[loop].union(t.direct_libs)
597 if loop in loops[loop]:
598 loops[loop].remove(loop)
600 # expand indirect includes loops
601 for loop in inc_loops.copy():
602 t = bld.name_to_obj(loop, bld.env)
603 inc_loops[loop] = inc_loops[loop].union(t.includes_objects)
604 if loop in inc_loops[loop]:
605 inc_loops[loop].remove(loop)
607 # add in the replacement dependencies
608 for t in tgt_list:
609 for loop in loops:
610 for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
611 objs = getattr(t, attr, set())
612 if loop in objs:
613 diff = loops[loop].difference(objs)
614 if t.sname in diff:
615 diff.remove(t.sname)
616 if diff:
617 debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
618 objs = objs.union(diff)
619 setattr(t, attr, objs)
621 for loop in inc_loops:
622 objs = getattr(t, 'includes_objects', set())
623 if loop in objs:
624 diff = inc_loops[loop].difference(objs)
625 if t.sname in diff:
626 diff.remove(t.sname)
627 if diff:
628 debug('deps: Expanded target %s includes of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
629 objs = objs.union(diff)
630 setattr(t, 'includes_objects', objs)
633 def reduce_objects(bld, tgt_list):
634 '''reduce objects by looking for indirect object dependencies'''
635 rely_on = {}
637 for t in tgt_list:
638 t.extended_objects = None
640 changed = False
642 for type in ['BINARY', 'PYTHON', 'LIBRARY']:
643 for t in tgt_list:
644 if t.samba_type != type: continue
645 # if we will indirectly link to a target then we don't need it
646 new = t.final_objects.copy()
647 for l in t.final_libs:
648 t2 = bld.name_to_obj(l, bld.env)
649 t2_obj = extended_objects(bld, t2, set())
650 dup = new.intersection(t2_obj)
651 if t.sname in rely_on:
652 dup = dup.difference(rely_on[t.sname])
653 if dup:
654 debug('deps: removing dups from %s of type %s: %s also in %s %s',
655 t.sname, t.samba_type, dup, t2.samba_type, l)
656 new = new.difference(dup)
657 changed = True
658 if not l in rely_on:
659 rely_on[l] = set()
660 rely_on[l] = rely_on[l].union(dup)
661 t.final_objects = new
663 if not changed:
664 return False
666 # add back in any objects that were relied upon by the reduction rules
667 for r in rely_on:
668 t = bld.name_to_obj(r, bld.env)
669 t.final_objects = t.final_objects.union(rely_on[r])
671 return True
674 def calculate_final_deps(bld, tgt_list, loops):
675 '''calculate the final library and object dependencies'''
676 for t in tgt_list:
677 # start with the maximum possible list
678 t.final_libs = t.direct_libs.union(indirect_libs(bld, t, set(), loops))
679 t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops))
681 for t in tgt_list:
682 # don't depend on ourselves
683 if t.sname in t.final_libs:
684 t.final_libs.remove(t.sname)
685 if t.sname in t.final_objects:
686 t.final_objects.remove(t.sname)
688 # handle any non-shared binaries
689 for t in tgt_list:
690 if t.samba_type == 'BINARY' and bld.NONSHARED_BINARY(t.sname):
691 # replace lib deps with objlist deps
692 for l in t.final_libs:
693 objname = l + '.objlist'
694 t2 = bld.name_to_obj(objname, bld.env)
695 if t2 is None:
696 Logs.error('ERROR: subsystem %s not found' % objname)
697 sys.exit(1)
698 t.final_objects.add(objname)
699 t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
700 t.final_libs = set()
702 # find any library loops
703 for t in tgt_list:
704 if t.samba_type in ['LIBRARY', 'PYTHON']:
705 for l in t.final_libs.copy():
706 t2 = bld.name_to_obj(l, bld.env)
707 if t.sname in t2.final_libs:
708 # we could break this in either direction. If one of the libraries
709 # has a version number, and will this be distributed publicly, then
710 # we should make it the lower level library in the DAG
711 debug('deps: removing library loop %s from %s', t.sname, t2.sname)
712 dependency_loop(loops, t, t2.sname)
713 t2.final_libs.remove(t.sname)
716 for loop in loops:
717 debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
719 # we now need to make corrections for any library loops we broke up
720 # any target that depended on the target of the loop and doesn't
721 # depend on the source of the loop needs to get the loop source added
722 for type in ['BINARY','PYTHON','LIBRARY','BINARY']:
723 for t in tgt_list:
724 if t.samba_type != type: continue
725 for loop in loops:
726 if loop in t.final_libs:
727 diff = loops[loop].difference(t.final_libs)
728 if t.sname in diff:
729 diff.remove(t.sname)
730 if t.sname in diff:
731 diff.remove(t.sname)
732 # make sure we don't recreate the loop again!
733 for d in diff.copy():
734 t2 = bld.name_to_obj(d, bld.env)
735 if t2.samba_type == 'LIBRARY':
736 if t.sname in t2.final_libs:
737 debug('deps: removing expansion %s from %s', d, t.sname)
738 diff.remove(d)
739 if diff:
740 debug('deps: Expanded target %s by loop %s libraries (loop %s) %s', t.sname, loop,
741 loops[loop], diff)
742 t.final_libs = t.final_libs.union(diff)
744 # remove objects that are also available in linked libs
745 count = 0
746 while reduce_objects(bld, tgt_list):
747 count += 1
748 if count > 100:
749 Logs.warn("WARNING: Unable to remove all inter-target object duplicates")
750 break
751 debug('deps: Object reduction took %u iterations', count)
753 # add in any syslib dependencies
754 for t in tgt_list:
755 if not t.samba_type in ['BINARY','PYTHON','LIBRARY']:
756 continue
757 syslibs = set()
758 for d in t.final_objects:
759 t2 = bld.name_to_obj(d, bld.env)
760 syslibs = syslibs.union(t2.direct_syslibs)
761 # this adds the indirect syslibs as well, which may not be needed
762 # depending on the linker flags
763 for d in t.final_libs:
764 t2 = bld.name_to_obj(d, bld.env)
765 syslibs = syslibs.union(t2.direct_syslibs)
766 t.final_syslibs = syslibs
769 # find any unresolved library loops
770 lib_loop_error = False
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 Logs.error('ERROR: Unresolved library loop %s from %s' % (t.sname, t2.sname))
777 lib_loop_error = True
778 if lib_loop_error:
779 sys.exit(1)
781 debug('deps: removed duplicate dependencies')
784 ######################################################################
785 # this provides a way to save our dependency calculations between runs
786 savedeps_version = 3
787 savedeps_inputs = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags', 'source']
788 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags']
789 savedeps_outenv = ['INC_PATHS']
790 savedeps_envvars = ['NONSHARED_BINARIES', 'GLOBAL_DEPENDENCIES']
791 savedeps_caches = ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
792 savedeps_files = ['buildtools/wafsamba/samba_deps.py']
794 def save_samba_deps(bld, tgt_list):
795 '''save the dependency calculations between builds, to make
796 further builds faster'''
797 denv = Environment.Environment()
799 denv.version = savedeps_version
800 denv.savedeps_inputs = savedeps_inputs
801 denv.savedeps_outputs = savedeps_outputs
802 denv.input = {}
803 denv.output = {}
804 denv.outenv = {}
805 denv.caches = {}
806 denv.envvar = {}
807 denv.files = {}
809 for f in savedeps_files:
810 denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
812 for c in savedeps_caches:
813 denv.caches[c] = LOCAL_CACHE(bld, c)
815 for e in savedeps_envvars:
816 denv.envvar[e] = bld.env[e]
818 for t in tgt_list:
819 # save all the input attributes for each target
820 tdeps = {}
821 for attr in savedeps_inputs:
822 v = getattr(t, attr, None)
823 if v is not None:
824 tdeps[attr] = v
825 if tdeps != {}:
826 denv.input[t.sname] = tdeps
828 # save all the output attributes for each target
829 tdeps = {}
830 for attr in savedeps_outputs:
831 v = getattr(t, attr, None)
832 if v is not None:
833 tdeps[attr] = v
834 if tdeps != {}:
835 denv.output[t.sname] = tdeps
837 tdeps = {}
838 for attr in savedeps_outenv:
839 if attr in t.env:
840 tdeps[attr] = t.env[attr]
841 if tdeps != {}:
842 denv.outenv[t.sname] = tdeps
844 depsfile = os.path.join(bld.bdir, "sambadeps")
845 denv.store(depsfile)
849 def load_samba_deps(bld, tgt_list):
850 '''load a previous set of build dependencies if possible'''
851 depsfile = os.path.join(bld.bdir, "sambadeps")
852 denv = Environment.Environment()
853 try:
854 debug('deps: checking saved dependencies')
855 denv.load(depsfile)
856 if (denv.version != savedeps_version or
857 denv.savedeps_inputs != savedeps_inputs or
858 denv.savedeps_outputs != savedeps_outputs):
859 return False
860 except:
861 return False
863 # check if critical files have changed
864 for f in savedeps_files:
865 if f not in denv.files:
866 return False
867 if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
868 return False
870 # check if caches are the same
871 for c in savedeps_caches:
872 if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
873 return False
875 # check if caches are the same
876 for e in savedeps_envvars:
877 if e not in denv.envvar or denv.envvar[e] != bld.env[e]:
878 return False
880 # check inputs are the same
881 for t in tgt_list:
882 tdeps = {}
883 for attr in savedeps_inputs:
884 v = getattr(t, attr, None)
885 if v is not None:
886 tdeps[attr] = v
887 if t.sname in denv.input:
888 olddeps = denv.input[t.sname]
889 else:
890 olddeps = {}
891 if tdeps != olddeps:
892 #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
893 return False
895 # put outputs in place
896 for t in tgt_list:
897 if not t.sname in denv.output: continue
898 tdeps = denv.output[t.sname]
899 for a in tdeps:
900 setattr(t, a, tdeps[a])
902 # put output env vars in place
903 for t in tgt_list:
904 if not t.sname in denv.outenv: continue
905 tdeps = denv.outenv[t.sname]
906 for a in tdeps:
907 t.env[a] = tdeps[a]
909 debug('deps: loaded saved dependencies')
910 return True
914 def check_project_rules(bld):
915 '''check the project rules - ensuring the targets are sane'''
917 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
918 loops = {}
919 inc_loops = {}
921 # build a list of task generators we are interested in
922 tgt_list = []
923 for tgt in targets:
924 type = targets[tgt]
925 if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']:
926 continue
927 t = bld.name_to_obj(tgt, bld.env)
928 if t is None:
929 Logs.error("Target %s of type %s has no task generator" % (tgt, type))
930 sys.exit(1)
931 tgt_list.append(t)
933 add_samba_attributes(bld, tgt_list)
935 if load_samba_deps(bld, tgt_list):
936 return
938 Logs.info("Checking project rules ...")
940 debug('deps: project rules checking started')
942 expand_subsystem_deps(bld)
943 build_direct_deps(bld, tgt_list)
944 break_dependency_loops(bld, tgt_list)
945 calculate_final_deps(bld, tgt_list, loops)
947 # run the various attribute generators
948 for f in [ build_dependencies, build_includes, add_init_functions ]:
949 debug('deps: project rules checking %s', f)
950 for t in tgt_list: f(t)
952 debug('deps: project rules stage1 completed')
954 #check_orpaned_targets(bld, tgt_list)
956 if not check_duplicate_sources(bld, tgt_list):
957 Logs.error("Duplicate sources present - aborting")
958 sys.exit(1)
960 if not check_group_ordering(bld, tgt_list):
961 Logs.error("Bad group ordering - aborting")
962 sys.exit(1)
964 show_final_deps(bld, tgt_list)
966 debug('deps: project rules checking completed - %u targets checked',
967 len(tgt_list))
969 save_samba_deps(bld, tgt_list)
971 Logs.info("Project rules pass")
974 def CHECK_PROJECT_RULES(bld):
975 '''enable checking of project targets for sanity'''
976 if bld.env.added_project_rules:
977 return
978 bld.env.added_project_rules = True
979 bld.add_pre_fun(check_project_rules)
980 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES