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_errtable import *
18 from samba_asn1 import *
19 from samba_autoproto import *
20 from samba_python import *
21 from samba_deps import *
22 from samba_bundled import *
24 import samba_conftests
32 # some systems have broken threading in python
33 if os.environ.get('WAF_NOTHREADS') == '1':
38 os.putenv('PYTHONUNBUFFERED', '1')
41 if Constants.HEXVERSION < 0x105016:
43 Please use the version of waf that comes with Samba, not
44 a system installed version. See http://wiki.samba.org/index.php/Waf
47 Alternatively, please use ./autogen-waf.sh, and then
48 run ./configure and make as usual. That will call the right version of waf.
54 def SAMBA_BUILD_ENV(conf):
55 '''create the samba build environment'''
56 conf.env.BUILD_DIRECTORY = conf.blddir
57 mkdir_p(os.path.join(conf.blddir, LIB_PATH))
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']:
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,
120 '''define a Samba library'''
123 SET_TARGET_TYPE(bld, libname, 'DISABLED')
126 source = bld.EXPAND_VARIABLES(source, vars=vars)
128 # remember empty libraries, so we can strip the dependencies
129 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
130 SET_TARGET_TYPE(bld, libname, 'EMPTY')
133 if BUILTIN_LIBRARY(bld, libname):
136 obj_target = libname + '.objlist'
138 # first create a target for building the object files for this library
139 # by separating in this way, we avoid recompiling the C files
140 # separately for the install library and the build library
141 bld.SAMBA_SUBSYSTEM(obj_target,
144 public_deps = public_deps,
146 public_headers = public_headers,
147 header_path = header_path,
150 autoproto = autoproto,
151 depends_on = depends_on,
152 needs_python = needs_python,
153 hide_symbols = hide_symbols,
154 local_include = local_include)
156 if BUILTIN_LIBRARY(bld, libname):
159 if not SET_TARGET_TYPE(bld, libname, target_type):
162 # the library itself will depend on that object target
163 deps += ' ' + public_deps
165 deps.append(obj_target)
167 if target_type == 'PYTHON' or realname or not is_bundled:
168 # Sanitize the library name
169 bundled_name = libname.lower().replace('_', '-')
170 while bundled_name.startswith("lib"):
171 bundled_name = bundled_name[3:]
173 bundled_name = BUNDLED_NAME(bld, libname, bundled_extension)
175 features = 'cc cshlib symlink_lib install_lib'
176 if target_type == 'PYTHON':
179 features += ' pyembed'
181 features += ' abi_check'
184 abi_file = os.path.join(bld.curdir, abi_file)
186 bld.SET_BUILD_GROUP(group)
190 target = bundled_name,
191 samba_cflags = CURRENT_CFLAGS(bld, libname, cflags),
192 depends_on = depends_on,
194 samba_includes = includes,
195 local_include = local_include,
198 samba_inst_path = install_path,
200 samba_realname = realname,
201 samba_install = install,
203 abi_match = abi_match,
204 is_bundled = is_bundled
207 if realname and not link_name:
208 link_name = 'shared/%s' % realname
211 t.link_name = link_name
213 if pc_files is not None:
214 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
216 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
217 bld.MANPAGES(manpages)
220 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
223 #################################################################
224 def SAMBA_BINARY(bld, binname, source,
234 use_global_deps=True,
245 '''define a Samba binary'''
248 SET_TARGET_TYPE(bld, binname, 'DISABLED')
251 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
254 features = 'cc cprogram symlink_bin install_bin'
256 features += ' pyembed'
258 obj_target = binname + '.objlist'
260 source = bld.EXPAND_VARIABLES(source, vars=vars)
261 source = unique_list(TO_LIST(source))
263 # first create a target for building the object files for this binary
264 # by separating in this way, we avoid recompiling the C files
265 # separately for the install binary and the build binary
266 bld.SAMBA_SUBSYSTEM(obj_target,
272 autoproto = autoproto,
273 subsystem_name = subsystem_name,
274 needs_python = needs_python,
275 local_include = local_include,
276 use_hostcc = use_hostcc,
277 use_global_deps= use_global_deps)
279 bld.SET_BUILD_GROUP(group)
281 # the binary itself will depend on that object target
283 deps.append(obj_target)
289 samba_cflags = CURRENT_CFLAGS(bld, binname, cflags),
291 samba_includes = includes,
292 local_include = local_include,
293 samba_modules = modules,
295 samba_subsystem= subsystem_name,
297 samba_inst_path= install_path,
298 samba_install = install
301 # setup the subsystem_name as an alias for the real
302 # binary name, so it can be found when expanding
303 # subsystem dependencies
304 if subsystem_name is not None:
305 bld.TARGET_ALIAS(subsystem_name, binname)
307 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
308 bld.MANPAGES(manpages)
310 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
313 #################################################################
314 def SAMBA_MODULE(bld, modname, source,
320 autoproto_extra_source='',
323 internal_module=True,
327 '''define a Samba module.'''
329 source = bld.EXPAND_VARIABLES(source, vars=vars)
331 if internal_module or BUILTIN_LIBRARY(bld, modname):
332 # treat internal modules as subsystems for now
333 if subsystem is not None:
334 deps += ' ' + subsystem
336 bld.SAMBA_SUBSYSTEM(modname, source,
340 autoproto_extra_source=autoproto_extra_source,
342 local_include=local_include,
345 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
349 SET_TARGET_TYPE(bld, modname, 'DISABLED')
352 obj_target = modname + '.objlist'
355 if subsystem is not None:
356 deps += ' ' + subsystem
357 while realname.startswith("lib"+subsystem+"_"):
358 realname = realname[len("lib"+subsystem+"_"):]
359 while realname.startswith(subsystem+"_"):
360 realname = realname[len(subsystem+"_"):]
362 realname = bld.env.shlib_PATTERN % realname
363 while realname.startswith("lib"):
364 realname = realname[len("lib"):]
366 bld.SAMBA_LIBRARY(modname,
371 autoproto = autoproto,
372 local_include=local_include,
374 install_path="${MODULESDIR}/%s" % subsystem
377 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
380 #################################################################
381 def SAMBA_SUBSYSTEM(bld, modname, source,
390 init_function_sentinal=None,
391 heimdal_autoproto=None,
392 heimdal_autoproto_options=None,
393 heimdal_autoproto_private=None,
395 autoproto_extra_source='',
398 local_include_first=True,
402 use_global_deps=True,
406 '''define a Samba subsystem'''
409 SET_TARGET_TYPE(bld, modname, 'DISABLED')
412 # remember empty subsystems, so we can strip the dependencies
413 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
414 SET_TARGET_TYPE(bld, modname, 'EMPTY')
417 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
420 source = bld.EXPAND_VARIABLES(source, vars=vars)
421 source = unique_list(TO_LIST(source))
423 deps += ' ' + public_deps
425 bld.SET_BUILD_GROUP(group)
435 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
436 depends_on = depends_on,
437 samba_deps = TO_LIST(deps),
438 samba_includes = includes,
439 local_include = local_include,
440 local_include_first = local_include_first,
441 samba_subsystem= subsystem_name,
442 samba_use_hostcc = use_hostcc,
443 samba_use_global_deps = use_global_deps
446 if cflags_end is not None:
447 t.samba_cflags.extend(TO_LIST(cflags_end))
449 if heimdal_autoproto is not None:
450 bld.HEIMDAL_AUTOPROTO(heimdal_autoproto, source, options=heimdal_autoproto_options)
451 if heimdal_autoproto_private is not None:
452 bld.HEIMDAL_AUTOPROTO_PRIVATE(heimdal_autoproto_private, source)
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=0644, 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 bld.SAMBA_GENERATOR('python_%s' % destname,
592 rule="sed 's|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g' < ${SRC} > ${TGT}",
597 file = os.path.join(base_name, file)
598 bld.install_as(dest, file, chmod=chmod)
601 def INSTALL_FILES(bld, destdir, files, chmod=0644, flat=False,
602 python_fixup=False, destname=None, base_name=None):
603 '''install a set of files'''
604 for f in TO_LIST(files):
605 install_file(bld, destdir, f, chmod=chmod, flat=flat,
606 python_fixup=python_fixup, destname=destname,
608 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
611 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=0644, flat=False,
612 python_fixup=False, exclude=None, trim_path=None):
613 '''install a set of files matching a wildcard pattern'''
614 files=TO_LIST(bld.path.ant_glob(pattern))
618 files2.append(os_path_relpath(f, trim_path))
623 if fnmatch.fnmatch(f, exclude):
625 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
626 python_fixup=python_fixup, base_name=trim_path)
627 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
630 def INSTALL_DIRS(bld, destdir, dirs):
631 '''install a set of directories'''
632 destdir = bld.EXPAND_VARIABLES(destdir)
633 dirs = bld.EXPAND_VARIABLES(dirs)
634 for d in TO_LIST(dirs):
635 bld.install_dir(os.path.join(destdir, d))
636 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
639 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
640 class header_task(Task.Task):
642 The public headers (the one installed on the system) have both
643 different paths and contents, so the rename is not enough.
645 Intermediate .inst.h files are created because path manipulation
646 may be slow. The substitution is thus performed only once.
651 vars = ['INCLUDEDIR', 'HEADER_DEPS']
654 txt = self.inputs[0].read(self.env)
656 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
657 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
659 # use a regexp to substitute the #include lines in the files
660 map = self.generator.bld.hnodemap
661 dirnodes = self.generator.bld.hnodedirs
666 # pokemon headers: gotta catch'em all!
668 if s.startswith('bin/default'):
669 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
671 Logs.warn('could not find the public header for %r' % s)
675 Logs.warn('could not find the public header replacement for build header %r' % s)
677 # this part is more difficult since the path may be relative to anything
678 for dirnode in dirnodes:
679 node = dirnode.find_resource(s)
685 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
687 Logs.warn('-> could not find the public header for %r' % s)
689 return "#include <%s>" % fin
692 txt = re_header.sub(repl, txt)
694 # and write the output file
697 f = open(self.outputs[0].abspath(self.env), 'w')
703 @TaskGen.feature('pubh')
704 def make_public_headers(self):
706 collect the public headers to process and to install, then
707 create the substitutions (name and contents)
710 if not self.bld.is_install:
711 # install time only (lazy)
715 # hnodedirs: list of folders for searching the headers
716 # hnodemap: node ids and replacement string (node objects are unique)
718 self.bld.hnodedirs.append(self.path)
719 except AttributeError:
720 self.bld.hnodemap = {}
721 self.bld.hnodedirs = [self.bld.srcnode, self.path]
723 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
724 node = self.bld.srcnode.find_dir(k)
726 self.bld.hnodedirs.append(node)
728 header_path = getattr(self, 'header_path', None) or ''
730 for x in self.to_list(self.headers):
732 # too complicated, but what was the original idea?
733 if isinstance(header_path, list):
735 for (p1, dir) in header_path:
736 lst = self.to_list(p1)
738 if fnmatch.fnmatch(x, p2):
746 inst_path = header_path
750 if x.find(':') != -1:
755 inn = self.path.find_resource(name)
758 raise ValueError("could not find the public header %r in %r" % (name, self.path))
759 out = inn.change_ext('.inst.h')
760 self.create_task('header', inn, out)
766 inst_path = inst_path + '/'
767 inst_path = inst_path + dest
769 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
771 self.bld.hnodemap[inn.id] = inst_path
773 # create a hash (not md5) to make sure the headers are re-created if something changes
775 lst = list(self.bld.hnodemap.keys())
778 val = hash((val, k, self.bld.hnodemap[k]))
779 self.bld.env.HEADER_DEPS = val
781 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
782 '''install some headers
784 header_path may either be a string that is added to the INCLUDEDIR,
785 or it can be a dictionary of wildcard patterns which map to destination
786 directories relative to INCLUDEDIR
788 bld.SET_BUILD_GROUP('final')
789 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
791 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
794 def subst_at_vars(task):
795 '''substiture @VAR@ style variables in a file'''
796 src = task.inputs[0].srcpath(task.env)
797 tgt = task.outputs[0].bldpath(task.env)
803 a = re.split('(@\w+@)', s)
806 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
808 if re.match('@\w+@', v):
810 if not vname in task.env and vname.upper() in task.env:
811 vname = vname.upper()
812 if not vname in task.env:
813 Logs.error("Unknown substitution %s in %s" % (v, task.name))
815 v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
816 # now we back substitute the allowed pc vars
817 for (b, m) in back_sub:
820 if not b in done_var:
821 # we don't want to substitute the first usage
827 contents = ''.join(out)
829 s = f.write(contents)
835 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
836 '''install some pkg_config pc files'''
837 dest = '${PKGCONFIGDIR}'
838 dest = bld.EXPAND_VARIABLES(dest)
839 for f in TO_LIST(pc_files):
840 base=os.path.basename(f)
841 t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
846 t.env.PACKAGE_VERSION = vnum
847 INSTALL_FILES(bld, dest, f, flat=True, destname=base)
848 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
851 def MANPAGES(bld, manpages):
852 '''build and install manual pages'''
853 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
854 for m in manpages.split():
856 bld.SAMBA_GENERATOR(m,
860 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
862 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
863 Build.BuildContext.MANPAGES = MANPAGES
866 #############################################################
867 # give a nicer display when building different types of files
868 def progress_display(self, msg, fname):
869 col1 = Logs.colors(self.color)
870 col2 = Logs.colors.NORMAL
871 total = self.position[1]
873 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
874 return fs % (self.position[0], self.position[1], col1, fname, col2)
876 def link_display(self):
877 if Options.options.progress_bar != 0:
878 return Task.Task.old_display(self)
879 fname = self.outputs[0].bldpath(self.env)
880 return progress_display(self, 'Linking', fname)
881 Task.TaskBase.classes['cc_link'].display = link_display
883 def samba_display(self):
884 if Options.options.progress_bar != 0:
885 return Task.Task.old_display(self)
887 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
888 if self.name in targets:
889 target_type = targets[self.name]
890 type_map = { 'GENERATOR' : 'Generating',
891 'PROTOTYPE' : 'Generating'
893 if target_type in type_map:
894 return progress_display(self, type_map[target_type], self.name)
896 fname = self.inputs[0].bldpath(self.env)
897 if fname[0:3] == '../':
899 ext_loc = fname.rfind('.')
901 return Task.Task.old_display(self)
902 ext = fname[ext_loc:]
904 ext_map = { '.idl' : 'Compiling IDL',
905 '.et' : 'Compiling ERRTABLE',
906 '.asn1': 'Compiling ASN1',
909 return progress_display(self, ext_map[ext], fname)
910 return Task.Task.old_display(self)
912 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
913 Task.TaskBase.classes['Task'].display = samba_display