1 # Samba automatic dependency handling and project rules
3 import Build
, os
, re
, Environment
4 from samba_utils
import *
5 from samba_autoconf
import *
8 def ADD_GLOBAL_DEPENDENCY(ctx
, dep
):
9 '''add a dependency for all binaries and libraries'''
10 if not 'GLOBAL_DEPENDENCIES' in ctx
.env
:
11 ctx
.env
.GLOBAL_DEPENDENCIES
= []
12 ctx
.env
.GLOBAL_DEPENDENCIES
.append(dep
)
15 def TARGET_ALIAS(bld
, target
, alias
):
16 '''define an alias for a target name'''
17 cache
= LOCAL_CACHE(bld
, 'TARGET_ALIAS')
19 print("Target alias %s already set to %s : newalias %s" % (alias
, cache
[alias
], target
))
22 Build
.BuildContext
.TARGET_ALIAS
= TARGET_ALIAS
25 def EXPAND_ALIAS(bld
, target
):
26 '''expand a target name via an alias'''
27 aliases
= LOCAL_CACHE(bld
, 'TARGET_ALIAS')
29 return aliases
[target
]
31 Build
.BuildContext
.EXPAND_ALIAS
= EXPAND_ALIAS
34 def expand_subsystem_deps(bld
):
35 '''expand the reverse dependencies resulting from subsystem
36 attributes of modules'''
37 subsystems
= LOCAL_CACHE(bld
, 'INIT_FUNCTIONS')
38 aliases
= LOCAL_CACHE(bld
, 'TARGET_ALIAS')
39 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
44 bld
.ASSERT(s
in targets
, "Subsystem target %s not declared" % s
)
46 if type == 'DISABLED' or type == 'EMPTY':
49 t
= bld
.name_to_obj(s
, bld
.env
)
50 bld
.ASSERT(t
is not None, "Subsystem target %s not found" % s
)
51 for d
in subsystems
[s
]:
52 type = targets
[d
['TARGET']]
53 if type != 'DISABLED' and type != 'EMPTY':
54 t
.samba_deps_extended
.append(d
['TARGET'])
55 t2
= bld
.name_to_obj(d
['TARGET'], bld
.env
)
56 t2
.samba_includes_extended
.extend(t
.samba_includes_extended
)
57 t2
.samba_deps_extended
.extend(t
.samba_deps_extended
)
58 t
.samba_deps_extended
= unique_list(t
.samba_deps_extended
)
62 def build_dependencies(self
):
63 '''This builds the dependency list for a target. It runs after all the targets are declared
65 The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
66 the full dependency list for a target until we have all of the targets declared.
69 # we only should add extra library and object deps on libraries and binaries
70 if not self
.samba_type
in ['LIBRARY', 'BINARY', 'PYTHON']:
73 # we need to link against:
75 # 1) any direct system libs
76 # 2) any indirect system libs that come from subsystem dependencies
77 # 3) any direct local libs
78 # 4) any indirect local libs that come from subsystem dependencies
79 # 5) any direct objects
80 # 6) any indirect objects that come from subsystem dependencies
82 self
.uselib
= list(self
.final_syslibs
)
83 self
.uselib_local
= list(self
.final_libs
)
84 self
.add_objects
= list(self
.final_objects
)
86 debug('deps: computed dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
87 self
.sname
, self
.uselib
, self
.uselib_local
, self
.add_objects
)
91 def build_includes(self
):
92 '''This builds the right set of includes for a target.
94 One tricky part of this is that the includes= attribute for a
95 target needs to use paths which are relative to that targets
96 declaration directory (which we can get at via t.path).
98 The way this works is the includes list gets added as
99 samba_includes in the main build task declaration. Then this
100 function runs after all of the tasks are declared, and it
101 processes the samba_includes attribute to produce a includes=
105 if getattr(self
, 'samba_includes', None) is None:
110 inc_deps
= self
.includes_objects
114 # maybe add local includes
115 if getattr(self
, 'local_include', True) == True and getattr(self
, 'local_include_first', True):
118 includes
.extend(self
.samba_includes_extended
)
120 if 'EXTRA_INCLUDES' in bld
.env
:
121 includes
.extend(bld
.env
['EXTRA_INCLUDES'])
129 t
= bld
.name_to_obj(d
, bld
.env
)
130 bld
.ASSERT(t
is not None, "Unable to find dependency %s for %s" % (d
, self
.sname
))
131 inclist
= getattr(t
, 'samba_includes_extended', [])
132 if getattr(t
, 'local_include', True) == True:
136 tpath
= t
.samba_abspath
138 npath
= tpath
+ '/' + inc
139 if not npath
in inc_set
:
140 inc_abs
.append(npath
)
143 mypath
= self
.path
.abspath(bld
.env
)
145 relpath
= os_path_relpath(inc
, mypath
)
146 includes
.append(relpath
)
148 if getattr(self
, 'local_include', True) == True and not getattr(self
, 'local_include_first', True):
151 # now transform the includes list to be relative to the top directory
152 # which is represented by '#' in waf. This allows waf to cache the
153 # includes lists more efficiently
157 # some are already top based
158 includes_top
.append(i
)
160 absinc
= os
.path
.join(self
.path
.abspath(), i
)
161 relinc
= os_path_relpath(absinc
, self
.bld
.srcnode
.abspath())
162 includes_top
.append('#' + relinc
)
164 self
.includes
= unique_list(includes_top
)
165 debug('deps: includes for target %s: includes=%s',
166 self
.sname
, self
.includes
)
171 def add_init_functions(self
):
172 '''This builds the right set of init functions'''
176 subsystems
= LOCAL_CACHE(bld
, 'INIT_FUNCTIONS')
178 # cope with the separated object lists from BINARY and LIBRARY targets
180 if sname
.endswith('.objlist'):
184 if sname
in subsystems
:
185 modules
.append(sname
)
187 m
= getattr(self
, 'samba_modules', None)
189 modules
.extend(TO_LIST(m
))
191 m
= getattr(self
, 'samba_subsystem', None)
198 sentinal
= getattr(self
, 'init_function_sentinal', 'NULL')
200 cflags
= getattr(self
, 'samba_cflags', [])[:]
202 bld
.ASSERT(m
in subsystems
,
203 "No init_function defined for module '%s' in target '%s'" % (m
, self
.sname
))
205 for d
in subsystems
[m
]:
206 init_fn_list
.append(d
['INIT_FUNCTION'])
207 cflags
.append('-DSTATIC_%s_MODULES=%s' % (m
, ','.join(init_fn_list
) + ',' + sentinal
))
208 self
.ccflags
= cflags
212 def check_duplicate_sources(bld
, tgt_list
):
213 '''see if we are compiling the same source file into multiple
214 subsystem targets for the same library or binary'''
216 debug('deps: checking for duplicate sources')
218 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
221 if not targets
[t
.sname
] in [ 'LIBRARY', 'BINARY', 'PYTHON' ]:
225 for obj
in t
.add_objects
:
226 t2
= t
.bld
.name_to_obj(obj
, bld
.env
)
227 obj_sources
= getattr(t2
, 'source', '')
228 if obj_sources
== '': continue
229 tpath
= os_path_relpath(t2
.path
.abspath(bld
.env
), t
.env
['BUILD_DIRECTORY'] + '/default')
230 obj_sources
= bld
.SUBDIR(tpath
, obj_sources
)
231 sources
.append( { 'dep':obj
, 'src':set(TO_LIST(obj_sources
)) } )
232 #debug('deps: dependency expansion for target %s add_object %s: %s',
233 # t.sname, obj, obj_sources)
236 if s
['dep'] == s2
['dep']: continue
237 common
= s
['src'].intersection(s2
['src'])
240 "Target %s has duplicate source files in %s and %s : %s" % (t
.sname
,
244 def check_orpaned_targets(bld
, tgt_list
):
245 '''check if any build targets are orphaned'''
247 target_dict
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
249 debug('deps: checking for orphaned targets')
252 if getattr(t
, 'samba_used', False) == True:
254 type = target_dict
[t
.sname
]
255 if not type in ['BINARY', 'LIBRARY', 'MODULE', 'ET', 'PYTHON']:
256 if re
.search('^PIDL_', t
.sname
) is None:
257 print "Target %s of type %s is unused by any other target" % (t
.sname
, type)
260 def show_final_deps(bld
, tgt_list
):
261 '''show the final dependencies for all targets'''
263 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
266 if not targets
[t
.sname
] in ['LIBRARY', 'BINARY', 'PYTHON']:
268 debug('deps: final dependencies for target %s: uselib=%s uselib_local=%s add_objects=%s',
269 t
.sname
, t
.uselib
, t
.uselib_local
, t
.add_objects
)
272 def add_samba_attributes(bld
, tgt_list
):
273 '''ensure a target has a the required samba attributes'''
275 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
282 t
.samba_type
= targets
[t
.sname
]
283 t
.samba_abspath
= t
.path
.abspath(bld
.env
)
284 t
.samba_deps_extended
= t
.samba_deps
[:]
285 t
.samba_includes_extended
= TO_LIST(t
.samba_includes
)[:]
286 t
.ccflags
= getattr(t
, 'samba_cflags', '')
288 def build_direct_deps(bld
, tgt_list
):
289 '''build the direct_objects and direct_libs sets for each target'''
291 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
292 global_deps
= bld
.env
.GLOBAL_DEPENDENCIES
295 t
.direct_objects
= set()
296 t
.direct_libs
= set()
297 t
.direct_syslibs
= set()
298 deps
= t
.samba_deps_extended
299 deps
.extend(global_deps
)
301 d
= EXPAND_ALIAS(bld
, d
)
303 print "Unknown dependency %s in %s" % (d
, t
.sname
)
305 if targets
[d
] in [ 'EMPTY', 'DISABLED' ]:
307 if targets
[d
] == 'SYSLIB':
308 t
.direct_syslibs
.add(d
)
310 t2
= bld
.name_to_obj(d
, bld
.env
)
312 print "no task %s type %s" % (d
, targets
[d
])
313 if t2
.samba_type
in [ 'LIBRARY', 'MODULE' ]:
315 elif t2
.samba_type
in [ 'SUBSYSTEM', 'ASN1', 'PYTHON' ]:
316 t
.direct_objects
.add(d
)
317 debug('deps: built direct dependencies')
321 def indirect_libs(bld
, t
, chain
):
322 '''recursively calculate the indirect library dependencies for a target
324 An indirect library is a library that results from a dependency on
328 ret
= getattr(t
, 'indirect_libs', None)
333 for obj
in t
.direct_objects
:
337 t2
= bld
.name_to_obj(obj
, bld
.env
)
338 r2
= indirect_libs(bld
, t2
, chain
)
340 ret
= ret
.union(t2
.direct_libs
)
343 for obj
in t
.indirect_objects
:
347 t2
= bld
.name_to_obj(obj
, bld
.env
)
348 r2
= indirect_libs(bld
, t2
, chain
)
350 ret
= ret
.union(t2
.direct_libs
)
353 t
.indirect_libs
= ret
358 def indirect_syslibs(bld
, t
, chain
):
359 '''recursively calculate the indirect system library dependencies for a target
361 An indirect syslib results from a subsystem dependency
364 ret
= getattr(t
, 'indirect_syslibs', None)
368 for obj
in t
.direct_objects
:
372 t2
= bld
.name_to_obj(obj
, bld
.env
)
373 r2
= indirect_syslibs(bld
, t2
, chain
)
375 ret
= ret
.union(t2
.direct_syslibs
)
378 t
.indirect_syslibs
= ret
382 def indirect_objects(bld
, t
, chain
):
383 '''recursively calculate the indirect object dependencies for a target
385 indirect objects are the set of objects from expanding the
386 subsystem dependencies
389 ret
= getattr(t
, 'indirect_objects', None)
390 if ret
is not None: return ret
393 for lib
in t
.direct_objects
:
397 t2
= bld
.name_to_obj(lib
, bld
.env
)
398 r2
= indirect_objects(bld
, t2
, chain
)
400 ret
= ret
.union(t2
.direct_objects
)
403 t
.indirect_objects
= ret
407 def expanded_targets(bld
, t
, chain
):
408 '''recursively calculate the expanded targets for a target
410 expanded objects are the set of objects, libraries and syslibs
411 from expanding the subsystem dependencies, library dependencies
412 and syslib dependencies
415 ret
= getattr(t
, 'expanded_targets', None)
416 if ret
is not None: return ret
418 ret
= t
.direct_objects
.copy()
419 ret
= ret
.union(t
.direct_libs
)
420 ret
= ret
.union(t
.direct_syslibs
)
425 if d
in chain
: continue
427 t2
= bld
.name_to_obj(d
, bld
.env
)
428 if t2
is None: continue
429 r2
= expanded_targets(bld
, t2
, chain
)
436 t
.expanded_targets
= ret
440 def expanded_targets2(bld
, t
, chain
):
441 '''recursively calculate the expanded targets for a target
443 expanded objects are the set of objects from expanding the
444 subsystem dependencies and library dependencies
447 ret
= getattr(t
, 'expanded_targets2', None)
448 if ret
is not None: return ret
450 ret
= t
.final_objects
.copy()
452 for attr
in [ 'final_objects', 'final_libs' ]:
453 f
= getattr(t
, attr
, set())
458 t2
= bld
.name_to_obj(d
, bld
.env
)
459 if t2
is None: continue
460 r2
= expanded_targets2(bld
, t2
, chain
)
467 t
.expanded_targets2
= ret
471 def includes_objects(bld
, t
, chain
):
472 '''recursively calculate the includes object dependencies for a target
474 includes dependencies come from either library or object dependencies
476 ret
= getattr(t
, 'includes_objects', None)
480 ret
= t
.direct_objects
.copy()
481 ret
= ret
.union(t
.direct_libs
)
483 for obj
in t
.direct_objects
:
487 t2
= bld
.name_to_obj(obj
, bld
.env
)
488 r2
= includes_objects(bld
, t2
, chain
)
490 ret
= ret
.union(t2
.direct_objects
)
493 for lib
in t
.direct_libs
:
497 t2
= bld
.name_to_obj(lib
, bld
.env
)
498 r2
= includes_objects(bld
, t2
, chain
)
500 ret
= ret
.union(t2
.direct_objects
)
503 t
.includes_objects
= ret
507 def build_indirect_deps(bld
, tgt_list
):
508 '''build the indirect_objects and indirect_libs sets for each target'''
510 indirect_objects(bld
, t
, set())
511 indirect_libs(bld
, t
, set())
512 indirect_syslibs(bld
, t
, set())
513 includes_objects(bld
, t
, set())
514 expanded_targets(bld
, t
, set())
515 debug('deps: built indirect dependencies')
518 def re_expand2(bld
, tgt_list
):
520 t
.expanded_targets2
= None
521 for type in ['BINARY','LIBRARY','PYTHON']:
523 if t
.samba_type
== type:
524 expanded_targets2(bld
, t
, set())
526 expanded_targets2(bld
, t
, set())
529 def calculate_final_deps(bld
, tgt_list
):
530 '''calculate the final library and object dependencies'''
532 # start with the maximum possible list
533 t
.final_syslibs
= t
.direct_syslibs
.union(t
.indirect_syslibs
)
534 t
.final_libs
= t
.direct_libs
.union(t
.indirect_libs
)
535 t
.final_objects
= t
.direct_objects
.union(t
.indirect_objects
)
538 # don't depend on ourselves
539 if t
.sname
in t
.final_libs
:
540 t
.final_libs
.remove(t
.sname
)
541 if t
.sname
in t
.final_objects
:
542 t
.final_objects
.remove(t
.sname
)
544 re_expand2(bld
, tgt_list
)
548 # find any library loops
550 if t
.samba_type
in ['LIBRARY', 'PYTHON']:
551 for l
in t
.final_libs
.copy():
552 t2
= bld
.name_to_obj(l
, bld
.env
)
553 if t
.sname
in t2
.final_libs
:
554 # we could break this in either direction. If one of the libraries
555 # has a version number, and will this be distributed publicly, then
556 # we should make it the lower level library in the DAG
557 debug('deps: removing library loop %s<->%s', t
.sname
, l
)
558 t2
.final_libs
.remove(t
.sname
)
559 loops
[t2
.sname
] = t
.sname
;
561 re_expand2(bld
, tgt_list
)
563 for type in ['BINARY']:
567 if t
.samba_type
!= type: continue
568 # if we will indirectly link to a target then we don't need it
569 new
= t
.final_objects
.copy()
570 for l
in t
.final_libs
:
571 t2
= bld
.name_to_obj(l
, bld
.env
)
572 dup
= new
.intersection(t2
.expanded_targets2
)
574 debug('deps: removing dups from %s: %s also in %s %s',
575 t
.sname
, dup
, t2
.samba_type
, l
)
576 new
= new
.difference(dup
)
579 t
.final_objects
= new
584 # we now need to make corrections for any library loops we broke up
585 # any target that depended on the target of the loop and doesn't
586 # depend on the source of the loop needs to get the loop source added
587 for type in ['BINARY','PYTHON']:
589 if t
.samba_type
!= type: continue
591 if loop
in t
.final_libs
and loops
[loop
] not in t
.final_libs
:
592 t
.final_libs
.add(loops
[loop
])
594 debug('deps: removed duplicate dependencies')
597 ######################################################################
598 # this provides a way to save our dependency calculations between runs
600 savedeps_inputs
= ['samba_deps', 'samba_includes', 'local_include', 'local_include_first', 'samba_cflags']
601 savedeps_outputs
= ['uselib', 'uselib_local', 'add_objects', 'includes', 'ccflags']
602 savedeps_outenv
= ['INC_PATHS']
603 savedeps_caches
= ['GLOBAL_DEPENDENCIES', 'TARGET_ALIAS', 'TARGET_TYPE', 'INIT_FUNCTIONS']
605 def save_samba_deps(bld
, tgt_list
):
606 '''save the dependency calculations between builds, to make
607 further builds faster'''
608 denv
= Environment
.Environment()
610 denv
.version
= savedeps_version
611 denv
.savedeps_inputs
= savedeps_inputs
612 denv
.savedeps_outputs
= savedeps_outputs
618 for c
in savedeps_caches
:
619 denv
.caches
[c
] = LOCAL_CACHE(bld
, c
)
622 # save all the input attributes for each target
624 for attr
in savedeps_inputs
:
625 v
= getattr(t
, attr
, None)
629 denv
.input[t
.sname
] = tdeps
631 # save all the output attributes for each target
633 for attr
in savedeps_outputs
:
634 v
= getattr(t
, attr
, None)
638 denv
.output
[t
.sname
] = tdeps
641 for attr
in savedeps_outenv
:
643 tdeps
[attr
] = t
.env
[attr
]
645 denv
.outenv
[t
.sname
] = tdeps
647 depsfile
= os
.path
.join(bld
.bdir
, "sambadeps")
651 def load_samba_deps(bld
, tgt_list
):
652 '''load a previous set of build dependencies if possible'''
653 depsfile
= os
.path
.join(bld
.bdir
, "sambadeps")
654 denv
= Environment
.Environment()
656 debug('deps: checking saved dependencies')
658 if (denv
.version
!= savedeps_version
or
659 denv
.savedeps_inputs
!= savedeps_inputs
or
660 denv
.savedeps_outputs
!= savedeps_outputs
):
665 # check if caches are the same
666 for c
in savedeps_caches
:
667 if c
not in denv
.caches
or denv
.caches
[c
] != LOCAL_CACHE(bld
, c
):
670 # check inputs are the same
673 for attr
in savedeps_inputs
:
674 v
= getattr(t
, attr
, None)
677 if t
.sname
in denv
.input:
678 olddeps
= denv
.input[t
.sname
]
682 #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
685 # put outputs in place
687 if not t
.sname
in denv
.output
: continue
688 tdeps
= denv
.output
[t
.sname
]
690 setattr(t
, a
, tdeps
[a
])
692 # put output env vars in place
694 if not t
.sname
in denv
.outenv
: continue
695 tdeps
= denv
.outenv
[t
.sname
]
699 debug('deps: loaded saved dependencies')
703 def check_project_rules(bld
):
704 '''check the project rules - ensuring the targets are sane'''
706 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
708 # build a list of task generators we are interested in
712 if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']:
714 t
= bld
.name_to_obj(tgt
, bld
.env
)
716 print "Target %s of type %s has no task generator" % (tgt
, type)
720 add_samba_attributes(bld
, tgt_list
)
722 if load_samba_deps(bld
, tgt_list
):
725 debug('deps: project rules checking started')
727 expand_subsystem_deps(bld
)
728 build_direct_deps(bld
, tgt_list
)
729 build_indirect_deps(bld
, tgt_list
)
730 calculate_final_deps(bld
, tgt_list
)
732 # run the various attribute generators
733 for f
in [ build_dependencies
, build_includes
, add_init_functions
]:
734 debug('deps: project rules checking %s', f
)
735 for t
in tgt_list
: f(t
)
737 debug('deps: project rules stage1 completed')
739 #check_orpaned_targets(bld, tgt_list)
740 #check_duplicate_sources(bld, tgt_list)
741 show_final_deps(bld
, tgt_list
)
743 debug('deps: project rules checking completed - %u targets checked',
746 save_samba_deps(bld
, tgt_list
)
749 def CHECK_PROJECT_RULES(bld
):
750 '''enable checking of project targets for sanity'''
751 if bld
.env
.added_project_rules
:
753 bld
.env
.added_project_rules
= True
754 bld
.add_pre_fun(check_project_rules
)
755 Build
.BuildContext
.CHECK_PROJECT_RULES
= CHECK_PROJECT_RULES