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
30 # some systems have broken threading in python
31 if os.environ.get('WAF_NOTHREADS') == '1':
36 os.putenv('PYTHONUNBUFFERED', '1')
39 if Constants.HEXVERSION < 0x105019:
41 Please use the version of waf that comes with Samba, not
42 a system installed version. See http://wiki.samba.org/index.php/Waf
45 Alternatively, please use ./autogen-waf.sh, and then
46 run ./configure and make as usual. That will call the right version of waf.
52 def SAMBA_BUILD_ENV(conf):
53 '''create the samba build environment'''
54 conf.env.BUILD_DIRECTORY = conf.blddir
55 mkdir_p(os.path.join(conf.blddir, LIB_PATH))
56 mkdir_p(os.path.join(conf.blddir, "modules"))
57 mkdir_p(os.path.join(conf.blddir, 'python/samba/dcerpc'))
58 # this allows all of the bin/shared and bin/python targets
59 # to be expressed in terms of build directory paths
60 mkdir_p(os.path.join(conf.blddir, 'default'))
61 for p in ['python','shared', 'modules']:
62 link_target = os.path.join(conf.blddir, 'default/' + p)
63 if not os.path.lexists(link_target):
64 os.symlink('../' + p, link_target)
66 # get perl to put the blib files in the build directory
67 blib_bld = os.path.join(conf.blddir, 'default/pidl/blib')
68 blib_src = os.path.join(conf.srcdir, 'pidl/blib')
69 mkdir_p(blib_bld + '/man1')
70 mkdir_p(blib_bld + '/man3')
71 if os.path.islink(blib_src):
73 elif os.path.exists(blib_src):
74 shutil.rmtree(blib_src)
77 def ADD_INIT_FUNCTION(bld, subsystem, target, init_function):
78 '''add an init_function to the list for a subsystem'''
79 if init_function is None:
81 bld.ASSERT(subsystem is not None, "You must specify a subsystem for init_function '%s'" % init_function)
82 cache = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
83 if not subsystem in cache:
85 cache[subsystem].append( { 'TARGET':target, 'INIT_FUNCTION':init_function } )
86 Build.BuildContext.ADD_INIT_FUNCTION = ADD_INIT_FUNCTION
90 #################################################################
91 def SAMBA_LIBRARY(bld, libname, source,
100 external_library=False,
110 target_type='LIBRARY',
111 bundled_extension=True,
119 '''define a Samba library'''
122 SET_TARGET_TYPE(bld, libname, 'DISABLED')
125 source = bld.EXPAND_VARIABLES(source, vars=vars)
127 # remember empty libraries, so we can strip the dependencies
128 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
129 SET_TARGET_TYPE(bld, libname, 'EMPTY')
132 if BUILTIN_LIBRARY(bld, libname):
135 obj_target = libname + '.objlist'
137 # first create a target for building the object files for this library
138 # by separating in this way, we avoid recompiling the C files
139 # separately for the install library and the build library
140 bld.SAMBA_SUBSYSTEM(obj_target,
143 public_deps = public_deps,
145 public_headers = public_headers,
146 header_path = header_path,
149 autoproto = autoproto,
150 depends_on = depends_on,
152 hide_symbols = hide_symbols,
153 local_include = local_include)
155 if BUILTIN_LIBRARY(bld, libname):
158 if not SET_TARGET_TYPE(bld, libname, target_type):
161 # the library itself will depend on that object target
162 deps += ' ' + public_deps
164 deps.append(obj_target)
166 if target_type == 'PYTHON' or realname or not is_bundled:
167 # Sanitize the library name
168 bundled_name = libname.lower().replace('_', '-')
169 while bundled_name.startswith("lib"):
170 bundled_name = bundled_name[3:]
172 bundled_name = BUNDLED_NAME(bld, libname, bundled_extension)
174 features = 'cc cshlib symlink_lib install_lib'
175 if target_type == 'PYTHON':
178 features += ' pyembed'
180 features += ' abi_check'
183 abi_file = os.path.join(bld.curdir, abi_file)
185 bld.SET_BUILD_GROUP(group)
189 target = bundled_name,
190 samba_cflags = CURRENT_CFLAGS(bld, libname, cflags),
191 depends_on = depends_on,
193 samba_includes = includes,
194 local_include = local_include,
197 samba_inst_path = install_path,
199 samba_realname = realname,
200 samba_install = install,
202 abi_match = abi_match,
203 is_bundled = is_bundled
206 if realname and not link_name:
207 link_name = 'shared/%s' % realname
210 t.link_name = link_name
212 if pc_files is not None:
213 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
215 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
216 bld.MANPAGES(manpages)
219 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
222 #################################################################
223 def SAMBA_BINARY(bld, binname, source,
233 use_global_deps=True,
244 '''define a Samba binary'''
247 SET_TARGET_TYPE(bld, binname, 'DISABLED')
250 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
253 features = 'cc cprogram symlink_bin install_bin'
255 features += ' pyembed'
257 obj_target = binname + '.objlist'
259 source = bld.EXPAND_VARIABLES(source, vars=vars)
260 source = unique_list(TO_LIST(source))
262 # first create a target for building the object files for this binary
263 # by separating in this way, we avoid recompiling the C files
264 # separately for the install binary and the build binary
265 bld.SAMBA_SUBSYSTEM(obj_target,
271 autoproto = autoproto,
272 subsystem_name = subsystem_name,
274 local_include = local_include,
275 use_hostcc = use_hostcc,
276 use_global_deps= use_global_deps)
278 bld.SET_BUILD_GROUP(group)
280 # the binary itself will depend on that object target
282 deps.append(obj_target)
288 samba_cflags = CURRENT_CFLAGS(bld, binname, cflags),
290 samba_includes = includes,
291 local_include = local_include,
292 samba_modules = modules,
294 samba_subsystem= subsystem_name,
296 samba_inst_path= install_path,
297 samba_install = install
300 # setup the subsystem_name as an alias for the real
301 # binary name, so it can be found when expanding
302 # subsystem dependencies
303 if subsystem_name is not None:
304 bld.TARGET_ALIAS(subsystem_name, binname)
306 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
307 bld.MANPAGES(manpages)
309 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
312 #################################################################
313 def SAMBA_MODULE(bld, modname, source,
319 autoproto_extra_source='',
322 internal_module=True,
326 '''define a Samba module.'''
328 source = bld.EXPAND_VARIABLES(source, vars=vars)
330 if internal_module or BUILTIN_LIBRARY(bld, modname):
331 # treat internal modules as subsystems for now
332 if subsystem is not None:
333 deps += ' ' + subsystem
335 bld.SAMBA_SUBSYSTEM(modname, source,
339 autoproto_extra_source=autoproto_extra_source,
341 local_include=local_include,
344 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
348 SET_TARGET_TYPE(bld, modname, 'DISABLED')
351 modnames = [modname] + TO_LIST(aliases)
352 for modname in modnames:
353 obj_target = modname + '.objlist'
356 if subsystem is not None:
357 deps += ' ' + subsystem
358 while realname.startswith("lib"+subsystem+"_"):
359 realname = realname[len("lib"+subsystem+"_"):]
360 while realname.startswith(subsystem+"_"):
361 realname = realname[len(subsystem+"_"):]
363 realname = bld.env.shlib_PATTERN % realname
364 while realname.startswith("lib"):
365 realname = realname[len("lib"):]
367 build_link_name = "modules/%s/%s" % (subsystem, realname)
370 cflags += " -D%s=samba_init_module" % init_function
372 bld.SAMBA_LIBRARY(modname,
377 autoproto = autoproto,
378 local_include=local_include,
380 link_name=build_link_name,
381 install_path="${MODULESDIR}/%s" % subsystem
384 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
387 #################################################################
388 def SAMBA_SUBSYSTEM(bld, modname, source,
397 init_function_sentinal=None,
399 autoproto_extra_source='',
402 local_include_first=True,
406 use_global_deps=True,
410 '''define a Samba subsystem'''
413 SET_TARGET_TYPE(bld, modname, 'DISABLED')
416 # remember empty subsystems, so we can strip the dependencies
417 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
418 SET_TARGET_TYPE(bld, modname, 'EMPTY')
421 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
424 source = bld.EXPAND_VARIABLES(source, vars=vars)
425 source = unique_list(TO_LIST(source))
427 deps += ' ' + public_deps
429 bld.SET_BUILD_GROUP(group)
433 features += ' pyembed'
439 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
440 depends_on = depends_on,
441 samba_deps = TO_LIST(deps),
442 samba_includes = includes,
443 local_include = local_include,
444 local_include_first = local_include_first,
445 samba_subsystem= subsystem_name,
446 samba_use_hostcc = use_hostcc,
447 samba_use_global_deps = use_global_deps
450 if cflags_end is not None:
451 t.samba_cflags.extend(TO_LIST(cflags_end))
453 if autoproto is not None:
454 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
455 if public_headers is not None:
456 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
460 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
463 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
464 group='generators', enabled=True,
469 '''A generic source generator target'''
471 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
477 bld.SET_BUILD_GROUP(group)
480 source=bld.EXPAND_VARIABLES(source, vars=vars),
482 shell=isinstance(rule, str),
491 if public_headers is not None:
492 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
494 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
499 def SETUP_BUILD_GROUPS(bld):
500 '''setup build groups used to ensure that the different build
501 phases happen consecutively'''
502 bld.p_ln = bld.srcnode # we do want to see all targets!
503 bld.env['USING_BUILD_GROUPS'] = True
504 bld.add_group('setup')
505 bld.add_group('build_compiler_source')
506 bld.add_group('base_libraries')
507 bld.add_group('generators')
508 bld.add_group('compiler_prototypes')
509 bld.add_group('compiler_libraries')
510 bld.add_group('build_compilers')
511 bld.add_group('build_source')
512 bld.add_group('prototypes')
513 bld.add_group('main')
514 bld.add_group('binaries')
515 bld.add_group('final')
516 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
519 def SET_BUILD_GROUP(bld, group):
520 '''set the current build group'''
521 if not 'USING_BUILD_GROUPS' in bld.env:
524 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
529 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
530 """use timestamps instead of file contents for deps
531 this currently doesn't work"""
532 def h_file(filename):
534 st = os.stat(filename)
535 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
537 m.update(str(st.st_mtime))
538 m.update(str(st.st_size))
541 Utils.h_file = h_file
545 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
546 shell=True, color='PINK', ext_in='.bin')
549 @feature('copy_script')
550 @before('apply_link')
551 def copy_script(self):
552 tsk = self.create_task('copy_script', self.allnodes[0])
553 tsk.env.TARGET = self.target
555 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
556 '''used to copy scripts from the source tree into the build directory
557 for use by selftest'''
559 source = bld.path.ant_glob(pattern)
561 bld.SET_BUILD_GROUP('build_source')
562 for s in TO_LIST(source):
564 if installname != None:
566 target = os.path.join(installdir, iname)
567 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
569 t = bld(features='copy_script',
574 t.env.LINK_TARGET = target
576 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
579 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
580 python_fixup=False, destname=None, base_name=None):
582 destdir = bld.EXPAND_VARIABLES(destdir)
586 destname = os.path.basename(destname)
587 dest = os.path.join(destdir, destname)
589 # fixup the python path it will use to find Samba modules
590 inst_file = file + '.inst'
591 if bld.env["PYTHONDIR"] not in sys.path:
592 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
594 # Eliminate updating sys.path if the target python dir is already
596 regex = "s|sys.path.insert.*bin/python.*$||g"
597 bld.SAMBA_GENERATOR('python_%s' % destname,
598 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
603 file = os.path.join(base_name, file)
604 bld.install_as(dest, file, chmod=chmod)
607 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
608 python_fixup=False, destname=None, base_name=None):
609 '''install a set of files'''
610 for f in TO_LIST(files):
611 install_file(bld, destdir, f, chmod=chmod, flat=flat,
612 python_fixup=python_fixup, destname=destname,
614 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
617 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
618 python_fixup=False, exclude=None, trim_path=None):
619 '''install a set of files matching a wildcard pattern'''
620 files=TO_LIST(bld.path.ant_glob(pattern))
624 files2.append(os_path_relpath(f, trim_path))
629 if fnmatch.fnmatch(f, exclude):
631 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
632 python_fixup=python_fixup, base_name=trim_path)
633 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
636 def INSTALL_DIRS(bld, destdir, dirs):
637 '''install a set of directories'''
638 destdir = bld.EXPAND_VARIABLES(destdir)
639 dirs = bld.EXPAND_VARIABLES(dirs)
640 for d in TO_LIST(dirs):
641 bld.install_dir(os.path.join(destdir, d))
642 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
645 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
646 class header_task(Task.Task):
648 The public headers (the one installed on the system) have both
649 different paths and contents, so the rename is not enough.
651 Intermediate .inst.h files are created because path manipulation
652 may be slow. The substitution is thus performed only once.
657 vars = ['INCLUDEDIR', 'HEADER_DEPS']
660 txt = self.inputs[0].read(self.env)
662 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
663 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
665 # use a regexp to substitute the #include lines in the files
666 map = self.generator.bld.hnodemap
667 dirnodes = self.generator.bld.hnodedirs
672 # pokemon headers: gotta catch'em all!
674 if s.startswith('bin/default'):
675 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
677 Logs.warn('could not find the public header for %r' % s)
681 Logs.warn('could not find the public header replacement for build header %r' % s)
683 # this part is more difficult since the path may be relative to anything
684 for dirnode in dirnodes:
685 node = dirnode.find_resource(s)
691 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
693 Logs.warn('-> could not find the public header for %r' % s)
695 return "#include <%s>" % fin
698 txt = re_header.sub(repl, txt)
700 # and write the output file
703 f = open(self.outputs[0].abspath(self.env), 'w')
709 @TaskGen.feature('pubh')
710 def make_public_headers(self):
712 collect the public headers to process and to install, then
713 create the substitutions (name and contents)
716 if not self.bld.is_install:
717 # install time only (lazy)
721 # hnodedirs: list of folders for searching the headers
722 # hnodemap: node ids and replacement string (node objects are unique)
724 self.bld.hnodedirs.append(self.path)
725 except AttributeError:
726 self.bld.hnodemap = {}
727 self.bld.hnodedirs = [self.bld.srcnode, self.path]
729 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
730 node = self.bld.srcnode.find_dir(k)
732 self.bld.hnodedirs.append(node)
734 header_path = getattr(self, 'header_path', None) or ''
736 for x in self.to_list(self.headers):
738 # too complicated, but what was the original idea?
739 if isinstance(header_path, list):
741 for (p1, dir) in header_path:
742 lst = self.to_list(p1)
744 if fnmatch.fnmatch(x, p2):
752 inst_path = header_path
756 if x.find(':') != -1:
761 inn = self.path.find_resource(name)
764 raise ValueError("could not find the public header %r in %r" % (name, self.path))
765 out = inn.change_ext('.inst.h')
766 self.create_task('header', inn, out)
772 inst_path = inst_path + '/'
773 inst_path = inst_path + dest
775 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
777 self.bld.hnodemap[inn.id] = inst_path
779 # create a hash (not md5) to make sure the headers are re-created if something changes
781 lst = list(self.bld.hnodemap.keys())
784 val = hash((val, k, self.bld.hnodemap[k]))
785 self.bld.env.HEADER_DEPS = val
787 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
788 '''install some headers
790 header_path may either be a string that is added to the INCLUDEDIR,
791 or it can be a dictionary of wildcard patterns which map to destination
792 directories relative to INCLUDEDIR
794 bld.SET_BUILD_GROUP('final')
795 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
797 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
800 def subst_at_vars(task):
801 '''substiture @VAR@ style variables in a file'''
802 src = task.inputs[0].srcpath(task.env)
803 tgt = task.outputs[0].bldpath(task.env)
809 a = re.split('(@\w+@)', s)
812 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
814 if re.match('@\w+@', v):
816 if not vname in task.env and vname.upper() in task.env:
817 vname = vname.upper()
818 if not vname in task.env:
819 Logs.error("Unknown substitution %s in %s" % (v, task.name))
821 v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
822 # now we back substitute the allowed pc vars
823 for (b, m) in back_sub:
826 if not b in done_var:
827 # we don't want to substitute the first usage
833 contents = ''.join(out)
835 s = f.write(contents)
841 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
842 '''install some pkg_config pc files'''
843 dest = '${PKGCONFIGDIR}'
844 dest = bld.EXPAND_VARIABLES(dest)
845 for f in TO_LIST(pc_files):
846 base=os.path.basename(f)
847 t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
852 t.env.PACKAGE_VERSION = vnum
853 INSTALL_FILES(bld, dest, f, flat=True, destname=base)
854 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
857 def MANPAGES(bld, manpages):
858 '''build and install manual pages'''
859 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
860 for m in manpages.split():
862 bld.SAMBA_GENERATOR(m,
866 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
868 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
869 Build.BuildContext.MANPAGES = MANPAGES
872 #############################################################
873 # give a nicer display when building different types of files
874 def progress_display(self, msg, fname):
875 col1 = Logs.colors(self.color)
876 col2 = Logs.colors.NORMAL
877 total = self.position[1]
879 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
880 return fs % (self.position[0], self.position[1], col1, fname, col2)
882 def link_display(self):
883 if Options.options.progress_bar != 0:
884 return Task.Task.old_display(self)
885 fname = self.outputs[0].bldpath(self.env)
886 return progress_display(self, 'Linking', fname)
887 Task.TaskBase.classes['cc_link'].display = link_display
889 def samba_display(self):
890 if Options.options.progress_bar != 0:
891 return Task.Task.old_display(self)
893 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
894 if self.name in targets:
895 target_type = targets[self.name]
896 type_map = { 'GENERATOR' : 'Generating',
897 'PROTOTYPE' : 'Generating'
899 if target_type in type_map:
900 return progress_display(self, type_map[target_type], self.name)
902 fname = self.inputs[0].bldpath(self.env)
903 if fname[0:3] == '../':
905 ext_loc = fname.rfind('.')
907 return Task.Task.old_display(self)
908 ext = fname[ext_loc:]
910 ext_map = { '.idl' : 'Compiling IDL',
911 '.et' : 'Compiling ERRTABLE',
912 '.asn1': 'Compiling ASN1',
915 return progress_display(self, ext_map[ext], fname)
916 return Task.Task.old_display(self)
918 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
919 Task.TaskBase.classes['Task'].display = samba_display