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