Merge commit 'release-4-0-0alpha15' into master4-tmp
[nivanova/samba-autobuild/.git] / buildtools / wafsamba / samba_deps.py
1 # Samba automatic dependency handling and project rules
2
3 import Build, os, sys, re, Environment, Logs, time
4 from samba_utils import *
5 from samba_autoconf import *
6 from samba_bundled import BUILTIN_LIBRARY
7
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)
14
15
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
20
21
22 @conf
23 def SET_SYSLIB_DEPS(conf, target, deps):
24     '''setup some implied dependencies for a SYSLIB'''
25     cache = LOCAL_CACHE(conf, 'SYSLIB_DEPS')
26     cache[target] = deps
27
28
29 def expand_subsystem_deps(bld):
30     '''expand the reverse dependencies resulting from subsystem
31        attributes of modules. This is walking over the complete list
32        of declared subsystems, and expands the samba_deps_extended list for any
33        module<->subsystem dependencies'''
34
35     subsystem_list = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
36     targets    = LOCAL_CACHE(bld, 'TARGET_TYPE')
37
38     for subsystem_name in subsystem_list:
39         bld.ASSERT(subsystem_name in targets, "Subsystem target %s not declared" % subsystem_name)
40         type = targets[subsystem_name]
41         if type == 'DISABLED' or type == 'EMPTY':
42             continue
43
44         # for example,
45         #    subsystem_name = dcerpc_server (a subsystem)
46         #    subsystem      = dcerpc_server (a subsystem object)
47         #    module_name    = rpc_epmapper (a module within the dcerpc_server subsystem)
48         #    module         = rpc_epmapper (a module object within the dcerpc_server subsystem)
49
50         subsystem = bld.name_to_obj(subsystem_name, bld.env)
51         bld.ASSERT(subsystem is not None, "Unable to find subsystem %s" % subsystem_name)
52         for d in subsystem_list[subsystem_name]:
53             module_name = d['TARGET']
54             module_type = targets[module_name]
55             if module_type in ['DISABLED', 'EMPTY']:
56                 continue
57             bld.ASSERT(subsystem is not None,
58                        "Subsystem target %s for %s (%s) not found" % (subsystem_name, module_name, module_type))
59             if module_type in ['SUBSYSTEM']:
60                 # if a module is a plain object type (not a library) then the
61                 # subsystem it is part of needs to have it as a dependency, so targets
62                 # that depend on this subsystem get the modules of that subsystem
63                 subsystem.samba_deps_extended.append(module_name)
64         subsystem.samba_deps_extended = unique_list(subsystem.samba_deps_extended)
65
66
67
68 def build_dependencies(self):
69     '''This builds the dependency list for a target. It runs after all the targets are declared
70
71     The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
72     the full dependency list for a target until we have all of the targets declared.
73     '''
74
75     if self.samba_type in ['LIBRARY', 'BINARY', 'PYTHON']:
76         self.uselib        = list(self.final_syslibs)
77         self.uselib_local  = list(self.final_libs)
78         self.add_objects   = list(self.final_objects)
79
80         # extra link flags from pkg_config
81         libs = self.final_syslibs.copy()
82
83         (ccflags, ldflags) = library_flags(self, list(libs))
84         new_ldflags        = getattr(self, 'samba_ldflags', [])[:]
85         new_ldflags.extend(ldflags)
86         self.ldflags       = new_ldflags
87
88         if getattr(self, 'allow_undefined_symbols', False) and self.env.undefined_ldflags:
89             for f in self.env.undefined_ldflags:
90                 self.ldflags.remove(f)
91
92         if getattr(self, 'allow_undefined_symbols', False) and self.env.undefined_ignore_ldflags:
93             for f in self.env.undefined_ignore_ldflags:
94                 self.ldflags.append(f)
95
96         debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
97               self.sname, self.uselib, self.uselib_local, self.add_objects)
98
99     if self.samba_type in ['SUBSYSTEM']:
100         # this is needed for the ccflags of libs that come from pkg_config
101         self.uselib = list(self.final_syslibs)
102         self.uselib.extend(list(self.direct_syslibs))
103         for lib in self.final_libs:
104             t = self.bld.name_to_obj(lib, self.bld.env)
105             self.uselib.extend(list(t.final_syslibs))
106         self.uselib = unique_list(self.uselib)
107
108     if getattr(self, 'uselib', None):
109         up_list = []
110         for l in self.uselib:
111            up_list.append(l.upper())
112         self.uselib = up_list
113
114
115 def build_includes(self):
116     '''This builds the right set of includes for a target.
117
118     One tricky part of this is that the includes= attribute for a
119     target needs to use paths which are relative to that targets
120     declaration directory (which we can get at via t.path).
121
122     The way this works is the includes list gets added as
123     samba_includes in the main build task declaration. Then this
124     function runs after all of the tasks are declared, and it
125     processes the samba_includes attribute to produce a includes=
126     attribute
127     '''
128
129     if getattr(self, 'samba_includes', None) is None:
130         return
131
132     bld = self.bld
133
134     inc_deps = includes_objects(bld, self, set(), {})
135
136     includes = []
137
138     # maybe add local includes
139     if getattr(self, 'local_include', True) == True and getattr(self, 'local_include_first', True):
140         includes.append('.')
141
142     includes.extend(self.samba_includes_extended)
143
144     if 'EXTRA_INCLUDES' in bld.env and getattr(self, 'global_include', True):
145         includes.extend(bld.env['EXTRA_INCLUDES'])
146
147     includes.append('#')
148
149     inc_set = set()
150     inc_abs = []
151
152     for d in inc_deps:
153         t = bld.name_to_obj(d, bld.env)
154         bld.ASSERT(t is not None, "Unable to find dependency %s for %s" % (d, self.sname))
155         inclist = getattr(t, 'samba_includes_extended', [])[:]
156         if getattr(t, 'local_include', True) == True:
157             inclist.append('.')
158         if inclist == []:
159             continue
160         tpath = t.samba_abspath
161         for inc in inclist:
162             npath = tpath + '/' + inc
163             if not npath in inc_set:
164                 inc_abs.append(npath)
165                 inc_set.add(npath)
166
167     mypath = self.path.abspath(bld.env)
168     for inc in inc_abs:
169         relpath = os_path_relpath(inc, mypath)
170         includes.append(relpath)
171
172     if getattr(self, 'local_include', True) == True and not getattr(self, 'local_include_first', True):
173         includes.append('.')
174
175     # now transform the includes list to be relative to the top directory
176     # which is represented by '#' in waf. This allows waf to cache the
177     # includes lists more efficiently
178     includes_top = []
179     for i in includes:
180         if i[0] == '#':
181             # some are already top based
182             includes_top.append(i)
183             continue
184         absinc = os.path.join(self.path.abspath(), i)
185         relinc = os_path_relpath(absinc, self.bld.srcnode.abspath())
186         includes_top.append('#' + relinc)
187
188     self.includes = unique_list(includes_top)
189     debug('deps: includes for target %s: includes=%s',
190           self.sname, self.includes)
191
192
193
194
195 def add_init_functions(self):
196     '''This builds the right set of init functions'''
197
198     bld = self.bld
199
200     subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
201
202     # cope with the separated object lists from BINARY and LIBRARY targets
203     sname = self.sname
204     if sname.endswith('.objlist'):
205         sname = sname[0:-8]
206
207     modules = []
208     if sname in subsystems:
209         modules.append(sname)
210
211     m = getattr(self, 'samba_modules', None)
212     if m is not None:
213         modules.extend(TO_LIST(m))
214
215     m = getattr(self, 'samba_subsystem', None)
216     if m is not None:
217         modules.append(m)
218
219     sentinal = getattr(self, 'init_function_sentinal', 'NULL')
220
221     targets    = LOCAL_CACHE(bld, 'TARGET_TYPE')
222     cflags = getattr(self, 'samba_cflags', [])[:]
223
224     if modules == []:
225         sname = sname.replace('-','_')
226         sname = sname.replace('/','_')
227         cflags.append('-DSTATIC_%s_MODULES=%s' % (sname, sentinal))
228         if sentinal == 'NULL':
229             cflags.append('-DSTATIC_%s_MODULES_PROTO' % sname)
230         self.ccflags = cflags
231         return
232
233     for m in modules:
234         bld.ASSERT(m in subsystems,
235                    "No init_function defined for module '%s' in target '%s'" % (m, self.sname))
236         init_fn_list = []
237         for d in subsystems[m]:
238             if targets[d['TARGET']] != 'DISABLED':
239                 init_fn_list.append(d['INIT_FUNCTION'])
240         if init_fn_list == []:
241             cflags.append('-DSTATIC_%s_MODULES=%s' % (m, sentinal))
242             if sentinal == 'NULL':
243                 cflags.append('-DSTATIC_%s_MODULES_PROTO' % m)
244         else:
245             cflags.append('-DSTATIC_%s_MODULES=%s' % (m, ','.join(init_fn_list) + ',' + sentinal))
246             proto=''
247             for f in init_fn_list:
248                 proto = proto + '_MODULE_PROTO(%s)' % f
249             cflags.append('-DSTATIC_%s_MODULES_PROTO=%s' % (m, proto))
250     self.ccflags = cflags
251
252
253
254 def check_duplicate_sources(bld, tgt_list):
255     '''see if we are compiling the same source file more than once
256        without an allow_duplicates attribute'''
257
258     debug('deps: checking for duplicate sources')
259
260     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
261     ret = True
262
263     global tstart
264
265     for t in tgt_list:
266         source_list = TO_LIST(getattr(t, 'source', ''))
267         tpath = os.path.normpath(os_path_relpath(t.path.abspath(bld.env), t.env.BUILD_DIRECTORY + '/default'))
268         obj_sources = set()
269         for s in source_list:
270             p = os.path.normpath(os.path.join(tpath, s))
271             if p in obj_sources:
272                 Logs.error("ERROR: source %s appears twice in target '%s'" % (p, t.sname))
273                 sys.exit(1)
274             obj_sources.add(p)
275         t.samba_source_set = obj_sources
276
277     subsystems = {}
278
279     # build a list of targets that each source file is part of
280     for t in tgt_list:
281         sources = []
282         if not targets[t.sname] in [ 'LIBRARY', 'BINARY', 'PYTHON' ]:
283             continue
284         for obj in t.add_objects:
285             t2 = t.bld.name_to_obj(obj, bld.env)
286             source_set = getattr(t2, 'samba_source_set', set())
287             for s in source_set:
288                 if not s in subsystems:
289                     subsystems[s] = {}
290                 if not t.sname in subsystems[s]:
291                     subsystems[s][t.sname] = []
292                 subsystems[s][t.sname].append(t2.sname)
293
294     for s in subsystems:
295         if len(subsystems[s]) > 1 and Options.options.SHOW_DUPLICATES:
296             Logs.warn("WARNING: source %s is in more than one target: %s" % (s, subsystems[s].keys()))
297         for tname in subsystems[s]:
298             if len(subsystems[s][tname]) > 1:
299                 raise Utils.WafError("ERROR: source %s is in more than one subsystem of target '%s': %s" % (s, tname, subsystems[s][tname]))
300                 
301     return ret
302
303
304 def check_orpaned_targets(bld, tgt_list):
305     '''check if any build targets are orphaned'''
306
307     target_dict = LOCAL_CACHE(bld, 'TARGET_TYPE')
308
309     debug('deps: checking for orphaned targets')
310
311     for t in tgt_list:
312         if getattr(t, 'samba_used', False) == True:
313             continue
314         type = target_dict[t.sname]
315         if not type in ['BINARY', 'LIBRARY', 'MODULE', 'ET', 'PYTHON']:
316             if re.search('^PIDL_', t.sname) is None:
317                 Logs.warn("Target %s of type %s is unused by any other target" % (t.sname, type))
318
319
320 def check_group_ordering(bld, tgt_list):
321     '''see if we have any dependencies that violate the group ordering
322
323     It is an error for a target to depend on a target from a later
324     build group
325     '''
326
327     def group_name(g):
328         tm = bld.task_manager
329         return [x for x in tm.groups_names if id(tm.groups_names[x]) == id(g)][0]
330
331     for g in bld.task_manager.groups:
332         gname = group_name(g)
333         for t in g.tasks_gen:
334             t.samba_group = gname
335
336     grp_map = {}
337     idx = 0
338     for g in bld.task_manager.groups:
339         name = group_name(g)
340         grp_map[name] = idx
341         idx += 1
342
343     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
344
345     ret = True
346     for t in tgt_list:
347         tdeps = getattr(t, 'add_objects', []) + getattr(t, 'uselib_local', [])
348         for d in tdeps:
349             t2 = bld.name_to_obj(d, bld.env)
350             if t2 is None:
351                 continue
352             map1 = grp_map[t.samba_group]
353             map2 = grp_map[t2.samba_group]
354
355             if map2 > map1:
356                 Logs.error("Target %r in build group %r depends on target %r from later build group %r" % (
357                            t.sname, t.samba_group, t2.sname, t2.samba_group))
358                 ret = False
359
360     return ret
361
362
363 def show_final_deps(bld, tgt_list):
364     '''show the final dependencies for all targets'''
365
366     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
367
368     for t in tgt_list:
369         if not targets[t.sname] in ['LIBRARY', 'BINARY', 'PYTHON', 'SUBSYSTEM']:
370             continue
371         debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
372               t.sname, t.uselib, getattr(t, 'uselib_local', []), getattr(t, 'add_objects', []))
373
374
375 def add_samba_attributes(bld, tgt_list):
376     '''ensure a target has a the required samba attributes'''
377
378     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
379
380     for t in tgt_list:
381         if t.name != '':
382             t.sname = t.name
383         else:
384             t.sname = t.target
385         t.samba_type = targets[t.sname]
386         t.samba_abspath = t.path.abspath(bld.env)
387         t.samba_deps_extended = t.samba_deps[:]
388         t.samba_includes_extended = TO_LIST(t.samba_includes)[:]
389         t.ccflags = getattr(t, 'samba_cflags', '')
390
391 def replace_grouping_libraries(bld, tgt_list):
392     '''replace dependencies based on grouping libraries
393
394     If a library is marked as a grouping library, then any target that
395     depends on a subsystem that is part of that grouping library gets
396     that dependency replaced with a dependency on the grouping library
397     '''
398
399     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
400
401     grouping = {}
402
403     # find our list of grouping libraries, mapped from the subsystems they depend on
404     for t in tgt_list:
405         if not getattr(t, 'grouping_library', False):
406             continue
407         for dep in t.samba_deps_extended:
408             bld.ASSERT(dep in targets, "grouping library target %s not declared in %s" % (dep, t.sname))
409             if targets[dep] == 'SUBSYSTEM':
410                 grouping[dep] = t.sname
411
412     # now replace any dependencies on elements of grouping libraries
413     for t in tgt_list:
414         for i in range(len(t.samba_deps_extended)):
415             dep = t.samba_deps_extended[i]
416             if dep in grouping:
417                 if t.sname != grouping[dep]:
418                     debug("deps: target %s: replacing dependency %s with grouping library %s" % (t.sname, dep, grouping[dep]))
419                     t.samba_deps_extended[i] = grouping[dep]
420
421
422
423 def build_direct_deps(bld, tgt_list):
424     '''build the direct_objects and direct_libs sets for each target'''
425
426     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
427     syslib_deps  = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
428
429     global_deps = bld.env.GLOBAL_DEPENDENCIES
430     global_deps_exclude = set()
431     for dep in global_deps:
432         t = bld.name_to_obj(dep, bld.env)
433         for d in t.samba_deps:
434             # prevent loops from the global dependencies list
435             global_deps_exclude.add(d)
436             global_deps_exclude.add(d + '.objlist')
437
438     for t in tgt_list:
439         t.direct_objects = set()
440         t.direct_libs = set()
441         t.direct_syslibs = set()
442         deps = t.samba_deps_extended[:]
443         if getattr(t, 'samba_use_global_deps', False) and not t.sname in global_deps_exclude:
444             deps.extend(global_deps)
445         for d in deps:
446             if d == t.sname: continue
447             if not d in targets:
448                 Logs.error("Unknown dependency '%s' in '%s'" % (d, t.sname))
449                 sys.exit(1)
450             if targets[d] in [ 'EMPTY', 'DISABLED' ]:
451                 continue
452             if targets[d] == 'PYTHON' and targets[t.sname] != 'PYTHON' and t.sname.find('.objlist') == -1:
453                 # this check should be more restrictive, but for now we have pidl-generated python
454                 # code that directly depends on other python modules
455                 Logs.error('ERROR: Target %s has dependency on python module %s' % (t.sname, d))
456                 sys.exit(1)
457             if targets[d] == 'SYSLIB':
458                 t.direct_syslibs.add(d)
459                 if d in syslib_deps:
460                     for implied in TO_LIST(syslib_deps[d]):
461                         if BUILTIN_LIBRARY(bld, implied):
462                             t.direct_objects.add(implied)
463                         elif targets[implied] == 'SYSLIB':
464                             t.direct_syslibs.add(implied)
465                         elif targets[implied] in ['LIBRARY', 'MODULE']:
466                             t.direct_libs.add(implied)
467                         else:
468                             Logs.error('Implied dependency %s in %s is of type %s' % (
469                                 implied, t.sname, targets[implied]))
470                             sys.exit(1)
471                 continue
472             t2 = bld.name_to_obj(d, bld.env)
473             if t2 is None:
474                 Logs.error("no task %s of type %s in %s" % (d, targets[d], t.sname))
475                 sys.exit(1)
476             if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
477                 t.direct_libs.add(d)
478             elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
479                 t.direct_objects.add(d)
480     debug('deps: built direct dependencies')
481
482
483 def dependency_loop(loops, t, target):
484     '''add a dependency loop to the loops dictionary'''
485     if t.sname == target:
486         return
487     if not target in loops:
488         loops[target] = set()
489     if not t.sname in loops[target]:
490         loops[target].add(t.sname)
491
492
493 def indirect_libs(bld, t, chain, loops):
494     '''recursively calculate the indirect library dependencies for a target
495
496     An indirect library is a library that results from a dependency on
497     a subsystem
498     '''
499
500     ret = getattr(t, 'indirect_libs', None)
501     if ret is not None:
502         return ret
503
504     ret = set()
505     for obj in t.direct_objects:
506         if obj in chain:
507             dependency_loop(loops, t, obj)
508             continue
509         chain.add(obj)
510         t2 = bld.name_to_obj(obj, bld.env)
511         r2 = indirect_libs(bld, t2, chain, loops)
512         chain.remove(obj)
513         ret = ret.union(t2.direct_libs)
514         ret = ret.union(r2)
515
516     for obj in indirect_objects(bld, t, set(), loops):
517         if obj in chain:
518             dependency_loop(loops, t, obj)
519             continue
520         chain.add(obj)
521         t2 = bld.name_to_obj(obj, bld.env)
522         r2 = indirect_libs(bld, t2, chain, loops)
523         chain.remove(obj)
524         ret = ret.union(t2.direct_libs)
525         ret = ret.union(r2)
526
527     t.indirect_libs = ret
528
529     return ret
530
531
532 def indirect_objects(bld, t, chain, loops):
533     '''recursively calculate the indirect object dependencies for a target
534
535     indirect objects are the set of objects from expanding the
536     subsystem dependencies
537     '''
538
539     ret = getattr(t, 'indirect_objects', None)
540     if ret is not None: return ret
541
542     ret = set()
543     for lib in t.direct_objects:
544         if lib in chain:
545             dependency_loop(loops, t, lib)
546             continue
547         chain.add(lib)
548         t2 = bld.name_to_obj(lib, bld.env)
549         r2 = indirect_objects(bld, t2, chain, loops)
550         chain.remove(lib)
551         ret = ret.union(t2.direct_objects)
552         ret = ret.union(r2)
553
554     t.indirect_objects = ret
555     return ret
556
557
558 def extended_objects(bld, t, chain):
559     '''recursively calculate the extended object dependencies for a target
560
561     extended objects are the union of:
562        - direct objects
563        - indirect objects
564        - direct and indirect objects of all direct and indirect libraries
565     '''
566
567     ret = getattr(t, 'extended_objects', None)
568     if ret is not None: return ret
569
570     ret = set()
571     ret = ret.union(t.final_objects)
572
573     for lib in t.final_libs:
574         if lib in chain:
575             continue
576         t2 = bld.name_to_obj(lib, bld.env)
577         chain.add(lib)
578         r2 = extended_objects(bld, t2, chain)
579         chain.remove(lib)
580         ret = ret.union(t2.final_objects)
581         ret = ret.union(r2)
582
583     t.extended_objects = ret
584     return ret
585
586
587 def includes_objects(bld, t, chain, inc_loops):
588     '''recursively calculate the includes object dependencies for a target
589
590     includes dependencies come from either library or object dependencies
591     '''
592     ret = getattr(t, 'includes_objects', None)
593     if ret is not None:
594         return ret
595
596     ret = t.direct_objects.copy()
597     ret = ret.union(t.direct_libs)
598
599     for obj in t.direct_objects:
600         if obj in chain:
601             dependency_loop(inc_loops, t, obj)
602             continue
603         chain.add(obj)
604         t2 = bld.name_to_obj(obj, bld.env)
605         r2 = includes_objects(bld, t2, chain, inc_loops)
606         chain.remove(obj)
607         ret = ret.union(t2.direct_objects)
608         ret = ret.union(r2)
609
610     for lib in t.direct_libs:
611         if lib in chain:
612             dependency_loop(inc_loops, t, lib)
613             continue
614         chain.add(lib)
615         t2 = bld.name_to_obj(lib, bld.env)
616         if t2 is None:
617             targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
618             Logs.error('Target %s of type %s not found in direct_libs for %s' % (
619                 lib, targets[lib], t.sname))
620             sys.exit(1)
621         r2 = includes_objects(bld, t2, chain, inc_loops)
622         chain.remove(lib)
623         ret = ret.union(t2.direct_objects)
624         ret = ret.union(r2)
625
626     t.includes_objects = ret
627     return ret
628
629
630 def break_dependency_loops(bld, tgt_list):
631     '''find and break dependency loops'''
632     loops = {}
633     inc_loops = {}
634
635     # build up the list of loops
636     for t in tgt_list:
637         indirect_objects(bld, t, set(), loops)
638         indirect_libs(bld, t, set(), loops)
639         includes_objects(bld, t, set(), inc_loops)
640
641     # break the loops
642     for t in tgt_list:
643         if t.sname in loops:
644             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
645                 objs = getattr(t, attr, set())
646                 setattr(t, attr, objs.difference(loops[t.sname]))
647
648     for loop in loops:
649         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
650
651     for loop in inc_loops:
652         debug('deps: Found include loops for target %s : %s', loop, inc_loops[loop])
653
654     # expand the loops mapping by one level
655     for loop in loops.copy():
656         for tgt in loops[loop]:
657             if tgt in loops:
658                 loops[loop] = loops[loop].union(loops[tgt])
659
660     for loop in inc_loops.copy():
661         for tgt in inc_loops[loop]:
662             if tgt in inc_loops:
663                 inc_loops[loop] = inc_loops[loop].union(inc_loops[tgt])
664
665
666     # expand indirect subsystem and library loops
667     for loop in loops.copy():
668         t = bld.name_to_obj(loop, bld.env)
669         if t.samba_type in ['SUBSYSTEM']:
670             loops[loop] = loops[loop].union(t.indirect_objects)
671             loops[loop] = loops[loop].union(t.direct_objects)
672         if t.samba_type in ['LIBRARY','PYTHON']:
673             loops[loop] = loops[loop].union(t.indirect_libs)
674             loops[loop] = loops[loop].union(t.direct_libs)
675         if loop in loops[loop]:
676             loops[loop].remove(loop)
677
678     # expand indirect includes loops
679     for loop in inc_loops.copy():
680         t = bld.name_to_obj(loop, bld.env)
681         inc_loops[loop] = inc_loops[loop].union(t.includes_objects)
682         if loop in inc_loops[loop]:
683             inc_loops[loop].remove(loop)
684
685     # add in the replacement dependencies
686     for t in tgt_list:
687         for loop in loops:
688             for attr in ['indirect_objects', 'indirect_libs']:
689                 objs = getattr(t, attr, set())
690                 if loop in objs:
691                     diff = loops[loop].difference(objs)
692                     if t.sname in diff:
693                         diff.remove(t.sname)
694                     if diff:
695                         debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
696                         objs = objs.union(diff)
697                 setattr(t, attr, objs)
698
699         for loop in inc_loops:
700             objs = getattr(t, 'includes_objects', set())
701             if loop in objs:
702                 diff = inc_loops[loop].difference(objs)
703                 if t.sname in diff:
704                     diff.remove(t.sname)
705                 if diff:
706                     debug('deps: Expanded target %s includes of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
707                     objs = objs.union(diff)
708             setattr(t, 'includes_objects', objs)
709
710
711 def reduce_objects(bld, tgt_list):
712     '''reduce objects by looking for indirect object dependencies'''
713     rely_on = {}
714
715     for t in tgt_list:
716         t.extended_objects = None
717
718     changed = False
719
720     for type in ['BINARY', 'PYTHON', 'LIBRARY']:
721         for t in tgt_list:
722             if t.samba_type != type: continue
723             # if we will indirectly link to a target then we don't need it
724             new = t.final_objects.copy()
725             for l in t.final_libs:
726                 t2 = bld.name_to_obj(l, bld.env)
727                 t2_obj = extended_objects(bld, t2, set())
728                 dup = new.intersection(t2_obj)
729                 if t.sname in rely_on:
730                     dup = dup.difference(rely_on[t.sname])
731                 if dup:
732                     debug('deps: removing dups from %s of type %s: %s also in %s %s',
733                           t.sname, t.samba_type, dup, t2.samba_type, l)
734                     new = new.difference(dup)
735                     changed = True
736                     if not l in rely_on:
737                         rely_on[l] = set()
738                     rely_on[l] = rely_on[l].union(dup)
739             t.final_objects = new
740
741     if not changed:
742         return False
743
744     # add back in any objects that were relied upon by the reduction rules
745     for r in rely_on:
746         t = bld.name_to_obj(r, bld.env)
747         t.final_objects = t.final_objects.union(rely_on[r])
748
749     return True
750
751
752 def show_library_loop(bld, lib1, lib2, path, seen):
753     '''show the detailed path of a library loop between lib1 and lib2'''
754
755     t = bld.name_to_obj(lib1, bld.env)
756     if not lib2 in getattr(t, 'final_libs', set()):
757         return
758
759     for d in t.samba_deps_extended:
760         if d in seen:
761             continue
762         seen.add(d)
763         path2 = path + '=>' + d
764         if d == lib2:
765             Logs.warn('library loop path: ' + path2)
766             return
767         show_library_loop(bld, d, lib2, path2, seen)
768         seen.remove(d)
769
770
771 def calculate_final_deps(bld, tgt_list, loops):
772     '''calculate the final library and object dependencies'''
773     for t in tgt_list:
774         # start with the maximum possible list
775         t.final_libs    = t.direct_libs.union(indirect_libs(bld, t, set(), loops))
776         t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops))
777
778     for t in tgt_list:
779         # don't depend on ourselves
780         if t.sname in t.final_libs:
781             t.final_libs.remove(t.sname)
782         if t.sname in t.final_objects:
783             t.final_objects.remove(t.sname)
784
785     # handle any non-shared binaries
786     for t in tgt_list:
787         if t.samba_type == 'BINARY' and bld.NONSHARED_BINARY(t.sname):
788             subsystem_list = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
789             targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
790
791             # replace lib deps with objlist deps
792             for l in t.final_libs:
793                 objname = l + '.objlist'
794                 t2 = bld.name_to_obj(objname, bld.env)
795                 if t2 is None:
796                     Logs.error('ERROR: subsystem %s not found' % objname)
797                     sys.exit(1)
798                 t.final_objects.add(objname)
799                 t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
800                 if l in subsystem_list:
801                     # its a subsystem - we also need the contents of any modules
802                     for d in subsystem_list[l]:
803                         module_name = d['TARGET']
804                         if targets[module_name] == 'LIBRARY':
805                             objname = module_name + '.objlist'
806                         elif targets[module_name] == 'SUBSYSTEM':
807                             objname = module_name
808                         else:
809                             continue
810                         t2 = bld.name_to_obj(objname, bld.env)
811                         if t2 is None:
812                             Logs.error('ERROR: subsystem %s not found' % objname)
813                             sys.exit(1)
814                         t.final_objects.add(objname)
815                         t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
816             t.final_libs = set()
817
818     # find any library loops
819     for t in tgt_list:
820         if t.samba_type in ['LIBRARY', 'PYTHON']:
821             for l in t.final_libs.copy():
822                 t2 = bld.name_to_obj(l, bld.env)
823                 if t.sname in t2.final_libs:
824                     if getattr(bld.env, "ALLOW_CIRCULAR_LIB_DEPENDENCIES", False):
825                         # we could break this in either direction. If one of the libraries
826                         # has a version number, and will this be distributed publicly, then
827                         # we should make it the lower level library in the DAG
828                         Logs.warn('deps: removing library loop %s from %s' % (t.sname, t2.sname))
829                         dependency_loop(loops, t, t2.sname)
830                         t2.final_libs.remove(t.sname)
831                     else:
832                         Logs.error('ERROR: circular library dependency between %s and %s'
833                             % (t.sname, t2.sname))
834                         show_library_loop(bld, t.sname, t2.sname, t.sname, set())
835                         show_library_loop(bld, t2.sname, t.sname, t2.sname, set())
836                         sys.exit(1)
837
838     for loop in loops:
839         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
840
841     # we now need to make corrections for any library loops we broke up
842     # any target that depended on the target of the loop and doesn't
843     # depend on the source of the loop needs to get the loop source added
844     for type in ['BINARY','PYTHON','LIBRARY','BINARY']:
845         for t in tgt_list:
846             if t.samba_type != type: continue
847             for loop in loops:
848                 if loop in t.final_libs:
849                     diff = loops[loop].difference(t.final_libs)
850                     if t.sname in diff:
851                         diff.remove(t.sname)
852                     if t.sname in diff:
853                         diff.remove(t.sname)
854                     # make sure we don't recreate the loop again!
855                     for d in diff.copy():
856                         t2 = bld.name_to_obj(d, bld.env)
857                         if t2.samba_type == 'LIBRARY':
858                             if t.sname in t2.final_libs:
859                                 debug('deps: removing expansion %s from %s', d, t.sname)
860                                 diff.remove(d)
861                     if diff:
862                         debug('deps: Expanded target %s by loop %s libraries (loop %s) %s', t.sname, loop,
863                               loops[loop], diff)
864                         t.final_libs = t.final_libs.union(diff)
865
866     # remove objects that are also available in linked libs
867     count = 0
868     while reduce_objects(bld, tgt_list):
869         count += 1
870         if count > 100:
871             Logs.warn("WARNING: Unable to remove all inter-target object duplicates")
872             break
873     debug('deps: Object reduction took %u iterations', count)
874
875     # add in any syslib dependencies
876     for t in tgt_list:
877         if not t.samba_type in ['BINARY','PYTHON','LIBRARY','SUBSYSTEM']:
878             continue
879         syslibs = set()
880         for d in t.final_objects:
881             t2 = bld.name_to_obj(d, bld.env)
882             syslibs = syslibs.union(t2.direct_syslibs)
883         # this adds the indirect syslibs as well, which may not be needed
884         # depending on the linker flags
885         for d in t.final_libs:
886             t2 = bld.name_to_obj(d, bld.env)
887             syslibs = syslibs.union(t2.direct_syslibs)
888         t.final_syslibs = syslibs
889
890
891     # find any unresolved library loops
892     lib_loop_error = False
893     for t in tgt_list:
894         if t.samba_type in ['LIBRARY', 'PYTHON']:
895             for l in t.final_libs.copy():
896                 t2 = bld.name_to_obj(l, bld.env)
897                 if t.sname in t2.final_libs:
898                     Logs.error('ERROR: Unresolved library loop %s from %s' % (t.sname, t2.sname))
899                     lib_loop_error = True
900     if lib_loop_error:
901         sys.exit(1)
902
903     debug('deps: removed duplicate dependencies')
904
905
906 def show_dependencies(bld, target, seen):
907     '''recursively show the dependencies of target'''
908
909     if target in seen:
910         return
911
912     t = bld.name_to_obj(target, bld.env)
913     if t is None:
914         Logs.error("ERROR: Unable to find target '%s'" % target)
915         sys.exit(1)
916
917     Logs.info('%s(OBJECTS): %s' % (target, t.direct_objects))
918     Logs.info('%s(LIBS): %s' % (target, t.direct_libs))
919     Logs.info('%s(SYSLIBS): %s' % (target, t.direct_syslibs))
920
921     seen.add(target)
922
923     for t2 in t.direct_objects:
924         show_dependencies(bld, t2, seen)
925
926
927 def show_object_duplicates(bld, tgt_list):
928     '''show a list of object files that are included in more than
929     one library or binary'''
930
931     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
932
933     used_by = {}
934
935     Logs.info("showing duplicate objects")
936
937     for t in tgt_list:
938         if not targets[t.sname] in [ 'LIBRARY', 'PYTHON' ]:
939             continue
940         for n in getattr(t, 'final_objects', set()):
941             t2 = bld.name_to_obj(n, bld.env)
942             if not n in used_by:
943                 used_by[n] = set()
944             used_by[n].add(t.sname)
945
946     for n in used_by:
947         if len(used_by[n]) > 1:
948             Logs.info("target '%s' is used by %s" % (n, used_by[n]))
949
950     Logs.info("showing indirect dependency counts (sorted by count)")
951
952     def indirect_count(t1, t2):
953         return len(t2.indirect_objects) - len(t1.indirect_objects)
954
955     sorted_list = sorted(tgt_list, cmp=indirect_count)
956     for t in sorted_list:
957         if len(t.indirect_objects) > 1:
958             Logs.info("%s depends on %u indirect objects" % (t.sname, len(t.indirect_objects)))
959
960
961 ######################################################################
962 # this provides a way to save our dependency calculations between runs
963 savedeps_version = 3
964 savedeps_inputs  = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags',
965                     'source', 'grouping_library', 'samba_ldflags', 'allow_undefined_symbols',
966                     'use_global_deps', 'global_include' ]
967 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags', 'ldflags', 'samba_deps_extended']
968 savedeps_outenv  = ['INC_PATHS']
969 savedeps_envvars = ['NONSHARED_BINARIES', 'GLOBAL_DEPENDENCIES', 'EXTRA_CFLAGS', 'EXTRA_LDFLAGS', 'EXTRA_INCLUDES' ]
970 savedeps_caches  = ['GLOBAL_DEPENDENCIES', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
971 savedeps_files   = ['buildtools/wafsamba/samba_deps.py']
972
973 def save_samba_deps(bld, tgt_list):
974     '''save the dependency calculations between builds, to make
975        further builds faster'''
976     denv = Environment.Environment()
977
978     denv.version = savedeps_version
979     denv.savedeps_inputs = savedeps_inputs
980     denv.savedeps_outputs = savedeps_outputs
981     denv.input = {}
982     denv.output = {}
983     denv.outenv = {}
984     denv.caches = {}
985     denv.envvar = {}
986     denv.files  = {}
987
988     for f in savedeps_files:
989         denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
990
991     for c in savedeps_caches:
992         denv.caches[c] = LOCAL_CACHE(bld, c)
993
994     for e in savedeps_envvars:
995         denv.envvar[e] = bld.env[e]
996
997     for t in tgt_list:
998         # save all the input attributes for each target
999         tdeps = {}
1000         for attr in savedeps_inputs:
1001             v = getattr(t, attr, None)
1002             if v is not None:
1003                 tdeps[attr] = v
1004         if tdeps != {}:
1005             denv.input[t.sname] = tdeps
1006
1007         # save all the output attributes for each target
1008         tdeps = {}
1009         for attr in savedeps_outputs:
1010             v = getattr(t, attr, None)
1011             if v is not None:
1012                 tdeps[attr] = v
1013         if tdeps != {}:
1014             denv.output[t.sname] = tdeps
1015
1016         tdeps = {}
1017         for attr in savedeps_outenv:
1018             if attr in t.env:
1019                 tdeps[attr] = t.env[attr]
1020         if tdeps != {}:
1021             denv.outenv[t.sname] = tdeps
1022
1023     depsfile = os.path.join(bld.bdir, "sambadeps")
1024     denv.store(depsfile)
1025
1026
1027
1028 def load_samba_deps(bld, tgt_list):
1029     '''load a previous set of build dependencies if possible'''
1030     depsfile = os.path.join(bld.bdir, "sambadeps")
1031     denv = Environment.Environment()
1032     try:
1033         debug('deps: checking saved dependencies')
1034         denv.load(depsfile)
1035         if (denv.version != savedeps_version or
1036             denv.savedeps_inputs != savedeps_inputs or
1037             denv.savedeps_outputs != savedeps_outputs):
1038             return False
1039     except:
1040         return False
1041
1042     # check if critical files have changed
1043     for f in savedeps_files:
1044         if f not in denv.files:
1045             return False
1046         if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
1047             return False
1048
1049     # check if caches are the same
1050     for c in savedeps_caches:
1051         if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
1052             return False
1053
1054     # check if caches are the same
1055     for e in savedeps_envvars:
1056         if e not in denv.envvar or denv.envvar[e] != bld.env[e]:
1057             return False
1058
1059     # check inputs are the same
1060     for t in tgt_list:
1061         tdeps = {}
1062         for attr in savedeps_inputs:
1063             v = getattr(t, attr, None)
1064             if v is not None:
1065                 tdeps[attr] = v
1066         if t.sname in denv.input:
1067             olddeps = denv.input[t.sname]
1068         else:
1069             olddeps = {}
1070         if tdeps != olddeps:
1071             #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
1072             return False
1073
1074     # put outputs in place
1075     for t in tgt_list:
1076         if not t.sname in denv.output: continue
1077         tdeps = denv.output[t.sname]
1078         for a in tdeps:
1079             setattr(t, a, tdeps[a])
1080
1081     # put output env vars in place
1082     for t in tgt_list:
1083         if not t.sname in denv.outenv: continue
1084         tdeps = denv.outenv[t.sname]
1085         for a in tdeps:
1086             t.env[a] = tdeps[a]
1087
1088     debug('deps: loaded saved dependencies')
1089     return True
1090
1091
1092
1093 def check_project_rules(bld):
1094     '''check the project rules - ensuring the targets are sane'''
1095
1096     loops = {}
1097     inc_loops = {}
1098
1099     tgt_list = get_tgt_list(bld)
1100
1101     add_samba_attributes(bld, tgt_list)
1102
1103     force_project_rules = (Options.options.SHOWDEPS or
1104                            Options.options.SHOW_DUPLICATES)
1105
1106     if not force_project_rules and load_samba_deps(bld, tgt_list):
1107         return
1108
1109     global tstart
1110     tstart = time.clock()
1111
1112     bld.new_rules = True
1113     Logs.info("Checking project rules ...")
1114
1115     debug('deps: project rules checking started')
1116
1117     expand_subsystem_deps(bld)
1118
1119     debug("deps: expand_subsystem_deps: %f" % (time.clock() - tstart))
1120
1121     replace_grouping_libraries(bld, tgt_list)
1122
1123     debug("deps: replace_grouping_libraries: %f" % (time.clock() - tstart))
1124
1125     build_direct_deps(bld, tgt_list)
1126
1127     debug("deps: build_direct_deps: %f" % (time.clock() - tstart))
1128
1129     break_dependency_loops(bld, tgt_list)
1130
1131     debug("deps: break_dependency_loops: %f" % (time.clock() - tstart))
1132
1133     if Options.options.SHOWDEPS:
1134             show_dependencies(bld, Options.options.SHOWDEPS, set())
1135
1136     calculate_final_deps(bld, tgt_list, loops)
1137
1138     debug("deps: calculate_final_deps: %f" % (time.clock() - tstart))
1139
1140     if Options.options.SHOW_DUPLICATES:
1141             show_object_duplicates(bld, tgt_list)
1142
1143     # run the various attribute generators
1144     for f in [ build_dependencies, build_includes, add_init_functions ]:
1145         debug('deps: project rules checking %s', f)
1146         for t in tgt_list: f(t)
1147         debug("deps: %s: %f" % (f, time.clock() - tstart))
1148
1149     debug('deps: project rules stage1 completed')
1150
1151     #check_orpaned_targets(bld, tgt_list)
1152
1153     if not check_duplicate_sources(bld, tgt_list):
1154         Logs.error("Duplicate sources present - aborting")
1155         sys.exit(1)
1156
1157     debug("deps: check_duplicate_sources: %f" % (time.clock() - tstart))
1158
1159     if not check_group_ordering(bld, tgt_list):
1160         Logs.error("Bad group ordering - aborting")
1161         sys.exit(1)
1162
1163     debug("deps: check_group_ordering: %f" % (time.clock() - tstart))
1164
1165     show_final_deps(bld, tgt_list)
1166
1167     debug("deps: show_final_deps: %f" % (time.clock() - tstart))
1168
1169     debug('deps: project rules checking completed - %u targets checked',
1170           len(tgt_list))
1171
1172     if not bld.is_install:
1173         save_samba_deps(bld, tgt_list)
1174
1175     debug("deps: save_samba_deps: %f" % (time.clock() - tstart))
1176
1177     Logs.info("Project rules pass")
1178
1179
1180 def CHECK_PROJECT_RULES(bld):
1181     '''enable checking of project targets for sanity'''
1182     if bld.env.added_project_rules:
1183         return
1184     bld.env.added_project_rules = True
1185     bld.add_pre_fun(check_project_rules)
1186 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES
1187
1188