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, "modules"))
59 mkdir_p(os.path.join(conf.blddir, 'python/samba/dcerpc'))
60 # this allows all of the bin/shared and bin/python targets
61 # to be expressed in terms of build directory paths
62 mkdir_p(os.path.join(conf.blddir, 'default'))
63 for p in ['python','shared', 'modules']:
64 link_target = os.path.join(conf.blddir, 'default/' + p)
65 if not os.path.lexists(link_target):
66 os.symlink('../' + p, link_target)
68 # get perl to put the blib files in the build directory
69 blib_bld = os.path.join(conf.blddir, 'default/pidl/blib')
70 blib_src = os.path.join(conf.srcdir, 'pidl/blib')
71 mkdir_p(blib_bld + '/man1')
72 mkdir_p(blib_bld + '/man3')
73 if os.path.islink(blib_src):
75 elif os.path.exists(blib_src):
76 shutil.rmtree(blib_src)
79 def ADD_INIT_FUNCTION(bld, subsystem, target, init_function):
80 '''add an init_function to the list for a subsystem'''
81 if init_function is None:
83 bld.ASSERT(subsystem is not None, "You must specify a subsystem for init_function '%s'" % init_function)
84 cache = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
85 if not subsystem in cache:
87 cache[subsystem].append( { 'TARGET':target, 'INIT_FUNCTION':init_function } )
88 Build.BuildContext.ADD_INIT_FUNCTION = ADD_INIT_FUNCTION
92 #################################################################
93 def SAMBA_LIBRARY(bld, libname, source,
102 external_library=False,
112 target_type='LIBRARY',
113 bundled_extension=True,
120 heimdal_autoproto=None,
121 heimdal_autoproto_options=None,
122 heimdal_autoproto_private=None,
124 '''define a Samba library'''
127 SET_TARGET_TYPE(bld, libname, 'DISABLED')
130 source = bld.EXPAND_VARIABLES(source, vars=vars)
132 # remember empty libraries, so we can strip the dependencies
133 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
134 SET_TARGET_TYPE(bld, libname, 'EMPTY')
137 if BUILTIN_LIBRARY(bld, libname):
140 obj_target = libname + '.objlist'
142 # first create a target for building the object files for this library
143 # by separating in this way, we avoid recompiling the C files
144 # separately for the install library and the build library
145 bld.SAMBA_SUBSYSTEM(obj_target,
148 public_deps = public_deps,
150 public_headers = public_headers,
151 header_path = header_path,
154 autoproto = autoproto,
155 depends_on = depends_on,
156 needs_python = needs_python,
157 hide_symbols = hide_symbols,
158 local_include = local_include)
160 if BUILTIN_LIBRARY(bld, libname):
163 if not SET_TARGET_TYPE(bld, libname, target_type):
166 # the library itself will depend on that object target
167 deps += ' ' + public_deps
169 deps.append(obj_target)
171 if target_type == 'PYTHON' or realname or not is_bundled:
172 # Sanitize the library name
173 bundled_name = libname.lower().replace('_', '-')
174 while bundled_name.startswith("lib"):
175 bundled_name = bundled_name[3:]
177 bundled_name = BUNDLED_NAME(bld, libname, bundled_extension)
179 features = 'cc cshlib symlink_lib install_lib'
180 if target_type == 'PYTHON':
183 features += ' pyembed'
185 features += ' abi_check'
188 abi_file = os.path.join(bld.curdir, abi_file)
190 bld.SET_BUILD_GROUP(group)
194 target = bundled_name,
195 samba_cflags = CURRENT_CFLAGS(bld, libname, cflags),
196 depends_on = depends_on,
198 samba_includes = includes,
199 local_include = local_include,
202 samba_inst_path = install_path,
204 samba_realname = realname,
205 samba_install = install,
207 abi_match = abi_match,
208 is_bundled = is_bundled
211 if realname and not link_name:
212 link_name = 'shared/%s' % realname
215 t.link_name = link_name
217 if pc_files is not None:
218 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
220 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
221 bld.MANPAGES(manpages)
223 if heimdal_autoproto is not None:
224 bld.HEIMDAL_AUTOPROTO(heimdal_autoproto, source, options=heimdal_autoproto_options)
225 if heimdal_autoproto_private is not None:
226 bld.HEIMDAL_AUTOPROTO_PRIVATE(heimdal_autoproto_private, source)
229 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
232 #################################################################
233 def SAMBA_BINARY(bld, binname, source,
243 use_global_deps=True,
254 '''define a Samba binary'''
257 SET_TARGET_TYPE(bld, binname, 'DISABLED')
260 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
263 features = 'cc cprogram symlink_bin install_bin'
265 features += ' pyembed'
267 obj_target = binname + '.objlist'
269 source = bld.EXPAND_VARIABLES(source, vars=vars)
270 source = unique_list(TO_LIST(source))
272 # first create a target for building the object files for this binary
273 # by separating in this way, we avoid recompiling the C files
274 # separately for the install binary and the build binary
275 bld.SAMBA_SUBSYSTEM(obj_target,
281 autoproto = autoproto,
282 subsystem_name = subsystem_name,
283 needs_python = needs_python,
284 local_include = local_include,
285 use_hostcc = use_hostcc,
286 use_global_deps= use_global_deps)
288 bld.SET_BUILD_GROUP(group)
290 # the binary itself will depend on that object target
292 deps.append(obj_target)
298 samba_cflags = CURRENT_CFLAGS(bld, binname, cflags),
300 samba_includes = includes,
301 local_include = local_include,
302 samba_modules = modules,
304 samba_subsystem= subsystem_name,
306 samba_inst_path= install_path,
307 samba_install = install
310 # setup the subsystem_name as an alias for the real
311 # binary name, so it can be found when expanding
312 # subsystem dependencies
313 if subsystem_name is not None:
314 bld.TARGET_ALIAS(subsystem_name, binname)
316 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
317 bld.MANPAGES(manpages)
319 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
322 #################################################################
323 def SAMBA_MODULE(bld, modname, source,
329 autoproto_extra_source='',
332 internal_module=True,
336 '''define a Samba module.'''
338 source = bld.EXPAND_VARIABLES(source, vars=vars)
340 if internal_module or BUILTIN_LIBRARY(bld, modname):
341 # treat internal modules as subsystems for now
342 if subsystem is not None:
343 deps += ' ' + subsystem
345 bld.SAMBA_SUBSYSTEM(modname, source,
349 autoproto_extra_source=autoproto_extra_source,
351 local_include=local_include,
354 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
358 SET_TARGET_TYPE(bld, modname, 'DISABLED')
361 modnames = [modname] + TO_LIST(aliases)
362 for modname in modnames:
363 obj_target = modname + '.objlist'
366 if subsystem is not None:
367 deps += ' ' + subsystem
368 while realname.startswith("lib"+subsystem+"_"):
369 realname = realname[len("lib"+subsystem+"_"):]
370 while realname.startswith(subsystem+"_"):
371 realname = realname[len(subsystem+"_"):]
373 realname = bld.env.shlib_PATTERN % realname
374 while realname.startswith("lib"):
375 realname = realname[len("lib"):]
377 build_link_name = "modules/%s/%s" % (subsystem, realname)
379 bld.SAMBA_LIBRARY(modname,
384 autoproto = autoproto,
385 local_include=local_include,
387 link_name=build_link_name,
388 install_path="${MODULESDIR}/%s" % subsystem
391 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
394 #################################################################
395 def SAMBA_SUBSYSTEM(bld, modname, source,
404 init_function_sentinal=None,
406 autoproto_extra_source='',
409 local_include_first=True,
413 use_global_deps=True,
417 '''define a Samba subsystem'''
420 SET_TARGET_TYPE(bld, modname, 'DISABLED')
423 # remember empty subsystems, so we can strip the dependencies
424 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
425 SET_TARGET_TYPE(bld, modname, 'EMPTY')
428 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
431 source = bld.EXPAND_VARIABLES(source, vars=vars)
432 source = unique_list(TO_LIST(source))
434 deps += ' ' + public_deps
436 bld.SET_BUILD_GROUP(group)
446 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
447 depends_on = depends_on,
448 samba_deps = TO_LIST(deps),
449 samba_includes = includes,
450 local_include = local_include,
451 local_include_first = local_include_first,
452 samba_subsystem= subsystem_name,
453 samba_use_hostcc = use_hostcc,
454 samba_use_global_deps = use_global_deps
457 if cflags_end is not None:
458 t.samba_cflags.extend(TO_LIST(cflags_end))
460 if autoproto is not None:
461 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
462 if public_headers is not None:
463 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
467 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
470 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
471 group='generators', enabled=True,
476 '''A generic source generator target'''
478 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
484 bld.SET_BUILD_GROUP(group)
487 source=bld.EXPAND_VARIABLES(source, vars=vars),
489 shell=isinstance(rule, str),
498 if public_headers is not None:
499 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
501 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
506 def SETUP_BUILD_GROUPS(bld):
507 '''setup build groups used to ensure that the different build
508 phases happen consecutively'''
509 bld.p_ln = bld.srcnode # we do want to see all targets!
510 bld.env['USING_BUILD_GROUPS'] = True
511 bld.add_group('setup')
512 bld.add_group('build_compiler_source')
513 bld.add_group('base_libraries')
514 bld.add_group('generators')
515 bld.add_group('compiler_prototypes')
516 bld.add_group('compiler_libraries')
517 bld.add_group('build_compilers')
518 bld.add_group('build_source')
519 bld.add_group('prototypes')
520 bld.add_group('main')
521 bld.add_group('binaries')
522 bld.add_group('final')
523 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
526 def SET_BUILD_GROUP(bld, group):
527 '''set the current build group'''
528 if not 'USING_BUILD_GROUPS' in bld.env:
531 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
536 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
537 """use timestamps instead of file contents for deps
538 this currently doesn't work"""
539 def h_file(filename):
541 st = os.stat(filename)
542 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
544 m.update(str(st.st_mtime))
545 m.update(str(st.st_size))
548 Utils.h_file = h_file
552 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
553 shell=True, color='PINK', ext_in='.bin')
556 @feature('copy_script')
557 @before('apply_link')
558 def copy_script(self):
559 tsk = self.create_task('copy_script', self.allnodes[0])
560 tsk.env.TARGET = self.target
562 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
563 '''used to copy scripts from the source tree into the build directory
564 for use by selftest'''
566 source = bld.path.ant_glob(pattern)
568 bld.SET_BUILD_GROUP('build_source')
569 for s in TO_LIST(source):
571 if installname != None:
573 target = os.path.join(installdir, iname)
574 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
576 t = bld(features='copy_script',
581 t.env.LINK_TARGET = target
583 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
586 def install_file(bld, destdir, file, chmod=0644, flat=False,
587 python_fixup=False, destname=None, base_name=None):
589 destdir = bld.EXPAND_VARIABLES(destdir)
593 destname = os.path.basename(destname)
594 dest = os.path.join(destdir, destname)
596 # fixup the python path it will use to find Samba modules
597 inst_file = file + '.inst'
598 if bld.env["PYTHONDIR"] not in sys.path:
599 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
601 # Eliminate updating sys.path if the target python dir is already
603 regex = "s|sys.path.insert.*bin/python.*$||g"
604 bld.SAMBA_GENERATOR('python_%s' % destname,
605 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
610 file = os.path.join(base_name, file)
611 bld.install_as(dest, file, chmod=chmod)
614 def INSTALL_FILES(bld, destdir, files, chmod=0644, flat=False,
615 python_fixup=False, destname=None, base_name=None):
616 '''install a set of files'''
617 for f in TO_LIST(files):
618 install_file(bld, destdir, f, chmod=chmod, flat=flat,
619 python_fixup=python_fixup, destname=destname,
621 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
624 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=0644, flat=False,
625 python_fixup=False, exclude=None, trim_path=None):
626 '''install a set of files matching a wildcard pattern'''
627 files=TO_LIST(bld.path.ant_glob(pattern))
631 files2.append(os_path_relpath(f, trim_path))
636 if fnmatch.fnmatch(f, exclude):
638 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
639 python_fixup=python_fixup, base_name=trim_path)
640 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
643 def INSTALL_DIRS(bld, destdir, dirs):
644 '''install a set of directories'''
645 destdir = bld.EXPAND_VARIABLES(destdir)
646 dirs = bld.EXPAND_VARIABLES(dirs)
647 for d in TO_LIST(dirs):
648 bld.install_dir(os.path.join(destdir, d))
649 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
652 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
653 class header_task(Task.Task):
655 The public headers (the one installed on the system) have both
656 different paths and contents, so the rename is not enough.
658 Intermediate .inst.h files are created because path manipulation
659 may be slow. The substitution is thus performed only once.
664 vars = ['INCLUDEDIR', 'HEADER_DEPS']
667 txt = self.inputs[0].read(self.env)
669 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
670 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
672 # use a regexp to substitute the #include lines in the files
673 map = self.generator.bld.hnodemap
674 dirnodes = self.generator.bld.hnodedirs
679 # pokemon headers: gotta catch'em all!
681 if s.startswith('bin/default'):
682 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
684 Logs.warn('could not find the public header for %r' % s)
688 Logs.warn('could not find the public header replacement for build header %r' % s)
690 # this part is more difficult since the path may be relative to anything
691 for dirnode in dirnodes:
692 node = dirnode.find_resource(s)
698 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
700 Logs.warn('-> could not find the public header for %r' % s)
702 return "#include <%s>" % fin
705 txt = re_header.sub(repl, txt)
707 # and write the output file
710 f = open(self.outputs[0].abspath(self.env), 'w')
716 @TaskGen.feature('pubh')
717 def make_public_headers(self):
719 collect the public headers to process and to install, then
720 create the substitutions (name and contents)
723 if not self.bld.is_install:
724 # install time only (lazy)
728 # hnodedirs: list of folders for searching the headers
729 # hnodemap: node ids and replacement string (node objects are unique)
731 self.bld.hnodedirs.append(self.path)
732 except AttributeError:
733 self.bld.hnodemap = {}
734 self.bld.hnodedirs = [self.bld.srcnode, self.path]
736 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
737 node = self.bld.srcnode.find_dir(k)
739 self.bld.hnodedirs.append(node)
741 header_path = getattr(self, 'header_path', None) or ''
743 for x in self.to_list(self.headers):
745 # too complicated, but what was the original idea?
746 if isinstance(header_path, list):
748 for (p1, dir) in header_path:
749 lst = self.to_list(p1)
751 if fnmatch.fnmatch(x, p2):
759 inst_path = header_path
763 if x.find(':') != -1:
768 inn = self.path.find_resource(name)
771 raise ValueError("could not find the public header %r in %r" % (name, self.path))
772 out = inn.change_ext('.inst.h')
773 self.create_task('header', inn, out)
779 inst_path = inst_path + '/'
780 inst_path = inst_path + dest
782 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
784 self.bld.hnodemap[inn.id] = inst_path
786 # create a hash (not md5) to make sure the headers are re-created if something changes
788 lst = list(self.bld.hnodemap.keys())
791 val = hash((val, k, self.bld.hnodemap[k]))
792 self.bld.env.HEADER_DEPS = val
794 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
795 '''install some headers
797 header_path may either be a string that is added to the INCLUDEDIR,
798 or it can be a dictionary of wildcard patterns which map to destination
799 directories relative to INCLUDEDIR
801 bld.SET_BUILD_GROUP('final')
802 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
804 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
807 def subst_at_vars(task):
808 '''substiture @VAR@ style variables in a file'''
809 src = task.inputs[0].srcpath(task.env)
810 tgt = task.outputs[0].bldpath(task.env)
816 a = re.split('(@\w+@)', s)
819 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
821 if re.match('@\w+@', v):
823 if not vname in task.env and vname.upper() in task.env:
824 vname = vname.upper()
825 if not vname in task.env:
826 Logs.error("Unknown substitution %s in %s" % (v, task.name))
828 v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
829 # now we back substitute the allowed pc vars
830 for (b, m) in back_sub:
833 if not b in done_var:
834 # we don't want to substitute the first usage
840 contents = ''.join(out)
842 s = f.write(contents)
848 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
849 '''install some pkg_config pc files'''
850 dest = '${PKGCONFIGDIR}'
851 dest = bld.EXPAND_VARIABLES(dest)
852 for f in TO_LIST(pc_files):
853 base=os.path.basename(f)
854 t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
859 t.env.PACKAGE_VERSION = vnum
860 INSTALL_FILES(bld, dest, f, flat=True, destname=base)
861 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
864 def MANPAGES(bld, manpages):
865 '''build and install manual pages'''
866 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
867 for m in manpages.split():
869 bld.SAMBA_GENERATOR(m,
873 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
875 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
876 Build.BuildContext.MANPAGES = MANPAGES
879 #############################################################
880 # give a nicer display when building different types of files
881 def progress_display(self, msg, fname):
882 col1 = Logs.colors(self.color)
883 col2 = Logs.colors.NORMAL
884 total = self.position[1]
886 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
887 return fs % (self.position[0], self.position[1], col1, fname, col2)
889 def link_display(self):
890 if Options.options.progress_bar != 0:
891 return Task.Task.old_display(self)
892 fname = self.outputs[0].bldpath(self.env)
893 return progress_display(self, 'Linking', fname)
894 Task.TaskBase.classes['cc_link'].display = link_display
896 def samba_display(self):
897 if Options.options.progress_bar != 0:
898 return Task.Task.old_display(self)
900 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
901 if self.name in targets:
902 target_type = targets[self.name]
903 type_map = { 'GENERATOR' : 'Generating',
904 'PROTOTYPE' : 'Generating'
906 if target_type in type_map:
907 return progress_display(self, type_map[target_type], self.name)
909 fname = self.inputs[0].bldpath(self.env)
910 if fname[0:3] == '../':
912 ext_loc = fname.rfind('.')
914 return Task.Task.old_display(self)
915 ext = fname[ext_loc:]
917 ext_map = { '.idl' : 'Compiling IDL',
918 '.et' : 'Compiling ERRTABLE',
919 '.asn1': 'Compiling ASN1',
922 return progress_display(self, ext_map[ext], fname)
923 return Task.Task.old_display(self)
925 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
926 Task.TaskBase.classes['Task'].display = samba_display