1 # Samba automatic dependency handling and project rules
3 import Build
, os
, sys
, re
, Environment
, Logs
, time
4 from samba_utils
import *
5 from samba_autoconf
import *
6 from samba_bundled
import BUILTIN_LIBRARY
9 def ADD_GLOBAL_DEPENDENCY(ctx
, dep
):
10 '''add a dependency for all binaries and libraries'''
11 if not 'GLOBAL_DEPENDENCIES' in ctx
.env
:
12 ctx
.env
.GLOBAL_DEPENDENCIES
= []
13 ctx
.env
.GLOBAL_DEPENDENCIES
.append(dep
)
17 def BREAK_CIRCULAR_LIBRARY_DEPENDENCIES(ctx
):
18 '''indicate that circular dependencies between libraries should be broken.'''
19 ctx
.env
.ALLOW_CIRCULAR_LIB_DEPENDENCIES
= True
23 def SET_SYSLIB_DEPS(conf
, target
, deps
):
24 '''setup some implied dependencies for a SYSLIB'''
25 cache
= LOCAL_CACHE(conf
, 'SYSLIB_DEPS')
29 def expand_subsystem_deps(bld
):
30 '''expand the reverse dependencies resulting from subsystem
31 attributes of modules. This is walking over the complete list
32 of declared subsystems, and expands the samba_deps_extended list for any
33 module<->subsystem dependencies'''
35 subsystem_list
= LOCAL_CACHE(bld
, 'INIT_FUNCTIONS')
36 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
38 for subsystem_name
in subsystem_list
:
39 bld
.ASSERT(subsystem_name
in targets
, "Subsystem target %s not declared" % subsystem_name
)
40 type = targets
[subsystem_name
]
41 if type == 'DISABLED' or type == 'EMPTY':
45 # subsystem_name = dcerpc_server (a subsystem)
46 # subsystem = dcerpc_server (a subsystem object)
47 # module_name = rpc_epmapper (a module within the dcerpc_server subsystem)
48 # module = rpc_epmapper (a module object within the dcerpc_server subsystem)
50 subsystem
= bld
.name_to_obj(subsystem_name
, bld
.env
)
51 bld
.ASSERT(subsystem
is not None, "Unable to find subsystem %s" % subsystem_name
)
52 for d
in subsystem_list
[subsystem_name
]:
53 module_name
= d
['TARGET']
54 module_type
= targets
[module_name
]
55 if module_type
in ['DISABLED', 'EMPTY']:
57 bld
.ASSERT(subsystem
is not None,
58 "Subsystem target %s for %s (%s) not found" % (subsystem_name
, module_name
, module_type
))
59 if module_type
in ['SUBSYSTEM']:
60 # if a module is a plain object type (not a library) then the
61 # subsystem it is part of needs to have it as a dependency, so targets
62 # that depend on this subsystem get the modules of that subsystem
63 subsystem
.samba_deps_extended
.append(module_name
)
64 subsystem
.samba_deps_extended
= unique_list(subsystem
.samba_deps_extended
)
68 def build_dependencies(self
):
69 '''This builds the dependency list for a target. It runs after all the targets are declared
71 The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
72 the full dependency list for a target until we have all of the targets declared.
75 if self
.samba_type
in ['LIBRARY', 'BINARY', 'PYTHON']:
76 self
.uselib
= list(self
.final_syslibs
)
77 self
.uselib_local
= list(self
.final_libs
)
78 self
.add_objects
= list(self
.final_objects
)
80 # extra link flags from pkg_config
81 libs
= self
.final_syslibs
.copy()
83 (ccflags
, ldflags
, cpppath
) = library_flags(self
, list(libs
))
84 new_ldflags
= getattr(self
, 'samba_ldflags', [])[:]
85 new_ldflags
.extend(ldflags
)
86 self
.ldflags
= new_ldflags
88 if getattr(self
, 'allow_undefined_symbols', False) and self
.env
.undefined_ldflags
:
89 for f
in self
.env
.undefined_ldflags
:
90 self
.ldflags
.remove(f
)
92 if getattr(self
, 'allow_undefined_symbols', False) and self
.env
.undefined_ignore_ldflags
:
93 for f
in self
.env
.undefined_ignore_ldflags
:
94 self
.ldflags
.append(f
)
96 debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
97 self
.sname
, self
.uselib
, self
.uselib_local
, self
.add_objects
)
99 if self
.samba_type
in ['SUBSYSTEM']:
100 # this is needed for the ccflags of libs that come from pkg_config
101 self
.uselib
= list(self
.final_syslibs
)
102 self
.uselib
.extend(list(self
.direct_syslibs
))
103 for lib
in self
.final_libs
:
104 t
= self
.bld
.name_to_obj(lib
, self
.bld
.env
)
105 self
.uselib
.extend(list(t
.final_syslibs
))
106 self
.uselib
= unique_list(self
.uselib
)
108 if getattr(self
, 'uselib', None):
110 for l
in self
.uselib
:
111 up_list
.append(l
.upper())
112 self
.uselib
= up_list
115 def build_includes(self
):
116 '''This builds the right set of includes for a target.
118 One tricky part of this is that the includes= attribute for a
119 target needs to use paths which are relative to that targets
120 declaration directory (which we can get at via t.path).
122 The way this works is the includes list gets added as
123 samba_includes in the main build task declaration. Then this
124 function runs after all of the tasks are declared, and it
125 processes the samba_includes attribute to produce a includes=
129 if getattr(self
, 'samba_includes', None) is None:
134 inc_deps
= includes_objects(bld
, self
, set(), {})
138 # maybe add local includes
139 if getattr(self
, 'local_include', True) and getattr(self
, 'local_include_first', True):
142 includes
.extend(self
.samba_includes_extended
)
144 if 'EXTRA_INCLUDES' in bld
.env
and getattr(self
, 'global_include', True):
145 includes
.extend(bld
.env
['EXTRA_INCLUDES'])
153 t
= bld
.name_to_obj(d
, bld
.env
)
154 bld
.ASSERT(t
is not None, "Unable to find dependency %s for %s" % (d
, self
.sname
))
155 inclist
= getattr(t
, 'samba_includes_extended', [])[:]
156 if getattr(t
, 'local_include', True):
160 tpath
= t
.samba_abspath
162 npath
= tpath
+ '/' + inc
163 if not npath
in inc_set
:
164 inc_abs
.append(npath
)
167 mypath
= self
.path
.abspath(bld
.env
)
169 relpath
= os_path_relpath(inc
, mypath
)
170 includes
.append(relpath
)
172 if getattr(self
, 'local_include', True) and not getattr(self
, 'local_include_first', True):
175 # now transform the includes list to be relative to the top directory
176 # which is represented by '#' in waf. This allows waf to cache the
177 # includes lists more efficiently
181 # some are already top based
182 includes_top
.append(i
)
184 absinc
= os
.path
.join(self
.path
.abspath(), i
)
185 relinc
= os_path_relpath(absinc
, self
.bld
.srcnode
.abspath())
186 includes_top
.append('#' + relinc
)
188 self
.includes
= unique_list(includes_top
)
189 debug('deps: includes for target %s: includes=%s',
190 self
.sname
, self
.includes
)
193 def add_init_functions(self
):
194 '''This builds the right set of init functions'''
198 subsystems
= LOCAL_CACHE(bld
, 'INIT_FUNCTIONS')
200 # cope with the separated object lists from BINARY and LIBRARY targets
202 if sname
.endswith('.objlist'):
206 if sname
in subsystems
:
207 modules
.append(sname
)
209 m
= getattr(self
, 'samba_modules', None)
211 modules
.extend(TO_LIST(m
))
213 m
= getattr(self
, 'samba_subsystem', None)
217 sentinel
= getattr(self
, 'init_function_sentinel', 'NULL')
219 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
220 cflags
= getattr(self
, 'samba_cflags', [])[:]
223 sname
= sname
.replace('-','_')
224 sname
= sname
.replace('/','_')
225 cflags
.append('-DSTATIC_%s_MODULES=%s' % (sname
, sentinel
))
226 if sentinel
== 'NULL':
227 cflags
.append('-DSTATIC_%s_MODULES_PROTO=' % sname
)
228 self
.ccflags
= cflags
232 bld
.ASSERT(m
in subsystems
,
233 "No init_function defined for module '%s' in target '%s'" % (m
, self
.sname
))
235 for d
in subsystems
[m
]:
236 if targets
[d
['TARGET']] != 'DISABLED':
237 init_fn_list
.append(d
['INIT_FUNCTION'])
238 if init_fn_list
== []:
239 cflags
.append('-DSTATIC_%s_MODULES=%s' % (m
, sentinel
))
240 if sentinel
== 'NULL':
241 cflags
.append('-DSTATIC_%s_MODULES_PROTO' % m
)
243 cflags
.append('-DSTATIC_%s_MODULES=%s' % (m
, ','.join(init_fn_list
) + ',' + sentinel
))
245 for f
in init_fn_list
:
246 proto
= proto
+ '_MODULE_PROTO(%s)' % f
247 cflags
.append('-DSTATIC_%s_MODULES_PROTO=%s' % (m
, proto
))
248 self
.ccflags
= cflags
251 def check_duplicate_sources(bld
, tgt_list
):
252 '''see if we are compiling the same source file more than once
253 without an allow_duplicates attribute'''
255 debug('deps: checking for duplicate sources')
257 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
263 source_list
= TO_LIST(getattr(t
, 'source', ''))
264 tpath
= os
.path
.normpath(os_path_relpath(t
.path
.abspath(bld
.env
), t
.env
.BUILD_DIRECTORY
+ '/default'))
266 for s
in source_list
:
267 p
= os
.path
.normpath(os
.path
.join(tpath
, s
))
269 Logs
.error("ERROR: source %s appears twice in target '%s'" % (p
, t
.sname
))
272 t
.samba_source_set
= obj_sources
276 # build a list of targets that each source file is part of
279 if not targets
[t
.sname
] in [ 'LIBRARY', 'BINARY', 'PYTHON' ]:
281 for obj
in t
.add_objects
:
282 t2
= t
.bld
.name_to_obj(obj
, bld
.env
)
283 source_set
= getattr(t2
, 'samba_source_set', set())
285 if not s
in subsystems
:
287 if not t
.sname
in subsystems
[s
]:
288 subsystems
[s
][t
.sname
] = []
289 subsystems
[s
][t
.sname
].append(t2
.sname
)
292 if len(subsystems
[s
]) > 1 and Options
.options
.SHOW_DUPLICATES
:
293 Logs
.warn("WARNING: source %s is in more than one target: %s" % (s
, subsystems
[s
].keys()))
294 for tname
in subsystems
[s
]:
295 if len(subsystems
[s
][tname
]) > 1:
296 raise Utils
.WafError("ERROR: source %s is in more than one subsystem of target '%s': %s" % (s
, tname
, subsystems
[s
][tname
]))
301 def check_orphaned_targets(bld
, tgt_list
):
302 '''check if any build targets are orphaned'''
304 target_dict
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
306 debug('deps: checking for orphaned targets')
309 if getattr(t
, 'samba_used', False):
311 type = target_dict
[t
.sname
]
312 if not type in ['BINARY', 'LIBRARY', 'MODULE', 'ET', 'PYTHON']:
313 if re
.search('^PIDL_', t
.sname
) is None:
314 Logs
.warn("Target %s of type %s is unused by any other target" % (t
.sname
, type))
317 def check_group_ordering(bld
, tgt_list
):
318 '''see if we have any dependencies that violate the group ordering
320 It is an error for a target to depend on a target from a later
325 tm
= bld
.task_manager
326 return [x
for x
in tm
.groups_names
if id(tm
.groups_names
[x
]) == id(g
)][0]
328 for g
in bld
.task_manager
.groups
:
329 gname
= group_name(g
)
330 for t
in g
.tasks_gen
:
331 t
.samba_group
= gname
335 for g
in bld
.task_manager
.groups
:
340 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
344 tdeps
= getattr(t
, 'add_objects', []) + getattr(t
, 'uselib_local', [])
346 t2
= bld
.name_to_obj(d
, bld
.env
)
349 map1
= grp_map
[t
.samba_group
]
350 map2
= grp_map
[t2
.samba_group
]
353 Logs
.error("Target %r in build group %r depends on target %r from later build group %r" % (
354 t
.sname
, t
.samba_group
, t2
.sname
, t2
.samba_group
))
360 def show_final_deps(bld
, tgt_list
):
361 '''show the final dependencies for all targets'''
363 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
366 if not targets
[t
.sname
] in ['LIBRARY', 'BINARY', 'PYTHON', 'SUBSYSTEM']:
368 debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
369 t
.sname
, t
.uselib
, getattr(t
, 'uselib_local', []), getattr(t
, 'add_objects', []))
372 def add_samba_attributes(bld
, tgt_list
):
373 '''ensure a target has a the required samba attributes'''
375 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
382 t
.samba_type
= targets
[t
.sname
]
383 t
.samba_abspath
= t
.path
.abspath(bld
.env
)
384 t
.samba_deps_extended
= t
.samba_deps
[:]
385 t
.samba_includes_extended
= TO_LIST(t
.samba_includes
)[:]
386 t
.ccflags
= getattr(t
, 'samba_cflags', '')
388 def replace_grouping_libraries(bld
, tgt_list
):
389 '''replace dependencies based on grouping libraries
391 If a library is marked as a grouping library, then any target that
392 depends on a subsystem that is part of that grouping library gets
393 that dependency replaced with a dependency on the grouping library
396 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
400 # find our list of grouping libraries, mapped from the subsystems they depend on
402 if not getattr(t
, 'grouping_library', False):
404 for dep
in t
.samba_deps_extended
:
405 bld
.ASSERT(dep
in targets
, "grouping library target %s not declared in %s" % (dep
, t
.sname
))
406 if targets
[dep
] == 'SUBSYSTEM':
407 grouping
[dep
] = t
.sname
409 # now replace any dependencies on elements of grouping libraries
411 for i
in range(len(t
.samba_deps_extended
)):
412 dep
= t
.samba_deps_extended
[i
]
414 if t
.sname
!= grouping
[dep
]:
415 debug("deps: target %s: replacing dependency %s with grouping library %s" % (t
.sname
, dep
, grouping
[dep
]))
416 t
.samba_deps_extended
[i
] = grouping
[dep
]
420 def build_direct_deps(bld
, tgt_list
):
421 '''build the direct_objects and direct_libs sets for each target'''
423 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
424 syslib_deps
= LOCAL_CACHE(bld
, 'SYSLIB_DEPS')
426 global_deps
= bld
.env
.GLOBAL_DEPENDENCIES
427 global_deps_exclude
= set()
428 for dep
in global_deps
:
429 t
= bld
.name_to_obj(dep
, bld
.env
)
430 for d
in t
.samba_deps
:
431 # prevent loops from the global dependencies list
432 global_deps_exclude
.add(d
)
433 global_deps_exclude
.add(d
+ '.objlist')
436 t
.direct_objects
= set()
437 t
.direct_libs
= set()
438 t
.direct_syslibs
= set()
439 deps
= t
.samba_deps_extended
[:]
440 if getattr(t
, 'samba_use_global_deps', False) and not t
.sname
in global_deps_exclude
:
441 deps
.extend(global_deps
)
443 if d
== t
.sname
: continue
445 Logs
.error("Unknown dependency '%s' in '%s'" % (d
, t
.sname
))
447 if targets
[d
] in [ 'EMPTY', 'DISABLED' ]:
449 if targets
[d
] == 'PYTHON' and targets
[t
.sname
] != 'PYTHON' and t
.sname
.find('.objlist') == -1:
450 # this check should be more restrictive, but for now we have pidl-generated python
451 # code that directly depends on other python modules
452 Logs
.error('ERROR: Target %s has dependency on python module %s' % (t
.sname
, d
))
454 if targets
[d
] == 'SYSLIB':
455 t
.direct_syslibs
.add(d
)
457 for implied
in TO_LIST(syslib_deps
[d
]):
458 if BUILTIN_LIBRARY(bld
, implied
):
459 t
.direct_objects
.add(implied
)
460 elif targets
[implied
] == 'SYSLIB':
461 t
.direct_syslibs
.add(implied
)
462 elif targets
[implied
] in ['LIBRARY', 'MODULE']:
463 t
.direct_libs
.add(implied
)
465 Logs
.error('Implied dependency %s in %s is of type %s' % (
466 implied
, t
.sname
, targets
[implied
]))
469 t2
= bld
.name_to_obj(d
, bld
.env
)
471 Logs
.error("no task %s of type %s in %s" % (d
, targets
[d
], t
.sname
))
473 if t2
.samba_type
in [ 'LIBRARY', 'MODULE' ]:
475 elif t2
.samba_type
in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
476 t
.direct_objects
.add(d
)
477 debug('deps: built direct dependencies')
480 def dependency_loop(loops
, t
, target
):
481 '''add a dependency loop to the loops dictionary'''
482 if t
.sname
== target
:
484 if not target
in loops
:
485 loops
[target
] = set()
486 if not t
.sname
in loops
[target
]:
487 loops
[target
].add(t
.sname
)
490 def indirect_libs(bld
, t
, chain
, loops
):
491 '''recursively calculate the indirect library dependencies for a target
493 An indirect library is a library that results from a dependency on
497 ret
= getattr(t
, 'indirect_libs', None)
502 for obj
in t
.direct_objects
:
504 dependency_loop(loops
, t
, obj
)
507 t2
= bld
.name_to_obj(obj
, bld
.env
)
508 r2
= indirect_libs(bld
, t2
, chain
, loops
)
510 ret
= ret
.union(t2
.direct_libs
)
513 for obj
in indirect_objects(bld
, t
, set(), loops
):
515 dependency_loop(loops
, t
, obj
)
518 t2
= bld
.name_to_obj(obj
, bld
.env
)
519 r2
= indirect_libs(bld
, t2
, chain
, loops
)
521 ret
= ret
.union(t2
.direct_libs
)
524 t
.indirect_libs
= ret
529 def indirect_objects(bld
, t
, chain
, loops
):
530 '''recursively calculate the indirect object dependencies for a target
532 indirect objects are the set of objects from expanding the
533 subsystem dependencies
536 ret
= getattr(t
, 'indirect_objects', None)
537 if ret
is not None: return ret
540 for lib
in t
.direct_objects
:
542 dependency_loop(loops
, t
, lib
)
545 t2
= bld
.name_to_obj(lib
, bld
.env
)
546 r2
= indirect_objects(bld
, t2
, chain
, loops
)
548 ret
= ret
.union(t2
.direct_objects
)
551 t
.indirect_objects
= ret
555 def extended_objects(bld
, t
, chain
):
556 '''recursively calculate the extended object dependencies for a target
558 extended objects are the union of:
561 - direct and indirect objects of all direct and indirect libraries
564 ret
= getattr(t
, 'extended_objects', None)
565 if ret
is not None: return ret
568 ret
= ret
.union(t
.final_objects
)
570 for lib
in t
.final_libs
:
573 t2
= bld
.name_to_obj(lib
, bld
.env
)
575 r2
= extended_objects(bld
, t2
, chain
)
577 ret
= ret
.union(t2
.final_objects
)
580 t
.extended_objects
= ret
584 def includes_objects(bld
, t
, chain
, inc_loops
):
585 '''recursively calculate the includes object dependencies for a target
587 includes dependencies come from either library or object dependencies
589 ret
= getattr(t
, 'includes_objects', None)
593 ret
= t
.direct_objects
.copy()
594 ret
= ret
.union(t
.direct_libs
)
596 for obj
in t
.direct_objects
:
598 dependency_loop(inc_loops
, t
, obj
)
601 t2
= bld
.name_to_obj(obj
, bld
.env
)
602 r2
= includes_objects(bld
, t2
, chain
, inc_loops
)
604 ret
= ret
.union(t2
.direct_objects
)
607 for lib
in t
.direct_libs
:
609 dependency_loop(inc_loops
, t
, lib
)
612 t2
= bld
.name_to_obj(lib
, bld
.env
)
614 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
615 Logs
.error('Target %s of type %s not found in direct_libs for %s' % (
616 lib
, targets
[lib
], t
.sname
))
618 r2
= includes_objects(bld
, t2
, chain
, inc_loops
)
620 ret
= ret
.union(t2
.direct_objects
)
623 t
.includes_objects
= ret
627 def break_dependency_loops(bld
, tgt_list
):
628 '''find and break dependency loops'''
632 # build up the list of loops
634 indirect_objects(bld
, t
, set(), loops
)
635 indirect_libs(bld
, t
, set(), loops
)
636 includes_objects(bld
, t
, set(), inc_loops
)
641 for attr
in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
642 objs
= getattr(t
, attr
, set())
643 setattr(t
, attr
, objs
.difference(loops
[t
.sname
]))
646 debug('deps: Found dependency loops for target %s : %s', loop
, loops
[loop
])
648 for loop
in inc_loops
:
649 debug('deps: Found include loops for target %s : %s', loop
, inc_loops
[loop
])
651 # expand the loops mapping by one level
652 for loop
in loops
.copy():
653 for tgt
in loops
[loop
]:
655 loops
[loop
] = loops
[loop
].union(loops
[tgt
])
657 for loop
in inc_loops
.copy():
658 for tgt
in inc_loops
[loop
]:
660 inc_loops
[loop
] = inc_loops
[loop
].union(inc_loops
[tgt
])
663 # expand indirect subsystem and library loops
664 for loop
in loops
.copy():
665 t
= bld
.name_to_obj(loop
, bld
.env
)
666 if t
.samba_type
in ['SUBSYSTEM']:
667 loops
[loop
] = loops
[loop
].union(t
.indirect_objects
)
668 loops
[loop
] = loops
[loop
].union(t
.direct_objects
)
669 if t
.samba_type
in ['LIBRARY','PYTHON']:
670 loops
[loop
] = loops
[loop
].union(t
.indirect_libs
)
671 loops
[loop
] = loops
[loop
].union(t
.direct_libs
)
672 if loop
in loops
[loop
]:
673 loops
[loop
].remove(loop
)
675 # expand indirect includes loops
676 for loop
in inc_loops
.copy():
677 t
= bld
.name_to_obj(loop
, bld
.env
)
678 inc_loops
[loop
] = inc_loops
[loop
].union(t
.includes_objects
)
679 if loop
in inc_loops
[loop
]:
680 inc_loops
[loop
].remove(loop
)
682 # add in the replacement dependencies
685 for attr
in ['indirect_objects', 'indirect_libs']:
686 objs
= getattr(t
, attr
, set())
688 diff
= loops
[loop
].difference(objs
)
692 debug('deps: Expanded target %s of type %s from loop %s by %s', t
.sname
, t
.samba_type
, loop
, diff
)
693 objs
= objs
.union(diff
)
694 setattr(t
, attr
, objs
)
696 for loop
in inc_loops
:
697 objs
= getattr(t
, 'includes_objects', set())
699 diff
= inc_loops
[loop
].difference(objs
)
703 debug('deps: Expanded target %s includes of type %s from loop %s by %s', t
.sname
, t
.samba_type
, loop
, diff
)
704 objs
= objs
.union(diff
)
705 setattr(t
, 'includes_objects', objs
)
708 def reduce_objects(bld
, tgt_list
):
709 '''reduce objects by looking for indirect object dependencies'''
713 t
.extended_objects
= None
717 for type in ['BINARY', 'PYTHON', 'LIBRARY']:
719 if t
.samba_type
!= type: continue
720 # if we will indirectly link to a target then we don't need it
721 new
= t
.final_objects
.copy()
722 for l
in t
.final_libs
:
723 t2
= bld
.name_to_obj(l
, bld
.env
)
724 t2_obj
= extended_objects(bld
, t2
, set())
725 dup
= new
.intersection(t2_obj
)
726 if t
.sname
in rely_on
:
727 dup
= dup
.difference(rely_on
[t
.sname
])
729 debug('deps: removing dups from %s of type %s: %s also in %s %s',
730 t
.sname
, t
.samba_type
, dup
, t2
.samba_type
, l
)
731 new
= new
.difference(dup
)
735 rely_on
[l
] = rely_on
[l
].union(dup
)
736 t
.final_objects
= new
741 # add back in any objects that were relied upon by the reduction rules
743 t
= bld
.name_to_obj(r
, bld
.env
)
744 t
.final_objects
= t
.final_objects
.union(rely_on
[r
])
749 def show_library_loop(bld
, lib1
, lib2
, path
, seen
):
750 '''show the detailed path of a library loop between lib1 and lib2'''
752 t
= bld
.name_to_obj(lib1
, bld
.env
)
753 if not lib2
in getattr(t
, 'final_libs', set()):
756 for d
in t
.samba_deps_extended
:
760 path2
= path
+ '=>' + d
762 Logs
.warn('library loop path: ' + path2
)
764 show_library_loop(bld
, d
, lib2
, path2
, seen
)
768 def calculate_final_deps(bld
, tgt_list
, loops
):
769 '''calculate the final library and object dependencies'''
771 # start with the maximum possible list
772 t
.final_libs
= t
.direct_libs
.union(indirect_libs(bld
, t
, set(), loops
))
773 t
.final_objects
= t
.direct_objects
.union(indirect_objects(bld
, t
, set(), loops
))
776 # don't depend on ourselves
777 if t
.sname
in t
.final_libs
:
778 t
.final_libs
.remove(t
.sname
)
779 if t
.sname
in t
.final_objects
:
780 t
.final_objects
.remove(t
.sname
)
782 # handle any non-shared binaries
784 if t
.samba_type
== 'BINARY' and bld
.NONSHARED_BINARY(t
.sname
):
785 subsystem_list
= LOCAL_CACHE(bld
, 'INIT_FUNCTIONS')
786 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
788 # replace lib deps with objlist deps
789 for l
in t
.final_libs
:
790 objname
= l
+ '.objlist'
791 t2
= bld
.name_to_obj(objname
, bld
.env
)
793 Logs
.error('ERROR: subsystem %s not found' % objname
)
795 t
.final_objects
.add(objname
)
796 t
.final_objects
= t
.final_objects
.union(extended_objects(bld
, t2
, set()))
797 if l
in subsystem_list
:
798 # its a subsystem - we also need the contents of any modules
799 for d
in subsystem_list
[l
]:
800 module_name
= d
['TARGET']
801 if targets
[module_name
] == 'LIBRARY':
802 objname
= module_name
+ '.objlist'
803 elif targets
[module_name
] == 'SUBSYSTEM':
804 objname
= module_name
807 t2
= bld
.name_to_obj(objname
, bld
.env
)
809 Logs
.error('ERROR: subsystem %s not found' % objname
)
811 t
.final_objects
.add(objname
)
812 t
.final_objects
= t
.final_objects
.union(extended_objects(bld
, t2
, set()))
815 # find any library loops
817 if t
.samba_type
in ['LIBRARY', 'PYTHON']:
818 for l
in t
.final_libs
.copy():
819 t2
= bld
.name_to_obj(l
, bld
.env
)
820 if t
.sname
in t2
.final_libs
:
821 if getattr(bld
.env
, "ALLOW_CIRCULAR_LIB_DEPENDENCIES", False):
822 # we could break this in either direction. If one of the libraries
823 # has a version number, and will this be distributed publicly, then
824 # we should make it the lower level library in the DAG
825 Logs
.warn('deps: removing library loop %s from %s' % (t
.sname
, t2
.sname
))
826 dependency_loop(loops
, t
, t2
.sname
)
827 t2
.final_libs
.remove(t
.sname
)
829 Logs
.error('ERROR: circular library dependency between %s and %s'
830 % (t
.sname
, t2
.sname
))
831 show_library_loop(bld
, t
.sname
, t2
.sname
, t
.sname
, set())
832 show_library_loop(bld
, t2
.sname
, t
.sname
, t2
.sname
, set())
836 debug('deps: Found dependency loops for target %s : %s', loop
, loops
[loop
])
838 # we now need to make corrections for any library loops we broke up
839 # any target that depended on the target of the loop and doesn't
840 # depend on the source of the loop needs to get the loop source added
841 for type in ['BINARY','PYTHON','LIBRARY','BINARY']:
843 if t
.samba_type
!= type: continue
845 if loop
in t
.final_libs
:
846 diff
= loops
[loop
].difference(t
.final_libs
)
851 # make sure we don't recreate the loop again!
852 for d
in diff
.copy():
853 t2
= bld
.name_to_obj(d
, bld
.env
)
854 if t2
.samba_type
== 'LIBRARY':
855 if t
.sname
in t2
.final_libs
:
856 debug('deps: removing expansion %s from %s', d
, t
.sname
)
859 debug('deps: Expanded target %s by loop %s libraries (loop %s) %s', t
.sname
, loop
,
861 t
.final_libs
= t
.final_libs
.union(diff
)
863 # remove objects that are also available in linked libs
865 while reduce_objects(bld
, tgt_list
):
868 Logs
.warn("WARNING: Unable to remove all inter-target object duplicates")
870 debug('deps: Object reduction took %u iterations', count
)
872 # add in any syslib dependencies
874 if not t
.samba_type
in ['BINARY','PYTHON','LIBRARY','SUBSYSTEM']:
877 for d
in t
.final_objects
:
878 t2
= bld
.name_to_obj(d
, bld
.env
)
879 syslibs
= syslibs
.union(t2
.direct_syslibs
)
880 # this adds the indirect syslibs as well, which may not be needed
881 # depending on the linker flags
882 for d
in t
.final_libs
:
883 t2
= bld
.name_to_obj(d
, bld
.env
)
884 syslibs
= syslibs
.union(t2
.direct_syslibs
)
885 t
.final_syslibs
= syslibs
888 # find any unresolved library loops
889 lib_loop_error
= False
891 if t
.samba_type
in ['LIBRARY', 'PYTHON']:
892 for l
in t
.final_libs
.copy():
893 t2
= bld
.name_to_obj(l
, bld
.env
)
894 if t
.sname
in t2
.final_libs
:
895 Logs
.error('ERROR: Unresolved library loop %s from %s' % (t
.sname
, t2
.sname
))
896 lib_loop_error
= True
900 debug('deps: removed duplicate dependencies')
903 def show_dependencies(bld
, target
, seen
):
904 '''recursively show the dependencies of target'''
909 t
= bld
.name_to_obj(target
, bld
.env
)
911 Logs
.error("ERROR: Unable to find target '%s'" % target
)
914 Logs
.info('%s(OBJECTS): %s' % (target
, t
.direct_objects
))
915 Logs
.info('%s(LIBS): %s' % (target
, t
.direct_libs
))
916 Logs
.info('%s(SYSLIBS): %s' % (target
, t
.direct_syslibs
))
920 for t2
in t
.direct_objects
:
921 show_dependencies(bld
, t2
, seen
)
924 def show_object_duplicates(bld
, tgt_list
):
925 '''show a list of object files that are included in more than
926 one library or binary'''
928 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
932 Logs
.info("showing duplicate objects")
935 if not targets
[t
.sname
] in [ 'LIBRARY', 'PYTHON' ]:
937 for n
in getattr(t
, 'final_objects', set()):
938 t2
= bld
.name_to_obj(n
, bld
.env
)
941 used_by
[n
].add(t
.sname
)
944 if len(used_by
[n
]) > 1:
945 Logs
.info("target '%s' is used by %s" % (n
, used_by
[n
]))
947 Logs
.info("showing indirect dependency counts (sorted by count)")
949 def indirect_count(t1
, t2
):
950 return len(t2
.indirect_objects
) - len(t1
.indirect_objects
)
952 sorted_list
= sorted(tgt_list
, cmp=indirect_count
)
953 for t
in sorted_list
:
954 if len(t
.indirect_objects
) > 1:
955 Logs
.info("%s depends on %u indirect objects" % (t
.sname
, len(t
.indirect_objects
)))
958 ######################################################################
959 # this provides a way to save our dependency calculations between runs
961 savedeps_inputs
= ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags',
962 'source', 'grouping_library', 'samba_ldflags', 'allow_undefined_symbols',
963 'use_global_deps', 'global_include' ]
964 savedeps_outputs
= ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags', 'ldflags', 'samba_deps_extended']
965 savedeps_outenv
= ['INC_PATHS']
966 savedeps_envvars
= ['NONSHARED_BINARIES', 'GLOBAL_DEPENDENCIES', 'EXTRA_CFLAGS', 'EXTRA_LDFLAGS', 'EXTRA_INCLUDES' ]
967 savedeps_caches
= ['GLOBAL_DEPENDENCIES', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
968 savedeps_files
= ['buildtools/wafsamba/samba_deps.py']
970 def save_samba_deps(bld
, tgt_list
):
971 '''save the dependency calculations between builds, to make
972 further builds faster'''
973 denv
= Environment
.Environment()
975 denv
.version
= savedeps_version
976 denv
.savedeps_inputs
= savedeps_inputs
977 denv
.savedeps_outputs
= savedeps_outputs
985 for f
in savedeps_files
:
986 denv
.files
[f
] = os
.stat(os
.path
.join(bld
.srcnode
.abspath(), f
)).st_mtime
988 for c
in savedeps_caches
:
989 denv
.caches
[c
] = LOCAL_CACHE(bld
, c
)
991 for e
in savedeps_envvars
:
992 denv
.envvar
[e
] = bld
.env
[e
]
995 # save all the input attributes for each target
997 for attr
in savedeps_inputs
:
998 v
= getattr(t
, attr
, None)
1002 denv
.input[t
.sname
] = tdeps
1004 # save all the output attributes for each target
1006 for attr
in savedeps_outputs
:
1007 v
= getattr(t
, attr
, None)
1011 denv
.output
[t
.sname
] = tdeps
1014 for attr
in savedeps_outenv
:
1016 tdeps
[attr
] = t
.env
[attr
]
1018 denv
.outenv
[t
.sname
] = tdeps
1020 depsfile
= os
.path
.join(bld
.bdir
, "sambadeps")
1021 denv
.store(depsfile
)
1025 def load_samba_deps(bld
, tgt_list
):
1026 '''load a previous set of build dependencies if possible'''
1027 depsfile
= os
.path
.join(bld
.bdir
, "sambadeps")
1028 denv
= Environment
.Environment()
1030 debug('deps: checking saved dependencies')
1032 if (denv
.version
!= savedeps_version
or
1033 denv
.savedeps_inputs
!= savedeps_inputs
or
1034 denv
.savedeps_outputs
!= savedeps_outputs
):
1039 # check if critical files have changed
1040 for f
in savedeps_files
:
1041 if f
not in denv
.files
:
1043 if denv
.files
[f
] != os
.stat(os
.path
.join(bld
.srcnode
.abspath(), f
)).st_mtime
:
1046 # check if caches are the same
1047 for c
in savedeps_caches
:
1048 if c
not in denv
.caches
or denv
.caches
[c
] != LOCAL_CACHE(bld
, c
):
1051 # check if caches are the same
1052 for e
in savedeps_envvars
:
1053 if e
not in denv
.envvar
or denv
.envvar
[e
] != bld
.env
[e
]:
1056 # check inputs are the same
1059 for attr
in savedeps_inputs
:
1060 v
= getattr(t
, attr
, None)
1063 if t
.sname
in denv
.input:
1064 olddeps
= denv
.input[t
.sname
]
1067 if tdeps
!= olddeps
:
1068 #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
1071 # put outputs in place
1073 if not t
.sname
in denv
.output
: continue
1074 tdeps
= denv
.output
[t
.sname
]
1076 setattr(t
, a
, tdeps
[a
])
1078 # put output env vars in place
1080 if not t
.sname
in denv
.outenv
: continue
1081 tdeps
= denv
.outenv
[t
.sname
]
1085 debug('deps: loaded saved dependencies')
1090 def check_project_rules(bld
):
1091 '''check the project rules - ensuring the targets are sane'''
1096 tgt_list
= get_tgt_list(bld
)
1098 add_samba_attributes(bld
, tgt_list
)
1100 force_project_rules
= (Options
.options
.SHOWDEPS
or
1101 Options
.options
.SHOW_DUPLICATES
)
1103 if not force_project_rules
and load_samba_deps(bld
, tgt_list
):
1107 tstart
= time
.clock()
1109 bld
.new_rules
= True
1110 Logs
.info("Checking project rules ...")
1112 debug('deps: project rules checking started')
1114 expand_subsystem_deps(bld
)
1116 debug("deps: expand_subsystem_deps: %f" % (time
.clock() - tstart
))
1118 replace_grouping_libraries(bld
, tgt_list
)
1120 debug("deps: replace_grouping_libraries: %f" % (time
.clock() - tstart
))
1122 build_direct_deps(bld
, tgt_list
)
1124 debug("deps: build_direct_deps: %f" % (time
.clock() - tstart
))
1126 break_dependency_loops(bld
, tgt_list
)
1128 debug("deps: break_dependency_loops: %f" % (time
.clock() - tstart
))
1130 if Options
.options
.SHOWDEPS
:
1131 show_dependencies(bld
, Options
.options
.SHOWDEPS
, set())
1133 calculate_final_deps(bld
, tgt_list
, loops
)
1135 debug("deps: calculate_final_deps: %f" % (time
.clock() - tstart
))
1137 if Options
.options
.SHOW_DUPLICATES
:
1138 show_object_duplicates(bld
, tgt_list
)
1140 # run the various attribute generators
1141 for f
in [ build_dependencies
, build_includes
, add_init_functions
]:
1142 debug('deps: project rules checking %s', f
)
1143 for t
in tgt_list
: f(t
)
1144 debug("deps: %s: %f" % (f
, time
.clock() - tstart
))
1146 debug('deps: project rules stage1 completed')
1148 #check_orphaned_targets(bld, tgt_list)
1150 if not check_duplicate_sources(bld
, tgt_list
):
1151 Logs
.error("Duplicate sources present - aborting")
1154 debug("deps: check_duplicate_sources: %f" % (time
.clock() - tstart
))
1156 if not check_group_ordering(bld
, tgt_list
):
1157 Logs
.error("Bad group ordering - aborting")
1160 debug("deps: check_group_ordering: %f" % (time
.clock() - tstart
))
1162 show_final_deps(bld
, tgt_list
)
1164 debug("deps: show_final_deps: %f" % (time
.clock() - tstart
))
1166 debug('deps: project rules checking completed - %u targets checked',
1169 if not bld
.is_install
:
1170 save_samba_deps(bld
, tgt_list
)
1172 debug("deps: save_samba_deps: %f" % (time
.clock() - tstart
))
1174 Logs
.info("Project rules pass")
1177 def CHECK_PROJECT_RULES(bld
):
1178 '''enable checking of project targets for sanity'''
1179 if bld
.env
.added_project_rules
:
1181 bld
.env
.added_project_rules
= True
1182 bld
.add_pre_fun(check_project_rules
)
1183 Build
.BuildContext
.CHECK_PROJECT_RULES
= CHECK_PROJECT_RULES