waf: loosen the restriction on depending on python libs
[abartlet/samba.git/.git] / buildtools / wafsamba / samba_deps.py
1 # Samba automatic dependency handling and project rules
2
3 import Build, os, re, Environment, Logs
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 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
30
31
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
37
38
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
46
47
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'''
53
54     subsystem_list = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
55     aliases    = LOCAL_CACHE(bld, 'TARGET_ALIAS')
56     targets    = LOCAL_CACHE(bld, 'TARGET_TYPE')
57
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
65
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)
71
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)
92
93
94
95 def build_dependencies(self):
96     '''This builds the dependency list for a target. It runs after all the targets are declared
97
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.
100     '''
101
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)
106
107         # extra link flags from pkg_config
108         libs = self.final_syslibs.copy()
109
110         (ccflags, ldflags) = library_flags(self, list(libs))
111         new_ldflags        = getattr(self, 'ldflags', [])
112         new_ldflags.extend(ldflags)
113         self.ldflags       = new_ldflags
114
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)
117
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)
121
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
127
128 def build_includes(self):
129     '''This builds the right set of includes for a target.
130
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).
134
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
140     '''
141
142     if getattr(self, 'samba_includes', None) is None:
143         return
144
145     bld = self.bld
146
147     inc_deps = includes_objects(bld, self, set(), {})
148
149     includes = []
150
151     # maybe add local includes
152     if getattr(self, 'local_include', True) == True and getattr(self, 'local_include_first', True):
153         includes.append('.')
154
155     includes.extend(self.samba_includes_extended)
156
157     if 'EXTRA_INCLUDES' in bld.env:
158         includes.extend(bld.env['EXTRA_INCLUDES'])
159
160     includes.append('#')
161
162     inc_set = set()
163     inc_abs = []
164
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)
179
180     mypath = self.path.abspath(bld.env)
181     for inc in inc_abs:
182         relpath = os_path_relpath(inc, mypath)
183         includes.append(relpath)
184
185     if getattr(self, 'local_include', True) == True and not getattr(self, 'local_include_first', True):
186         includes.append('.')
187
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)
200
201     self.includes = unique_list(includes_top)
202     debug('deps: includes for target %s: includes=%s',
203           self.sname, self.includes)
204
205
206
207
208 def add_init_functions(self):
209     '''This builds the right set of init functions'''
210
211     bld = self.bld
212
213     subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
214
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]
219
220     modules = []
221     if sname in subsystems:
222         modules.append(sname)
223
224     m = getattr(self, 'samba_modules', None)
225     if m is not None:
226         modules.extend(TO_LIST(m))
227
228     m = getattr(self, 'samba_subsystem', None)
229     if m is not None:
230         modules.append(m)
231
232     if modules == []:
233         return
234
235     sentinal = getattr(self, 'init_function_sentinal', 'NULL')
236
237     targets    = LOCAL_CACHE(bld, 'TARGET_TYPE')
238
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
252
253
254
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'''
258
259     debug('deps: checking for duplicate sources')
260
261     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
262     ret = True
263
264     seen = set()
265
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))
271
272     for t in tgt_list:
273         if not targets[t.sname] in [ 'LIBRARY', 'BINARY', 'PYTHON' ]:
274             continue
275
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
292
293
294 def check_orpaned_targets(bld, tgt_list):
295     '''check if any build targets are orphaned'''
296
297     target_dict = LOCAL_CACHE(bld, 'TARGET_TYPE')
298
299     debug('deps: checking for orphaned targets')
300
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))
308
309
310 def check_group_ordering(bld, tgt_list):
311     '''see if we have any dependencies that violate the group ordering
312
313     It is an error for a target to depend on a target from a later
314     build group
315     '''
316
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]
320
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
325
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
332
333     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
334
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]
344
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
349
350     return ret
351
352
353 def show_final_deps(bld, tgt_list):
354     '''show the final dependencies for all targets'''
355
356     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
357
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)
363
364
365 def add_samba_attributes(bld, tgt_list):
366     '''ensure a target has a the required samba attributes'''
367
368     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
369
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', '')
380
381 def replace_grouping_libraries(bld, tgt_list):
382     '''replace dependencies based on grouping libraries
383
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
387     '''
388
389     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
390
391     grouping = {}
392
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
400
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]
409
410
411
412 def build_direct_deps(bld, tgt_list):
413     '''build the direct_objects and direct_libs sets for each target'''
414
415     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
416     syslib_deps  = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
417
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')
426
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')
471
472
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)
481
482
483 def indirect_libs(bld, t, chain, loops):
484     '''recursively calculate the indirect library dependencies for a target
485
486     An indirect library is a library that results from a dependency on
487     a subsystem
488     '''
489
490     ret = getattr(t, 'indirect_libs', None)
491     if ret is not None:
492         return ret
493
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)
505
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)
516
517     t.indirect_libs = ret
518
519     return ret
520
521
522 def indirect_objects(bld, t, chain, loops):
523     '''recursively calculate the indirect object dependencies for a target
524
525     indirect objects are the set of objects from expanding the
526     subsystem dependencies
527     '''
528
529     ret = getattr(t, 'indirect_objects', None)
530     if ret is not None: return ret
531
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)
543
544     t.indirect_objects = ret
545     return ret
546
547
548 def extended_objects(bld, t, chain):
549     '''recursively calculate the extended object dependencies for a target
550
551     extended objects are the union of:
552        - direct objects
553        - indirect objects
554        - direct and indirect objects of all direct and indirect libraries
555     '''
556
557     ret = getattr(t, 'extended_objects', None)
558     if ret is not None: return ret
559
560     ret = set()
561     ret = ret.union(t.final_objects)
562
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)
572
573     t.extended_objects = ret
574     return ret
575
576
577 def includes_objects(bld, t, chain, inc_loops):
578     '''recursively calculate the includes object dependencies for a target
579
580     includes dependencies come from either library or object dependencies
581     '''
582     ret = getattr(t, 'includes_objects', None)
583     if ret is not None:
584         return ret
585
586     ret = t.direct_objects.copy()
587     ret = ret.union(t.direct_libs)
588
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)
599
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)
615
616     t.includes_objects = ret
617     return ret
618
619
620 def break_dependency_loops(bld, tgt_list):
621     '''find and break dependency loops'''
622     loops = {}
623     inc_loops = {}
624
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)
630
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]))
637
638     for loop in loops:
639         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
640
641     for loop in inc_loops:
642         debug('deps: Found include loops for target %s : %s', loop, inc_loops[loop])
643
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])
649
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])
654
655
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)
667
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)
674
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)
688
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)
699
700
701 def reduce_objects(bld, tgt_list):
702     '''reduce objects by looking for indirect object dependencies'''
703     rely_on = {}
704
705     for t in tgt_list:
706         t.extended_objects = None
707
708     changed = False
709
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
730
731     if not changed:
732         return False
733
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])
738
739     return True
740
741
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))
748
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)
755
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()
769
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)
787
788     for loop in loops:
789         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
790
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)
815
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)
824
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
839
840
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)
852
853     debug('deps: removed duplicate dependencies')
854
855
856 def show_dependencies(bld, target, seen):
857     '''recursively show the dependencies of target'''
858
859     if target in seen:
860         return
861
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)
866
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))
870
871     seen.add(target)
872
873     for t2 in t.direct_objects:
874         show_dependencies(bld, t2, seen)
875
876
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'''
880
881     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
882
883     used_by = {}
884
885     Logs.info("showing duplicate objects")
886
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)
895
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]))
899
900     Logs.info("showing indirect dependency counts (sorted by count)")
901
902     def indirect_count(t1, t2):
903         return len(t2.indirect_objects) - len(t1.indirect_objects)
904
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)))
909
910
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']
920
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()
925
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  = {}
935
936     for f in savedeps_files:
937         denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
938
939     for c in savedeps_caches:
940         denv.caches[c] = LOCAL_CACHE(bld, c)
941
942     for e in savedeps_envvars:
943         denv.envvar[e] = bld.env[e]
944
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
954
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
963
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
970
971     depsfile = os.path.join(bld.bdir, "sambadeps")
972     denv.store(depsfile)
973
974
975
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
989
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
996
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
1001
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
1006
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
1021
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])
1028
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]
1035
1036     debug('deps: loaded saved dependencies')
1037     return True
1038
1039
1040
1041 def check_project_rules(bld):
1042     '''check the project rules - ensuring the targets are sane'''
1043
1044     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
1045     loops = {}
1046     inc_loops = {}
1047
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)
1059
1060     add_samba_attributes(bld, tgt_list)
1061
1062     force_project_rules = (Options.options.SHOWDEPS or
1063                            Options.options.SHOW_DUPLICATES)
1064
1065     if not force_project_rules and load_samba_deps(bld, tgt_list):
1066         return
1067
1068     bld.new_rules = True    
1069     Logs.info("Checking project rules ...")
1070
1071     debug('deps: project rules checking started')
1072
1073     expand_subsystem_deps(bld)
1074     replace_grouping_libraries(bld, tgt_list)
1075     build_direct_deps(bld, tgt_list)
1076
1077     break_dependency_loops(bld, tgt_list)
1078
1079     if Options.options.SHOWDEPS:
1080             show_dependencies(bld, Options.options.SHOWDEPS, set())
1081
1082     calculate_final_deps(bld, tgt_list, loops)
1083
1084     if Options.options.SHOW_DUPLICATES:
1085             show_object_duplicates(bld, tgt_list)
1086
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)
1091
1092     debug('deps: project rules stage1 completed')
1093
1094     #check_orpaned_targets(bld, tgt_list)
1095
1096     if not check_duplicate_sources(bld, tgt_list):
1097         Logs.error("Duplicate sources present - aborting")
1098         sys.exit(1)
1099
1100     if not check_group_ordering(bld, tgt_list):
1101         Logs.error("Bad group ordering - aborting")
1102         sys.exit(1)
1103
1104     show_final_deps(bld, tgt_list)
1105
1106     debug('deps: project rules checking completed - %u targets checked',
1107           len(tgt_list))
1108
1109     if not bld.is_install:
1110         save_samba_deps(bld, tgt_list)
1111
1112     Logs.info("Project rules pass")
1113
1114
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
1122
1123