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,
112 target_type='LIBRARY',
113 bundled_extension=True,
119 private_library=False,
120 grouping_library=False,
122 '''define a Samba library'''
125 SET_TARGET_TYPE(bld, libname, 'DISABLED')
128 source = bld.EXPAND_VARIABLES(source, vars=vars)
130 # remember empty libraries, so we can strip the dependencies
131 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
132 SET_TARGET_TYPE(bld, libname, 'EMPTY')
135 if BUILTIN_LIBRARY(bld, libname):
138 obj_target = libname + '.objlist'
140 # first create a target for building the object files for this library
141 # by separating in this way, we avoid recompiling the C files
142 # separately for the install library and the build library
143 bld.SAMBA_SUBSYSTEM(obj_target,
146 public_deps = public_deps,
148 public_headers = public_headers,
149 header_path = header_path,
152 autoproto = autoproto,
153 depends_on = depends_on,
154 hide_symbols = hide_symbols,
155 pyext = pyext or (target_type == "PYTHON"),
156 local_include = local_include)
158 if BUILTIN_LIBRARY(bld, libname):
161 if not SET_TARGET_TYPE(bld, libname, target_type):
164 # the library itself will depend on that object target
165 deps += ' ' + public_deps
167 deps.append(obj_target)
169 realname = bld.map_shlib_extension(realname, python=(target_type=='PYTHON'))
170 link_name = bld.map_shlib_extension(link_name, python=(target_type=='PYTHON'))
172 # we don't want any public libraries without version numbers
173 if not private_library and vnum is None and target_type != 'PYTHON' and not realname:
174 raise Utils.WafError("public library '%s' must have a vnum" % libname)
176 if target_type == 'PYTHON' or realname or not private_library:
177 # Sanitize the library name
178 bundled_name = libname.lower().replace('_', '-')
180 bundled_name = PRIVATE_NAME(bld, libname, bundled_extension, private_library)
184 Logs.error("vnum is invalid for private libraries")
186 vnum = bld.env.PACKAGE_VERSION
188 features = 'cc cshlib symlink_lib install_lib'
189 if target_type == 'PYTHON':
192 # this is quite strange. we should add pyext feature for pyext
193 # but that breaks the build. This may be a bug in the waf python tool
194 features += ' pyembed'
196 features += ' abi_check'
199 abi_file = os.path.join(bld.curdir, abi_file)
201 bld.SET_BUILD_GROUP(group)
205 target = bundled_name,
206 depends_on = depends_on,
208 samba_includes = includes,
209 local_include = local_include,
212 samba_inst_path = install_path,
214 samba_realname = realname,
215 samba_install = install,
217 abi_match = abi_match,
218 private_library = private_library,
219 grouping_library=grouping_library
222 if realname and not link_name:
223 link_name = 'shared/%s' % realname
226 t.link_name = link_name
228 if pc_files is not None:
229 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
231 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
232 bld.MANPAGES(manpages)
235 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
238 #################################################################
239 def SAMBA_BINARY(bld, binname, source,
249 use_global_deps=True,
260 '''define a Samba binary'''
263 SET_TARGET_TYPE(bld, binname, 'DISABLED')
266 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
269 features = 'cc cprogram symlink_bin install_bin'
271 features += ' pyembed'
273 obj_target = binname + '.objlist'
275 source = bld.EXPAND_VARIABLES(source, vars=vars)
276 source = unique_list(TO_LIST(source))
278 # first create a target for building the object files for this binary
279 # by separating in this way, we avoid recompiling the C files
280 # separately for the install binary and the build binary
281 bld.SAMBA_SUBSYSTEM(obj_target,
287 autoproto = autoproto,
288 subsystem_name = subsystem_name,
289 local_include = local_include,
290 use_hostcc = use_hostcc,
292 use_global_deps= use_global_deps)
294 bld.SET_BUILD_GROUP(group)
296 # the binary itself will depend on that object target
298 deps.append(obj_target)
305 samba_includes = includes,
306 local_include = local_include,
307 samba_modules = modules,
309 samba_subsystem= subsystem_name,
311 samba_inst_path= install_path,
312 samba_install = install
315 # setup the subsystem_name as an alias for the real
316 # binary name, so it can be found when expanding
317 # subsystem dependencies
318 if subsystem_name is not None:
319 bld.TARGET_ALIAS(subsystem_name, binname)
321 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
322 bld.MANPAGES(manpages)
324 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
327 #################################################################
328 def SAMBA_MODULE(bld, modname, source,
334 autoproto_extra_source='',
337 internal_module=True,
343 '''define a Samba module.'''
345 source = bld.EXPAND_VARIABLES(source, vars=vars)
347 if internal_module or BUILTIN_LIBRARY(bld, modname):
348 # treat internal modules as subsystems for now
349 if subsystem is not None:
350 deps += ' ' + subsystem
352 bld.SAMBA_SUBSYSTEM(modname, source,
356 autoproto_extra_source=autoproto_extra_source,
358 local_include=local_include,
361 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
365 SET_TARGET_TYPE(bld, modname, 'DISABLED')
368 if aliases is not None:
369 # if we have aliases, then create a private base library, and a set
370 # of modules on top of that library
372 cflags += " -D%s=samba_init_module" % init_function
374 basename = modname + '-base'
375 bld.SAMBA_LIBRARY(basename,
379 autoproto = autoproto,
380 local_include=local_include,
386 aliases = TO_LIST(aliases)
387 aliases.append(modname)
389 for alias in aliases:
390 bld.SAMBA_MODULE(alias,
392 internal_module=False,
394 init_function=init_function,
399 obj_target = modname + '.objlist'
402 if subsystem is not None:
403 deps += ' ' + subsystem
404 while realname.startswith("lib"+subsystem+"_"):
405 realname = realname[len("lib"+subsystem+"_"):]
406 while realname.startswith(subsystem+"_"):
407 realname = realname[len(subsystem+"_"):]
409 realname = bld.make_libname(realname)
410 while realname.startswith("lib"):
411 realname = realname[len("lib"):]
413 build_link_name = "modules/%s/%s" % (subsystem, realname)
416 cflags += " -D%s=samba_init_module" % init_function
418 bld.SAMBA_LIBRARY(modname,
423 autoproto = autoproto,
424 local_include=local_include,
426 link_name=build_link_name,
427 install_path="${MODULESDIR}/%s" % subsystem,
432 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
435 #################################################################
436 def SAMBA_SUBSYSTEM(bld, modname, source,
445 init_function_sentinal=None,
447 autoproto_extra_source='',
450 local_include_first=True,
454 use_global_deps=True,
458 '''define a Samba subsystem'''
461 SET_TARGET_TYPE(bld, modname, 'DISABLED')
464 # remember empty subsystems, so we can strip the dependencies
465 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
466 SET_TARGET_TYPE(bld, modname, 'EMPTY')
469 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
472 source = bld.EXPAND_VARIABLES(source, vars=vars)
473 source = unique_list(TO_LIST(source))
475 deps += ' ' + public_deps
477 bld.SET_BUILD_GROUP(group)
487 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
488 depends_on = depends_on,
489 samba_deps = TO_LIST(deps),
490 samba_includes = includes,
491 local_include = local_include,
492 local_include_first = local_include_first,
493 samba_subsystem= subsystem_name,
494 samba_use_hostcc = use_hostcc,
495 samba_use_global_deps = use_global_deps
498 if cflags_end is not None:
499 t.samba_cflags.extend(TO_LIST(cflags_end))
501 if autoproto is not None:
502 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
503 if public_headers is not None:
504 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
508 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
511 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
512 group='generators', enabled=True,
517 '''A generic source generator target'''
519 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
525 bld.SET_BUILD_GROUP(group)
528 source=bld.EXPAND_VARIABLES(source, vars=vars),
530 shell=isinstance(rule, str),
539 if public_headers is not None:
540 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
542 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
547 def SETUP_BUILD_GROUPS(bld):
548 '''setup build groups used to ensure that the different build
549 phases happen consecutively'''
550 bld.p_ln = bld.srcnode # we do want to see all targets!
551 bld.env['USING_BUILD_GROUPS'] = True
552 bld.add_group('setup')
553 bld.add_group('build_compiler_source')
554 bld.add_group('base_libraries')
555 bld.add_group('generators')
556 bld.add_group('compiler_prototypes')
557 bld.add_group('compiler_libraries')
558 bld.add_group('build_compilers')
559 bld.add_group('build_source')
560 bld.add_group('prototypes')
561 bld.add_group('main')
562 bld.add_group('binaries')
563 bld.add_group('final')
564 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
567 def SET_BUILD_GROUP(bld, group):
568 '''set the current build group'''
569 if not 'USING_BUILD_GROUPS' in bld.env:
572 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
577 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
578 """use timestamps instead of file contents for deps
579 this currently doesn't work"""
580 def h_file(filename):
582 st = os.stat(filename)
583 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
585 m.update(str(st.st_mtime))
586 m.update(str(st.st_size))
589 Utils.h_file = h_file
593 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
594 shell=True, color='PINK', ext_in='.bin')
597 @feature('copy_script')
598 @before('apply_link')
599 def copy_script(self):
600 tsk = self.create_task('copy_script', self.allnodes[0])
601 tsk.env.TARGET = self.target
603 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
604 '''used to copy scripts from the source tree into the build directory
605 for use by selftest'''
607 source = bld.path.ant_glob(pattern)
609 bld.SET_BUILD_GROUP('build_source')
610 for s in TO_LIST(source):
612 if installname != None:
614 target = os.path.join(installdir, iname)
615 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
617 t = bld(features='copy_script',
622 t.env.LINK_TARGET = target
624 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
627 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
628 python_fixup=False, destname=None, base_name=None):
630 destdir = bld.EXPAND_VARIABLES(destdir)
634 destname = os.path.basename(destname)
635 dest = os.path.join(destdir, destname)
637 # fixup the python path it will use to find Samba modules
638 inst_file = file + '.inst'
639 if bld.env["PYTHONDIR"] not in sys.path:
640 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
642 # Eliminate updating sys.path if the target python dir is already
644 regex = "s|sys.path.insert.*bin/python.*$||g"
645 bld.SAMBA_GENERATOR('python_%s' % destname,
646 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
651 file = os.path.join(base_name, file)
652 bld.install_as(dest, file, chmod=chmod)
655 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
656 python_fixup=False, destname=None, base_name=None):
657 '''install a set of files'''
658 for f in TO_LIST(files):
659 install_file(bld, destdir, f, chmod=chmod, flat=flat,
660 python_fixup=python_fixup, destname=destname,
662 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
665 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
666 python_fixup=False, exclude=None, trim_path=None):
667 '''install a set of files matching a wildcard pattern'''
668 files=TO_LIST(bld.path.ant_glob(pattern))
672 files2.append(os_path_relpath(f, trim_path))
677 if fnmatch.fnmatch(f, exclude):
679 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
680 python_fixup=python_fixup, base_name=trim_path)
681 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
684 def INSTALL_DIRS(bld, destdir, dirs):
685 '''install a set of directories'''
686 destdir = bld.EXPAND_VARIABLES(destdir)
687 dirs = bld.EXPAND_VARIABLES(dirs)
688 for d in TO_LIST(dirs):
689 bld.install_dir(os.path.join(destdir, d))
690 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
693 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
694 class header_task(Task.Task):
696 The public headers (the one installed on the system) have both
697 different paths and contents, so the rename is not enough.
699 Intermediate .inst.h files are created because path manipulation
700 may be slow. The substitution is thus performed only once.
705 vars = ['INCLUDEDIR', 'HEADER_DEPS']
708 txt = self.inputs[0].read(self.env)
710 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
711 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
713 # use a regexp to substitute the #include lines in the files
714 map = self.generator.bld.hnodemap
715 dirnodes = self.generator.bld.hnodedirs
720 # pokemon headers: gotta catch'em all!
722 if s.startswith('bin/default'):
723 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
725 Logs.warn('could not find the public header for %r' % s)
729 Logs.warn('could not find the public header replacement for build header %r' % s)
731 # this part is more difficult since the path may be relative to anything
732 for dirnode in dirnodes:
733 node = dirnode.find_resource(s)
739 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
741 Logs.warn('-> could not find the public header for %r' % s)
743 return "#include <%s>" % fin
746 txt = re_header.sub(repl, txt)
748 # and write the output file
751 f = open(self.outputs[0].abspath(self.env), 'w')
757 @TaskGen.feature('pubh')
758 def make_public_headers(self):
760 collect the public headers to process and to install, then
761 create the substitutions (name and contents)
764 if not self.bld.is_install:
765 # install time only (lazy)
769 # hnodedirs: list of folders for searching the headers
770 # hnodemap: node ids and replacement string (node objects are unique)
772 self.bld.hnodedirs.append(self.path)
773 except AttributeError:
774 self.bld.hnodemap = {}
775 self.bld.hnodedirs = [self.bld.srcnode, self.path]
777 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
778 node = self.bld.srcnode.find_dir(k)
780 self.bld.hnodedirs.append(node)
782 header_path = getattr(self, 'header_path', None) or ''
784 for x in self.to_list(self.headers):
786 # too complicated, but what was the original idea?
787 if isinstance(header_path, list):
789 for (p1, dir) in header_path:
790 lst = self.to_list(p1)
792 if fnmatch.fnmatch(x, p2):
800 inst_path = header_path
804 if x.find(':') != -1:
809 inn = self.path.find_resource(name)
812 raise ValueError("could not find the public header %r in %r" % (name, self.path))
813 out = inn.change_ext('.inst.h')
814 self.create_task('header', inn, out)
820 inst_path = inst_path + '/'
821 inst_path = inst_path + dest
823 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
825 self.bld.hnodemap[inn.id] = inst_path
827 # create a hash (not md5) to make sure the headers are re-created if something changes
829 lst = list(self.bld.hnodemap.keys())
832 val = hash((val, k, self.bld.hnodemap[k]))
833 self.bld.env.HEADER_DEPS = val
835 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
836 '''install some headers
838 header_path may either be a string that is added to the INCLUDEDIR,
839 or it can be a dictionary of wildcard patterns which map to destination
840 directories relative to INCLUDEDIR
842 bld.SET_BUILD_GROUP('final')
843 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
845 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
848 def subst_at_vars(task):
849 '''substiture @VAR@ style variables in a file'''
850 src = task.inputs[0].srcpath(task.env)
851 tgt = task.outputs[0].bldpath(task.env)
857 a = re.split('(@\w+@)', s)
860 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
862 if re.match('@\w+@', v):
864 if not vname in task.env and vname.upper() in task.env:
865 vname = vname.upper()
866 if not vname in task.env:
867 Logs.error("Unknown substitution %s in %s" % (v, task.name))
869 v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
870 # now we back substitute the allowed pc vars
871 for (b, m) in back_sub:
874 if not b in done_var:
875 # we don't want to substitute the first usage
881 contents = ''.join(out)
883 s = f.write(contents)
888 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
889 '''install some pkg_config pc files'''
890 dest = '${PKGCONFIGDIR}'
891 dest = bld.EXPAND_VARIABLES(dest)
892 for f in TO_LIST(pc_files):
893 base=os.path.basename(f)
894 t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
899 t.env.PACKAGE_VERSION = vnum
900 INSTALL_FILES(bld, dest, f, flat=True, destname=base)
901 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
904 def MANPAGES(bld, manpages):
905 '''build and install manual pages'''
906 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
907 for m in manpages.split():
909 bld.SAMBA_GENERATOR(m,
913 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
915 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
916 Build.BuildContext.MANPAGES = MANPAGES
919 #############################################################
920 # give a nicer display when building different types of files
921 def progress_display(self, msg, fname):
922 col1 = Logs.colors(self.color)
923 col2 = Logs.colors.NORMAL
924 total = self.position[1]
926 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
927 return fs % (self.position[0], self.position[1], col1, fname, col2)
929 def link_display(self):
930 if Options.options.progress_bar != 0:
931 return Task.Task.old_display(self)
932 fname = self.outputs[0].bldpath(self.env)
933 return progress_display(self, 'Linking', fname)
934 Task.TaskBase.classes['cc_link'].display = link_display
936 def samba_display(self):
937 if Options.options.progress_bar != 0:
938 return Task.Task.old_display(self)
940 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
941 if self.name in targets:
942 target_type = targets[self.name]
943 type_map = { 'GENERATOR' : 'Generating',
944 'PROTOTYPE' : 'Generating'
946 if target_type in type_map:
947 return progress_display(self, type_map[target_type], self.name)
949 fname = self.inputs[0].bldpath(self.env)
950 if fname[0:3] == '../':
952 ext_loc = fname.rfind('.')
954 return Task.Task.old_display(self)
955 ext = fname[ext_loc:]
957 ext_map = { '.idl' : 'Compiling IDL',
958 '.et' : 'Compiling ERRTABLE',
959 '.asn1': 'Compiling ASN1',
962 return progress_display(self, ext_map[ext], fname)
963 return Task.Task.old_display(self)
965 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
966 Task.TaskBase.classes['Task'].display = samba_display