1 # Samba automatic dependency handling and project rules
5 from waflib
import Build
, Options
, Logs
, Utils
, Errors
, Task
6 from waflib
.Logs
import debug
7 from waflib
.Configure
import conf
8 from waflib
import ConfigSet
10 from samba_utils
import LOCAL_CACHE
, TO_LIST
, get_tgt_list
, unique_list
11 from samba_autoconf
import library_flags
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
)
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
28 def SET_SYSLIB_DEPS(conf
, target
, deps
):
29 '''setup some implied dependencies for a SYSLIB'''
30 cache
= LOCAL_CACHE(conf
, 'SYSLIB_DEPS')
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'''
40 subsystem_list
= LOCAL_CACHE(bld
, 'INIT_FUNCTIONS')
41 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
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':
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)
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']:
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
)
73 def build_dependencies(self
):
74 '''This builds the dependency list for a target. It runs after all the targets are declared
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.
80 if self
.samba_type
in ['LIBRARY', 'PLUGIN', '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
)
85 # extra link flags from pkg_config
86 (cflags
, ldflags
, cpppath
, libs
) = library_flags(
87 self
, list(self
.final_syslibs
.copy()))
88 new_ldflags
= getattr(self
, 'samba_ldflags', [])[:]
89 new_ldflags
.extend(ldflags
)
90 self
.ldflags
= new_ldflags
92 if getattr(self
, 'allow_undefined_symbols', False) and self
.env
.undefined_ldflags
:
93 for f
in self
.env
.undefined_ldflags
:
94 self
.ldflags
.remove(f
)
96 if getattr(self
, 'allow_undefined_symbols', False) and self
.env
.undefined_ignore_ldflags
:
97 for f
in self
.env
.undefined_ignore_ldflags
:
98 self
.ldflags
.append(f
)
100 debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
101 self
.sname
, self
.uselib
, self
.uselib_local
, self
.add_objects
)
103 if self
.samba_type
in ['SUBSYSTEM', 'BUILTIN']:
104 # this is needed for the cflags of libs that come from pkg_config
105 self
.uselib
= list(self
.final_syslibs
)
106 self
.uselib
.extend(list(self
.direct_syslibs
))
107 for lib
in self
.final_libs
:
108 t
= self
.bld
.get_tgen_by_name(lib
)
109 self
.uselib
.extend(list(t
.final_syslibs
))
110 self
.uselib
= unique_list(self
.uselib
)
112 if getattr(self
, 'uselib', None):
114 for l
in self
.uselib
:
115 up_list
.append(l
.upper())
116 self
.uselib
= up_list
119 def build_includes(self
):
120 '''This builds the right set of includes for a target.
122 One tricky part of this is that the includes= attribute for a
123 target needs to use paths which are relative to that targets
124 declaration directory (which we can get at via t.path).
126 The way this works is the includes list gets added as
127 samba_includes in the main build task declaration. Then this
128 function runs after all of the tasks are declared, and it
129 processes the samba_includes attribute to produce a includes=
133 if getattr(self
, 'samba_includes', None) is None:
138 inc_deps
= includes_objects(bld
, self
, set(), {})
142 # maybe add local includes
143 if getattr(self
, 'local_include', True) and getattr(self
, 'local_include_first', True):
146 includes
.extend(self
.samba_includes_extended
)
148 if 'EXTRA_INCLUDES' in bld
.env
and getattr(self
, 'global_include', True):
149 includes
.extend(bld
.env
['EXTRA_INCLUDES'])
157 t
= bld
.get_tgen_by_name(d
)
158 bld
.ASSERT(t
is not None, "Unable to find dependency %s for %s" % (d
, self
.sname
))
159 inclist
= getattr(t
, 'samba_includes_extended', [])[:]
160 if getattr(t
, 'local_include', True):
164 tpath
= t
.samba_abspath
166 npath
= tpath
+ '/' + inc
167 if not npath
in inc_set
:
168 inc_abs
.append(npath
)
171 mypath
= self
.path
.abspath(bld
.env
)
173 relpath
= os
.path
.relpath(inc
, mypath
)
174 includes
.append(relpath
)
176 if getattr(self
, 'local_include', True) and not getattr(self
, 'local_include_first', True):
179 # now transform the includes list to be relative to the top directory
180 # which is represented by '#' in waf. This allows waf to cache the
181 # includes lists more efficiently
185 # some are already top based
186 includes_top
.append(i
)
188 absinc
= os
.path
.join(self
.path
.abspath(), i
)
189 relinc
= os
.path
.relpath(absinc
, self
.bld
.srcnode
.abspath())
190 includes_top
.append('#' + relinc
)
192 self
.includes
= unique_list(includes_top
)
193 debug('deps: includes for target %s: includes=%s',
194 self
.sname
, self
.includes
)
197 def add_init_functions(self
):
198 '''This builds the right set of init functions'''
202 subsystems
= LOCAL_CACHE(bld
, 'INIT_FUNCTIONS')
204 # cope with the separated object lists from BINARY and LIBRARY targets
206 if sname
.endswith('.objlist'):
210 if sname
in subsystems
:
211 modules
.append(sname
)
213 m
= getattr(self
, 'samba_modules', None)
215 modules
.extend(TO_LIST(m
))
217 m
= getattr(self
, 'samba_subsystem', None)
221 if 'pyembed' in self
.features
:
224 sentinel
= getattr(self
, 'init_function_sentinel', 'NULL')
226 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
227 cflags
= getattr(self
, 'samba_cflags', [])[:]
230 sname
= sname
.replace('-','_')
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
))
241 bld
.ASSERT(m
in subsystems
,
242 "No init_function defined for module '%s' in target '%s'" % (m
, self
.sname
))
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
))
253 cflags
.append('-DSTATIC_%s_MODULES=%s' % (m
, ','.join(init_fn_list
) + ',' + sentinel
))
254 proto
= "".join('_MODULE_PROTO(%s)' % f
for f
in init_fn_list
) +\
255 "extern void __%s_dummy_module_proto(void)" % (m
)
256 cflags
.append('-DSTATIC_%s_MODULES_PROTO=%s' % (m
, proto
))
260 def check_duplicate_sources(bld
, tgt_list
):
261 '''see if we are compiling the same source file more than once'''
263 debug('deps: checking for duplicate sources')
264 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
267 source_list
= TO_LIST(getattr(t
, 'source', ''))
268 tpath
= os
.path
.normpath(os
.path
.relpath(t
.path
.abspath(bld
.env
), t
.env
.BUILD_DIRECTORY
+ '/default'))
270 for s
in source_list
:
271 if not isinstance(s
, str):
272 print('strange path in check_duplicate_sources %r' % s
)
274 p
= os
.path
.normpath(os
.path
.join(tpath
, s
))
276 Logs
.error("ERROR: source %s appears twice in target '%s'" % (p
, t
.sname
))
279 t
.samba_source_set
= obj_sources
283 # build a list of targets that each source file is part of
285 if not targets
[t
.sname
] in [ 'LIBRARY', 'PLUGIN', 'BINARY', 'PYTHON' ]:
287 for obj
in t
.add_objects
:
288 t2
= t
.bld
.get_tgen_by_name(obj
)
289 source_set
= getattr(t2
, 'samba_source_set', set())
291 if not s
in subsystems
:
293 if not t
.sname
in subsystems
[s
]:
294 subsystems
[s
][t
.sname
] = []
295 subsystems
[s
][t
.sname
].append(t2
.sname
)
298 if len(subsystems
[s
]) > 1 and Options
.options
.SHOW_DUPLICATES
:
299 Logs
.warn("WARNING: source %s is in more than one target: %s" % (s
, subsystems
[s
].keys()))
300 for tname
in subsystems
[s
]:
301 if len(subsystems
[s
][tname
]) > 1:
302 raise Errors
.WafError("ERROR: source %s is in more than one subsystem of target '%s': %s" % (s
, tname
, subsystems
[s
][tname
]))
306 def check_group_ordering(bld
, tgt_list
):
307 '''see if we have any dependencies that violate the group ordering
309 It is an error for a target to depend on a target from a later
314 tm
= bld
.task_manager
315 return [x
for x
in tm
.groups_names
if id(tm
.groups_names
[x
]) == id(g
)][0]
317 for g
in bld
.task_manager
.groups
:
318 gname
= group_name(g
)
319 for t
in g
.tasks_gen
:
320 t
.samba_group
= gname
324 for g
in bld
.task_manager
.groups
:
329 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
333 tdeps
= getattr(t
, 'add_objects', []) + getattr(t
, 'uselib_local', [])
335 t2
= bld
.get_tgen_by_name(d
)
338 map1
= grp_map
[t
.samba_group
]
339 map2
= grp_map
[t2
.samba_group
]
342 Logs
.error("Target %r in build group %r depends on target %r from later build group %r" % (
343 t
.sname
, t
.samba_group
, t2
.sname
, t2
.samba_group
))
347 Build
.BuildContext
.check_group_ordering
= check_group_ordering
349 def show_final_deps(bld
, tgt_list
):
350 '''show the final dependencies for all targets'''
352 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
355 if not targets
[t
.sname
] in ['LIBRARY', 'PLUGIN', 'BINARY', 'PYTHON', 'SUBSYSTEM', 'BUILTIN']:
357 debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
358 t
.sname
, t
.uselib
, getattr(t
, 'uselib_local', []), getattr(t
, 'add_objects', []))
361 def add_samba_attributes(bld
, tgt_list
):
362 '''ensure a target has the required samba attributes'''
364 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
371 t
.samba_type
= targets
[t
.sname
]
372 t
.samba_abspath
= t
.path
.abspath(bld
.env
)
373 t
.samba_deps_extended
= t
.samba_deps
[:]
374 t
.samba_includes_extended
= TO_LIST(t
.samba_includes
)[:]
375 t
.cflags
= getattr(t
, 'samba_cflags', '')
377 def replace_builtin_subsystem_deps(bld
, tgt_list
):
378 '''replace dependencies based on builtin subsystems/libraries
382 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
384 # If either the target or the dependency require builtin linking
385 # we should replace the dependency
387 t_require_builtin_deps
= getattr(t
, 'samba_require_builtin_deps', False)
388 if t_require_builtin_deps
:
389 debug("deps: target %s: requires builtin dependencies..." % (t
.sname
))
391 debug("deps: target %s: does not require builtin dependencies..." % (t
.sname
))
395 for dep
in t
.samba_deps_extended
:
396 bld
.ASSERT(dep
in targets
, "target %s: dependency target %s not declared" % (t
.sname
, dep
))
398 bld
.ASSERT(dtype
!= 'BUILTIN', "target %s: dependency target %s is BUILTIN" % (t
.sname
, dep
))
399 bld
.ASSERT(dtype
!= 'PLUGIN', "target %s: dependency target %s is PLUGIN" % (t
.sname
, dep
))
400 if dtype
not in ['SUBSYSTEM', 'LIBRARY']:
401 debug("deps: target %s: keep %s dependency %s" % (t
.sname
, dtype
, dep
))
403 dt
= bld
.get_tgen_by_name(dep
)
404 bld
.ASSERT(dt
is not None, "target %s: dependency target %s not found by name" % (t
.sname
, dep
))
405 dt_require_builtin_deps
= getattr(dt
, 'samba_require_builtin_deps', False)
406 if not dt_require_builtin_deps
and not t_require_builtin_deps
:
407 # both target and dependency don't require builtin linking
409 sdt
= getattr(dt
, 'samba_builtin_subsystem', None)
410 if not t_require_builtin_deps
:
412 debug("deps: target %s: dependency %s requires builtin deps only" % (t
.sname
, dep
))
414 debug("deps: target %s: dependency %s requires builtin linking" % (t
.sname
, dep
))
415 bld
.ASSERT(sdt
is not None, "target %s: dependency target %s is missing samba_builtin_subsystem" % (t
.sname
, dep
))
417 bld
.ASSERT(sdep
in targets
, "target %s: builtin dependency target %s (from %s) not declared" % (t
.sname
, sdep
, dep
))
419 bld
.ASSERT(sdt
== 'BUILTIN', "target %s: builtin dependency target %s (from %s) is not BUILTIN" % (t
.sname
, sdep
, dep
))
420 replacing
[dep
] = sdep
422 for i
in range(len(t
.samba_deps_extended
)):
423 dep
= t
.samba_deps_extended
[i
]
425 sdep
= replacing
[dep
]
426 debug("deps: target %s: replacing dependency %s with builtin subsystem %s" % (t
.sname
, dep
, sdep
))
427 t
.samba_deps_extended
[i
] = sdep
429 def replace_grouping_libraries(bld
, tgt_list
):
430 '''replace dependencies based on grouping libraries
432 If a library is marked as a grouping library, then any target that
433 depends on a subsystem that is part of that grouping library gets
434 that dependency replaced with a dependency on the grouping library
437 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
441 # find our list of grouping libraries, mapped from the subsystems they depend on
443 if not getattr(t
, 'grouping_library', False):
445 for dep
in t
.samba_deps_extended
:
446 bld
.ASSERT(dep
in targets
, "grouping library target %s not declared in %s" % (dep
, t
.sname
))
447 if targets
[dep
] == 'SUBSYSTEM':
448 grouping
[dep
] = t
.sname
450 # now replace any dependencies on elements of grouping libraries
452 for i
in range(len(t
.samba_deps_extended
)):
453 dep
= t
.samba_deps_extended
[i
]
455 if t
.sname
!= grouping
[dep
]:
456 debug("deps: target %s: replacing dependency %s with grouping library %s" % (t
.sname
, dep
, grouping
[dep
]))
457 t
.samba_deps_extended
[i
] = grouping
[dep
]
461 def build_direct_deps(bld
, tgt_list
):
462 '''build the direct_objects and direct_libs sets for each target'''
464 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
465 syslib_deps
= LOCAL_CACHE(bld
, 'SYSLIB_DEPS')
467 global_deps
= bld
.env
.GLOBAL_DEPENDENCIES
468 global_deps_exclude
= set()
469 for dep
in global_deps
:
470 t
= bld
.get_tgen_by_name(dep
)
471 for d
in t
.samba_deps
:
472 # prevent loops from the global dependencies list
473 global_deps_exclude
.add(d
)
474 global_deps_exclude
.add(d
+ '.objlist')
477 t
.direct_objects
= set()
478 t
.direct_libs
= set()
479 t
.direct_syslibs
= set()
480 deps
= t
.samba_deps_extended
[:]
481 if getattr(t
, 'samba_use_global_deps', False) and not t
.sname
in global_deps_exclude
:
482 deps
.extend(global_deps
)
484 if d
== t
.sname
: continue
486 Logs
.error("Unknown dependency '%s' in '%s'" % (d
, t
.sname
))
488 if targets
[d
] in [ 'EMPTY', 'DISABLED' ]:
490 if targets
[d
] == 'PYTHON' and targets
[t
.sname
] != 'PYTHON' and t
.sname
.find('.objlist') == -1:
491 # this check should be more restrictive, but for now we have pidl-generated python
492 # code that directly depends on other python modules
493 Logs
.error('ERROR: Target %s has dependency on python module %s' % (t
.sname
, d
))
495 if targets
[d
] == 'SYSLIB':
496 t
.direct_syslibs
.add(d
)
498 for implied
in TO_LIST(syslib_deps
[d
]):
499 if targets
[implied
] == 'SUBSYSTEM':
500 it
= bld
.get_tgen_by_name(implied
)
501 sit
= getattr(it
, 'samba_builtin_subsystem', None)
504 if targets
[implied
] == 'BUILTIN':
505 t
.direct_objects
.add(implied
)
506 elif targets
[implied
] == 'SYSLIB':
507 t
.direct_syslibs
.add(implied
)
508 elif targets
[implied
] in ['LIBRARY', 'MODULE']:
509 t
.direct_libs
.add(implied
)
511 Logs
.error('Implied dependency %s in %s is of type %s' % (
512 implied
, t
.sname
, targets
[implied
]))
515 t2
= bld
.get_tgen_by_name(d
)
517 Logs
.error("no task %s of type %s in %s" % (d
, targets
[d
], t
.sname
))
519 if t2
.samba_type
in [ 'LIBRARY', 'MODULE' ]:
521 elif t2
.samba_type
in [ 'SUBSYSTEM', 'BUILTIN', 'ASN1', 'PYTHON' ]:
522 t
.direct_objects
.add(d
)
523 elif t2
.samba_type
in [ 'PLUGIN' ]:
524 Logs
.error('Implicit dependency %s in %s is of type %s' % (
525 d
, t
.sname
, t2
.samba_type
))
528 debug('deps: built direct dependencies')
531 def dependency_loop(loops
, t
, target
):
532 '''add a dependency loop to the loops dictionary'''
533 if t
.sname
== target
:
535 if not target
in loops
:
536 loops
[target
] = set()
537 if not t
.sname
in loops
[target
]:
538 loops
[target
].add(t
.sname
)
541 def indirect_libs(bld
, t
, chain
, loops
):
542 '''recursively calculate the indirect library dependencies for a target
544 An indirect library is a library that results from a dependency on
548 ret
= getattr(t
, 'indirect_libs', None)
553 for obj
in t
.direct_objects
:
555 dependency_loop(loops
, t
, obj
)
558 t2
= bld
.get_tgen_by_name(obj
)
559 r2
= indirect_libs(bld
, t2
, chain
, loops
)
561 ret
= ret
.union(t2
.direct_libs
)
564 for obj
in indirect_objects(bld
, t
, set(), loops
):
566 dependency_loop(loops
, t
, obj
)
569 t2
= bld
.get_tgen_by_name(obj
)
570 r2
= indirect_libs(bld
, t2
, chain
, loops
)
572 ret
= ret
.union(t2
.direct_libs
)
575 t
.indirect_libs
= ret
580 def indirect_objects(bld
, t
, chain
, loops
):
581 '''recursively calculate the indirect object dependencies for a target
583 indirect objects are the set of objects from expanding the
584 subsystem dependencies
587 ret
= getattr(t
, 'indirect_objects', None)
588 if ret
is not None: return ret
591 for lib
in t
.direct_objects
:
593 dependency_loop(loops
, t
, lib
)
596 t2
= bld
.get_tgen_by_name(lib
)
597 r2
= indirect_objects(bld
, t2
, chain
, loops
)
599 ret
= ret
.union(t2
.direct_objects
)
602 t
.indirect_objects
= ret
606 def extended_objects(bld
, t
, chain
):
607 '''recursively calculate the extended object dependencies for a target
609 extended objects are the union of:
612 - direct and indirect objects of all direct and indirect libraries
615 ret
= getattr(t
, 'extended_objects', None)
616 if ret
is not None: return ret
619 ret
= ret
.union(t
.final_objects
)
621 for lib
in t
.final_libs
:
624 t2
= bld
.get_tgen_by_name(lib
)
626 r2
= extended_objects(bld
, t2
, chain
)
628 ret
= ret
.union(t2
.final_objects
)
631 t
.extended_objects
= ret
635 def includes_objects(bld
, t
, chain
, inc_loops
):
636 '''recursively calculate the includes object dependencies for a target
638 includes dependencies come from either library or object dependencies
640 ret
= getattr(t
, 'includes_objects', None)
644 ret
= t
.direct_objects
.copy()
645 ret
= ret
.union(t
.direct_libs
)
647 for obj
in t
.direct_objects
:
649 dependency_loop(inc_loops
, t
, obj
)
652 t2
= bld
.get_tgen_by_name(obj
)
653 r2
= includes_objects(bld
, t2
, chain
, inc_loops
)
655 ret
= ret
.union(t2
.direct_objects
)
658 for lib
in t
.direct_libs
:
660 dependency_loop(inc_loops
, t
, lib
)
663 t2
= bld
.get_tgen_by_name(lib
)
665 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
666 Logs
.error('Target %s of type %s not found in direct_libs for %s' % (
667 lib
, targets
[lib
], t
.sname
))
669 r2
= includes_objects(bld
, t2
, chain
, inc_loops
)
671 ret
= ret
.union(t2
.direct_objects
)
674 t
.includes_objects
= ret
678 def break_dependency_loops(bld
, tgt_list
):
679 '''find and break dependency loops'''
683 # build up the list of loops
685 indirect_objects(bld
, t
, set(), loops
)
686 indirect_libs(bld
, t
, set(), loops
)
687 includes_objects(bld
, t
, set(), inc_loops
)
692 for attr
in ['direct_objects', 'indirect_objects', 'direct_libs', 'indirect_libs']:
693 objs
= getattr(t
, attr
, set())
694 setattr(t
, attr
, objs
.difference(loops
[t
.sname
]))
697 debug('deps: Found dependency loops for target %s : %s', loop
, loops
[loop
])
699 for loop
in inc_loops
:
700 debug('deps: Found include loops for target %s : %s', loop
, inc_loops
[loop
])
702 # expand the loops mapping by one level
703 for loop
in loops
.copy():
704 for tgt
in loops
[loop
]:
706 loops
[loop
] = loops
[loop
].union(loops
[tgt
])
708 for loop
in inc_loops
.copy():
709 for tgt
in inc_loops
[loop
]:
711 inc_loops
[loop
] = inc_loops
[loop
].union(inc_loops
[tgt
])
714 # expand indirect subsystem and library loops
715 for loop
in loops
.copy():
716 t
= bld
.get_tgen_by_name(loop
)
717 if t
.samba_type
in ['SUBSYSTEM', 'BUILTIN']:
718 loops
[loop
] = loops
[loop
].union(t
.indirect_objects
)
719 loops
[loop
] = loops
[loop
].union(t
.direct_objects
)
720 if t
.samba_type
in ['LIBRARY', 'PLUGIN', 'PYTHON']:
721 loops
[loop
] = loops
[loop
].union(t
.indirect_libs
)
722 loops
[loop
] = loops
[loop
].union(t
.direct_libs
)
723 if loop
in loops
[loop
]:
724 loops
[loop
].remove(loop
)
726 # expand indirect includes loops
727 for loop
in inc_loops
.copy():
728 t
= bld
.get_tgen_by_name(loop
)
729 inc_loops
[loop
] = inc_loops
[loop
].union(t
.includes_objects
)
730 if loop
in inc_loops
[loop
]:
731 inc_loops
[loop
].remove(loop
)
733 # add in the replacement dependencies
736 for attr
in ['indirect_objects', 'indirect_libs']:
737 objs
= getattr(t
, attr
, set())
739 diff
= loops
[loop
].difference(objs
)
743 debug('deps: Expanded target %s of type %s from loop %s by %s', t
.sname
, t
.samba_type
, loop
, diff
)
744 objs
= objs
.union(diff
)
745 setattr(t
, attr
, objs
)
747 for loop
in inc_loops
:
748 objs
= getattr(t
, 'includes_objects', set())
750 diff
= inc_loops
[loop
].difference(objs
)
754 debug('deps: Expanded target %s includes of type %s from loop %s by %s', t
.sname
, t
.samba_type
, loop
, diff
)
755 objs
= objs
.union(diff
)
756 setattr(t
, 'includes_objects', objs
)
759 def reduce_objects(bld
, tgt_list
):
760 '''reduce objects by looking for indirect object dependencies'''
761 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
766 t
.extended_objects
= None
770 for type in ['BINARY', 'PYTHON', 'LIBRARY', 'PLUGIN']:
772 if t
.samba_type
!= type: continue
773 # if we will indirectly link to a target then we don't need it
774 new
= t
.final_objects
.copy()
775 for l
in t
.final_libs
:
776 t2
= bld
.get_tgen_by_name(l
)
777 t2_obj
= extended_objects(bld
, t2
, set())
778 dup
= new
.intersection(t2_obj
)
779 if t
.sname
in rely_on
:
780 dup
= dup
.difference(rely_on
[t
.sname
])
782 # Do not remove duplicates of BUILTINS
783 for d
in iter(dup
.copy()):
785 if dtype
== 'BUILTIN':
786 debug('deps: BUILTIN SKIP: removing dups from %s of type %s: %s also in %s %s',
787 t
.sname
, t
.samba_type
, d
, t2
.samba_type
, l
)
792 debug('deps: removing dups from %s of type %s: %s also in %s %s',
793 t
.sname
, t
.samba_type
, dup
, t2
.samba_type
, l
)
794 new
= new
.difference(dup
)
798 rely_on
[l
] = rely_on
[l
].union(dup
)
799 for n
in iter(new
.copy()):
800 # if we got the builtin version as well
801 # as the native one, we keep using the
802 # builtin one and remove the rest.
803 # Otherwise our check_duplicate_sources()
804 # checks would trigger!
805 if n
.endswith('.builtin.objlist'):
806 unused
= n
.replace('.builtin.objlist', '.objlist')
809 unused
= n
.replace('.builtin.objlist', '')
812 t
.final_objects
= new
817 # add back in any objects that were relied upon by the reduction rules
819 t
= bld
.get_tgen_by_name(r
)
820 t
.final_objects
= t
.final_objects
.union(rely_on
[r
])
825 def show_library_loop(bld
, lib1
, lib2
, path
, seen
):
826 '''show the detailed path of a library loop between lib1 and lib2'''
828 t
= bld
.get_tgen_by_name(lib1
)
829 if not lib2
in getattr(t
, 'final_libs', set()):
832 for d
in t
.samba_deps_extended
:
836 path2
= path
+ '=>' + d
838 Logs
.warn('library loop path: ' + path2
)
840 show_library_loop(bld
, d
, lib2
, path2
, seen
)
844 def calculate_final_deps(bld
, tgt_list
, loops
):
845 '''calculate the final library and object dependencies'''
847 # start with the maximum possible list
848 t
.final_libs
= t
.direct_libs
.union(indirect_libs(bld
, t
, set(), loops
))
849 t
.final_objects
= t
.direct_objects
.union(indirect_objects(bld
, t
, set(), loops
))
852 # don't depend on ourselves
853 if t
.sname
in t
.final_libs
:
854 t
.final_libs
.remove(t
.sname
)
855 if t
.sname
in t
.final_objects
:
856 t
.final_objects
.remove(t
.sname
)
858 # handle any non-shared binaries
860 if t
.samba_type
== 'BINARY' and bld
.NONSHARED_BINARY(t
.sname
):
861 subsystem_list
= LOCAL_CACHE(bld
, 'INIT_FUNCTIONS')
862 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
864 # replace lib deps with objlist deps
865 for l
in t
.final_libs
:
866 objname
= l
+ '.objlist'
867 t2
= bld
.get_tgen_by_name(objname
)
869 Logs
.error('ERROR: subsystem %s not found' % objname
)
871 t
.final_objects
.add(objname
)
872 t
.final_objects
= t
.final_objects
.union(extended_objects(bld
, t2
, set()))
873 if l
in subsystem_list
:
874 # its a subsystem - we also need the contents of any modules
875 for d
in subsystem_list
[l
]:
876 module_name
= d
['TARGET']
877 if targets
[module_name
] == 'LIBRARY':
878 objname
= module_name
+ '.objlist'
879 elif targets
[module_name
] == 'SUBSYSTEM':
880 objname
= module_name
883 t2
= bld
.get_tgen_by_name(objname
)
885 Logs
.error('ERROR: subsystem %s not found' % objname
)
887 t
.final_objects
.add(objname
)
888 t
.final_objects
= t
.final_objects
.union(extended_objects(bld
, t2
, set()))
891 # find any library loops
893 if t
.samba_type
in ['LIBRARY', 'PYTHON']:
894 for l
in t
.final_libs
.copy():
895 t2
= bld
.get_tgen_by_name(l
)
896 if t
.sname
in t2
.final_libs
:
897 if getattr(bld
.env
, "ALLOW_CIRCULAR_LIB_DEPENDENCIES", False):
898 # we could break this in either direction. If one of the libraries
899 # has a version number, and will this be distributed publicly, then
900 # we should make it the lower level library in the DAG
901 Logs
.warn('deps: removing library loop %s from %s' % (t
.sname
, t2
.sname
))
902 dependency_loop(loops
, t
, t2
.sname
)
903 t2
.final_libs
.remove(t
.sname
)
905 Logs
.error('ERROR: circular library dependency between %s and %s'
906 % (t
.sname
, t2
.sname
))
907 show_library_loop(bld
, t
.sname
, t2
.sname
, t
.sname
, set())
908 show_library_loop(bld
, t2
.sname
, t
.sname
, t2
.sname
, set())
912 debug('deps: Found dependency loops for target %s : %s', loop
, loops
[loop
])
914 # we now need to make corrections for any library loops we broke up
915 # any target that depended on the target of the loop and doesn't
916 # depend on the source of the loop needs to get the loop source added
917 for type in ['BINARY','PYTHON','LIBRARY','PLUGIN','BINARY']:
919 if t
.samba_type
!= type: continue
921 if loop
in t
.final_libs
:
922 diff
= loops
[loop
].difference(t
.final_libs
)
927 # make sure we don't recreate the loop again!
928 for d
in diff
.copy():
929 t2
= bld
.get_tgen_by_name(d
)
930 if t2
.samba_type
== 'LIBRARY':
931 if t
.sname
in t2
.final_libs
:
932 debug('deps: removing expansion %s from %s', d
, t
.sname
)
935 debug('deps: Expanded target %s by loop %s libraries (loop %s) %s', t
.sname
, loop
,
937 t
.final_libs
= t
.final_libs
.union(diff
)
939 # remove objects that are also available in linked libs
941 while reduce_objects(bld
, tgt_list
):
944 Logs
.warn("WARNING: Unable to remove all inter-target object duplicates")
946 debug('deps: Object reduction took %u iterations', count
)
948 # add in any syslib dependencies
950 if not t
.samba_type
in ['BINARY','PYTHON','LIBRARY','PLUGIN','SUBSYSTEM','BUILTIN']:
953 for d
in t
.final_objects
:
954 t2
= bld
.get_tgen_by_name(d
)
955 syslibs
= syslibs
.union(t2
.direct_syslibs
)
956 # this adds the indirect syslibs as well, which may not be needed
957 # depending on the linker flags
958 for d
in t
.final_libs
:
959 t2
= bld
.get_tgen_by_name(d
)
960 syslibs
= syslibs
.union(t2
.direct_syslibs
)
961 t
.final_syslibs
= syslibs
964 # find any unresolved library loops
965 lib_loop_error
= False
967 if t
.samba_type
in ['LIBRARY', 'PLUGIN', 'PYTHON']:
968 for l
in t
.final_libs
.copy():
969 t2
= bld
.get_tgen_by_name(l
)
970 if t
.sname
in t2
.final_libs
:
971 Logs
.error('ERROR: Unresolved library loop %s from %s' % (t
.sname
, t2
.sname
))
972 lib_loop_error
= True
976 debug('deps: removed duplicate dependencies')
979 def show_dependencies(bld
, target
, seen
):
980 '''recursively show the dependencies of target'''
985 t
= bld
.get_tgen_by_name(target
)
987 Logs
.error("ERROR: Unable to find target '%s'" % target
)
990 Logs
.info('%s(OBJECTS): %s' % (target
, t
.direct_objects
))
991 Logs
.info('%s(LIBS): %s' % (target
, t
.direct_libs
))
992 Logs
.info('%s(SYSLIBS): %s' % (target
, t
.direct_syslibs
))
996 for t2
in t
.direct_objects
:
997 show_dependencies(bld
, t2
, seen
)
1000 def show_object_duplicates(bld
, tgt_list
):
1001 '''show a list of object files that are included in more than
1002 one library or binary'''
1004 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
1008 Logs
.info("showing duplicate objects")
1011 if not targets
[t
.sname
] in [ 'LIBRARY', 'PYTHON' ]:
1013 for n
in getattr(t
, 'final_objects', set()):
1014 t2
= bld
.get_tgen_by_name(n
)
1015 if not n
in used_by
:
1017 used_by
[n
].add(t
.sname
)
1020 if len(used_by
[n
]) > 1:
1021 Logs
.info("target '%s' is used by %s" % (n
, used_by
[n
]))
1023 Logs
.info("showing indirect dependency counts (sorted by count)")
1025 def indirect_count(t
):
1026 return len(t
.indirect_objects
)
1028 sorted_list
= sorted(tgt_list
, key
=indirect_count
, reverse
=True)
1029 for t
in sorted_list
:
1030 if len(t
.indirect_objects
) > 1:
1031 Logs
.info("%s depends on %u indirect objects" % (t
.sname
, len(t
.indirect_objects
)))
1034 ######################################################################
1035 # this provides a way to save our dependency calculations between runs
1036 savedeps_version
= 3
1037 savedeps_inputs
= ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags',
1038 'source', 'grouping_library', 'samba_ldflags', 'allow_undefined_symbols',
1039 'use_global_deps', 'global_include' ]
1040 savedeps_outputs
= ['uselib', 'uselib_local', 'add_objects', 'includes',
1041 'cflags', 'ldflags', 'samba_deps_extended', 'final_libs']
1042 savedeps_outenv
= ['INC_PATHS']
1043 savedeps_envvars
= ['NONSHARED_BINARIES', 'GLOBAL_DEPENDENCIES', 'EXTRA_CFLAGS', 'EXTRA_LDFLAGS', 'EXTRA_INCLUDES' ]
1044 savedeps_caches
= ['GLOBAL_DEPENDENCIES', 'TARGET_TYPE', 'INIT_FUNCTIONS', 'SYSLIB_DEPS']
1045 savedeps_files
= ['buildtools/wafsamba/samba_deps.py']
1047 def save_samba_deps(bld
, tgt_list
):
1048 '''save the dependency calculations between builds, to make
1049 further builds faster'''
1050 denv
= ConfigSet
.ConfigSet()
1052 denv
.version
= savedeps_version
1053 denv
.savedeps_inputs
= savedeps_inputs
1054 denv
.savedeps_outputs
= savedeps_outputs
1062 for f
in savedeps_files
:
1063 denv
.files
[f
] = os
.stat(os
.path
.join(bld
.srcnode
.abspath(), f
)).st_mtime
1065 for c
in savedeps_caches
:
1066 denv
.caches
[c
] = LOCAL_CACHE(bld
, c
)
1068 for e
in savedeps_envvars
:
1069 denv
.envvar
[e
] = bld
.env
[e
]
1072 # save all the input attributes for each target
1074 for attr
in savedeps_inputs
:
1075 v
= getattr(t
, attr
, None)
1079 denv
.input[t
.sname
] = tdeps
1081 # save all the output attributes for each target
1083 for attr
in savedeps_outputs
:
1084 v
= getattr(t
, attr
, None)
1088 denv
.output
[t
.sname
] = tdeps
1091 for attr
in savedeps_outenv
:
1093 tdeps
[attr
] = t
.env
[attr
]
1095 denv
.outenv
[t
.sname
] = tdeps
1097 depsfile
= os
.path
.join(bld
.cache_dir
, "sambadeps")
1098 denv
.store_fast(depsfile
)
1102 def load_samba_deps(bld
, tgt_list
):
1103 '''load a previous set of build dependencies if possible'''
1104 depsfile
= os
.path
.join(bld
.cache_dir
, "sambadeps")
1105 denv
= ConfigSet
.ConfigSet()
1107 debug('deps: checking saved dependencies')
1108 denv
.load_fast(depsfile
)
1109 if (denv
.version
!= savedeps_version
or
1110 denv
.savedeps_inputs
!= savedeps_inputs
or
1111 denv
.savedeps_outputs
!= savedeps_outputs
):
1116 # check if critical files have changed
1117 for f
in savedeps_files
:
1118 if f
not in denv
.files
:
1120 if denv
.files
[f
] != os
.stat(os
.path
.join(bld
.srcnode
.abspath(), f
)).st_mtime
:
1123 # check if caches are the same
1124 for c
in savedeps_caches
:
1125 if c
not in denv
.caches
or denv
.caches
[c
] != LOCAL_CACHE(bld
, c
):
1128 # check if caches are the same
1129 for e
in savedeps_envvars
:
1130 if e
not in denv
.envvar
or denv
.envvar
[e
] != bld
.env
[e
]:
1133 # check inputs are the same
1136 for attr
in savedeps_inputs
:
1137 v
= getattr(t
, attr
, None)
1140 if t
.sname
in denv
.input:
1141 olddeps
= denv
.input[t
.sname
]
1144 if tdeps
!= olddeps
:
1145 #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
1148 # put outputs in place
1150 if not t
.sname
in denv
.output
: continue
1151 tdeps
= denv
.output
[t
.sname
]
1153 setattr(t
, a
, tdeps
[a
])
1155 # put output env vars in place
1157 if not t
.sname
in denv
.outenv
: continue
1158 tdeps
= denv
.outenv
[t
.sname
]
1162 debug('deps: loaded saved dependencies')
1166 def generate_clangdb(bld
):
1168 for x
in ('c', 'cxx'):
1169 cls
= Task
.classes
.get(x
)
1172 task_classes
= tuple(classes
)
1175 for g
in bld
.groups
:
1177 if isinstance(tg
, Task
.Task
):
1184 except AttributeError:
1186 if isinstance(task
, task_classes
):
1191 database_file
= bld
.bldnode
.make_node('compile_commands.json')
1192 Logs
.info('Build commands will be stored in %s',
1193 database_file
.path_from(bld
.path
))
1195 root
= database_file
.read_json()
1198 clang_db
= dict((x
['file'], x
) for x
in root
)
1200 f_node
= task
.inputs
[0]
1202 filename
= f_node
.path_from(task
.get_cwd())
1204 "directory": task
.get_cwd().abspath(),
1208 clang_db
[filename
] = entry
1209 root
= list(clang_db
.values())
1210 database_file
.write_json(root
)
1213 def check_project_rules(bld
):
1214 '''check the project rules - ensuring the targets are sane'''
1219 tgt_list
= get_tgt_list(bld
)
1221 add_samba_attributes(bld
, tgt_list
)
1223 force_project_rules
= (Options
.options
.SHOWDEPS
or
1224 Options
.options
.SHOW_DUPLICATES
)
1226 if not force_project_rules
and load_samba_deps(bld
, tgt_list
):
1229 timer
= Utils
.Timer()
1231 bld
.new_rules
= True
1232 Logs
.info("Checking project rules ...")
1234 debug('deps: project rules checking started')
1236 replace_builtin_subsystem_deps(bld
, tgt_list
)
1238 debug("deps: replace_builtin_subsystem_deps: %s" % str(timer
))
1240 expand_subsystem_deps(bld
)
1242 debug("deps: expand_subsystem_deps: %s" % str(timer
))
1244 replace_grouping_libraries(bld
, tgt_list
)
1246 debug("deps: replace_grouping_libraries: %s" % str(timer
))
1248 build_direct_deps(bld
, tgt_list
)
1250 debug("deps: build_direct_deps: %s" % str(timer
))
1252 break_dependency_loops(bld
, tgt_list
)
1254 debug("deps: break_dependency_loops: %s" % str(timer
))
1256 if Options
.options
.SHOWDEPS
:
1257 show_dependencies(bld
, Options
.options
.SHOWDEPS
, set())
1259 calculate_final_deps(bld
, tgt_list
, loops
)
1261 debug("deps: calculate_final_deps: %s" % str(timer
))
1263 if Options
.options
.SHOW_DUPLICATES
:
1264 show_object_duplicates(bld
, tgt_list
)
1266 # run the various attribute generators
1267 for f
in [ build_dependencies
, build_includes
, add_init_functions
]:
1268 debug('deps: project rules checking %s', f
)
1269 for t
in tgt_list
: f(t
)
1270 debug("deps: %s: %s" % (f
, str(timer
)))
1272 debug('deps: project rules stage1 completed')
1274 if not check_duplicate_sources(bld
, tgt_list
):
1275 Logs
.error("Duplicate sources present - aborting")
1278 debug("deps: check_duplicate_sources: %s" % str(timer
))
1280 if not bld
.check_group_ordering(tgt_list
):
1281 Logs
.error("Bad group ordering - aborting")
1284 debug("deps: check_group_ordering: %s" % str(timer
))
1286 show_final_deps(bld
, tgt_list
)
1288 debug("deps: show_final_deps: %s" % str(timer
))
1290 debug('deps: project rules checking completed - %u targets checked',
1293 if not bld
.is_install
:
1294 save_samba_deps(bld
, tgt_list
)
1296 debug("deps: save_samba_deps: %s" % str(timer
))
1298 Logs
.info("Project rules pass")
1300 if bld
.cmd
== 'build':
1301 Task
.Task
.keep_last_cmd
= True
1302 bld
.add_post_fun(generate_clangdb
)
1305 def CHECK_PROJECT_RULES(bld
):
1306 '''enable checking of project targets for sanity'''
1307 if bld
.env
.added_project_rules
:
1309 bld
.env
.added_project_rules
= True
1310 bld
.add_pre_fun(check_project_rules
)
1311 Build
.BuildContext
.CHECK_PROJECT_RULES
= CHECK_PROJECT_RULES