1 # Samba automatic dependency handling
3 from TaskGen
import taskgen
, before
4 import Build
, os
, string
, Utils
, re
5 from samba_utils
import *
6 from samba_autoconf
import *
9 def ADD_GLOBAL_DEPENDENCY(ctx
, dep
):
10 '''add a dependency for all binaries and libraries'''
11 if not 'GLOBAL_DEPENDENCIES' in ctx
.env
:
12 ctx
.env
.GLOBAL_DEPENDENCIES
= []
13 ctx
.env
.GLOBAL_DEPENDENCIES
.append(dep
)
16 def TARGET_ALIAS(bld
, target
, alias
):
17 '''define an alias for a target name'''
18 cache
= LOCAL_CACHE(bld
, 'TARGET_ALIAS')
19 bld
.ASSERT(alias
not in cache
, "Target alias %s already set" % alias
)
21 Build
.BuildContext
.TARGET_ALIAS
= TARGET_ALIAS
24 def EXPAND_ALIAS(bld
, target
):
25 '''expand a target name via an alias'''
26 aliases
= LOCAL_CACHE(bld
, 'TARGET_ALIAS')
28 return aliases
[target
]
30 Build
.BuildContext
.EXPAND_ALIAS
= EXPAND_ALIAS
33 def expand_dependencies(bld
, dep
, chain
, path
):
34 '''expand a dependency recursively
35 return a triple of (uselib, uselib_local, add_objects)
38 dep
= EXPAND_ALIAS(bld
, dep
)
40 t
= bld
.name_to_obj(dep
, bld
.env
)
42 # check for a cached list
44 expanded
= getattr(t
, 'expanded_dependencies', None)
45 if expanded
is not None:
48 target_dict
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
56 bld
.ASSERT(dep
in target_dict
, "Dependency %s not found in %s" % (dep
, path
))
57 type = target_dict
[dep
]
60 elif type == 'LIBRARY':
61 uselib_local
.append(dep
)
63 elif type == 'SUBSYSTEM':
64 add_objects
.append(dep
)
66 elif type == 'MODULE':
67 add_objects
.append(dep
)
69 elif type == 'PYTHON':
70 add_objects
.append(dep
)
73 add_objects
.append(dep
)
75 elif type == 'BINARY':
79 elif type == 'DISABLED':
80 debug('deps: Ignoring dependency on disabled target %s: %s' % (dep
, path
))
82 bld
.ASSERT(False, "Unknown target type %s for %s" % (type, dep
))
84 # for some types we have to build the list recursively
86 bld
.ASSERT(t
is not None, "Unable to find target %s" % dep
)
87 rec_deps
= getattr(t
, 'samba_deps', None)
88 bld
.ASSERT(rec_deps
is not None, "Unable to find dependencies of target %s" % dep
)
91 bld
.ASSERT(d2
not in chain
, "Circular dependency for %s: %s->%s" % (dep
, path
, d2
))
93 print "Removing dependency %s from target %s" % (d2
, dep
)
98 (rec_uselib
, rec_uselib_local
,
99 rec_add_objects
) = expand_dependencies(bld
, d2
, c2
, "%s->%s" % (path
, d2
))
100 uselib
.extend(rec_uselib
)
101 uselib_local
.extend(rec_uselib_local
)
102 add_objects
.extend(rec_add_objects
)
105 t
.expanded_dependencies
= (uselib
, uselib_local
, add_objects
)
107 return (uselib
, uselib_local
, add_objects
)
110 def expand_deplist(self
):
111 '''return an expanded list of dependencies from the samba_deps attribute'''
113 if not getattr(self
, 'samba_deps', None):
117 deps
= self
.samba_deps
124 (u
, ul
, ao
) = expand_dependencies(bld
, d
, { self
.name
:True }, self
.name
)
126 uselib_local
.extend(ul
)
127 add_objects
.extend(ao
)
129 return (uselib
, uselib_local
, add_objects
)
133 @feature('cc', 'cshlib', 'cprogram')
134 @before('apply_lib_vars', 'apply_verif', 'apply_objdeps', 'apply_obj_vars', 'apply_incpaths', 'build_includes')
136 def build_dependencies(self
):
137 '''This builds the dependency list for a target. It runs after all the targets are declared
139 The reason this is not just done in the SAMBA_*() rules is that we have no way of knowing
140 the full dependency list for a target until we have all of the targets declared. So what we do is
141 add a samba_deps attribute on the task generator when we declare it, then
142 this rule runs after all the task generators are declared and maps the samba_deps attribute
143 to a set of uselib, uselib_local and add_objects dependencies
146 if getattr(self
, 'build_dependencies_done', False):
148 self
.build_dependencies_done
= True
150 if getattr(self
, 'samba_deps', None) is None:
153 target_dict
= LOCAL_CACHE(self
.bld
, 'TARGET_TYPE')
155 # we only should add extra library and object deps on libraries and binaries
156 type = target_dict
[self
.name
]
157 if type != 'LIBRARY' and type != 'BINARY':
160 (uselib
, uselib_local
, add_objects
) = expand_deplist(self
)
162 if 'GLOBAL_DEPENDENCIES' in self
.bld
.env
:
163 add_objects
.extend(self
.bld
.env
.GLOBAL_DEPENDENCIES
)
165 self
.uselib
= unique_list(uselib
)
166 self
.uselib_local
= unique_list(uselib_local
)
167 self
.add_objects
= unique_list(add_objects
)
169 debug('deps: dependency counts for %s: uselib=%u uselib_local=%u add_objects=%u' % (
170 self
.name
, len(uselib
), len(uselib_local
), len(add_objects
)))
174 @feature('cc', 'cshlib', 'cprogram')
175 @before('apply_lib_vars', 'apply_verif', 'apply_objdeps', 'apply_obj_vars', 'apply_incpaths', 'add_init_functions')
176 @after('build_dependencies')
177 def build_includes(self
):
178 '''This builds the right set of includes for a target.
180 This is closely related to building the set of dependencies, and
181 calls into the same expand_dependencies() function to do the work.
183 One tricky part of this is that the includes= attribute for a
184 target needs to use paths which are relative to that targets
185 declaration directory (which we can get at via t.path).
187 The way this works is the includes list gets added as
188 samba_includes in the main build task declaration. Then this
189 function runs after all of the tasks are declared, and it
190 processes the samba_includes attribute to produce a includes=
194 if not getattr(self
, 'build_dependencies_done', False):
195 build_dependencies(self
)
196 if getattr(self
, 'build_includes_done', False):
198 self
.build_includes_done
= True
200 if getattr(self
, 'samba_includes', None) is None:
205 (uselib
, uselib_local
, add_objects
) = expand_deplist(self
)
207 # get the list of all dependencies
209 # all_deps.extend(uselib)
210 all_deps
.extend(uselib_local
)
211 all_deps
.extend(add_objects
)
212 all_deps
= unique_list(all_deps
)
216 # build a list of includes
217 if getattr(self
, 'local_include', True) == True and getattr(self
, 'local_include_first', True):
220 includes
.extend(TO_LIST(self
.samba_includes
))
222 if 'EXTRA_INCLUDES' in bld
.env
:
223 includes
.extend(bld
.env
['EXTRA_INCLUDES'])
227 mypath
= self
.path
.abspath(bld
.env
)
230 t
= bld
.name_to_obj(d
, bld
.env
)
231 bld
.ASSERT(t
is not None, "Unable to find dependency %s for %s" % (d
, self
.name
))
233 samba_includes
= getattr(t
, 'samba_includes', None)
234 inclist
= TO_LIST(samba_includes
)
235 if getattr(t
, 'local_include', True) == True:
239 tpath
= t
.path
.abspath(bld
.env
)
240 relpath
= os
.path
.relpath(tpath
, mypath
)
242 includes
.append(os
.path
.normpath(os
.path
.join(relpath
, inc
)))
244 if getattr(self
, 'local_include', True) == True and not getattr(self
, 'local_include_first', True):
247 self
.includes
= unique_list(includes
)
248 debug('deps: Target %s has includes=%s all_deps=%s' % (self
.name
, self
.includes
, all_deps
))
251 @feature('cc', 'cshlib', 'cprogram')
252 @before('apply_lib_vars', 'apply_verif', 'apply_objdeps', 'apply_obj_vars', 'apply_incpaths')
253 @after('build_includes')
254 def add_init_functions(self
):
255 '''This builds the right set of init functions'''
257 if not getattr(self
, 'build_includes_done', False):
259 if getattr(self
, 'add_init_functions_done', False):
261 self
.add_init_functions_done
= True
265 subsystems
= LOCAL_CACHE(bld
, 'INIT_FUNCTIONS')
268 if self
.name
in subsystems
:
269 modules
.append(self
.name
)
271 m
= getattr(self
, 'samba_modules', None)
273 modules
.extend(TO_LIST(m
))
275 m
= getattr(self
, 'samba_subsystem', None)
282 cflags
= getattr(self
, 'ccflags', [])
284 if not m
in subsystems
:
285 print "subsystems: %s" % subsystems
286 bld
.ASSERT(m
in subsystems
,
287 "No init_function defined for module '%s' in target '%s'" % (m
, self
.name
))
288 cflags
.append('-DSTATIC_%s_MODULES="%s"' % (m
, ','.join(subsystems
[m
])))
289 self
.ccflags
= cflags
292 def check_orpaned_targets(bld
):
293 '''check if any build targets are orphaned'''
295 target_dict
= LOCAL_CACHE(bld
, 'TARGET_TYPE')
297 # make sure all the earlier functions have run
298 for t
in bld
.all_task_gen
:
299 if not t
.name
in target_dict
:
301 if not getattr(t
, 'add_init_functions_done', False):
302 add_init_functions(t
)
304 for t
in bld
.all_task_gen
:
305 if not t
.name
in target_dict
:
307 if getattr(t
, 'samba_used', False) == True:
309 type = target_dict
[t
.name
]
310 if type != 'BINARY' and type != 'LIBRARY' and type != 'MODULE':
311 if re
.search('^PIDL_', t
.name
) is None:
312 print "Target %s of type %s is unused by any other target" % (t
.name
, type)
315 def CHECK_ORPANED_TARGETS(bld
):
316 bld
.add_pre_fun(check_orpaned_targets
)
317 Build
.BuildContext
.CHECK_ORPANED_TARGETS
= CHECK_ORPANED_TARGETS
321 def samba_post_process(self
):
322 '''samba specific post processing of task'''
323 if getattr(self
, 'meths', None) is None:
325 count
= getattr(self
, 'moved_to_end', 0)
327 # there has got to be a better way!!
328 self
.moved_to_end
= count
+ 1
329 self
.meths
.append('samba_post_process')
332 samba_post
= getattr(self
, 'samba_post', None)
333 if samba_post
is None:
335 (tgt
, cmd
) = samba_post
336 self
.env
.TARGET_DIRECTORY
= self
.path
.abspath(self
.env
)
337 #print "cmd=%s tgt=%s" % (cmd, tgt)
338 cmd
= Utils
.subst_vars(cmd
, self
.env
)
339 tgt
= Utils
.subst_vars(tgt
, self
.env
)
340 if os
.path
.isfile(tgt
):
341 debug('deps: post processing for %s: %s' % (self
.name
, cmd
))
343 self
.bld
.ASSERT(ret
== 0, "Post processing for %s failed (%d): %s" % (self
.name
, ret
, cmd
))
346 ##############################
347 # handle the creation of links for libraries and binaries
348 # note that we use a relative symlink path to allow the whole tree
349 # to me moved/copied elsewhere without breaking the links
350 t
= Task
.simple_task_type('symlink_lib', 'ln -sf ../${SRC} ${LINK_TARGET}', color
='PINK',
354 @feature('symlink_lib')
356 def symlink_lib(self
):
357 tsk
= self
.create_task('symlink_lib', self
.link_task
.outputs
[0])
359 # calculat the link target and put it in the environment
361 vnum
= getattr(self
, 'vnum', None)
363 soext
= '.' + vnum
.split('.')[0]
365 libname
= self
.target
366 tsk
.env
.LINK_TARGET
= '%s/lib%s.so%s' % (LIB_PATH
, libname
, soext
)
367 debug('task_gen: LINK_TARGET for %s is %s', self
.name
, tsk
.env
.LINK_TARGET
)
370 # for binaries we need to copy the executable to avoid the rpath changing
371 # in the local bin/ directory on install
372 t
= Task
.simple_task_type('copy_bin', 'rm -f ${BIN_TARGET} && cp ${SRC} ${BIN_TARGET}', color
='PINK',
373 ext_in
='.bin', shell
=True)
379 if Options
.is_install
:
380 # we don't want to copy the install binary, as
381 # that has the install rpath, not the build rpath
382 # The rpath of the binaries in bin/default/foo/blah is different
383 # during the install phase, as distros insist on not using rpath in installed binaries
385 tsk
= self
.create_task('copy_bin', self
.link_task
.outputs
[0])
387 tsk
.env
.BIN_TARGET
= self
.target
388 debug('task_gen: BIN_TARGET for %s is %s', self
.name
, tsk
.env
.BIN_TARGET
)