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 if aliases is not None:
368 # if we have aliases, then create a private base library, and a set
369 # of modules on top of that library
371 cflags += " -D%s=samba_init_module" % init_function
373 basename = modname + '-base'
374 bld.SAMBA_LIBRARY(basename,
378 autoproto = autoproto,
379 local_include=local_include,
385 aliases = TO_LIST(aliases)
386 aliases.append(modname)
388 for alias in aliases:
389 bld.SAMBA_MODULE(alias,
391 internal_module=False,
393 init_function=init_function,
398 obj_target = modname + '.objlist'
401 if subsystem is not None:
402 deps += ' ' + subsystem
403 while realname.startswith("lib"+subsystem+"_"):
404 realname = realname[len("lib"+subsystem+"_"):]
405 while realname.startswith(subsystem+"_"):
406 realname = realname[len(subsystem+"_"):]
408 realname = bld.make_libname(realname)
409 while realname.startswith("lib"):
410 realname = realname[len("lib"):]
412 build_link_name = "modules/%s/%s" % (subsystem, realname)
415 cflags += " -D%s=samba_init_module" % init_function
417 bld.SAMBA_LIBRARY(modname,
422 autoproto = autoproto,
423 local_include=local_include,
425 link_name=build_link_name,
426 install_path="${MODULESDIR}/%s" % subsystem,
431 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
434 #################################################################
435 def SAMBA_SUBSYSTEM(bld, modname, source,
444 init_function_sentinal=None,
446 autoproto_extra_source='',
449 local_include_first=True,
453 use_global_deps=True,
457 '''define a Samba subsystem'''
460 SET_TARGET_TYPE(bld, modname, 'DISABLED')
463 # remember empty subsystems, so we can strip the dependencies
464 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
465 SET_TARGET_TYPE(bld, modname, 'EMPTY')
468 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
471 source = bld.EXPAND_VARIABLES(source, vars=vars)
472 source = unique_list(TO_LIST(source))
474 deps += ' ' + public_deps
476 bld.SET_BUILD_GROUP(group)
486 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
487 depends_on = depends_on,
488 samba_deps = TO_LIST(deps),
489 samba_includes = includes,
490 local_include = local_include,
491 local_include_first = local_include_first,
492 samba_subsystem= subsystem_name,
493 samba_use_hostcc = use_hostcc,
494 samba_use_global_deps = use_global_deps
497 if cflags_end is not None:
498 t.samba_cflags.extend(TO_LIST(cflags_end))
500 if autoproto is not None:
501 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
502 if public_headers is not None:
503 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
507 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
510 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
511 group='generators', enabled=True,
516 '''A generic source generator target'''
518 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
524 bld.SET_BUILD_GROUP(group)
527 source=bld.EXPAND_VARIABLES(source, vars=vars),
529 shell=isinstance(rule, str),
538 if public_headers is not None:
539 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
541 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
546 def SETUP_BUILD_GROUPS(bld):
547 '''setup build groups used to ensure that the different build
548 phases happen consecutively'''
549 bld.p_ln = bld.srcnode # we do want to see all targets!
550 bld.env['USING_BUILD_GROUPS'] = True
551 bld.add_group('setup')
552 bld.add_group('build_compiler_source')
553 bld.add_group('base_libraries')
554 bld.add_group('generators')
555 bld.add_group('compiler_prototypes')
556 bld.add_group('compiler_libraries')
557 bld.add_group('build_compilers')
558 bld.add_group('build_source')
559 bld.add_group('prototypes')
560 bld.add_group('main')
561 bld.add_group('binaries')
562 bld.add_group('final')
563 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
566 def SET_BUILD_GROUP(bld, group):
567 '''set the current build group'''
568 if not 'USING_BUILD_GROUPS' in bld.env:
571 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
576 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
577 """use timestamps instead of file contents for deps
578 this currently doesn't work"""
579 def h_file(filename):
581 st = os.stat(filename)
582 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
584 m.update(str(st.st_mtime))
585 m.update(str(st.st_size))
588 Utils.h_file = h_file
592 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
593 shell=True, color='PINK', ext_in='.bin')
596 @feature('copy_script')
597 @before('apply_link')
598 def copy_script(self):
599 tsk = self.create_task('copy_script', self.allnodes[0])
600 tsk.env.TARGET = self.target
602 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
603 '''used to copy scripts from the source tree into the build directory
604 for use by selftest'''
606 source = bld.path.ant_glob(pattern)
608 bld.SET_BUILD_GROUP('build_source')
609 for s in TO_LIST(source):
611 if installname != None:
613 target = os.path.join(installdir, iname)
614 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
616 t = bld(features='copy_script',
621 t.env.LINK_TARGET = target
623 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
626 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
627 python_fixup=False, destname=None, base_name=None):
629 destdir = bld.EXPAND_VARIABLES(destdir)
633 destname = os.path.basename(destname)
634 dest = os.path.join(destdir, destname)
636 # fixup the python path it will use to find Samba modules
637 inst_file = file + '.inst'
638 if bld.env["PYTHONDIR"] not in sys.path:
639 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
641 # Eliminate updating sys.path if the target python dir is already
643 regex = "s|sys.path.insert.*bin/python.*$||g"
644 bld.SAMBA_GENERATOR('python_%s' % destname,
645 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
650 file = os.path.join(base_name, file)
651 bld.install_as(dest, file, chmod=chmod)
654 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
655 python_fixup=False, destname=None, base_name=None):
656 '''install a set of files'''
657 for f in TO_LIST(files):
658 install_file(bld, destdir, f, chmod=chmod, flat=flat,
659 python_fixup=python_fixup, destname=destname,
661 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
664 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
665 python_fixup=False, exclude=None, trim_path=None):
666 '''install a set of files matching a wildcard pattern'''
667 files=TO_LIST(bld.path.ant_glob(pattern))
671 files2.append(os_path_relpath(f, trim_path))
676 if fnmatch.fnmatch(f, exclude):
678 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
679 python_fixup=python_fixup, base_name=trim_path)
680 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
683 def INSTALL_DIRS(bld, destdir, dirs):
684 '''install a set of directories'''
685 destdir = bld.EXPAND_VARIABLES(destdir)
686 dirs = bld.EXPAND_VARIABLES(dirs)
687 for d in TO_LIST(dirs):
688 bld.install_dir(os.path.join(destdir, d))
689 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
692 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
693 class header_task(Task.Task):
695 The public headers (the one installed on the system) have both
696 different paths and contents, so the rename is not enough.
698 Intermediate .inst.h files are created because path manipulation
699 may be slow. The substitution is thus performed only once.
704 vars = ['INCLUDEDIR', 'HEADER_DEPS']
707 txt = self.inputs[0].read(self.env)
709 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
710 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
712 # use a regexp to substitute the #include lines in the files
713 map = self.generator.bld.hnodemap
714 dirnodes = self.generator.bld.hnodedirs
719 # pokemon headers: gotta catch'em all!
721 if s.startswith('bin/default'):
722 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
724 Logs.warn('could not find the public header for %r' % s)
728 Logs.warn('could not find the public header replacement for build header %r' % s)
730 # this part is more difficult since the path may be relative to anything
731 for dirnode in dirnodes:
732 node = dirnode.find_resource(s)
738 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
740 Logs.warn('-> could not find the public header for %r' % s)
742 return "#include <%s>" % fin
745 txt = re_header.sub(repl, txt)
747 # and write the output file
750 f = open(self.outputs[0].abspath(self.env), 'w')
756 @TaskGen.feature('pubh')
757 def make_public_headers(self):
759 collect the public headers to process and to install, then
760 create the substitutions (name and contents)
763 if not self.bld.is_install:
764 # install time only (lazy)
768 # hnodedirs: list of folders for searching the headers
769 # hnodemap: node ids and replacement string (node objects are unique)
771 self.bld.hnodedirs.append(self.path)
772 except AttributeError:
773 self.bld.hnodemap = {}
774 self.bld.hnodedirs = [self.bld.srcnode, self.path]
776 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
777 node = self.bld.srcnode.find_dir(k)
779 self.bld.hnodedirs.append(node)
781 header_path = getattr(self, 'header_path', None) or ''
783 for x in self.to_list(self.headers):
785 # too complicated, but what was the original idea?
786 if isinstance(header_path, list):
788 for (p1, dir) in header_path:
789 lst = self.to_list(p1)
791 if fnmatch.fnmatch(x, p2):
799 inst_path = header_path
803 if x.find(':') != -1:
808 inn = self.path.find_resource(name)
811 raise ValueError("could not find the public header %r in %r" % (name, self.path))
812 out = inn.change_ext('.inst.h')
813 self.create_task('header', inn, out)
819 inst_path = inst_path + '/'
820 inst_path = inst_path + dest
822 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
824 self.bld.hnodemap[inn.id] = inst_path
826 # create a hash (not md5) to make sure the headers are re-created if something changes
828 lst = list(self.bld.hnodemap.keys())
831 val = hash((val, k, self.bld.hnodemap[k]))
832 self.bld.env.HEADER_DEPS = val
834 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
835 '''install some headers
837 header_path may either be a string that is added to the INCLUDEDIR,
838 or it can be a dictionary of wildcard patterns which map to destination
839 directories relative to INCLUDEDIR
841 bld.SET_BUILD_GROUP('final')
842 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
844 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
847 def subst_at_vars(task):
848 '''substiture @VAR@ style variables in a file'''
849 src = task.inputs[0].srcpath(task.env)
850 tgt = task.outputs[0].bldpath(task.env)
856 a = re.split('(@\w+@)', s)
859 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
861 if re.match('@\w+@', v):
863 if not vname in task.env and vname.upper() in task.env:
864 vname = vname.upper()
865 if not vname in task.env:
866 Logs.error("Unknown substitution %s in %s" % (v, task.name))
868 v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
869 # now we back substitute the allowed pc vars
870 for (b, m) in back_sub:
873 if not b in done_var:
874 # we don't want to substitute the first usage
880 contents = ''.join(out)
882 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