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