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
31 # some systems have broken threading in python
32 if os.environ.get('WAF_NOTHREADS') == '1':
37 os.putenv('PYTHONUNBUFFERED', '1')
40 if Constants.HEXVERSION < 0x105019:
42 Please use the version of waf that comes with Samba, not
43 a system installed version. See http://wiki.samba.org/index.php/Waf
46 Alternatively, please use ./autogen-waf.sh, and then
47 run ./configure and make as usual. That will call the right version of waf.
53 def SAMBA_BUILD_ENV(conf):
54 '''create the samba build environment'''
55 conf.env.BUILD_DIRECTORY = conf.blddir
56 mkdir_p(os.path.join(conf.blddir, LIB_PATH))
57 mkdir_p(os.path.join(conf.blddir, "modules"))
58 mkdir_p(os.path.join(conf.blddir, 'python/samba/dcerpc'))
59 # this allows all of the bin/shared and bin/python targets
60 # to be expressed in terms of build directory paths
61 mkdir_p(os.path.join(conf.blddir, 'default'))
62 for p in ['python','shared', 'modules']:
63 link_target = os.path.join(conf.blddir, 'default/' + p)
64 if not os.path.lexists(link_target):
65 os.symlink('../' + p, link_target)
67 # get perl to put the blib files in the build directory
68 blib_bld = os.path.join(conf.blddir, 'default/pidl/blib')
69 blib_src = os.path.join(conf.srcdir, 'pidl/blib')
70 mkdir_p(blib_bld + '/man1')
71 mkdir_p(blib_bld + '/man3')
72 if os.path.islink(blib_src):
74 elif os.path.exists(blib_src):
75 shutil.rmtree(blib_src)
78 def ADD_INIT_FUNCTION(bld, subsystem, target, init_function):
79 '''add an init_function to the list for a subsystem'''
80 if init_function is None:
82 bld.ASSERT(subsystem is not None, "You must specify a subsystem for init_function '%s'" % init_function)
83 cache = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
84 if not subsystem in cache:
86 cache[subsystem].append( { 'TARGET':target, 'INIT_FUNCTION':init_function } )
87 Build.BuildContext.ADD_INIT_FUNCTION = ADD_INIT_FUNCTION
91 #################################################################
92 def SAMBA_LIBRARY(bld, libname, source,
101 external_library=False,
111 target_type='LIBRARY',
112 bundled_extension=True,
119 private_library=False,
121 '''define a Samba library'''
124 SET_TARGET_TYPE(bld, libname, 'DISABLED')
127 source = bld.EXPAND_VARIABLES(source, vars=vars)
129 # remember empty libraries, so we can strip the dependencies
130 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
131 SET_TARGET_TYPE(bld, libname, 'EMPTY')
134 if BUILTIN_LIBRARY(bld, libname):
137 obj_target = libname + '.objlist'
139 # first create a target for building the object files for this library
140 # by separating in this way, we avoid recompiling the C files
141 # separately for the install library and the build library
142 bld.SAMBA_SUBSYSTEM(obj_target,
145 public_deps = public_deps,
147 public_headers = public_headers,
148 header_path = header_path,
151 autoproto = autoproto,
152 depends_on = depends_on,
153 hide_symbols = hide_symbols,
154 pyext = (target_type == "PYTHON"),
155 local_include = local_include)
157 if BUILTIN_LIBRARY(bld, libname):
160 if not SET_TARGET_TYPE(bld, libname, target_type):
163 # the library itself will depend on that object target
164 deps += ' ' + public_deps
166 deps.append(obj_target)
168 realname = bld.map_shlib_extension(realname, python=(target_type=='PYTHON'))
169 link_name = bld.map_shlib_extension(link_name, python=(target_type=='PYTHON'))
172 # private libraries always get the 'bundling' treatment with respect
173 # to the library name suffix
176 # we don't want any public libraries without version numbers
177 if not private_library and vnum is None and target_type != 'PYTHON' and not realname:
178 raise Utils.WafError("public library '%s' must have a vnum" % libname)
180 if target_type == 'PYTHON' or realname or not is_bundled:
181 # Sanitize the library name
182 bundled_name = libname.lower().replace('_', '-')
183 while bundled_name.startswith("lib"):
184 bundled_name = bundled_name[3:]
186 bundled_name = BUNDLED_NAME(bld, libname, bundled_extension, private_library)
188 features = 'cc cshlib symlink_lib install_lib'
189 if target_type == 'PYTHON':
192 features += ' pyembed'
194 features += ' abi_check'
197 abi_file = os.path.join(bld.curdir, abi_file)
199 bld.SET_BUILD_GROUP(group)
203 target = bundled_name,
204 samba_cflags = CURRENT_LDFLAGS(bld, libname, cflags),
205 depends_on = depends_on,
207 samba_includes = includes,
208 local_include = local_include,
211 samba_inst_path = install_path,
213 samba_realname = realname,
214 samba_install = install,
216 abi_match = abi_match,
217 is_bundled = is_bundled
220 if realname and not link_name:
221 link_name = 'shared/%s' % realname
224 t.link_name = link_name
226 if pc_files is not None:
227 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
229 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
230 bld.MANPAGES(manpages)
233 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
236 #################################################################
237 def SAMBA_BINARY(bld, binname, source,
247 use_global_deps=True,
258 '''define a Samba binary'''
261 SET_TARGET_TYPE(bld, binname, 'DISABLED')
264 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
267 features = 'cc cprogram symlink_bin install_bin'
269 features += ' pyembed'
271 obj_target = binname + '.objlist'
273 source = bld.EXPAND_VARIABLES(source, vars=vars)
274 source = unique_list(TO_LIST(source))
276 # first create a target for building the object files for this binary
277 # by separating in this way, we avoid recompiling the C files
278 # separately for the install binary and the build binary
279 bld.SAMBA_SUBSYSTEM(obj_target,
285 autoproto = autoproto,
286 subsystem_name = subsystem_name,
287 local_include = local_include,
288 use_hostcc = use_hostcc,
290 use_global_deps= use_global_deps)
292 bld.SET_BUILD_GROUP(group)
294 # the binary itself will depend on that object target
296 deps.append(obj_target)
302 samba_cflags = CURRENT_LDFLAGS(bld, binname, cflags),
304 samba_includes = includes,
305 local_include = local_include,
306 samba_modules = modules,
308 samba_subsystem= subsystem_name,
310 samba_inst_path= install_path,
311 samba_install = install
314 # setup the subsystem_name as an alias for the real
315 # binary name, so it can be found when expanding
316 # subsystem dependencies
317 if subsystem_name is not None:
318 bld.TARGET_ALIAS(subsystem_name, binname)
320 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
321 bld.MANPAGES(manpages)
323 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
326 #################################################################
327 def SAMBA_MODULE(bld, modname, source,
333 autoproto_extra_source='',
336 internal_module=True,
342 '''define a Samba module.'''
344 source = bld.EXPAND_VARIABLES(source, vars=vars)
346 if internal_module or BUILTIN_LIBRARY(bld, modname):
347 # treat internal modules as subsystems for now
348 if subsystem is not None:
349 deps += ' ' + subsystem
351 bld.SAMBA_SUBSYSTEM(modname, source,
355 autoproto_extra_source=autoproto_extra_source,
357 local_include=local_include,
360 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
364 SET_TARGET_TYPE(bld, modname, 'DISABLED')
367 modnames = [modname] + TO_LIST(aliases)
368 for modname in modnames:
369 obj_target = modname + '.objlist'
372 if subsystem is not None:
373 deps += ' ' + subsystem
374 while realname.startswith("lib"+subsystem+"_"):
375 realname = realname[len("lib"+subsystem+"_"):]
376 while realname.startswith(subsystem+"_"):
377 realname = realname[len(subsystem+"_"):]
379 realname = bld.make_libname(realname)
380 while realname.startswith("lib"):
381 realname = realname[len("lib"):]
383 build_link_name = "modules/%s/%s" % (subsystem, realname)
386 cflags += " -D%s=samba_init_module" % init_function
388 bld.SAMBA_LIBRARY(modname,
393 autoproto = autoproto,
394 local_include=local_include,
396 link_name=build_link_name,
397 install_path="${MODULESDIR}/%s" % subsystem,
401 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
404 #################################################################
405 def SAMBA_SUBSYSTEM(bld, modname, source,
414 init_function_sentinal=None,
416 autoproto_extra_source='',
419 local_include_first=True,
423 use_global_deps=True,
427 '''define a Samba subsystem'''
430 SET_TARGET_TYPE(bld, modname, 'DISABLED')
433 # remember empty subsystems, so we can strip the dependencies
434 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
435 SET_TARGET_TYPE(bld, modname, 'EMPTY')
438 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
441 source = bld.EXPAND_VARIABLES(source, vars=vars)
442 source = unique_list(TO_LIST(source))
444 deps += ' ' + public_deps
446 bld.SET_BUILD_GROUP(group)
456 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
457 depends_on = depends_on,
458 samba_deps = TO_LIST(deps),
459 samba_includes = includes,
460 local_include = local_include,
461 local_include_first = local_include_first,
462 samba_subsystem= subsystem_name,
463 samba_use_hostcc = use_hostcc,
464 samba_use_global_deps = use_global_deps
467 if cflags_end is not None:
468 t.samba_cflags.extend(TO_LIST(cflags_end))
470 if autoproto is not None:
471 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
472 if public_headers is not None:
473 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
477 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
480 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
481 group='generators', enabled=True,
486 '''A generic source generator target'''
488 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
494 bld.SET_BUILD_GROUP(group)
497 source=bld.EXPAND_VARIABLES(source, vars=vars),
499 shell=isinstance(rule, str),
508 if public_headers is not None:
509 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
511 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
516 def SETUP_BUILD_GROUPS(bld):
517 '''setup build groups used to ensure that the different build
518 phases happen consecutively'''
519 bld.p_ln = bld.srcnode # we do want to see all targets!
520 bld.env['USING_BUILD_GROUPS'] = True
521 bld.add_group('setup')
522 bld.add_group('build_compiler_source')
523 bld.add_group('base_libraries')
524 bld.add_group('generators')
525 bld.add_group('compiler_prototypes')
526 bld.add_group('compiler_libraries')
527 bld.add_group('build_compilers')
528 bld.add_group('build_source')
529 bld.add_group('prototypes')
530 bld.add_group('main')
531 bld.add_group('binaries')
532 bld.add_group('final')
533 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
536 def SET_BUILD_GROUP(bld, group):
537 '''set the current build group'''
538 if not 'USING_BUILD_GROUPS' in bld.env:
541 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
546 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
547 """use timestamps instead of file contents for deps
548 this currently doesn't work"""
549 def h_file(filename):
551 st = os.stat(filename)
552 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
554 m.update(str(st.st_mtime))
555 m.update(str(st.st_size))
558 Utils.h_file = h_file
562 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
563 shell=True, color='PINK', ext_in='.bin')
566 @feature('copy_script')
567 @before('apply_link')
568 def copy_script(self):
569 tsk = self.create_task('copy_script', self.allnodes[0])
570 tsk.env.TARGET = self.target
572 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
573 '''used to copy scripts from the source tree into the build directory
574 for use by selftest'''
576 source = bld.path.ant_glob(pattern)
578 bld.SET_BUILD_GROUP('build_source')
579 for s in TO_LIST(source):
581 if installname != None:
583 target = os.path.join(installdir, iname)
584 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
586 t = bld(features='copy_script',
591 t.env.LINK_TARGET = target
593 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
596 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
597 python_fixup=False, destname=None, base_name=None):
599 destdir = bld.EXPAND_VARIABLES(destdir)
603 destname = os.path.basename(destname)
604 dest = os.path.join(destdir, destname)
606 # fixup the python path it will use to find Samba modules
607 inst_file = file + '.inst'
608 if bld.env["PYTHONDIR"] not in sys.path:
609 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
611 # Eliminate updating sys.path if the target python dir is already
613 regex = "s|sys.path.insert.*bin/python.*$||g"
614 bld.SAMBA_GENERATOR('python_%s' % destname,
615 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
620 file = os.path.join(base_name, file)
621 bld.install_as(dest, file, chmod=chmod)
624 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
625 python_fixup=False, destname=None, base_name=None):
626 '''install a set of files'''
627 for f in TO_LIST(files):
628 install_file(bld, destdir, f, chmod=chmod, flat=flat,
629 python_fixup=python_fixup, destname=destname,
631 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
634 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
635 python_fixup=False, exclude=None, trim_path=None):
636 '''install a set of files matching a wildcard pattern'''
637 files=TO_LIST(bld.path.ant_glob(pattern))
641 files2.append(os_path_relpath(f, trim_path))
646 if fnmatch.fnmatch(f, exclude):
648 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
649 python_fixup=python_fixup, base_name=trim_path)
650 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
653 def INSTALL_DIRS(bld, destdir, dirs):
654 '''install a set of directories'''
655 destdir = bld.EXPAND_VARIABLES(destdir)
656 dirs = bld.EXPAND_VARIABLES(dirs)
657 for d in TO_LIST(dirs):
658 bld.install_dir(os.path.join(destdir, d))
659 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
662 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
663 class header_task(Task.Task):
665 The public headers (the one installed on the system) have both
666 different paths and contents, so the rename is not enough.
668 Intermediate .inst.h files are created because path manipulation
669 may be slow. The substitution is thus performed only once.
674 vars = ['INCLUDEDIR', 'HEADER_DEPS']
677 txt = self.inputs[0].read(self.env)
679 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
680 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
682 # use a regexp to substitute the #include lines in the files
683 map = self.generator.bld.hnodemap
684 dirnodes = self.generator.bld.hnodedirs
689 # pokemon headers: gotta catch'em all!
691 if s.startswith('bin/default'):
692 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
694 Logs.warn('could not find the public header for %r' % s)
698 Logs.warn('could not find the public header replacement for build header %r' % s)
700 # this part is more difficult since the path may be relative to anything
701 for dirnode in dirnodes:
702 node = dirnode.find_resource(s)
708 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
710 Logs.warn('-> could not find the public header for %r' % s)
712 return "#include <%s>" % fin
715 txt = re_header.sub(repl, txt)
717 # and write the output file
720 f = open(self.outputs[0].abspath(self.env), 'w')
726 @TaskGen.feature('pubh')
727 def make_public_headers(self):
729 collect the public headers to process and to install, then
730 create the substitutions (name and contents)
733 if not self.bld.is_install:
734 # install time only (lazy)
738 # hnodedirs: list of folders for searching the headers
739 # hnodemap: node ids and replacement string (node objects are unique)
741 self.bld.hnodedirs.append(self.path)
742 except AttributeError:
743 self.bld.hnodemap = {}
744 self.bld.hnodedirs = [self.bld.srcnode, self.path]
746 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
747 node = self.bld.srcnode.find_dir(k)
749 self.bld.hnodedirs.append(node)
751 header_path = getattr(self, 'header_path', None) or ''
753 for x in self.to_list(self.headers):
755 # too complicated, but what was the original idea?
756 if isinstance(header_path, list):
758 for (p1, dir) in header_path:
759 lst = self.to_list(p1)
761 if fnmatch.fnmatch(x, p2):
769 inst_path = header_path
773 if x.find(':') != -1:
778 inn = self.path.find_resource(name)
781 raise ValueError("could not find the public header %r in %r" % (name, self.path))
782 out = inn.change_ext('.inst.h')
783 self.create_task('header', inn, out)
789 inst_path = inst_path + '/'
790 inst_path = inst_path + dest
792 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
794 self.bld.hnodemap[inn.id] = inst_path
796 # create a hash (not md5) to make sure the headers are re-created if something changes
798 lst = list(self.bld.hnodemap.keys())
801 val = hash((val, k, self.bld.hnodemap[k]))
802 self.bld.env.HEADER_DEPS = val
804 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
805 '''install some headers
807 header_path may either be a string that is added to the INCLUDEDIR,
808 or it can be a dictionary of wildcard patterns which map to destination
809 directories relative to INCLUDEDIR
811 bld.SET_BUILD_GROUP('final')
812 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
814 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
817 def subst_at_vars(task):
818 '''substiture @VAR@ style variables in a file'''
819 src = task.inputs[0].srcpath(task.env)
820 tgt = task.outputs[0].bldpath(task.env)
826 a = re.split('(@\w+@)', s)
829 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
831 if re.match('@\w+@', v):
833 if not vname in task.env and vname.upper() in task.env:
834 vname = vname.upper()
835 if not vname in task.env:
836 Logs.error("Unknown substitution %s in %s" % (v, task.name))
838 v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
839 # now we back substitute the allowed pc vars
840 for (b, m) in back_sub:
843 if not b in done_var:
844 # we don't want to substitute the first usage
850 contents = ''.join(out)
852 s = f.write(contents)
858 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
859 '''install some pkg_config pc files'''
860 dest = '${PKGCONFIGDIR}'
861 dest = bld.EXPAND_VARIABLES(dest)
862 for f in TO_LIST(pc_files):
863 base=os.path.basename(f)
864 t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
869 t.env.PACKAGE_VERSION = vnum
870 INSTALL_FILES(bld, dest, f, flat=True, destname=base)
871 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
874 def MANPAGES(bld, manpages):
875 '''build and install manual pages'''
876 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
877 for m in manpages.split():
879 bld.SAMBA_GENERATOR(m,
883 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
885 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
886 Build.BuildContext.MANPAGES = MANPAGES
889 #############################################################
890 # give a nicer display when building different types of files
891 def progress_display(self, msg, fname):
892 col1 = Logs.colors(self.color)
893 col2 = Logs.colors.NORMAL
894 total = self.position[1]
896 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
897 return fs % (self.position[0], self.position[1], col1, fname, col2)
899 def link_display(self):
900 if Options.options.progress_bar != 0:
901 return Task.Task.old_display(self)
902 fname = self.outputs[0].bldpath(self.env)
903 return progress_display(self, 'Linking', fname)
904 Task.TaskBase.classes['cc_link'].display = link_display
906 def samba_display(self):
907 if Options.options.progress_bar != 0:
908 return Task.Task.old_display(self)
910 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
911 if self.name in targets:
912 target_type = targets[self.name]
913 type_map = { 'GENERATOR' : 'Generating',
914 'PROTOTYPE' : 'Generating'
916 if target_type in type_map:
917 return progress_display(self, type_map[target_type], self.name)
919 fname = self.inputs[0].bldpath(self.env)
920 if fname[0:3] == '../':
922 ext_loc = fname.rfind('.')
924 return Task.Task.old_display(self)
925 ext = fname[ext_loc:]
927 ext_map = { '.idl' : 'Compiling IDL',
928 '.et' : 'Compiling ERRTABLE',
929 '.asn1': 'Compiling ASN1',
932 return progress_display(self, ext_map[ext], fname)
933 return Task.Task.old_display(self)
935 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
936 Task.TaskBase.classes['Task'].display = samba_display