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']
604 savedeps_files
= ['buildtools/wafsamba/samba_deps.py']
606 def save_samba_deps(bld
, tgt_list
):
607 '''save the dependency calculations between builds, to make
608 further builds faster'''
609 denv
= Environment
.Environment()
611 denv
.version
= savedeps_version
612 denv
.savedeps_inputs
= savedeps_inputs
613 denv
.savedeps_outputs
= savedeps_outputs
620 for f
in savedeps_files
:
621 denv
.files
[f
] = os
.stat(os
.path
.join(bld
.srcnode
.abspath(), f
)).st_mtime
623 for c
in savedeps_caches
:
624 denv
.caches
[c
] = LOCAL_CACHE(bld
, c
)
627 # save all the input attributes for each target
629 for attr
in savedeps_inputs
:
630 v
= getattr(t
, attr
, None)
634 denv
.input[t
.sname
] = tdeps
636 # save all the output attributes for each target
638 for attr
in savedeps_outputs
:
639 v
= getattr(t
, attr
, None)
643 denv
.output
[t
.sname
] = tdeps
646 for attr
in savedeps_outenv
:
648 tdeps
[attr
] = t
.env
[attr
]
650 denv
.outenv
[t
.sname
] = tdeps
652 depsfile
= os
.path
.join(bld
.bdir
, "sambadeps")
656 def load_samba_deps(bld
, tgt_list
):
657 '''load a previous set of build dependencies if possible'''
658 depsfile
= os
.path
.join(bld
.bdir
, "sambadeps")
659 denv
= Environment
.Environment()
661 debug('deps: checking saved dependencies')
663 if (denv
.version
!= savedeps_version
or
664 denv
.savedeps_inputs
!= savedeps_inputs
or
665 denv
.savedeps_outputs
!= savedeps_outputs
):
670 # check if critical files have changed
671 for f
in savedeps_files
:
672 if f
not in denv
.files
:
674 if denv
.files
[f
] != os
.stat(os
.path
.join(bld
.srcnode
.abspath(), f
)).st_mtime
:
677 # check if caches are the same
678 for c
in savedeps_caches
:
679 if c
not in denv
.caches
or denv
.caches
[c
] != LOCAL_CACHE(bld
, c
):
682 # check inputs are the same
685 for attr
in savedeps_inputs
:
686 v
= getattr(t
, attr
, None)
689 if t
.sname
in denv
.input:
690 olddeps
= denv
.input[t
.sname
]
694 #print '%s: \ntdeps=%s \nodeps=%s' % (t.sname, tdeps, olddeps)
697 # put outputs in place
699 if not t
.sname
in denv
.output
: continue
700 tdeps
= denv
.output
[t
.sname
]
702 setattr(t
, a
, tdeps
[a
])
704 # put output env vars in place
706 if not t
.sname
in denv
.outenv
: continue
707 tdeps
= denv
.outenv
[t
.sname
]
711 debug('deps: loaded saved dependencies')
715 def check_project_rules(bld
):
716 '''check the project rules - ensuring the targets are sane'''
718 targets
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
720 # build a list of task generators we are interested in
724 if not type in ['SUBSYSTEM', 'MODULE', 'BINARY', 'LIBRARY', 'ASN1', 'PYTHON']:
726 t
= bld
.name_to_obj(tgt
, bld
.env
)
728 print "Target %s of type %s has no task generator" % (tgt
, type)
732 add_samba_attributes(bld
, tgt_list
)
734 if load_samba_deps(bld
, tgt_list
):
737 debug('deps: project rules checking started')
739 expand_subsystem_deps(bld
)
740 build_direct_deps(bld
, tgt_list
)
741 build_indirect_deps(bld
, tgt_list
)
742 calculate_final_deps(bld
, tgt_list
)
744 # run the various attribute generators
745 for f
in [ build_dependencies
, build_includes
, add_init_functions
]:
746 debug('deps: project rules checking %s', f
)
747 for t
in tgt_list
: f(t
)
749 debug('deps: project rules stage1 completed')
751 #check_orpaned_targets(bld, tgt_list)
752 #check_duplicate_sources(bld, tgt_list)
753 show_final_deps(bld
, tgt_list
)
755 debug('deps: project rules checking completed - %u targets checked',
758 save_samba_deps(bld
, tgt_list
)
761 def CHECK_PROJECT_RULES(bld
):
762 '''enable checking of project targets for sanity'''
763 if bld
.env
.added_project_rules
:
765 bld
.env
.added_project_rules
= True
766 bld
.add_pre_fun(check_project_rules
)
767 Build
.BuildContext
.CHECK_PROJECT_RULES
= CHECK_PROJECT_RULES