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' in bld.env and bld.env['XSLTPROC']:
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' in bld.env and bld.env['XSLTPROC']:
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 if not SET_TARGET_TYPE(bld, modname, 'MODULE'):
355 obj_target = modname + '.objlist'
357 bld.SAMBA_SUBSYSTEM(obj_target, source,
361 autoproto_extra_source=autoproto_extra_source,
363 local_include=local_include,
367 deps.append(obj_target)
369 if subsystem is not None:
370 deps.append(subsystem)
371 while realname.startswith("lib"+subsystem+"_"):
372 realname = realname[len("lib"+subsystem+"_"):]
373 while realname.startswith(subsystem+"_"):
374 realname = realname[len(subsystem+"_"):]
375 while realname.startswith("lib"):
376 realname = realname[len("lib"):]
378 bld.SET_BUILD_GROUP('main')
380 features = 'cc cshlib install_lib',
384 link_name = "modules/%s/%s.${SHLIBEXT}" % (subsystem, realname),
385 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags),
386 samba_includes = includes,
387 local_include = local_include,
390 samba_inst_path= "${MODULESDIR}/%s" % subsystem,
391 samba_realname = realname+ ".${SHLIBEXT}",
393 samba_install = True,
397 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
400 #################################################################
401 def SAMBA_SUBSYSTEM(bld, modname, source,
410 init_function_sentinal=None,
411 heimdal_autoproto=None,
412 heimdal_autoproto_options=None,
413 heimdal_autoproto_private=None,
415 autoproto_extra_source='',
418 local_include_first=True,
422 use_global_deps=True,
426 '''define a Samba subsystem'''
429 SET_TARGET_TYPE(bld, modname, 'DISABLED')
432 # remember empty subsystems, so we can strip the dependencies
433 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
434 SET_TARGET_TYPE(bld, modname, 'EMPTY')
437 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
440 source = bld.EXPAND_VARIABLES(source, vars=vars)
441 source = unique_list(TO_LIST(source))
443 deps += ' ' + public_deps
445 bld.SET_BUILD_GROUP(group)
455 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
456 depends_on = depends_on,
457 samba_deps = TO_LIST(deps),
458 samba_includes = includes,
459 local_include = local_include,
460 local_include_first = local_include_first,
461 samba_subsystem= subsystem_name,
462 samba_use_hostcc = use_hostcc,
463 samba_use_global_deps = use_global_deps
466 if cflags_end is not None:
467 t.samba_cflags.extend(TO_LIST(cflags_end))
469 if heimdal_autoproto is not None:
470 bld.HEIMDAL_AUTOPROTO(heimdal_autoproto, source, options=heimdal_autoproto_options)
471 if heimdal_autoproto_private is not None:
472 bld.HEIMDAL_AUTOPROTO_PRIVATE(heimdal_autoproto_private, source)
473 if autoproto is not None:
474 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
475 if public_headers is not None:
476 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
480 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
483 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
484 group='generators', enabled=True,
488 '''A generic source generator target'''
490 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
496 bld.SET_BUILD_GROUP(group)
499 source=bld.EXPAND_VARIABLES(source, vars=vars),
501 shell=isinstance(rule, str),
507 if public_headers is not None:
508 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
510 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
515 def SETUP_BUILD_GROUPS(bld):
516 '''setup build groups used to ensure that the different build
517 phases happen consecutively'''
518 bld.p_ln = bld.srcnode # we do want to see all targets!
519 bld.env['USING_BUILD_GROUPS'] = True
520 bld.add_group('setup')
521 bld.add_group('build_compiler_source')
522 bld.add_group('base_libraries')
523 bld.add_group('generators')
524 bld.add_group('compiler_prototypes')
525 bld.add_group('compiler_libraries')
526 bld.add_group('build_compilers')
527 bld.add_group('build_source')
528 bld.add_group('prototypes')
529 bld.add_group('main')
530 bld.add_group('binaries')
531 bld.add_group('final')
532 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
535 def SET_BUILD_GROUP(bld, group):
536 '''set the current build group'''
537 if not 'USING_BUILD_GROUPS' in bld.env:
540 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
545 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
546 """use timestamps instead of file contents for deps
547 this currently doesn't work"""
548 def h_file(filename):
550 st = os.stat(filename)
551 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
553 m.update(str(st.st_mtime))
554 m.update(str(st.st_size))
557 Utils.h_file = h_file
561 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
562 shell=True, color='PINK', ext_in='.bin')
565 @feature('copy_script')
566 @before('apply_link')
567 def copy_script(self):
568 tsk = self.create_task('copy_script', self.allnodes[0])
569 tsk.env.TARGET = self.target
571 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
572 '''used to copy scripts from the source tree into the build directory
573 for use by selftest'''
575 source = bld.path.ant_glob(pattern)
577 bld.SET_BUILD_GROUP('build_source')
578 for s in TO_LIST(source):
580 if installname != None:
582 target = os.path.join(installdir, iname)
583 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
585 t = bld(features='copy_script',
590 t.env.LINK_TARGET = target
592 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
595 def install_file(bld, destdir, file, chmod=0644, flat=False,
596 python_fixup=False, destname=None, base_name=None):
598 destdir = bld.EXPAND_VARIABLES(destdir)
602 destname = os.path.basename(destname)
603 dest = os.path.join(destdir, destname)
605 # fixup the python path it will use to find Samba modules
606 inst_file = file + '.inst'
607 bld.SAMBA_GENERATOR('python_%s' % destname,
608 rule="sed 's|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g' < ${SRC} > ${TGT}",
613 file = os.path.join(base_name, file)
614 bld.install_as(dest, file, chmod=chmod)
617 def INSTALL_FILES(bld, destdir, files, chmod=0644, flat=False,
618 python_fixup=False, destname=None, base_name=None):
619 '''install a set of files'''
620 for f in TO_LIST(files):
621 install_file(bld, destdir, f, chmod=chmod, flat=flat,
622 python_fixup=python_fixup, destname=destname,
624 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
627 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=0644, flat=False,
628 python_fixup=False, exclude=None, trim_path=None):
629 '''install a set of files matching a wildcard pattern'''
630 files=TO_LIST(bld.path.ant_glob(pattern))
634 files2.append(os_path_relpath(f, trim_path))
639 if fnmatch.fnmatch(f, exclude):
641 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
642 python_fixup=python_fixup, base_name=trim_path)
643 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
646 def INSTALL_DIRS(bld, destdir, dirs):
647 '''install a set of directories'''
648 destdir = bld.EXPAND_VARIABLES(destdir)
649 dirs = bld.EXPAND_VARIABLES(dirs)
650 for d in TO_LIST(dirs):
651 bld.install_dir(os.path.join(destdir, d))
652 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
655 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
656 class header_task(Task.Task):
658 The public headers (the one installed on the system) have both
659 different paths and contents, so the rename is not enough.
661 Intermediate .inst.h files are created because path manipulation
662 may be slow. The substitution is thus performed only once.
667 vars = ['INCLUDEDIR', 'HEADER_DEPS']
670 txt = self.inputs[0].read(self.env)
672 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
673 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
675 # use a regexp to substitute the #include lines in the files
676 map = self.generator.bld.hnodemap
677 dirnodes = self.generator.bld.hnodedirs
682 # pokemon headers: gotta catch'em all!
684 if s.startswith('bin/default'):
685 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
687 Logs.warn('could not find the public header for %r' % s)
691 Logs.warn('could not find the public header replacement for build header %r' % s)
693 # this part is more difficult since the path may be relative to anything
694 for dirnode in dirnodes:
695 node = dirnode.find_resource(s)
701 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
703 Logs.warn('-> could not find the public header for %r' % s)
705 return "#include <%s>" % fin
708 txt = re_header.sub(repl, txt)
710 # and write the output file
713 f = open(self.outputs[0].abspath(self.env), 'w')
719 @TaskGen.feature('pubh')
720 def make_public_headers(self):
722 collect the public headers to process and to install, then
723 create the substitutions (name and contents)
726 if not self.bld.is_install:
727 # install time only (lazy)
731 # hnodedirs: list of folders for searching the headers
732 # hnodemap: node ids and replacement string (node objects are unique)
734 self.bld.hnodedirs.append(self.path)
735 except AttributeError:
736 self.bld.hnodemap = {}
737 self.bld.hnodedirs = [self.bld.srcnode, self.path]
739 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
740 node = self.bld.srcnode.find_dir(k)
742 self.bld.hnodedirs.append(node)
744 header_path = getattr(self, 'header_path', None) or ''
746 for x in self.to_list(self.headers):
748 # too complicated, but what was the original idea?
749 if isinstance(header_path, list):
751 for (p1, dir) in header_path:
752 lst = self.to_list(p1)
754 if fnmatch.fnmatch(x, p2):
762 inst_path = header_path
766 if x.find(':') != -1:
771 inn = self.path.find_resource(name)
774 raise ValueError("could not find the public header %r in %r" % (name, self.path))
775 out = inn.change_ext('.inst.h')
776 self.create_task('header', inn, out)
782 inst_path = inst_path + '/'
783 inst_path = inst_path + dest
785 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
787 self.bld.hnodemap[inn.id] = inst_path
789 # create a hash (not md5) to make sure the headers are re-created if something changes
791 lst = list(self.bld.hnodemap.keys())
794 val = hash((val, k, self.bld.hnodemap[k]))
795 self.bld.env.HEADER_DEPS = val
797 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
798 '''install some headers
800 header_path may either be a string that is added to the INCLUDEDIR,
801 or it can be a dictionary of wildcard patterns which map to destination
802 directories relative to INCLUDEDIR
804 bld.SET_BUILD_GROUP('final')
805 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
807 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
810 def subst_at_vars(task):
811 '''substiture @VAR@ style variables in a file'''
812 src = task.inputs[0].srcpath(task.env)
813 tgt = task.outputs[0].bldpath(task.env)
819 a = re.split('(@\w+@)', s)
822 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
824 if re.match('@\w+@', v):
826 if not vname in task.env and vname.upper() in task.env:
827 vname = vname.upper()
828 if not vname in task.env:
829 Logs.error("Unknown substitution %s in %s" % (v, task.name))
831 v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
832 # now we back substitute the allowed pc vars
833 for (b, m) in back_sub:
836 if not b in done_var:
837 # we don't want to substitute the first usage
843 contents = ''.join(out)
845 s = f.write(contents)
851 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
852 '''install some pkg_config pc files'''
853 dest = '${PKGCONFIGDIR}'
854 dest = bld.EXPAND_VARIABLES(dest)
855 for f in TO_LIST(pc_files):
856 base=os.path.basename(f)
857 t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
862 t.env.PACKAGE_VERSION = vnum
863 INSTALL_FILES(bld, dest, f, flat=True, destname=base)
864 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
867 def MANPAGES(bld, manpages):
868 '''build and install manual pages'''
869 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
870 for m in manpages.split():
872 bld.SAMBA_GENERATOR(m,
876 rule='${XSLTPROC} -o ${TGT} ${MAN_XSL} ${SRC}'
878 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
879 Build.BuildContext.MANPAGES = MANPAGES
882 #############################################################
883 # give a nicer display when building different types of files
884 def progress_display(self, msg, fname):
885 col1 = Logs.colors(self.color)
886 col2 = Logs.colors.NORMAL
887 total = self.position[1]
889 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
890 return fs % (self.position[0], self.position[1], col1, fname, col2)
892 def link_display(self):
893 if Options.options.progress_bar != 0:
894 return Task.Task.old_display(self)
895 fname = self.outputs[0].bldpath(self.env)
896 return progress_display(self, 'Linking', fname)
897 Task.TaskBase.classes['cc_link'].display = link_display
899 def samba_display(self):
900 if Options.options.progress_bar != 0:
901 return Task.Task.old_display(self)
903 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
904 if self.name in targets:
905 target_type = targets[self.name]
906 type_map = { 'GENERATOR' : 'Generating',
907 'PROTOTYPE' : 'Generating'
909 if target_type in type_map:
910 return progress_display(self, type_map[target_type], self.name)
912 fname = self.inputs[0].bldpath(self.env)
913 if fname[0:3] == '../':
915 ext_loc = fname.rfind('.')
917 return Task.Task.old_display(self)
918 ext = fname[ext_loc:]
920 ext_map = { '.idl' : 'Compiling IDL',
921 '.et' : 'Compiling ERRTABLE',
922 '.asn1': 'Compiling ASN1',
925 return progress_display(self, ext_map[ext], fname)
926 return Task.Task.old_display(self)
928 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
929 Task.TaskBase.classes['Task'].display = samba_display