1 # Samba automatic dependency handling and project rules
3 import Build, os, re, Environment
4 from samba_utils import *
5 from samba_autoconf import *
8 def ADD_GLOBAL_DEPENDENCY(ctx, dep):
9 '''add a dependency for all binaries and libraries'''
10 if not 'GLOBAL_DEPENDENCIES' in ctx.env:
11 ctx.env.GLOBAL_DEPENDENCIES = []
12 ctx.env.GLOBAL_DEPENDENCIES.append(dep)
15 def TARGET_ALIAS(bld, target, alias):
16 '''define an alias for a target name'''
17 cache = LOCAL_CACHE(bld, 'TARGET_ALIAS')
18 bld.ASSERT(alias not in cache, "Target alias %s already set" % alias)
20 Build.BuildContext.TARGET_ALIAS = TARGET_ALIAS
23 def EXPAND_ALIAS(bld, target):
24 '''expand a target name via an alias'''
25 aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS')
27 return aliases[target]
29 Build.BuildContext.EXPAND_ALIAS = EXPAND_ALIAS
32 def expand_subsystem_deps(bld):
33 '''expand the reverse dependencies resulting from subsystem
34 attributes of modules'''
35 subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
36 aliases = LOCAL_CACHE(bld, 'TARGET_ALIAS')
37 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
42 bld.ASSERT(s in targets, "Subsystem target %s not declared" % s)
44 if type == 'DISABLED' or type == 'EMPTY':
47 t = bld.name_to_obj(s, bld.env)
48 bld.ASSERT(t is not None, "Subsystem target %s not found" % s)
49 for d in subsystems[s]:
50 type = targets[d['TARGET']]
51 if type != 'DISABLED' and type != 'EMPTY':
52 t.samba_deps_extended.append(d['TARGET'])
53 t2 = bld.name_to_obj(d['TARGET'], bld.env)
54 t2.samba_includes_extended.extend(t.samba_includes_extended)
55 t2.samba_deps_extended.extend(t.samba_deps_extended)
56 t.samba_deps_extended = unique_list(t.samba_deps_extended)
60 def build_dependencies(self):
61 '''This builds the dependency list for a target. It runs after all the targets are declared
63 The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
64 the full dependency list for a target until we have all of the targets declared.
67 # we only should add extra library and object deps on libraries and binaries
68 if not self.samba_type in ['LIBRARY', 'BINARY', 'PYTHON']:
71 # we need to link against:
73 # 1) any direct system libs
74 # 2) any indirect system libs that come from subsystem dependencies
75 # 3) any direct local libs
76 # 4) any indirect local libs that come from subsystem dependencies
77 # 5) any direct objects
78 # 6) any indirect objects that come from subsystem dependencies
80 self.uselib = list(self.final_syslibs)
81 self.uselib_local = list(self.final_libs)
82 self.add_objects = list(self.final_objects)
84 debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
85 self.sname, self.uselib, self.uselib_local, self.add_objects)
89 def build_includes(self):
90 '''This builds the right set of includes for a target.
92 One tricky part of this is that the includes= attribute for a
93 target needs to use paths which are relative to that targets
94 declaration directory (which we can get at via t.path).
96 The way this works is the includes list gets added as
97 samba_includes in the main build task declaration. Then this
98 function runs after all of the tasks are declared, and it
99 processes the samba_includes attribute to produce a includes=
103 if getattr(self, 'samba_includes', None) is None:
108 inc_deps = self.includes_objects
112 # maybe add local includes
113 if getattr(self, 'local_include', True) == True and getattr(self, 'local_include_first', True):
116 includes.extend(self.samba_includes_extended)
118 if 'EXTRA_INCLUDES' in bld.env:
119 includes.extend(bld.env['EXTRA_INCLUDES'])
127 t = bld.name_to_obj(d, bld.env)
128 bld.ASSERT(t is not None, "Unable to find dependency %s for %s" % (d, self.sname))
129 inclist = getattr(t, 'samba_includes_extended', [])
130 if getattr(t, 'local_include', True) == True:
134 tpath = t.samba_abspath
136 npath = tpath + '/' + inc
137 if not npath in inc_set:
138 inc_abs.append(npath)
141 mypath = self.path.abspath(bld.env)
143 relpath = os_path_relpath(inc, mypath)
144 includes.append(relpath)
146 if getattr(self, 'local_include', True) == True and not getattr(self, 'local_include_first', True):
149 self.includes = unique_list(includes)
150 debug('deps: includes for target %s: includes=%s',
151 self.sname, self.includes)
155 def add_init_functions(self):
156 '''This builds the right set of init functions'''
160 subsystems = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
163 if self.sname in subsystems:
164 modules.append(self.sname)
166 m = getattr(self, 'samba_modules', None)
168 modules.extend(TO_LIST(m))
170 m = getattr(self, 'samba_subsystem', None)
177 sentinal = getattr(self, 'init_function_sentinal', 'NULL')
179 cflags = getattr(self, 'samba_cflags', [])[:]
181 bld.ASSERT(m in subsystems,
182 "No init_function defined for module '%s' in target '%s'" % (m, self.sname))
184 for d in subsystems[m]:
185 init_fn_list.append(d['INIT_FUNCTION'])
186 cflags.append('-DSTATIC_%s_MODULES=%s' % (m, ','.join(init_fn_list) + ',' + sentinal))
187 self.ccflags = cflags
191 def check_duplicate_sources(bld, tgt_list):
192 '''see if we are compiling the same source file into multiple
193 subsystem targets for the same library or binary'''
195 debug('deps: checking for duplicate sources')
197 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
200 if not targets[t.sname] in [ 'LIBRARY', 'BINARY', 'PYTHON' ]:
204 for obj in t.add_objects:
205 t2 = t.bld.name_to_obj(obj, bld.env)
206 obj_sources = getattr(t2, 'source', '')
207 if obj_sources == '': continue
208 tpath = os_path_relpath(t2.path.abspath(bld.env), t.env['BUILD_DIRECTORY'] + '/default')
209 obj_sources = bld.SUBDIR(tpath, obj_sources)
210 sources.append( { 'dep':obj, 'src':set(TO_LIST(obj_sources)) } )
211 #debug('deps: dependency expansion for target %s add_object %s: %s',
212 # t.sname, obj, obj_sources)
215 if s['dep'] == s2['dep']: continue
216 common = s['src'].intersection(s2['src'])
219 "Target %s has duplicate source files in %s and %s : %s" % (t.sname,
223 def check_orpaned_targets(bld, tgt_list):
224 '''check if any build targets are orphaned'''
226 target_dict = LOCAL_CACHE(bld, 'TARGET_TYPE')
228 debug('deps: checking for orphaned targets')
231 if getattr(t, 'samba_used', False) == True:
233 type = target_dict[t.sname]
234 if not type in ['BINARY', 'LIBRARY', 'MODULE', 'ET', 'PYTHON']:
235 if re.search('^PIDL_', t.sname) is None:
236 print "Target %s of type %s is unused by any other target" % (t.sname, type)
239 def show_final_deps(bld, tgt_list):
240 '''show the final dependencies for all targets'''
242 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
245 if not targets[t.sname] in ['LIBRARY', 'BINARY', 'PYTHON']:
247 debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
248 t.sname, t.uselib, t.uselib_local, t.add_objects)
251 def add_samba_attributes(bld, tgt_list):
252 '''ensure a target has a the required samba attributes'''
254 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
261 t.samba_type = targets[t.sname]
262 t.samba_abspath = t.path.abspath(bld.env)
263 t.samba_deps_extended = t.samba_deps[:]
264 t.samba_includes_extended = TO_LIST(t.samba_includes)[:]
265 t.ccflags = getattr(t, 'samba_cflags', '')
267 def build_direct_deps(bld, tgt_list):
268 '''build the direct_objects and direct_libs sets for each target'''
270 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
271 global_deps = bld.env.GLOBAL_DEPENDENCIES
274 t.direct_objects = set()
275 t.direct_libs = set()
276 t.direct_syslibs = set()
277 deps = t.samba_deps_extended
278 deps.extend(global_deps)
280 d = EXPAND_ALIAS(bld, d)
282 print "Unknown dependency %s in %s" % (d, t.sname)
284 if targets[d] in [ 'EMPTY', 'DISABLED' ]:
286 if targets[d] == 'SYSLIB':
287 t.direct_syslibs.add(d)
289 t2 = bld.name_to_obj(d, bld.env)
291 print "no task %s type %s" % (d, targets[d])
292 if t2.samba_type in [ 'LIBRARY', 'MODULE' ]:
294 elif t2.samba_type in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
295 t.direct_objects.add(d)
296 debug('deps: built direct dependencies')
300 def indirect_libs(bld, t, chain):
301 '''recursively calculate the indirect library dependencies for a target
303 An indirect library is a library that results from a dependency on
307 ret = getattr(t, 'indirect_libs', None)
312 for obj in t.direct_objects:
316 t2 = bld.name_to_obj(obj, bld.env)
317 r2 = indirect_libs(bld, t2, chain)
319 ret = ret.union(t2.direct_libs)
322 for obj in t.indirect_objects:
326 t2 = bld.name_to_obj(obj, bld.env)
327 r2 = indirect_libs(bld, t2, chain)
329 ret = ret.union(t2.direct_libs)
332 t.indirect_libs = ret
337 def indirect_syslibs(bld, t, chain):
338 '''recursively calculate the indirect system library dependencies for a target
340 An indirect syslib results from a subsystem dependency
343 ret = getattr(t, 'indirect_syslibs', None)
347 for obj in t.direct_objects:
351 t2 = bld.name_to_obj(obj, bld.env)
352 r2 = indirect_syslibs(bld, t2, chain)
354 ret = ret.union(t2.direct_syslibs)
357 t.indirect_syslibs = ret
361 def indirect_objects(bld, t, chain):
362 '''recursively calculate the indirect object dependencies for a target
364 indirect objects are the set of objects from expanding the
365 subsystem dependencies
368 ret = getattr(t, 'indirect_objects', None)
369 if ret is not None: return ret
372 for lib in t.direct_objects:
376 t2 = bld.name_to_obj(lib, bld.env)
377 r2 = indirect_objects(bld, t2, chain)
379 ret = ret.union(t2.direct_objects)
382 t.indirect_objects = ret
386 def expanded_targets(bld, t, chain):
387 '''recursively calculate the expanded targets for a target
389 expanded objects are the set of objects, libraries and syslibs
390 from expanding the subsystem dependencies, library dependencies
391 and syslib dependencies
394 ret = getattr(t, 'expanded_targets', None)
395 if ret is not None: return ret
397 ret = t.direct_objects.copy()
398 ret = ret.union(t.direct_libs)
399 ret = ret.union(t.direct_syslibs)
404 if d in chain: continue
406 t2 = bld.name_to_obj(d, bld.env)
407 if t2 is None: continue
408 r2 = expanded_targets(bld, t2, chain)
415 t.expanded_targets = ret
419 def expanded_targets2(bld, t, chain):
420 '''recursively calculate the expanded targets for a target
422 expanded objects are the set of objects from expanding the
423 subsystem dependencies and library dependencies
426 ret = getattr(t, 'expanded_targets2', None)
427 if ret is not None: return ret
429 ret = t.final_objects.copy()
431 for attr in [ 'final_objects', 'final_libs' ]:
432 f = getattr(t, attr, set())
437 t2 = bld.name_to_obj(d, bld.env)
438 if t2 is None: continue
439 r2 = expanded_targets2(bld, t2, chain)
446 t.expanded_targets2 = ret
450 def includes_objects(bld, t, chain):
451 '''recursively calculate the includes object dependencies for a target
453 includes dependencies come from either library or object dependencies
455 ret = getattr(t, 'includes_objects', None)
459 ret = t.direct_objects.copy()
460 ret = ret.union(t.direct_libs)
462 for obj in t.direct_objects:
466 t2 = bld.name_to_obj(obj, bld.env)
467 r2 = includes_objects(bld, t2, chain)
469 ret = ret.union(t2.direct_objects)
472 for lib in t.direct_libs:
476 t2 = bld.name_to_obj(lib, bld.env)
477 r2 = includes_objects(bld, t2, chain)
479 ret = ret.union(t2.direct_objects)
482 t.includes_objects = ret
486 def build_indirect_deps(bld, tgt_list):
487 '''build the indirect_objects and indirect_libs sets for each target'''
489 indirect_objects(bld, t, set())
490 indirect_libs(bld, t, set())
491 indirect_syslibs(bld, t, set())
492 includes_objects(bld, t, set())
493 expanded_targets(bld, t, set())
494 debug('deps: built indirect dependencies')
497 def re_expand2(bld, tgt_list):
499 t.expanded_targets2 = None
500 for type in ['BINARY','LIBRARY','PYTHON']:
502 if t.samba_type == type:
503 expanded_targets2(bld, t, set())
505 expanded_targets2(bld, t, set())
508 def calculate_final_deps(bld, tgt_list):
509 '''calculate the final library and object dependencies'''
511 # start with the maximum possible list
512 t.final_syslibs = t.direct_syslibs.union(t.indirect_syslibs)
513 t.final_libs = t.direct_libs.union(t.indirect_libs)
514 t.final_objects = t.direct_objects.union(t.indirect_objects)
517 # don't depend on ourselves
518 if t.sname in t.final_libs:
519 t.final_libs.remove(t.sname)
520 if t.sname in t.final_objects:
521 t.final_objects.remove(t.sname)
523 re_expand2(bld, tgt_list)
527 # find any library loops
529 if t.samba_type in ['LIBRARY', 'PYTHON']:
530 for l in t.final_libs.copy():
531 t2 = bld.name_to_obj(l, bld.env)
532 if t.sname in t2.final_libs:
533 debug('deps: removing library loop %s<->%s', t.sname, l)
534 t2.final_libs.remove(t.sname)
535 loops[t2.sname] = t.sname;
537 re_expand2(bld, tgt_list)
539 for type in ['BINARY']:
543 if t.samba_type != type: continue
544 # if we will indirectly link to a target then we don't need it
545 new = t.final_objects.copy()
546 for l in t.final_libs:
547 t2 = bld.name_to_obj(l, bld.env)
548 dup = new.intersection(t2.expanded_targets2)
550 debug('deps: removing dups from %s: %s also in %s %s',
551 t.sname, dup, t2.samba_type, l)
552 new = new.difference(dup)
555 t.final_objects = new
559 debug('deps: removed duplicate dependencies')
562 ######################################################################
563 # this provides a way to save our dependency calculations between runs
565 savedeps_inputs = ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags']
566 savedeps_outputs = ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags']
567 savedeps_caches = ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS']
569 def save_samba_deps(bld, tgt_list):
570 '''save the dependency calculations between builds, to make
571 further builds faster'''
572 denv = Environment.Environment()
574 denv.version = savedeps_version
575 denv.savedeps_inputs = savedeps_inputs
576 denv.savedeps_outputs = savedeps_outputs
581 for c in savedeps_caches:
582 denv.caches[c] = LOCAL_CACHE(bld, c)
585 # save all the input attributes for each target
587 for attr in savedeps_inputs:
588 v = getattr(t, attr, None)
592 denv.input[t.sname] = tdeps
594 # save all the output attributes for each target
596 for attr in savedeps_outputs:
597 v = getattr(t, attr, None)
601 denv.output[t.sname] = tdeps
603 depsfile = os.path.join(bld.bdir, "sambadeps")
607 def load_samba_deps(bld, tgt_list):
608 '''load a previous set of build dependencies if possible'''
609 depsfile = os.path.join(bld.bdir, "sambadeps")
610 denv = Environment.Environment()
612 debug('deps: checking saved dependencies')
614 if (denv.version != savedeps_version or
615 denv.savedeps_inputs != savedeps_inputs or
616 denv.savedeps_outputs != savedeps_outputs):
621 # check if caches are the same
622 for c in savedeps_caches:
623 if c not in denv.caches or denv.caches[c] != LOCAL_CACHE(bld, c):
626 # check inputs are the same
629 for attr in savedeps_inputs:
630 v = getattr(t, attr, None)
633 if t.sname in denv.input:
634 olddeps = denv.input[t.sname]
638 #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
641 # put outputs in place
643 if not t.sname in denv.output: continue
644 tdeps = denv.output[t.sname]
646 setattr(t, a, tdeps[a])
648 debug('deps: loaded saved dependencies')
652 def check_project_rules(bld):
653 '''check the project rules - ensuring the targets are sane'''
655 targets = LOCAL_CACHE(bld, 'TARGET_TYPE')
657 # build a list of task generators we are interested in
661 if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']:
663 t = bld.name_to_obj(tgt, bld.env)
666 add_samba_attributes(bld, tgt_list)
668 if load_samba_deps(bld, tgt_list):
671 debug('deps: project rules checking started')
673 expand_subsystem_deps(bld)
674 build_direct_deps(bld, tgt_list)
675 build_indirect_deps(bld, tgt_list)
676 calculate_final_deps(bld, tgt_list)
678 # run the various attribute generators
679 for f in [ build_dependencies, build_includes, add_init_functions ]:
680 debug('deps: project rules checking %s', f)
681 for t in tgt_list: f(t)
683 debug('deps: project rules stage1 completed')
685 #check_orpaned_targets(bld, tgt_list)
686 #check_duplicate_sources(bld, tgt_list)
687 show_final_deps(bld, tgt_list)
689 debug('deps: project rules checking completed - %u targets checked',
692 save_samba_deps(bld, tgt_list)
695 def CHECK_PROJECT_RULES(bld):
696 '''enable checking of project targets for sanity'''
697 if bld.env.added_project_rules:
699 bld.env.added_project_rules = True
700 bld.add_pre_fun(check_project_rules)
701 Build.BuildContext.CHECK_PROJECT_RULES = CHECK_PROJECT_RULES