wafsamba: add maxversion and version_blacklist to CHECK_BUNDLED_SYSTEM[_PKG]()
[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         (ccflags, 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 ccflags 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.ccflags = 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.ccflags = 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             p = os.path.normpath(os.path.join(tpath, s))
275             if p in obj_sources:
276                 Logs.error("ERROR: source %s appears twice in target '%s'" % (p, t.sname))
277                 sys.exit(1)
278             obj_sources.add(p)
279         t.samba_source_set = obj_sources
280
281     subsystems = {}
282
283     # build a list of targets that each source file is part of
284     for t in tgt_list:
285         if not targets[t.sname] in [ 'LIBRARY', 'BINARY', 'PYTHON' ]:
286             continue
287         for obj in t.add_objects:
288             t2 = t.bld.get_tgen_by_name(obj)
289             source_set = getattr(t2, 'samba_source_set', set())
290             for s in source_set:
291                 if not s in subsystems:
292                     subsystems[s] = {}
293                 if not t.sname in subsystems[s]:
294                     subsystems[s][t.sname] = []
295                 subsystems[s][t.sname].append(t2.sname)
296
297     for s in subsystems:
298         if len(subsystems[s]) > 1 and Options.options.SHOW_DUPLICATES:
299             Logs.warn("WARNING: source %s is in more than one target: %s" % (s, subsystems[s].keys()))
300         for tname in subsystems[s]:
301             if len(subsystems[s][tname]) > 1:
302                 raise Utils.WafError("ERROR: source %s is in more than one subsystem of target '%s': %s" % (s, tname, subsystems[s][tname]))
303
304     return True
305
306 def check_group_ordering(bld, tgt_list):
307     '''see if we have any dependencies that violate the group ordering
308
309     It is an error for a target to depend on a target from a later
310     build group
311     '''
312
313     def group_name(g):
314         tm = bld.task_manager
315         return [x for x in tm.groups_names if id(tm.groups_names[x]) == id(g)][0]
316
317     for g in bld.task_manager.groups:
318         gname = group_name(g)
319         for t in g.tasks_gen:
320             t.samba_group = gname
321
322     grp_map = {}
323     idx = 0
324     for g in bld.task_manager.groups:
325         name = group_name(g)
326         grp_map[name] = idx
327         idx += 1
328
329     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
330
331     ret = True
332     for t in tgt_list:
333         tdeps = getattr(t, 'add_objects', []) + getattr(t, 'uselib_local', [])
334         for d in tdeps:
335             t2 = bld.get_tgen_by_name(d)
336             if t2 is None:
337                 continue
338             map1 = grp_map[t.samba_group]
339             map2 = grp_map[t2.samba_group]
340
341             if map2 > map1:
342                 Logs.error("Target %r in build group %r depends on target %r from later build group %r" % (
343                            t.sname, t.samba_group, t2.sname, t2.samba_group))
344                 ret = False
345
346     return ret
347 Build.BuildContext.check_group_ordering = check_group_ordering
348
349 def show_final_deps(bld, tgt_list):
350     '''show the final dependencies for all targets'''
351
352     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
353
354     for t in tgt_list:
355         if not targets[t.sname] in ['LIBRARY', 'BINARY', 'PYTHON', 'SUBSYSTEM']:
356             continue
357         debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
358               t.sname, t.uselib, getattr(t, 'uselib_local', []), getattr(t, 'add_objects', []))
359
360
361 def add_samba_attributes(bld, tgt_list):
362     '''ensure a target has a the required samba attributes'''
363
364     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
365
366     for t in tgt_list:
367         if t.name != '':
368             t.sname = t.name
369         else:
370             t.sname = t.target
371         t.samba_type = targets[t.sname]
372         t.samba_abspath = t.path.abspath(bld.env)
373         t.samba_deps_extended = t.samba_deps[:]
374         t.samba_includes_extended = TO_LIST(t.samba_includes)[:]
375         t.ccflags = getattr(t, 'samba_cflags', '')
376
377 def replace_grouping_libraries(bld, tgt_list):
378     '''replace dependencies based on grouping libraries
379
380     If a library is marked as a grouping library, then any target that
381     depends on a subsystem that is part of that grouping library gets
382     that dependency replaced with a dependency on the grouping library
383     '''
384
385     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
386
387     grouping = {}
388
389     # find our list of grouping libraries, mapped from the subsystems they depend on
390     for t in tgt_list:
391         if not getattr(t, 'grouping_library', False):
392             continue
393         for dep in t.samba_deps_extended:
394             bld.ASSERT(dep in targets, "grouping library target %s not declared in %s" % (dep, t.sname))
395             if targets[dep] == 'SUBSYSTEM':
396                 grouping[dep] = t.sname
397
398     # now replace any dependencies on elements of grouping libraries
399     for t in tgt_list:
400         for i in range(len(t.samba_deps_extended)):
401             dep = t.samba_deps_extended[i]
402             if dep in grouping:
403                 if t.sname != grouping[dep]:
404                     debug("deps: target %s: replacing dependency %s with grouping library %s" % (t.sname, dep, grouping[dep]))
405                     t.samba_deps_extended[i] = grouping[dep]
406
407
408
409 def build_direct_deps(bld, tgt_list):
410     '''build the direct_objects and direct_libs sets for each target'''
411
412     targets  = LOCAL_CACHE(bld, 'TARGET_TYPE')
413     syslib_deps  = LOCAL_CACHE(bld, 'SYSLIB_DEPS')
414
415     global_deps = bld.env.GLOBAL_DEPENDENCIES
416     global_deps_exclude = set()
417     for dep in global_deps:
418         t = bld.get_tgen_by_name(dep)
419         for d in t.samba_deps:
420             # prevent loops from the global dependencies list
421             global_deps_exclude.add(d)
422             global_deps_exclude.add(d + '.objlist')
423
424     for t in tgt_list:
425         t.direct_objects = set()
426         t.direct_libs = set()
427         t.direct_syslibs = set()
428         deps = t.samba_deps_extended[:]
429         if getattr(t, 'samba_use_global_deps', False) and not t.sname in global_deps_exclude:
430             deps.extend(global_deps)
431         for d in deps:
432             if d == t.sname: continue
433             if not d in targets:
434                 Logs.error("Unknown dependency '%s' in '%s'" % (d, t.sname))
435                 sys.exit(1)
436             if targets[d] in [ 'EMPTY', 'DISABLED' ]:
437                 continue
438             if targets[d] == 'PYTHON' and targets[t.sname] != 'PYTHON' and t.sname.find('.objlist') == -1:
439                 # this check should be more restrictive, but for now we have pidl-generated python
440                 # code that directly depends on other python modules
441                 Logs.error('ERROR: Target %s has dependency on python module %s' % (t.sname, d))
442                 sys.exit(1)
443             if targets[d] == 'SYSLIB':
444                 t.direct_syslibs.add(d)
445                 if d in syslib_deps:
446                     for implied in TO_LIST(syslib_deps[d]):
447                         if BUILTIN_LIBRARY(bld, implied):
448                             t.direct_objects.add(implied)
449                         elif targets[implied] == 'SYSLIB':
450                             t.direct_syslibs.add(implied)
451                         elif targets[implied] in ['LIBRARY', 'MODULE']:
452                             t.direct_libs.add(implied)
453                         else:
454                             Logs.error('Implied dependency %s in %s is of type %s' % (
455                                 implied, t.sname, targets[implied]))
456                             sys.exit(1)
457                 continue
458             t2 = bld.get_tgen_by_name(d)
459             if t2 is None:
460                 Logs.error("no task %s of type %s in %s" % (d, targets[d], t.sname))
461                 sys.exit(1)
462             if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
463                 t.direct_libs.add(d)
464             elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
465                 t.direct_objects.add(d)
466     debug('deps: built direct dependencies')
467
468
469 def dependency_loop(loops, t, target):
470     '''add a dependency loop to the loops dictionary'''
471     if t.sname == target:
472         return
473     if not target in loops:
474         loops[target] = set()
475     if not t.sname in loops[target]:
476         loops[target].add(t.sname)
477
478
479 def indirect_libs(bld, t, chain, loops):
480     '''recursively calculate the indirect library dependencies for a target
481
482     An indirect library is a library that results from a dependency on
483     a subsystem
484     '''
485
486     ret = getattr(t, 'indirect_libs', None)
487     if ret is not None:
488         return ret
489
490     ret = set()
491     for obj in t.direct_objects:
492         if obj in chain:
493             dependency_loop(loops, t, obj)
494             continue
495         chain.add(obj)
496         t2 = bld.get_tgen_by_name(obj)
497         r2 = indirect_libs(bld, t2, chain, loops)
498         chain.remove(obj)
499         ret = ret.union(t2.direct_libs)
500         ret = ret.union(r2)
501
502     for obj in indirect_objects(bld, t, set(), loops):
503         if obj in chain:
504             dependency_loop(loops, t, obj)
505             continue
506         chain.add(obj)
507         t2 = bld.get_tgen_by_name(obj)
508         r2 = indirect_libs(bld, t2, chain, loops)
509         chain.remove(obj)
510         ret = ret.union(t2.direct_libs)
511         ret = ret.union(r2)
512
513     t.indirect_libs = ret
514
515     return ret
516
517
518 def indirect_objects(bld, t, chain, loops):
519     '''recursively calculate the indirect object dependencies for a target
520
521     indirect objects are the set of objects from expanding the
522     subsystem dependencies
523     '''
524
525     ret = getattr(t, 'indirect_objects', None)
526     if ret is not None: return ret
527
528     ret = set()
529     for lib in t.direct_objects:
530         if lib in chain:
531             dependency_loop(loops, t, lib)
532             continue
533         chain.add(lib)
534         t2 = bld.get_tgen_by_name(lib)
535         r2 = indirect_objects(bld, t2, chain, loops)
536         chain.remove(lib)
537         ret = ret.union(t2.direct_objects)
538         ret = ret.union(r2)
539
540     t.indirect_objects = ret
541     return ret
542
543
544 def extended_objects(bld, t, chain):
545     '''recursively calculate the extended object dependencies for a target
546
547     extended objects are the union of:
548        - direct objects
549        - indirect objects
550        - direct and indirect objects of all direct and indirect libraries
551     '''
552
553     ret = getattr(t, 'extended_objects', None)
554     if ret is not None: return ret
555
556     ret = set()
557     ret = ret.union(t.final_objects)
558
559     for lib in t.final_libs:
560         if lib in chain:
561             continue
562         t2 = bld.get_tgen_by_name(lib)
563         chain.add(lib)
564         r2 = extended_objects(bld, t2, chain)
565         chain.remove(lib)
566         ret = ret.union(t2.final_objects)
567         ret = ret.union(r2)
568
569     t.extended_objects = ret
570     return ret
571
572
573 def includes_objects(bld, t, chain, inc_loops):
574     '''recursively calculate the includes object dependencies for a target
575
576     includes dependencies come from either library or object dependencies
577     '''
578     ret = getattr(t, 'includes_objects', None)
579     if ret is not None:
580         return ret
581
582     ret = t.direct_objects.copy()
583     ret = ret.union(t.direct_libs)
584
585     for obj in t.direct_objects:
586         if obj in chain:
587             dependency_loop(inc_loops, t, obj)
588             continue
589         chain.add(obj)
590         t2 = bld.get_tgen_by_name(obj)
591         r2 = includes_objects(bld, t2, chain, inc_loops)
592         chain.remove(obj)
593         ret = ret.union(t2.direct_objects)
594         ret = ret.union(r2)
595
596     for lib in t.direct_libs:
597         if lib in chain:
598             dependency_loop(inc_loops, t, lib)
599             continue
600         chain.add(lib)
601         t2 = bld.get_tgen_by_name(lib)
602         if t2 is None:
603             targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
604             Logs.error('Target %s of type %s not found in direct_libs for %s' % (
605                 lib, targets[lib], t.sname))
606             sys.exit(1)
607         r2 = includes_objects(bld, t2, chain, inc_loops)
608         chain.remove(lib)
609         ret = ret.union(t2.direct_objects)
610         ret = ret.union(r2)
611
612     t.includes_objects = ret
613     return ret
614
615
616 def break_dependency_loops(bld, tgt_list):
617     '''find and break dependency loops'''
618     loops = {}
619     inc_loops = {}
620
621     # build up the list of loops
622     for t in tgt_list:
623         indirect_objects(bld, t, set(), loops)
624         indirect_libs(bld, t, set(), loops)
625         includes_objects(bld, t, set(), inc_loops)
626
627     # break the loops
628     for t in tgt_list:
629         if t.sname in loops:
630             for attr in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
631                 objs = getattr(t, attr, set())
632                 setattr(t, attr, objs.difference(loops[t.sname]))
633
634     for loop in loops:
635         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
636
637     for loop in inc_loops:
638         debug('deps: Found include loops for target %s : %s', loop, inc_loops[loop])
639
640     # expand the loops mapping by one level
641     for loop in loops.copy():
642         for tgt in loops[loop]:
643             if tgt in loops:
644                 loops[loop] = loops[loop].union(loops[tgt])
645
646     for loop in inc_loops.copy():
647         for tgt in inc_loops[loop]:
648             if tgt in inc_loops:
649                 inc_loops[loop] = inc_loops[loop].union(inc_loops[tgt])
650
651
652     # expand indirect subsystem and library loops
653     for loop in loops.copy():
654         t = bld.get_tgen_by_name(loop)
655         if t.samba_type in ['SUBSYSTEM']:
656             loops[loop] = loops[loop].union(t.indirect_objects)
657             loops[loop] = loops[loop].union(t.direct_objects)
658         if t.samba_type in ['LIBRARY','PYTHON']:
659             loops[loop] = loops[loop].union(t.indirect_libs)
660             loops[loop] = loops[loop].union(t.direct_libs)
661         if loop in loops[loop]:
662             loops[loop].remove(loop)
663
664     # expand indirect includes loops
665     for loop in inc_loops.copy():
666         t = bld.get_tgen_by_name(loop)
667         inc_loops[loop] = inc_loops[loop].union(t.includes_objects)
668         if loop in inc_loops[loop]:
669             inc_loops[loop].remove(loop)
670
671     # add in the replacement dependencies
672     for t in tgt_list:
673         for loop in loops:
674             for attr in ['indirect_objects', 'indirect_libs']:
675                 objs = getattr(t, attr, set())
676                 if loop in objs:
677                     diff = loops[loop].difference(objs)
678                     if t.sname in diff:
679                         diff.remove(t.sname)
680                     if diff:
681                         debug('deps: Expanded target %s of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
682                         objs = objs.union(diff)
683                 setattr(t, attr, objs)
684
685         for loop in inc_loops:
686             objs = getattr(t, 'includes_objects', set())
687             if loop in objs:
688                 diff = inc_loops[loop].difference(objs)
689                 if t.sname in diff:
690                     diff.remove(t.sname)
691                 if diff:
692                     debug('deps: Expanded target %s includes of type %s from loop %s by %s', t.sname, t.samba_type, loop, diff)
693                     objs = objs.union(diff)
694             setattr(t, 'includes_objects', objs)
695
696
697 def reduce_objects(bld, tgt_list):
698     '''reduce objects by looking for indirect object dependencies'''
699     rely_on = {}
700
701     for t in tgt_list:
702         t.extended_objects = None
703
704     changed = False
705
706     for type in ['BINARY', 'PYTHON', 'LIBRARY']:
707         for t in tgt_list:
708             if t.samba_type != type: continue
709             # if we will indirectly link to a target then we don't need it
710             new = t.final_objects.copy()
711             for l in t.final_libs:
712                 t2 = bld.get_tgen_by_name(l)
713                 t2_obj = extended_objects(bld, t2, set())
714                 dup = new.intersection(t2_obj)
715                 if t.sname in rely_on:
716                     dup = dup.difference(rely_on[t.sname])
717                 if dup:
718                     debug('deps: removing dups from %s of type %s: %s also in %s %s',
719                           t.sname, t.samba_type, dup, t2.samba_type, l)
720                     new = new.difference(dup)
721                     changed = True
722                     if not l in rely_on:
723                         rely_on[l] = set()
724                     rely_on[l] = rely_on[l].union(dup)
725             t.final_objects = new
726
727     if not changed:
728         return False
729
730     # add back in any objects that were relied upon by the reduction rules
731     for r in rely_on:
732         t = bld.get_tgen_by_name(r)
733         t.final_objects = t.final_objects.union(rely_on[r])
734
735     return True
736
737
738 def show_library_loop(bld, lib1, lib2, path, seen):
739     '''show the detailed path of a library loop between lib1 and lib2'''
740
741     t = bld.get_tgen_by_name(lib1)
742     if not lib2 in getattr(t, 'final_libs', set()):
743         return
744
745     for d in t.samba_deps_extended:
746         if d in seen:
747             continue
748         seen.add(d)
749         path2 = path + '=>' + d
750         if d == lib2:
751             Logs.warn('library loop path: ' + path2)
752             return
753         show_library_loop(bld, d, lib2, path2, seen)
754         seen.remove(d)
755
756
757 def calculate_final_deps(bld, tgt_list, loops):
758     '''calculate the final library and object dependencies'''
759     for t in tgt_list:
760         # start with the maximum possible list
761         t.final_libs    = t.direct_libs.union(indirect_libs(bld, t, set(), loops))
762         t.final_objects = t.direct_objects.union(indirect_objects(bld, t, set(), loops))
763
764     for t in tgt_list:
765         # don't depend on ourselves
766         if t.sname in t.final_libs:
767             t.final_libs.remove(t.sname)
768         if t.sname in t.final_objects:
769             t.final_objects.remove(t.sname)
770
771     # handle any non-shared binaries
772     for t in tgt_list:
773         if t.samba_type == 'BINARY' and bld.NONSHARED_BINARY(t.sname):
774             subsystem_list = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
775             targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
776
777             # replace lib deps with objlist deps
778             for l in t.final_libs:
779                 objname = l + '.objlist'
780                 t2 = bld.get_tgen_by_name(objname)
781                 if t2 is None:
782                     Logs.error('ERROR: subsystem %s not found' % objname)
783                     sys.exit(1)
784                 t.final_objects.add(objname)
785                 t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
786                 if l in subsystem_list:
787                     # its a subsystem - we also need the contents of any modules
788                     for d in subsystem_list[l]:
789                         module_name = d['TARGET']
790                         if targets[module_name] == 'LIBRARY':
791                             objname = module_name + '.objlist'
792                         elif targets[module_name] == 'SUBSYSTEM':
793                             objname = module_name
794                         else:
795                             continue
796                         t2 = bld.get_tgen_by_name(objname)
797                         if t2 is None:
798                             Logs.error('ERROR: subsystem %s not found' % objname)
799                             sys.exit(1)
800                         t.final_objects.add(objname)
801                         t.final_objects = t.final_objects.union(extended_objects(bld, t2, set()))
802             t.final_libs = set()
803
804     # find any library loops
805     for t in tgt_list:
806         if t.samba_type in ['LIBRARY', 'PYTHON']:
807             for l in t.final_libs.copy():
808                 t2 = bld.get_tgen_by_name(l)
809                 if t.sname in t2.final_libs:
810                     if getattr(bld.env, "ALLOW_CIRCULAR_LIB_DEPENDENCIES", False):
811                         # we could break this in either direction. If one of the libraries
812                         # has a version number, and will this be distributed publicly, then
813                         # we should make it the lower level library in the DAG
814                         Logs.warn('deps: removing library loop %s from %s' % (t.sname, t2.sname))
815                         dependency_loop(loops, t, t2.sname)
816                         t2.final_libs.remove(t.sname)
817                     else:
818                         Logs.error('ERROR: circular library dependency between %s and %s'
819                             % (t.sname, t2.sname))
820                         show_library_loop(bld, t.sname, t2.sname, t.sname, set())
821                         show_library_loop(bld, t2.sname, t.sname, t2.sname, set())
822                         sys.exit(1)
823
824     for loop in loops:
825         debug('deps: Found dependency loops for target %s : %s', loop, loops[loop])
826
827     # we now need to make corrections for any library loops we broke up
828     # any target that depended on the target of the loop and doesn't
829     # depend on the source of the loop needs to get the loop source added
830     for type in ['BINARY','PYTHON','LIBRARY','BINARY']:
831         for t in tgt_list:
832             if t.samba_type != type: continue
833             for loop in loops:
834                 if loop in t.final_libs:
835                     diff = loops[loop].difference(t.final_libs)
836                     if t.sname in diff:
837                         diff.remove(t.sname)
838                     if t.sname in diff:
839                         diff.remove(t.sname)
840                     # make sure we don't recreate the loop again!
841                     for d in diff.copy():
842                         t2 = bld.get_tgen_by_name(d)
843                         if t2.samba_type == 'LIBRARY':
844                             if t.sname in t2.final_libs:
845                                 debug('deps: removing expansion %s from %s', d, t.sname)
846                                 diff.remove(d)
847                     if diff:
848                         debug('deps: Expanded target %s by loop %s libraries (loop %s) %s', t.sname, loop,
849                               loops[loop], diff)
850                         t.final_libs = t.final_libs.union(diff)
851
852     # remove objects that are also available in linked libs
853     count = 0
854     while reduce_objects(bld, tgt_list):
855         count += 1
856         if count > 100:
857             Logs.warn("WARNING: Unable to remove all inter-target object duplicates")
858             break
859     debug('deps: Object reduction took %u iterations', count)
860
861     # add in any syslib dependencies
862     for t in tgt_list:
863         if not t.samba_type in ['BINARY','PYTHON','LIBRARY','SUBSYSTEM']:
864             continue
865         syslibs = set()
866         for d in t.final_objects:
867             t2 = bld.get_tgen_by_name(d)
868             syslibs = syslibs.union(t2.direct_syslibs)
869         # this adds the indirect syslibs as well, which may not be needed
870         # depending on the linker flags
871         for d in t.final_libs:
872             t2 = bld.get_tgen_by_name(d)
873             syslibs = syslibs.union(t2.direct_syslibs)
874         t.final_syslibs = syslibs
875
876
877     # find any unresolved library loops
878     lib_loop_error = False
879     for t in tgt_list:
880         if t.samba_type in ['LIBRARY', 'PYTHON']:
881             for l in t.final_libs.copy():
882                 t2 = bld.get_tgen_by_name(l)
883                 if t.sname in t2.final_libs:
884                     Logs.error('ERROR: Unresolved library loop %s from %s' % (t.sname, t2.sname))
885                     lib_loop_error = True
886     if lib_loop_error:
887         sys.exit(1)
888
889     debug('deps: removed duplicate dependencies')
890
891
892 def show_dependencies(bld, target, seen):
893     '''recursively show the dependencies of target'''
894
895     if target in seen:
896         return
897
898     t = bld.get_tgen_by_name(target)
899     if t is None:
900         Logs.error("ERROR: Unable to find target '%s'" % target)
901         sys.exit(1)
902
903     Logs.info('%s(OBJECTS): %s' % (target, t.direct_objects))
904     Logs.info('%s(LIBS): %s' % (target, t.direct_libs))
905     Logs.info('%s(SYSLIBS): %s' % (target, t.direct_syslibs))
906
907     seen.add(target)
908
909     for t2 in t.direct_objects:
910         show_dependencies(bld, t2, seen)
911
912
913 def show_object_duplicates(bld, tgt_list):
914     '''show a list of object files that are included in more than
915     one library or binary'''
916
917     targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
918
919     used_by = {}
920
921     Logs.info("showing duplicate objects")
922
923     for t in tgt_list:
924         if not targets[t.sname] in [ 'LIBRARY', 'PYTHON' ]:
925             continue
926         for n in getattr(t, 'final_objects', set()):
927             t2 = bld.get_tgen_by_name(n)
928             if not n in used_by:
929                 used_by[n] = set()
930             used_by[n].add(t.sname)
931
932     for n in used_by:
933         if len(used_by[n]) > 1:
934             Logs.info("target '%s' is used by %s" % (n, used_by[n]))
935
936     Logs.info("showing indirect dependency counts (sorted by count)")
937
938     def indirect_count(t1, t2):
939         return len(t2.indirect_objects) - len(t1.indirect_objects)
940
941     sorted_list = sorted(tgt_list, cmp=indirect_count)
942     for t in sorted_list:
943         if len(t.indirect_objects) > 1:
944             Logs.info("%s depends on %u indirect objects" % (t.sname, len(t.indirect_objects)))
945
946
947 ######################################################################
948 # this provides a way to save our dependency calculations between runs
949 savedeps_version = 3
950 savedeps_inputs  = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags',
951                     'source', 'grouping_library', 'samba_ldflags', 'allow_undefined_symbols',
952                     'use_global_deps', 'global_include' ]
953 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes',
954                     'ccflags', 'ldflags', 'samba_deps_extended', 'final_libs']
955 savedeps_outenv  = ['INC_PATHS']
956 savedeps_envvars = ['NONSHARED_BINARIES', 'GLOBAL_DEPENDENCIES', 'EXTRA_CFLAGS', 'EXTRA_LDFLAGS', 'EXTRA_INCLUDES' ]
957 savedeps_caches  = ['GLOBAL_DEPENDENCIES', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
958 savedeps_files   = ['buildtools/wafsamba/samba_deps.py']
959
960 def save_samba_deps(bld, tgt_list):
961     '''save the dependency calculations between builds, to make
962        further builds faster'''
963     denv = Environment.Environment()
964
965     denv.version = savedeps_version
966     denv.savedeps_inputs = savedeps_inputs
967     denv.savedeps_outputs = savedeps_outputs
968     denv.input = {}
969     denv.output = {}
970     denv.outenv = {}
971     denv.caches = {}
972     denv.envvar = {}
973     denv.files  = {}
974
975     for f in savedeps_files:
976         denv.files[f] = os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime
977
978     for c in savedeps_caches:
979         denv.caches[c] = LOCAL_CACHE(bld, c)
980
981     for e in savedeps_envvars:
982         denv.envvar[e] = bld.env[e]
983
984     for t in tgt_list:
985         # save all the input attributes for each target
986         tdeps = {}
987         for attr in savedeps_inputs:
988             v = getattr(t, attr, None)
989             if v is not None:
990                 tdeps[attr] = v
991         if tdeps != {}:
992             denv.input[t.sname] = tdeps
993
994         # save all the output attributes for each target
995         tdeps = {}
996         for attr in savedeps_outputs:
997             v = getattr(t, attr, None)
998             if v is not None:
999                 tdeps[attr] = v
1000         if tdeps != {}:
1001             denv.output[t.sname] = tdeps
1002
1003         tdeps = {}
1004         for attr in savedeps_outenv:
1005             if attr in t.env:
1006                 tdeps[attr] = t.env[attr]
1007         if tdeps != {}:
1008             denv.outenv[t.sname] = tdeps
1009
1010     depsfile = os.path.join(bld.bdir, "sambadeps")
1011     denv.store_fast(depsfile)
1012
1013
1014
1015 def load_samba_deps(bld, tgt_list):
1016     '''load a previous set of build dependencies if possible'''
1017     depsfile = os.path.join(bld.bdir, "sambadeps")
1018     denv = Environment.Environment()
1019     try:
1020         debug('deps: checking saved dependencies')
1021         denv.load_fast(depsfile)
1022         if (denv.version != savedeps_version or
1023             denv.savedeps_inputs != savedeps_inputs or
1024             denv.savedeps_outputs != savedeps_outputs):
1025             return False
1026     except Exception:
1027         return False
1028
1029     # check if critical files have changed
1030     for f in savedeps_files:
1031         if f not in denv.files:
1032             return False
1033         if denv.files[f] != os.stat(os.path.join(bld.srcnode.abspath(), f)).st_mtime:
1034             return False
1035
1036     # check if caches are the same
1037     for c in savedeps_caches:
1038         if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
1039             return False
1040
1041     # check if caches are the same
1042     for e in savedeps_envvars:
1043         if e not in denv.envvar or denv.envvar[e] != bld.env[e]:
1044             return False
1045
1046     # check inputs are the same
1047     for t in tgt_list:
1048         tdeps = {}
1049         for attr in savedeps_inputs:
1050             v = getattr(t, attr, None)
1051             if v is not None:
1052                 tdeps[attr] = v
1053         if t.sname in denv.input:
1054             olddeps = denv.input[t.sname]
1055         else:
1056             olddeps = {}
1057         if tdeps != olddeps:
1058             #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
1059             return False
1060
1061     # put outputs in place
1062     for t in tgt_list:
1063         if not t.sname in denv.output: continue
1064         tdeps = denv.output[t.sname]
1065         for a in tdeps:
1066             setattr(t, a, tdeps[a])
1067
1068     # put output env vars in place
1069     for t in tgt_list:
1070         if not t.sname in denv.outenv: continue
1071         tdeps = denv.outenv[t.sname]
1072         for a in tdeps:
1073             t.env[a] = tdeps[a]
1074
1075     debug('deps: loaded saved dependencies')
1076     return True
1077
1078
1079
1080 def check_project_rules(bld):
1081     '''check the project rules - ensuring the targets are sane'''
1082
1083     loops = {}
1084     inc_loops = {}
1085
1086     tgt_list = get_tgt_list(bld)
1087
1088     add_samba_attributes(bld, tgt_list)
1089
1090     force_project_rules = (Options.options.SHOWDEPS or
1091                            Options.options.SHOW_DUPLICATES)
1092
1093     if not force_project_rules and load_samba_deps(bld, tgt_list):
1094         return
1095
1096     global tstart
1097     tstart = time.clock()
1098
1099     bld.new_rules = True
1100     Logs.info("Checking project rules ...")
1101
1102     debug('deps: project rules checking started')
1103
1104     expand_subsystem_deps(bld)
1105
1106     debug("deps: expand_subsystem_deps: %f" % (time.clock() - tstart))
1107
1108     replace_grouping_libraries(bld, tgt_list)
1109
1110     debug("deps: replace_grouping_libraries: %f" % (time.clock() - tstart))
1111
1112     build_direct_deps(bld, tgt_list)
1113
1114     debug("deps: build_direct_deps: %f" % (time.clock() - tstart))
1115
1116     break_dependency_loops(bld, tgt_list)
1117
1118     debug("deps: break_dependency_loops: %f" % (time.clock() - tstart))
1119
1120     if Options.options.SHOWDEPS:
1121             show_dependencies(bld, Options.options.SHOWDEPS, set())
1122
1123     calculate_final_deps(bld, tgt_list, loops)
1124
1125     debug("deps: calculate_final_deps: %f" % (time.clock() - tstart))
1126
1127     if Options.options.SHOW_DUPLICATES:
1128             show_object_duplicates(bld, tgt_list)
1129
1130     # run the various attribute generators
1131     for f in [ build_dependencies, build_includes, add_init_functions ]:
1132         debug('deps: project rules checking %s', f)
1133         for t in tgt_list: f(t)
1134         debug("deps: %s: %f" % (f, time.clock() - tstart))
1135
1136     debug('deps: project rules stage1 completed')
1137
1138     if not check_duplicate_sources(bld, tgt_list):
1139         Logs.error("Duplicate sources present - aborting")
1140         sys.exit(1)
1141
1142     debug("deps: check_duplicate_sources: %f" % (time.clock() - tstart))
1143
1144     if not bld.check_group_ordering(tgt_list):
1145         Logs.error("Bad group ordering - aborting")
1146         sys.exit(1)
1147
1148     debug("deps: check_group_ordering: %f" % (time.clock() - tstart))
1149
1150     show_final_deps(bld, tgt_list)
1151
1152     debug("deps: show_final_deps: %f" % (time.clock() - tstart))
1153
1154     debug('deps: project rules checking completed - %u targets checked',
1155           len(tgt_list))
1156
1157     if not bld.is_install:
1158         save_samba_deps(bld, tgt_list)
1159
1160     debug("deps: save_samba_deps: %f" % (time.clock() - tstart))
1161
1162     Logs.info("Project rules pass")
1163
1164
1165 def CHECK_PROJECT_RULES(bld):
1166     '''enable checking of project targets for sanity'''
1167     if bld.env.added_project_rules:
1168         return
1169     bld.env.added_project_rules = True
1170     bld.add_pre_fun(check_project_rules)
1171 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES
1172
1173