1 # Samba automatic dependency handling and project rules
3 import os
, sys
, re
, time
5 from waflib
import Build
, Options
, Logs
, Utils
, Errors
6 from waflib
.Logs
import debug
7 from waflib
.Configure
import conf
8 from waflib
import ConfigSet
10 from samba_bundled
import BUILTIN_LIBRARY
11 from samba_utils
import LOCAL_CACHE
, TO_LIST
, get_tgt_list
, unique_list
, os_path_relpath
12 from samba_autoconf
import library_flags
15 def ADD_GLOBAL_DEPENDENCY(ctx
, dep
):
16 '''add a dependency for all binaries and libraries'''
17 if not 'GLOBAL_DEPENDENCIES' in ctx
.env
:
18 ctx
.env
.GLOBAL_DEPENDENCIES
= []
19 ctx
.env
.GLOBAL_DEPENDENCIES
.append(dep
)
23 def BREAK_CIRCULAR_LIBRARY_DEPENDENCIES(ctx
):
24 '''indicate that circular dependencies between libraries should be broken.'''
25 ctx
.env
.ALLOW_CIRCULAR_LIB_DEPENDENCIES
= True
29 def SET_SYSLIB_DEPS(conf
, target
, deps
):
30 '''setup some implied dependencies for a SYSLIB'''
31 cache
= LOCAL_CACHE(conf
, 'SYSLIB_DEPS')
35 def expand_subsystem_deps(bld
):
36 '''expand the reverse dependencies resulting from subsystem
37 attributes of modules. This is walking over the complete list
38 of declared subsystems, and expands the samba_deps_extended list for any
39 module<->subsystem dependencies'''
41 subsystem_list
= LOCAL_CACHE(bld
, 'INIT_FUNCTIONS')
42 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
44 for subsystem_name
in subsystem_list
:
45 bld
.ASSERT(subsystem_name
in targets
, "Subsystem target %s not declared" % subsystem_name
)
46 type = targets
[subsystem_name
]
47 if type == 'DISABLED' or type == 'EMPTY':
51 # subsystem_name = dcerpc_server (a subsystem)
52 # subsystem = dcerpc_server (a subsystem object)
53 # module_name = rpc_epmapper (a module within the dcerpc_server subsystem)
54 # module = rpc_epmapper (a module object within the dcerpc_server subsystem)
56 subsystem
= bld
.get_tgen_by_name(subsystem_name
)
57 bld
.ASSERT(subsystem
is not None, "Unable to find subsystem %s" % subsystem_name
)
58 for d
in subsystem_list
[subsystem_name
]:
59 module_name
= d
['TARGET']
60 module_type
= targets
[module_name
]
61 if module_type
in ['DISABLED', 'EMPTY']:
63 bld
.ASSERT(subsystem
is not None,
64 "Subsystem target %s for %s (%s) not found" % (subsystem_name
, module_name
, module_type
))
65 if module_type
in ['SUBSYSTEM']:
66 # if a module is a plain object type (not a library) then the
67 # subsystem it is part of needs to have it as a dependency, so targets
68 # that depend on this subsystem get the modules of that subsystem
69 subsystem
.samba_deps_extended
.append(module_name
)
70 subsystem
.samba_deps_extended
= unique_list(subsystem
.samba_deps_extended
)
74 def build_dependencies(self
):
75 '''This builds the dependency list for a target. It runs after all the targets are declared
77 The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
78 the full dependency list for a target until we have all of the targets declared.
81 if self
.samba_type
in ['LIBRARY', 'BINARY', 'PYTHON']:
82 self
.uselib
= list(self
.final_syslibs
)
83 self
.uselib_local
= list(self
.final_libs
)
84 self
.add_objects
= list(self
.final_objects
)
86 # extra link flags from pkg_config
87 libs
= self
.final_syslibs
.copy()
89 (cflags
, ldflags
, cpppath
) = library_flags(self
, list(libs
))
90 new_ldflags
= getattr(self
, 'samba_ldflags', [])[:]
91 new_ldflags
.extend(ldflags
)
92 self
.ldflags
= new_ldflags
94 if getattr(self
, 'allow_undefined_symbols', False) and self
.env
.undefined_ldflags
:
95 for f
in self
.env
.undefined_ldflags
:
96 self
.ldflags
.remove(f
)
98 if getattr(self
, 'allow_undefined_symbols', False) and self
.env
.undefined_ignore_ldflags
:
99 for f
in self
.env
.undefined_ignore_ldflags
:
100 self
.ldflags
.append(f
)
102 debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
103 self
.sname
, self
.uselib
, self
.uselib_local
, self
.add_objects
)
105 if self
.samba_type
in ['SUBSYSTEM']:
106 # this is needed for the cflags of libs that come from pkg_config
107 self
.uselib
= list(self
.final_syslibs
)
108 self
.uselib
.extend(list(self
.direct_syslibs
))
109 for lib
in self
.final_libs
:
110 t
= self
.bld
.get_tgen_by_name(lib
)
111 self
.uselib
.extend(list(t
.final_syslibs
))
112 self
.uselib
= unique_list(self
.uselib
)
114 if getattr(self
, 'uselib', None):
116 for l
in self
.uselib
:
117 up_list
.append(l
.upper())
118 self
.uselib
= up_list
121 def build_includes(self
):
122 '''This builds the right set of includes for a target.
124 One tricky part of this is that the includes= attribute for a
125 target needs to use paths which are relative to that targets
126 declaration directory (which we can get at via t.path).
128 The way this works is the includes list gets added as
129 samba_includes in the main build task declaration. Then this
130 function runs after all of the tasks are declared, and it
131 processes the samba_includes attribute to produce a includes=
135 if getattr(self
, 'samba_includes', None) is None:
140 inc_deps
= includes_objects(bld
, self
, set(), {})
144 # maybe add local includes
145 if getattr(self
, 'local_include', True) and getattr(self
, 'local_include_first', True):
148 includes
.extend(self
.samba_includes_extended
)
150 if 'EXTRA_INCLUDES' in bld
.env
and getattr(self
, 'global_include', True):
151 includes
.extend(bld
.env
['EXTRA_INCLUDES'])
159 t
= bld
.get_tgen_by_name(d
)
160 bld
.ASSERT(t
is not None, "Unable to find dependency %s for %s" % (d
, self
.sname
))
161 inclist
= getattr(t
, 'samba_includes_extended', [])[:]
162 if getattr(t
, 'local_include', True):
166 tpath
= t
.samba_abspath
168 npath
= tpath
+ '/' + inc
169 if not npath
in inc_set
:
170 inc_abs
.append(npath
)
173 mypath
= self
.path
.abspath(bld
.env
)
175 relpath
= os_path_relpath(inc
, mypath
)
176 includes
.append(relpath
)
178 if getattr(self
, 'local_include', True) and not getattr(self
, 'local_include_first', True):
181 # now transform the includes list to be relative to the top directory
182 # which is represented by '#' in waf. This allows waf to cache the
183 # includes lists more efficiently
187 # some are already top based
188 includes_top
.append(i
)
190 absinc
= os
.path
.join(self
.path
.abspath(), i
)
191 relinc
= os_path_relpath(absinc
, self
.bld
.srcnode
.abspath())
192 includes_top
.append('#' + relinc
)
194 self
.includes
= unique_list(includes_top
)
195 debug('deps: includes for target %s: includes=%s',
196 self
.sname
, self
.includes
)
199 def add_init_functions(self
):
200 '''This builds the right set of init functions'''
204 subsystems
= LOCAL_CACHE(bld
, 'INIT_FUNCTIONS')
206 # cope with the separated object lists from BINARY and LIBRARY targets
208 if sname
.endswith('.objlist'):
212 if sname
in subsystems
:
213 modules
.append(sname
)
215 m
= getattr(self
, 'samba_modules', None)
217 modules
.extend(TO_LIST(m
))
219 m
= getattr(self
, 'samba_subsystem', None)
223 if 'pyembed' in self
.features
:
226 sentinel
= getattr(self
, 'init_function_sentinel', 'NULL')
228 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
229 cflags
= getattr(self
, 'samba_cflags', [])[:]
232 sname
= sname
.replace('-','_')
233 sname
= sname
.replace('.','_')
234 sname
= sname
.replace('/','_')
235 cflags
.append('-DSTATIC_%s_MODULES=%s' % (sname
, sentinel
))
236 if sentinel
== 'NULL':
237 proto
= "extern void __%s_dummy_module_proto(void)" % (sname
)
238 cflags
.append('-DSTATIC_%s_MODULES_PROTO=%s' % (sname
, proto
))
243 bld
.ASSERT(m
in subsystems
,
244 "No init_function defined for module '%s' in target '%s'" % (m
, self
.sname
))
246 for d
in subsystems
[m
]:
247 if targets
[d
['TARGET']] != 'DISABLED':
248 init_fn_list
.append(d
['INIT_FUNCTION'])
249 if init_fn_list
== []:
250 cflags
.append('-DSTATIC_%s_MODULES=%s' % (m
, sentinel
))
251 if sentinel
== 'NULL':
252 proto
= "extern void __%s_dummy_module_proto(void)" % (m
)
253 cflags
.append('-DSTATIC_%s_MODULES_PROTO=%s' % (m
, proto
))
255 cflags
.append('-DSTATIC_%s_MODULES=%s' % (m
, ','.join(init_fn_list
) + ',' + sentinel
))
257 for f
in init_fn_list
:
258 proto
+= '_MODULE_PROTO(%s)' % f
259 proto
+= "extern void __%s_dummy_module_proto(void)" % (m
)
260 cflags
.append('-DSTATIC_%s_MODULES_PROTO=%s' % (m
, proto
))
264 def check_duplicate_sources(bld
, tgt_list
):
265 '''see if we are compiling the same source file more than once'''
267 debug('deps: checking for duplicate sources')
268 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
271 source_list
= TO_LIST(getattr(t
, 'source', ''))
272 tpath
= os
.path
.normpath(os_path_relpath(t
.path
.abspath(bld
.env
), t
.env
.BUILD_DIRECTORY
+ '/default'))
274 for s
in source_list
:
275 if not isinstance(s
, str):
276 print('strange path in check_duplicate_sources %r' % s
)
278 p
= os
.path
.normpath(os
.path
.join(tpath
, s
))
280 Logs
.error("ERROR: source %s appears twice in target '%s'" % (p
, t
.sname
))
283 t
.samba_source_set
= obj_sources
287 # build a list of targets that each source file is part of
289 if not targets
[t
.sname
] in [ 'LIBRARY', 'BINARY', 'PYTHON' ]:
291 for obj
in t
.add_objects
:
292 t2
= t
.bld
.get_tgen_by_name(obj
)
293 source_set
= getattr(t2
, 'samba_source_set', set())
295 if not s
in subsystems
:
297 if not t
.sname
in subsystems
[s
]:
298 subsystems
[s
][t
.sname
] = []
299 subsystems
[s
][t
.sname
].append(t2
.sname
)
302 if len(subsystems
[s
]) > 1 and Options
.options
.SHOW_DUPLICATES
:
303 Logs
.warn("WARNING: source %s is in more than one target: %s" % (s
, subsystems
[s
].keys()))
304 for tname
in subsystems
[s
]:
305 if len(subsystems
[s
][tname
]) > 1:
306 raise Errors
.WafError("ERROR: source %s is in more than one subsystem of target '%s': %s" % (s
, tname
, subsystems
[s
][tname
]))
310 def check_group_ordering(bld
, tgt_list
):
311 '''see if we have any dependencies that violate the group ordering
313 It is an error for a target to depend on a target from a later
318 tm
= bld
.task_manager
319 return [x
for x
in tm
.groups_names
if id(tm
.groups_names
[x
]) == id(g
)][0]
321 for g
in bld
.task_manager
.groups
:
322 gname
= group_name(g
)
323 for t
in g
.tasks_gen
:
324 t
.samba_group
= gname
328 for g
in bld
.task_manager
.groups
:
333 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
337 tdeps
= getattr(t
, 'add_objects', []) + getattr(t
, 'uselib_local', [])
339 t2
= bld
.get_tgen_by_name(d
)
342 map1
= grp_map
[t
.samba_group
]
343 map2
= grp_map
[t2
.samba_group
]
346 Logs
.error("Target %r in build group %r depends on target %r from later build group %r" % (
347 t
.sname
, t
.samba_group
, t2
.sname
, t2
.samba_group
))
351 Build
.BuildContext
.check_group_ordering
= check_group_ordering
353 def show_final_deps(bld
, tgt_list
):
354 '''show the final dependencies for all targets'''
356 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
359 if not targets
[t
.sname
] in ['LIBRARY', 'BINARY', 'PYTHON', 'SUBSYSTEM']:
361 debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
362 t
.sname
, t
.uselib
, getattr(t
, 'uselib_local', []), getattr(t
, 'add_objects', []))
365 def add_samba_attributes(bld
, tgt_list
):
366 '''ensure a target has a the required samba attributes'''
368 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
375 t
.samba_type
= targets
[t
.sname
]
376 t
.samba_abspath
= t
.path
.abspath(bld
.env
)
377 t
.samba_deps_extended
= t
.samba_deps
[:]
378 t
.samba_includes_extended
= TO_LIST(t
.samba_includes
)[:]
379 t
.cflags
= getattr(t
, 'samba_cflags', '')
381 def replace_grouping_libraries(bld
, tgt_list
):
382 '''replace dependencies based on grouping libraries
384 If a library is marked as a grouping library, then any target that
385 depends on a subsystem that is part of that grouping library gets
386 that dependency replaced with a dependency on the grouping library
389 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
393 # find our list of grouping libraries, mapped from the subsystems they depend on
395 if not getattr(t
, 'grouping_library', False):
397 for dep
in t
.samba_deps_extended
:
398 bld
.ASSERT(dep
in targets
, "grouping library target %s not declared in %s" % (dep
, t
.sname
))
399 if targets
[dep
] == 'SUBSYSTEM':
400 grouping
[dep
] = t
.sname
402 # now replace any dependencies on elements of grouping libraries
404 for i
in range(len(t
.samba_deps_extended
)):
405 dep
= t
.samba_deps_extended
[i
]
407 if t
.sname
!= grouping
[dep
]:
408 debug("deps: target %s: replacing dependency %s with grouping library %s" % (t
.sname
, dep
, grouping
[dep
]))
409 t
.samba_deps_extended
[i
] = grouping
[dep
]
413 def build_direct_deps(bld
, tgt_list
):
414 '''build the direct_objects and direct_libs sets for each target'''
416 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
417 syslib_deps
= LOCAL_CACHE(bld
, 'SYSLIB_DEPS')
419 global_deps
= bld
.env
.GLOBAL_DEPENDENCIES
420 global_deps_exclude
= set()
421 for dep
in global_deps
:
422 t
= bld
.get_tgen_by_name(dep
)
423 for d
in t
.samba_deps
:
424 # prevent loops from the global dependencies list
425 global_deps_exclude
.add(d
)
426 global_deps_exclude
.add(d
+ '.objlist')
429 t
.direct_objects
= set()
430 t
.direct_libs
= set()
431 t
.direct_syslibs
= set()
432 deps
= t
.samba_deps_extended
[:]
433 if getattr(t
, 'samba_use_global_deps', False) and not t
.sname
in global_deps_exclude
:
434 deps
.extend(global_deps
)
436 if d
== t
.sname
: continue
438 Logs
.error("Unknown dependency '%s' in '%s'" % (d
, t
.sname
))
440 if targets
[d
] in [ 'EMPTY', 'DISABLED' ]:
442 if targets
[d
] == 'PYTHON' and targets
[t
.sname
] != 'PYTHON' and t
.sname
.find('.objlist') == -1:
443 # this check should be more restrictive, but for now we have pidl-generated python
444 # code that directly depends on other python modules
445 Logs
.error('ERROR: Target %s has dependency on python module %s' % (t
.sname
, d
))
447 if targets
[d
] == 'SYSLIB':
448 t
.direct_syslibs
.add(d
)
450 for implied
in TO_LIST(syslib_deps
[d
]):
451 if BUILTIN_LIBRARY(bld
, implied
):
452 t
.direct_objects
.add(implied
)
453 elif targets
[implied
] == 'SYSLIB':
454 t
.direct_syslibs
.add(implied
)
455 elif targets
[implied
] in ['LIBRARY', 'MODULE']:
456 t
.direct_libs
.add(implied
)
458 Logs
.error('Implied dependency %s in %s is of type %s' % (
459 implied
, t
.sname
, targets
[implied
]))
462 t2
= bld
.get_tgen_by_name(d
)
464 Logs
.error("no task %s of type %s in %s" % (d
, targets
[d
], t
.sname
))
466 if t2
.samba_type
in [ 'LIBRARY', 'MODULE' ]:
468 elif t2
.samba_type
in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
469 t
.direct_objects
.add(d
)
470 debug('deps: built direct dependencies')
473 def dependency_loop(loops
, t
, target
):
474 '''add a dependency loop to the loops dictionary'''
475 if t
.sname
== target
:
477 if not target
in loops
:
478 loops
[target
] = set()
479 if not t
.sname
in loops
[target
]:
480 loops
[target
].add(t
.sname
)
483 def indirect_libs(bld
, t
, chain
, loops
):
484 '''recursively calculate the indirect library dependencies for a target
486 An indirect library is a library that results from a dependency on
490 ret
= getattr(t
, 'indirect_libs', None)
495 for obj
in t
.direct_objects
:
497 dependency_loop(loops
, t
, obj
)
500 t2
= bld
.get_tgen_by_name(obj
)
501 r2
= indirect_libs(bld
, t2
, chain
, loops
)
503 ret
= ret
.union(t2
.direct_libs
)
506 for obj
in indirect_objects(bld
, t
, set(), loops
):
508 dependency_loop(loops
, t
, obj
)
511 t2
= bld
.get_tgen_by_name(obj
)
512 r2
= indirect_libs(bld
, t2
, chain
, loops
)
514 ret
= ret
.union(t2
.direct_libs
)
517 t
.indirect_libs
= ret
522 def indirect_objects(bld
, t
, chain
, loops
):
523 '''recursively calculate the indirect object dependencies for a target
525 indirect objects are the set of objects from expanding the
526 subsystem dependencies
529 ret
= getattr(t
, 'indirect_objects', None)
530 if ret
is not None: return ret
533 for lib
in t
.direct_objects
:
535 dependency_loop(loops
, t
, lib
)
538 t2
= bld
.get_tgen_by_name(lib
)
539 r2
= indirect_objects(bld
, t2
, chain
, loops
)
541 ret
= ret
.union(t2
.direct_objects
)
544 t
.indirect_objects
= ret
548 def extended_objects(bld
, t
, chain
):
549 '''recursively calculate the extended object dependencies for a target
551 extended objects are the union of:
554 - direct and indirect objects of all direct and indirect libraries
557 ret
= getattr(t
, 'extended_objects', None)
558 if ret
is not None: return ret
561 ret
= ret
.union(t
.final_objects
)
563 for lib
in t
.final_libs
:
566 t2
= bld
.get_tgen_by_name(lib
)
568 r2
= extended_objects(bld
, t2
, chain
)
570 ret
= ret
.union(t2
.final_objects
)
573 t
.extended_objects
= ret
577 def includes_objects(bld
, t
, chain
, inc_loops
):
578 '''recursively calculate the includes object dependencies for a target
580 includes dependencies come from either library or object dependencies
582 ret
= getattr(t
, 'includes_objects', None)
586 ret
= t
.direct_objects
.copy()
587 ret
= ret
.union(t
.direct_libs
)
589 for obj
in t
.direct_objects
:
591 dependency_loop(inc_loops
, t
, obj
)
594 t2
= bld
.get_tgen_by_name(obj
)
595 r2
= includes_objects(bld
, t2
, chain
, inc_loops
)
597 ret
= ret
.union(t2
.direct_objects
)
600 for lib
in t
.direct_libs
:
602 dependency_loop(inc_loops
, t
, lib
)
605 t2
= bld
.get_tgen_by_name(lib
)
607 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
608 Logs
.error('Target %s of type %s not found in direct_libs for %s' % (
609 lib
, targets
[lib
], t
.sname
))
611 r2
= includes_objects(bld
, t2
, chain
, inc_loops
)
613 ret
= ret
.union(t2
.direct_objects
)
616 t
.includes_objects
= ret
620 def break_dependency_loops(bld
, tgt_list
):
621 '''find and break dependency loops'''
625 # build up the list of loops
627 indirect_objects(bld
, t
, set(), loops
)
628 indirect_libs(bld
, t
, set(), loops
)
629 includes_objects(bld
, t
, set(), inc_loops
)
634 for attr
in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
635 objs
= getattr(t
, attr
, set())
636 setattr(t
, attr
, objs
.difference(loops
[t
.sname
]))
639 debug('deps: Found dependency loops for target %s : %s', loop
, loops
[loop
])
641 for loop
in inc_loops
:
642 debug('deps: Found include loops for target %s : %s', loop
, inc_loops
[loop
])
644 # expand the loops mapping by one level
645 for loop
in loops
.copy():
646 for tgt
in loops
[loop
]:
648 loops
[loop
] = loops
[loop
].union(loops
[tgt
])
650 for loop
in inc_loops
.copy():
651 for tgt
in inc_loops
[loop
]:
653 inc_loops
[loop
] = inc_loops
[loop
].union(inc_loops
[tgt
])
656 # expand indirect subsystem and library loops
657 for loop
in loops
.copy():
658 t
= bld
.get_tgen_by_name(loop
)
659 if t
.samba_type
in ['SUBSYSTEM']:
660 loops
[loop
] = loops
[loop
].union(t
.indirect_objects
)
661 loops
[loop
] = loops
[loop
].union(t
.direct_objects
)
662 if t
.samba_type
in ['LIBRARY','PYTHON']:
663 loops
[loop
] = loops
[loop
].union(t
.indirect_libs
)
664 loops
[loop
] = loops
[loop
].union(t
.direct_libs
)
665 if loop
in loops
[loop
]:
666 loops
[loop
].remove(loop
)
668 # expand indirect includes loops
669 for loop
in inc_loops
.copy():
670 t
= bld
.get_tgen_by_name(loop
)
671 inc_loops
[loop
] = inc_loops
[loop
].union(t
.includes_objects
)
672 if loop
in inc_loops
[loop
]:
673 inc_loops
[loop
].remove(loop
)
675 # add in the replacement dependencies
678 for attr
in ['indirect_objects', 'indirect_libs']:
679 objs
= getattr(t
, attr
, set())
681 diff
= loops
[loop
].difference(objs
)
685 debug('deps: Expanded target %s of type %s from loop %s by %s', t
.sname
, t
.samba_type
, loop
, diff
)
686 objs
= objs
.union(diff
)
687 setattr(t
, attr
, objs
)
689 for loop
in inc_loops
:
690 objs
= getattr(t
, 'includes_objects', set())
692 diff
= inc_loops
[loop
].difference(objs
)
696 debug('deps: Expanded target %s includes of type %s from loop %s by %s', t
.sname
, t
.samba_type
, loop
, diff
)
697 objs
= objs
.union(diff
)
698 setattr(t
, 'includes_objects', objs
)
701 def reduce_objects(bld
, tgt_list
):
702 '''reduce objects by looking for indirect object dependencies'''
706 t
.extended_objects
= None
710 for type in ['BINARY', 'PYTHON', 'LIBRARY']:
712 if t
.samba_type
!= type: continue
713 # if we will indirectly link to a target then we don't need it
714 new
= t
.final_objects
.copy()
715 for l
in t
.final_libs
:
716 t2
= bld
.get_tgen_by_name(l
)
717 t2_obj
= extended_objects(bld
, t2
, set())
718 dup
= new
.intersection(t2_obj
)
719 if t
.sname
in rely_on
:
720 dup
= dup
.difference(rely_on
[t
.sname
])
722 debug('deps: removing dups from %s of type %s: %s also in %s %s',
723 t
.sname
, t
.samba_type
, dup
, t2
.samba_type
, l
)
724 new
= new
.difference(dup
)
728 rely_on
[l
] = rely_on
[l
].union(dup
)
729 t
.final_objects
= new
734 # add back in any objects that were relied upon by the reduction rules
736 t
= bld
.get_tgen_by_name(r
)
737 t
.final_objects
= t
.final_objects
.union(rely_on
[r
])
742 def show_library_loop(bld
, lib1
, lib2
, path
, seen
):
743 '''show the detailed path of a library loop between lib1 and lib2'''
745 t
= bld
.get_tgen_by_name(lib1
)
746 if not lib2
in getattr(t
, 'final_libs', set()):
749 for d
in t
.samba_deps_extended
:
753 path2
= path
+ '=>' + d
755 Logs
.warn('library loop path: ' + path2
)
757 show_library_loop(bld
, d
, lib2
, path2
, seen
)
761 def calculate_final_deps(bld
, tgt_list
, loops
):
762 '''calculate the final library and object dependencies'''
764 # start with the maximum possible list
765 t
.final_libs
= t
.direct_libs
.union(indirect_libs(bld
, t
, set(), loops
))
766 t
.final_objects
= t
.direct_objects
.union(indirect_objects(bld
, t
, set(), loops
))
769 # don't depend on ourselves
770 if t
.sname
in t
.final_libs
:
771 t
.final_libs
.remove(t
.sname
)
772 if t
.sname
in t
.final_objects
:
773 t
.final_objects
.remove(t
.sname
)
775 # handle any non-shared binaries
777 if t
.samba_type
== 'BINARY' and bld
.NONSHARED_BINARY(t
.sname
):
778 subsystem_list
= LOCAL_CACHE(bld
, 'INIT_FUNCTIONS')
779 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
781 # replace lib deps with objlist deps
782 for l
in t
.final_libs
:
783 objname
= l
+ '.objlist'
784 t2
= bld
.get_tgen_by_name(objname
)
786 Logs
.error('ERROR: subsystem %s not found' % objname
)
788 t
.final_objects
.add(objname
)
789 t
.final_objects
= t
.final_objects
.union(extended_objects(bld
, t2
, set()))
790 if l
in subsystem_list
:
791 # its a subsystem - we also need the contents of any modules
792 for d
in subsystem_list
[l
]:
793 module_name
= d
['TARGET']
794 if targets
[module_name
] == 'LIBRARY':
795 objname
= module_name
+ '.objlist'
796 elif targets
[module_name
] == 'SUBSYSTEM':
797 objname
= module_name
800 t2
= bld
.get_tgen_by_name(objname
)
802 Logs
.error('ERROR: subsystem %s not found' % objname
)
804 t
.final_objects
.add(objname
)
805 t
.final_objects
= t
.final_objects
.union(extended_objects(bld
, t2
, set()))
808 # find any library loops
810 if t
.samba_type
in ['LIBRARY', 'PYTHON']:
811 for l
in t
.final_libs
.copy():
812 t2
= bld
.get_tgen_by_name(l
)
813 if t
.sname
in t2
.final_libs
:
814 if getattr(bld
.env
, "ALLOW_CIRCULAR_LIB_DEPENDENCIES", False):
815 # we could break this in either direction. If one of the libraries
816 # has a version number, and will this be distributed publicly, then
817 # we should make it the lower level library in the DAG
818 Logs
.warn('deps: removing library loop %s from %s' % (t
.sname
, t2
.sname
))
819 dependency_loop(loops
, t
, t2
.sname
)
820 t2
.final_libs
.remove(t
.sname
)
822 Logs
.error('ERROR: circular library dependency between %s and %s'
823 % (t
.sname
, t2
.sname
))
824 show_library_loop(bld
, t
.sname
, t2
.sname
, t
.sname
, set())
825 show_library_loop(bld
, t2
.sname
, t
.sname
, t2
.sname
, set())
829 debug('deps: Found dependency loops for target %s : %s', loop
, loops
[loop
])
831 # we now need to make corrections for any library loops we broke up
832 # any target that depended on the target of the loop and doesn't
833 # depend on the source of the loop needs to get the loop source added
834 for type in ['BINARY','PYTHON','LIBRARY','BINARY']:
836 if t
.samba_type
!= type: continue
838 if loop
in t
.final_libs
:
839 diff
= loops
[loop
].difference(t
.final_libs
)
844 # make sure we don't recreate the loop again!
845 for d
in diff
.copy():
846 t2
= bld
.get_tgen_by_name(d
)
847 if t2
.samba_type
== 'LIBRARY':
848 if t
.sname
in t2
.final_libs
:
849 debug('deps: removing expansion %s from %s', d
, t
.sname
)
852 debug('deps: Expanded target %s by loop %s libraries (loop %s) %s', t
.sname
, loop
,
854 t
.final_libs
= t
.final_libs
.union(diff
)
856 # remove objects that are also available in linked libs
858 while reduce_objects(bld
, tgt_list
):
861 Logs
.warn("WARNING: Unable to remove all inter-target object duplicates")
863 debug('deps: Object reduction took %u iterations', count
)
865 # add in any syslib dependencies
867 if not t
.samba_type
in ['BINARY','PYTHON','LIBRARY','SUBSYSTEM']:
870 for d
in t
.final_objects
:
871 t2
= bld
.get_tgen_by_name(d
)
872 syslibs
= syslibs
.union(t2
.direct_syslibs
)
873 # this adds the indirect syslibs as well, which may not be needed
874 # depending on the linker flags
875 for d
in t
.final_libs
:
876 t2
= bld
.get_tgen_by_name(d
)
877 syslibs
= syslibs
.union(t2
.direct_syslibs
)
878 t
.final_syslibs
= syslibs
881 # find any unresolved library loops
882 lib_loop_error
= False
884 if t
.samba_type
in ['LIBRARY', 'PYTHON']:
885 for l
in t
.final_libs
.copy():
886 t2
= bld
.get_tgen_by_name(l
)
887 if t
.sname
in t2
.final_libs
:
888 Logs
.error('ERROR: Unresolved library loop %s from %s' % (t
.sname
, t2
.sname
))
889 lib_loop_error
= True
893 debug('deps: removed duplicate dependencies')
896 def show_dependencies(bld
, target
, seen
):
897 '''recursively show the dependencies of target'''
902 t
= bld
.get_tgen_by_name(target
)
904 Logs
.error("ERROR: Unable to find target '%s'" % target
)
907 Logs
.info('%s(OBJECTS): %s' % (target
, t
.direct_objects
))
908 Logs
.info('%s(LIBS): %s' % (target
, t
.direct_libs
))
909 Logs
.info('%s(SYSLIBS): %s' % (target
, t
.direct_syslibs
))
913 for t2
in t
.direct_objects
:
914 show_dependencies(bld
, t2
, seen
)
917 def show_object_duplicates(bld
, tgt_list
):
918 '''show a list of object files that are included in more than
919 one library or binary'''
921 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
925 Logs
.info("showing duplicate objects")
928 if not targets
[t
.sname
] in [ 'LIBRARY', 'PYTHON' ]:
930 for n
in getattr(t
, 'final_objects', set()):
931 t2
= bld
.get_tgen_by_name(n
)
934 used_by
[n
].add(t
.sname
)
937 if len(used_by
[n
]) > 1:
938 Logs
.info("target '%s' is used by %s" % (n
, used_by
[n
]))
940 Logs
.info("showing indirect dependency counts (sorted by count)")
942 def indirect_count(t1
, t2
):
943 return len(t2
.indirect_objects
) - len(t1
.indirect_objects
)
945 sorted_list
= sorted(tgt_list
, cmp=indirect_count
)
946 for t
in sorted_list
:
947 if len(t
.indirect_objects
) > 1:
948 Logs
.info("%s depends on %u indirect objects" % (t
.sname
, len(t
.indirect_objects
)))
951 ######################################################################
952 # this provides a way to save our dependency calculations between runs
954 savedeps_inputs
= ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags',
955 'source', 'grouping_library', 'samba_ldflags', 'allow_undefined_symbols',
956 'use_global_deps', 'global_include' ]
957 savedeps_outputs
= ['uselib', 'uselib_local', 'add_objects', 'includes',
958 'cflags', 'ldflags', 'samba_deps_extended', 'final_libs']
959 savedeps_outenv
= ['INC_PATHS']
960 savedeps_envvars
= ['NONSHARED_BINARIES', 'GLOBAL_DEPENDENCIES', 'EXTRA_CFLAGS', 'EXTRA_LDFLAGS', 'EXTRA_INCLUDES' ]
961 savedeps_caches
= ['GLOBAL_DEPENDENCIES', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
962 savedeps_files
= ['buildtools/wafsamba/samba_deps.py']
964 def save_samba_deps(bld
, tgt_list
):
965 '''save the dependency calculations between builds, to make
966 further builds faster'''
967 denv
= ConfigSet
.ConfigSet()
969 denv
.version
= savedeps_version
970 denv
.savedeps_inputs
= savedeps_inputs
971 denv
.savedeps_outputs
= savedeps_outputs
979 for f
in savedeps_files
:
980 denv
.files
[f
] = os
.stat(os
.path
.join(bld
.srcnode
.abspath(), f
)).st_mtime
982 for c
in savedeps_caches
:
983 denv
.caches
[c
] = LOCAL_CACHE(bld
, c
)
985 for e
in savedeps_envvars
:
986 denv
.envvar
[e
] = bld
.env
[e
]
989 # save all the input attributes for each target
991 for attr
in savedeps_inputs
:
992 v
= getattr(t
, attr
, None)
996 denv
.input[t
.sname
] = tdeps
998 # save all the output attributes for each target
1000 for attr
in savedeps_outputs
:
1001 v
= getattr(t
, attr
, None)
1005 denv
.output
[t
.sname
] = tdeps
1008 for attr
in savedeps_outenv
:
1010 tdeps
[attr
] = t
.env
[attr
]
1012 denv
.outenv
[t
.sname
] = tdeps
1014 depsfile
= os
.path
.join(bld
.bdir
, "sambadeps")
1015 denv
.store_fast(depsfile
)
1019 def load_samba_deps(bld
, tgt_list
):
1020 '''load a previous set of build dependencies if possible'''
1021 depsfile
= os
.path
.join(bld
.bldnode
.abspath(), "sambadeps")
1022 denv
= ConfigSet
.ConfigSet()
1024 debug('deps: checking saved dependencies')
1025 denv
.load_fast(depsfile
)
1026 if (denv
.version
!= savedeps_version
or
1027 denv
.savedeps_inputs
!= savedeps_inputs
or
1028 denv
.savedeps_outputs
!= savedeps_outputs
):
1033 # check if critical files have changed
1034 for f
in savedeps_files
:
1035 if f
not in denv
.files
:
1037 if denv
.files
[f
] != os
.stat(os
.path
.join(bld
.srcnode
.abspath(), f
)).st_mtime
:
1040 # check if caches are the same
1041 for c
in savedeps_caches
:
1042 if c
not in denv
.caches
or denv
.caches
[c
] != LOCAL_CACHE(bld
, c
):
1045 # check if caches are the same
1046 for e
in savedeps_envvars
:
1047 if e
not in denv
.envvar
or denv
.envvar
[e
] != bld
.env
[e
]:
1050 # check inputs are the same
1053 for attr
in savedeps_inputs
:
1054 v
= getattr(t
, attr
, None)
1057 if t
.sname
in denv
.input:
1058 olddeps
= denv
.input[t
.sname
]
1061 if tdeps
!= olddeps
:
1062 #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
1065 # put outputs in place
1067 if not t
.sname
in denv
.output
: continue
1068 tdeps
= denv
.output
[t
.sname
]
1070 setattr(t
, a
, tdeps
[a
])
1072 # put output env vars in place
1074 if not t
.sname
in denv
.outenv
: continue
1075 tdeps
= denv
.outenv
[t
.sname
]
1079 debug('deps: loaded saved dependencies')
1084 def check_project_rules(bld
):
1085 '''check the project rules - ensuring the targets are sane'''
1090 tgt_list
= get_tgt_list(bld
)
1092 add_samba_attributes(bld
, tgt_list
)
1094 force_project_rules
= (Options
.options
.SHOWDEPS
or
1095 Options
.options
.SHOW_DUPLICATES
)
1097 if not force_project_rules
and load_samba_deps(bld
, tgt_list
):
1101 tstart
= time
.clock()
1103 bld
.new_rules
= True
1104 Logs
.info("Checking project rules ...")
1106 debug('deps: project rules checking started')
1108 expand_subsystem_deps(bld
)
1110 debug("deps: expand_subsystem_deps: %f" % (time
.clock() - tstart
))
1112 replace_grouping_libraries(bld
, tgt_list
)
1114 debug("deps: replace_grouping_libraries: %f" % (time
.clock() - tstart
))
1116 build_direct_deps(bld
, tgt_list
)
1118 debug("deps: build_direct_deps: %f" % (time
.clock() - tstart
))
1120 break_dependency_loops(bld
, tgt_list
)
1122 debug("deps: break_dependency_loops: %f" % (time
.clock() - tstart
))
1124 if Options
.options
.SHOWDEPS
:
1125 show_dependencies(bld
, Options
.options
.SHOWDEPS
, set())
1127 calculate_final_deps(bld
, tgt_list
, loops
)
1129 debug("deps: calculate_final_deps: %f" % (time
.clock() - tstart
))
1131 if Options
.options
.SHOW_DUPLICATES
:
1132 show_object_duplicates(bld
, tgt_list
)
1134 # run the various attribute generators
1135 for f
in [ build_dependencies
, build_includes
, add_init_functions
]:
1136 debug('deps: project rules checking %s', f
)
1137 for t
in tgt_list
: f(t
)
1138 debug("deps: %s: %f" % (f
, time
.clock() - tstart
))
1140 debug('deps: project rules stage1 completed')
1142 if not check_duplicate_sources(bld
, tgt_list
):
1143 Logs
.error("Duplicate sources present - aborting")
1146 debug("deps: check_duplicate_sources: %f" % (time
.clock() - tstart
))
1148 if not bld
.check_group_ordering(tgt_list
):
1149 Logs
.error("Bad group ordering - aborting")
1152 debug("deps: check_group_ordering: %f" % (time
.clock() - tstart
))
1154 show_final_deps(bld
, tgt_list
)
1156 debug("deps: show_final_deps: %f" % (time
.clock() - tstart
))
1158 debug('deps: project rules checking completed - %u targets checked',
1161 if not bld
.is_install
:
1162 save_samba_deps(bld
, tgt_list
)
1164 debug("deps: save_samba_deps: %f" % (time
.clock() - tstart
))
1166 Logs
.info("Project rules pass")
1169 def CHECK_PROJECT_RULES(bld
):
1170 '''enable checking of project targets for sanity'''
1171 if bld
.env
.added_project_rules
:
1173 bld
.env
.added_project_rules
= True
1174 bld
.add_pre_fun(check_project_rules
)
1175 Build
.BuildContext
.CHECK_PROJECT_RULES
= CHECK_PROJECT_RULES