1 # a waf tool to add autoconf-like macros to the configure section
2 # and for SAMBA_ macros for building libraries, binaries etc
4 import Build, os, Options, Task, Utils, cc, TaskGen, fnmatch, re, shutil, Logs, Constants
5 from Configure import conf
7 from samba_utils import SUBST_VARS_RECURSIVE
8 TaskGen.task_gen.apply_verif = Utils.nada
10 # bring in the other samba modules
11 from samba_optimisation import *
12 from samba_utils import *
13 from samba_version import *
14 from samba_autoconf import *
15 from samba_patterns import *
16 from samba_pidl import *
17 from samba_autoproto import *
18 from samba_python import *
19 from samba_deps import *
20 from samba_bundled import *
22 import samba_conftests
34 # some systems have broken threading in python
35 if os.environ.get('WAF_NOTHREADS') == '1':
40 os.putenv('PYTHONUNBUFFERED', '1')
43 if Constants.HEXVERSION < 0x105019:
45 Please use the version of waf that comes with Samba, not
46 a system installed version. See http://wiki.samba.org/index.php/Waf
49 Alternatively, please use ./autogen-waf.sh, and then
50 run ./configure and make as usual. That will call the right version of waf.
56 def SAMBA_BUILD_ENV(conf):
57 '''create the samba build environment'''
58 conf.env.BUILD_DIRECTORY = conf.blddir
59 mkdir_p(os.path.join(conf.blddir, LIB_PATH))
60 mkdir_p(os.path.join(conf.blddir, LIB_PATH, "private"))
61 mkdir_p(os.path.join(conf.blddir, "modules"))
62 mkdir_p(os.path.join(conf.blddir, 'python/samba/dcerpc'))
63 # this allows all of the bin/shared and bin/python targets
64 # to be expressed in terms of build directory paths
65 mkdir_p(os.path.join(conf.blddir, 'default'))
66 for p in ['python','shared', 'modules']:
67 link_target = os.path.join(conf.blddir, 'default/' + p)
68 if not os.path.lexists(link_target):
69 os.symlink('../' + p, link_target)
71 # get perl to put the blib files in the build directory
72 blib_bld = os.path.join(conf.blddir, 'default/pidl/blib')
73 blib_src = os.path.join(conf.srcdir, 'pidl/blib')
74 mkdir_p(blib_bld + '/man1')
75 mkdir_p(blib_bld + '/man3')
76 if os.path.islink(blib_src):
78 elif os.path.exists(blib_src):
79 shutil.rmtree(blib_src)
82 def ADD_INIT_FUNCTION(bld, subsystem, target, init_function):
83 '''add an init_function to the list for a subsystem'''
84 if init_function is None:
86 bld.ASSERT(subsystem is not None, "You must specify a subsystem for init_function '%s'" % init_function)
87 cache = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
88 if not subsystem in cache:
90 cache[subsystem].append( { 'TARGET':target, 'INIT_FUNCTION':init_function } )
91 Build.BuildContext.ADD_INIT_FUNCTION = ADD_INIT_FUNCTION
95 #################################################################
96 def SAMBA_LIBRARY(bld, libname, source,
107 external_library=False,
118 target_type='LIBRARY',
119 bundled_extension=True,
125 private_library=False,
126 grouping_library=False,
128 '''define a Samba library'''
131 SET_TARGET_TYPE(bld, libname, 'DISABLED')
134 source = bld.EXPAND_VARIABLES(source, vars=vars)
136 # remember empty libraries, so we can strip the dependencies
137 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
138 SET_TARGET_TYPE(bld, libname, 'EMPTY')
141 if BUILTIN_LIBRARY(bld, libname):
144 obj_target = libname + '.objlist'
146 if group == 'libraries':
147 subsystem_group = 'main'
149 subsystem_group = group
151 # first create a target for building the object files for this library
152 # by separating in this way, we avoid recompiling the C files
153 # separately for the install library and the build library
154 bld.SAMBA_SUBSYSTEM(obj_target,
157 public_deps = public_deps,
159 public_headers = public_headers,
160 header_path = header_path,
162 group = subsystem_group,
163 autoproto = autoproto,
164 depends_on = depends_on,
165 hide_symbols = hide_symbols,
166 pyext = pyext or (target_type == "PYTHON"),
167 local_include = local_include)
169 if BUILTIN_LIBRARY(bld, libname):
172 if not SET_TARGET_TYPE(bld, libname, target_type):
175 # the library itself will depend on that object target
176 deps += ' ' + public_deps
178 deps.append(obj_target)
180 realname = bld.map_shlib_extension(realname, python=(target_type=='PYTHON'))
181 link_name = bld.map_shlib_extension(link_name, python=(target_type=='PYTHON'))
183 # we don't want any public libraries without version numbers
184 if not private_library and vnum is None and soname is None and target_type != 'PYTHON' and not realname:
185 raise Utils.WafError("public library '%s' must have a vnum" % libname)
187 if target_type == 'PYTHON' or realname or not private_library:
188 bundled_name = libname.replace('_', '-')
190 bundled_name = PRIVATE_NAME(bld, libname, bundled_extension, private_library)
192 ldflags = TO_LIST(ldflags)
194 features = 'cc cshlib symlink_lib install_lib'
195 if target_type == 'PYTHON':
198 # this is quite strange. we should add pyext feature for pyext
199 # but that breaks the build. This may be a bug in the waf python tool
200 features += ' pyembed'
203 features += ' abi_check'
205 if bld.env.HAVE_LD_VERSION_SCRIPT:
206 vscript = "%s.vscript" % libname
208 version = "%s_%s" % (Utils.g_module.APPNAME, Utils.g_module.VERSION)
210 version = "%s_%s" % (libname, vnum)
214 bld.ABI_VSCRIPT(libname, abi_directory, version, vscript)
215 ldflags.append("-Wl,--version-script=%s/%s" % (bld.path.abspath(bld.env), vscript))
216 fullname = bld.env.shlib_PATTERN % bundled_name
217 bld.add_manual_dependency(bld.path.find_or_declare(fullname), bld.path.find_or_declare(vscript))
218 if Options.is_install:
219 # also make the .inst file depend on the vscript
220 instname = bld.env.shlib_PATTERN % (bundled_name + '.inst')
221 bld.add_manual_dependency(bld.path.find_or_declare(instname), bld.path.find_or_declare(vscript))
223 bld.SET_BUILD_GROUP(group)
227 target = bundled_name,
228 depends_on = depends_on,
231 samba_includes = includes,
232 local_include = local_include,
236 samba_inst_path = install_path,
238 samba_realname = realname,
239 samba_install = install,
240 abi_directory = "%s/%s" % (bld.path.abspath(), abi_directory),
241 abi_match = abi_match,
242 private_library = private_library,
243 grouping_library=grouping_library
246 if realname and not link_name:
247 link_name = 'shared/%s' % realname
250 t.link_name = link_name
252 if pc_files is not None:
253 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
255 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
256 bld.MANPAGES(manpages)
259 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
262 #################################################################
263 def SAMBA_BINARY(bld, binname, source,
273 use_global_deps=True,
284 '''define a Samba binary'''
287 SET_TARGET_TYPE(bld, binname, 'DISABLED')
290 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
293 features = 'cc cprogram symlink_bin install_bin'
295 features += ' pyembed'
297 obj_target = binname + '.objlist'
299 source = bld.EXPAND_VARIABLES(source, vars=vars)
300 source = unique_list(TO_LIST(source))
302 if group == 'binaries':
303 subsystem_group = 'main'
305 subsystem_group = group
307 # first create a target for building the object files for this binary
308 # by separating in this way, we avoid recompiling the C files
309 # separately for the install binary and the build binary
310 bld.SAMBA_SUBSYSTEM(obj_target,
315 group = subsystem_group,
316 autoproto = autoproto,
317 subsystem_name = subsystem_name,
318 local_include = local_include,
319 use_hostcc = use_hostcc,
321 use_global_deps= use_global_deps)
323 bld.SET_BUILD_GROUP(group)
325 # the binary itself will depend on that object target
327 deps.append(obj_target)
334 samba_includes = includes,
335 local_include = local_include,
336 samba_modules = modules,
338 samba_subsystem= subsystem_name,
340 samba_inst_path= install_path,
341 samba_install = install
344 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
345 bld.MANPAGES(manpages)
347 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
350 #################################################################
351 def SAMBA_MODULE(bld, modname, source,
356 module_init_name='samba_init_module',
358 autoproto_extra_source='',
360 internal_module=True,
366 '''define a Samba module.'''
368 source = bld.EXPAND_VARIABLES(source, vars=vars)
370 if internal_module or BUILTIN_LIBRARY(bld, modname):
371 bld.SAMBA_SUBSYSTEM(modname, source,
375 autoproto_extra_source=autoproto_extra_source,
377 local_include=local_include,
380 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
384 SET_TARGET_TYPE(bld, modname, 'DISABLED')
387 obj_target = modname + '.objlist'
390 if subsystem is not None:
391 deps += ' ' + subsystem
392 while realname.startswith("lib"+subsystem+"_"):
393 realname = realname[len("lib"+subsystem+"_"):]
394 while realname.startswith(subsystem+"_"):
395 realname = realname[len(subsystem+"_"):]
397 realname = bld.make_libname(realname)
398 while realname.startswith("lib"):
399 realname = realname[len("lib"):]
401 build_link_name = "modules/%s/%s" % (subsystem, realname)
404 cflags += " -D%s=%s" % (init_function, module_init_name)
406 bld.SAMBA_LIBRARY(modname,
411 autoproto = autoproto,
412 local_include=local_include,
414 link_name=build_link_name,
415 install_path="${MODULESDIR}/%s" % subsystem,
420 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
423 #################################################################
424 def SAMBA_SUBSYSTEM(bld, modname, source,
433 init_function_sentinal=None,
435 autoproto_extra_source='',
438 local_include_first=True,
442 use_global_deps=True,
446 '''define a Samba subsystem'''
449 SET_TARGET_TYPE(bld, modname, 'DISABLED')
452 # remember empty subsystems, so we can strip the dependencies
453 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
454 SET_TARGET_TYPE(bld, modname, 'EMPTY')
457 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
460 source = bld.EXPAND_VARIABLES(source, vars=vars)
461 source = unique_list(TO_LIST(source))
463 deps += ' ' + public_deps
465 bld.SET_BUILD_GROUP(group)
475 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
476 depends_on = depends_on,
477 samba_deps = TO_LIST(deps),
478 samba_includes = includes,
479 local_include = local_include,
480 local_include_first = local_include_first,
481 samba_subsystem= subsystem_name,
482 samba_use_hostcc = use_hostcc,
483 samba_use_global_deps = use_global_deps
486 if cflags_end is not None:
487 t.samba_cflags.extend(TO_LIST(cflags_end))
489 if autoproto is not None:
490 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
491 if public_headers is not None:
492 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
496 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
499 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
500 group='generators', enabled=True,
505 '''A generic source generator target'''
507 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
513 bld.SET_BUILD_GROUP(group)
516 source=bld.EXPAND_VARIABLES(source, vars=vars),
518 shell=isinstance(rule, str),
522 samba_type='GENERATOR',
529 if public_headers is not None:
530 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
532 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
537 def SETUP_BUILD_GROUPS(bld):
538 '''setup build groups used to ensure that the different build
539 phases happen consecutively'''
540 bld.p_ln = bld.srcnode # we do want to see all targets!
541 bld.env['USING_BUILD_GROUPS'] = True
542 bld.add_group('setup')
543 bld.add_group('build_compiler_source')
544 bld.add_group('vscripts')
545 bld.add_group('base_libraries')
546 bld.add_group('generators')
547 bld.add_group('compiler_prototypes')
548 bld.add_group('compiler_libraries')
549 bld.add_group('build_compilers')
550 bld.add_group('build_source')
551 bld.add_group('prototypes')
552 bld.add_group('main')
553 bld.add_group('symbolcheck')
554 bld.add_group('libraries')
555 bld.add_group('binaries')
556 bld.add_group('syslibcheck')
557 bld.add_group('final')
558 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
561 def SET_BUILD_GROUP(bld, group):
562 '''set the current build group'''
563 if not 'USING_BUILD_GROUPS' in bld.env:
566 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
571 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
572 """use timestamps instead of file contents for deps
573 this currently doesn't work"""
574 def h_file(filename):
576 st = os.stat(filename)
577 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
579 m.update(str(st.st_mtime))
580 m.update(str(st.st_size))
583 Utils.h_file = h_file
587 t = Task.simple_task_type('copy_script', 'rm -f "${LINK_TARGET}" && ln -s "${SRC[0].abspath(env)}" ${LINK_TARGET}',
588 shell=True, color='PINK', ext_in='.bin')
591 @feature('copy_script')
592 @before('apply_link')
593 def copy_script(self):
594 tsk = self.create_task('copy_script', self.allnodes[0])
595 tsk.env.TARGET = self.target
597 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
598 '''used to copy scripts from the source tree into the build directory
599 for use by selftest'''
601 source = bld.path.ant_glob(pattern)
603 bld.SET_BUILD_GROUP('build_source')
604 for s in TO_LIST(source):
606 if installname != None:
608 target = os.path.join(installdir, iname)
609 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
611 t = bld(features='copy_script',
616 t.env.LINK_TARGET = target
618 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
621 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
622 python_fixup=False, destname=None, base_name=None):
624 destdir = bld.EXPAND_VARIABLES(destdir)
628 destname = os.path.basename(destname)
629 dest = os.path.join(destdir, destname)
631 # fixup the python path it will use to find Samba modules
632 inst_file = file + '.inst'
633 if bld.env["PYTHONDIR"] not in sys.path:
634 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
636 # Eliminate updating sys.path if the target python dir is already
638 regex = "s|sys.path.insert.*bin/python.*$||g"
639 bld.SAMBA_GENERATOR('python_%s' % destname,
640 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
645 file = os.path.join(base_name, file)
646 bld.install_as(dest, file, chmod=chmod)
649 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
650 python_fixup=False, destname=None, base_name=None):
651 '''install a set of files'''
652 for f in TO_LIST(files):
653 install_file(bld, destdir, f, chmod=chmod, flat=flat,
654 python_fixup=python_fixup, destname=destname,
656 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
659 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
660 python_fixup=False, exclude=None, trim_path=None):
661 '''install a set of files matching a wildcard pattern'''
662 files=TO_LIST(bld.path.ant_glob(pattern))
666 files2.append(os_path_relpath(f, trim_path))
671 if fnmatch.fnmatch(f, exclude):
673 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
674 python_fixup=python_fixup, base_name=trim_path)
675 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
678 def INSTALL_DIRS(bld, destdir, dirs):
679 '''install a set of directories'''
680 destdir = bld.EXPAND_VARIABLES(destdir)
681 dirs = bld.EXPAND_VARIABLES(dirs)
682 for d in TO_LIST(dirs):
683 bld.install_dir(os.path.join(destdir, d))
684 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
687 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
688 class header_task(Task.Task):
690 The public headers (the one installed on the system) have both
691 different paths and contents, so the rename is not enough.
693 Intermediate .inst.h files are created because path manipulation
694 may be slow. The substitution is thus performed only once.
699 vars = ['INCLUDEDIR', 'HEADER_DEPS']
702 txt = self.inputs[0].read(self.env)
704 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
705 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
707 # use a regexp to substitute the #include lines in the files
708 map = self.generator.bld.hnodemap
709 dirnodes = self.generator.bld.hnodedirs
714 # pokemon headers: gotta catch'em all!
716 if s.startswith('bin/default'):
717 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
719 Logs.warn('could not find the public header for %r' % s)
723 Logs.warn('could not find the public header replacement for build header %r' % s)
725 # this part is more difficult since the path may be relative to anything
726 for dirnode in dirnodes:
727 node = dirnode.find_resource(s)
733 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
735 Logs.warn('-> could not find the public header for %r' % s)
737 return "#include <%s>" % fin
740 txt = re_header.sub(repl, txt)
742 # and write the output file
745 f = open(self.outputs[0].abspath(self.env), 'w')
751 @TaskGen.feature('pubh')
752 def make_public_headers(self):
754 collect the public headers to process and to install, then
755 create the substitutions (name and contents)
758 if not self.bld.is_install:
759 # install time only (lazy)
763 # hnodedirs: list of folders for searching the headers
764 # hnodemap: node ids and replacement string (node objects are unique)
766 self.bld.hnodedirs.append(self.path)
767 except AttributeError:
768 self.bld.hnodemap = {}
769 self.bld.hnodedirs = [self.bld.srcnode, self.path]
771 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
772 node = self.bld.srcnode.find_dir(k)
774 self.bld.hnodedirs.append(node)
776 header_path = getattr(self, 'header_path', None) or ''
778 for x in self.to_list(self.headers):
780 # too complicated, but what was the original idea?
781 if isinstance(header_path, list):
783 for (p1, dir) in header_path:
784 lst = self.to_list(p1)
786 if fnmatch.fnmatch(x, p2):
794 inst_path = header_path
798 if x.find(':') != -1:
803 inn = self.path.find_resource(name)
806 raise ValueError("could not find the public header %r in %r" % (name, self.path))
807 out = inn.change_ext('.inst.h')
808 self.create_task('header', inn, out)
814 inst_path = inst_path + '/'
815 inst_path = inst_path + dest
817 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
819 self.bld.hnodemap[inn.id] = inst_path
821 # create a hash (not md5) to make sure the headers are re-created if something changes
823 lst = list(self.bld.hnodemap.keys())
826 val = hash((val, k, self.bld.hnodemap[k]))
827 self.bld.env.HEADER_DEPS = val
829 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
830 '''install some headers
832 header_path may either be a string that is added to the INCLUDEDIR,
833 or it can be a dictionary of wildcard patterns which map to destination
834 directories relative to INCLUDEDIR
836 bld.SET_BUILD_GROUP('final')
837 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
839 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
842 def MANPAGES(bld, manpages):
843 '''build and install manual pages'''
844 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
845 for m in manpages.split():
847 bld.SAMBA_GENERATOR(m,
851 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
853 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
854 Build.BuildContext.MANPAGES = MANPAGES
857 #############################################################
858 # give a nicer display when building different types of files
859 def progress_display(self, msg, fname):
860 col1 = Logs.colors(self.color)
861 col2 = Logs.colors.NORMAL
862 total = self.position[1]
864 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
865 return fs % (self.position[0], self.position[1], col1, fname, col2)
867 def link_display(self):
868 if Options.options.progress_bar != 0:
869 return Task.Task.old_display(self)
870 fname = self.outputs[0].bldpath(self.env)
871 return progress_display(self, 'Linking', fname)
872 Task.TaskBase.classes['cc_link'].display = link_display
874 def samba_display(self):
875 if Options.options.progress_bar != 0:
876 return Task.Task.old_display(self)
878 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
879 if self.name in targets:
880 target_type = targets[self.name]
881 type_map = { 'GENERATOR' : 'Generating',
882 'PROTOTYPE' : 'Generating'
884 if target_type in type_map:
885 return progress_display(self, type_map[target_type], self.name)
887 if len(self.inputs) == 0:
888 return Task.Task.old_display(self)
890 fname = self.inputs[0].bldpath(self.env)
891 if fname[0:3] == '../':
893 ext_loc = fname.rfind('.')
895 return Task.Task.old_display(self)
896 ext = fname[ext_loc:]
898 ext_map = { '.idl' : 'Compiling IDL',
899 '.et' : 'Compiling ERRTABLE',
900 '.asn1': 'Compiling ASN1',
903 return progress_display(self, ext_map[ext], fname)
904 return Task.Task.old_display(self)
906 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
907 Task.TaskBase.classes['Task'].display = samba_display
912 def apply_bundle_remove_dynamiclib_patch(self):
913 if self.env['MACBUNDLE'] or getattr(self,'mac_bundle',False):
914 if not getattr(self,'vnum',None):
916 self.env['LINKFLAGS'].remove('-dynamiclib')
917 self.env['LINKFLAGS'].remove('-single_module')