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
30 # some systems have broken threading in python
31 if os.environ.get('WAF_NOTHREADS') == '1':
36 os.putenv('PYTHONUNBUFFERED', '1')
39 if Constants.HEXVERSION < 0x105019:
41 Please use the version of waf that comes with Samba, not
42 a system installed version. See http://wiki.samba.org/index.php/Waf
45 Alternatively, please use ./autogen-waf.sh, and then
46 run ./configure and make as usual. That will call the right version of waf.
52 def SAMBA_BUILD_ENV(conf):
53 '''create the samba build environment'''
54 conf.env.BUILD_DIRECTORY = conf.blddir
55 mkdir_p(os.path.join(conf.blddir, LIB_PATH))
56 mkdir_p(os.path.join(conf.blddir, "modules"))
57 mkdir_p(os.path.join(conf.blddir, 'python/samba/dcerpc'))
58 # this allows all of the bin/shared and bin/python targets
59 # to be expressed in terms of build directory paths
60 mkdir_p(os.path.join(conf.blddir, 'default'))
61 for p in ['python','shared', 'modules']:
62 link_target = os.path.join(conf.blddir, 'default/' + p)
63 if not os.path.lexists(link_target):
64 os.symlink('../' + p, link_target)
66 # get perl to put the blib files in the build directory
67 blib_bld = os.path.join(conf.blddir, 'default/pidl/blib')
68 blib_src = os.path.join(conf.srcdir, 'pidl/blib')
69 mkdir_p(blib_bld + '/man1')
70 mkdir_p(blib_bld + '/man3')
71 if os.path.islink(blib_src):
73 elif os.path.exists(blib_src):
74 shutil.rmtree(blib_src)
77 def ADD_INIT_FUNCTION(bld, subsystem, target, init_function):
78 '''add an init_function to the list for a subsystem'''
79 if init_function is None:
81 bld.ASSERT(subsystem is not None, "You must specify a subsystem for init_function '%s'" % init_function)
82 cache = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
83 if not subsystem in cache:
85 cache[subsystem].append( { 'TARGET':target, 'INIT_FUNCTION':init_function } )
86 Build.BuildContext.ADD_INIT_FUNCTION = ADD_INIT_FUNCTION
90 #################################################################
91 def SAMBA_LIBRARY(bld, libname, source,
100 external_library=False,
110 target_type='LIBRARY',
111 bundled_extension=True,
119 '''define a Samba library'''
122 SET_TARGET_TYPE(bld, libname, 'DISABLED')
125 source = bld.EXPAND_VARIABLES(source, vars=vars)
127 # remember empty libraries, so we can strip the dependencies
128 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
129 SET_TARGET_TYPE(bld, libname, 'EMPTY')
132 if BUILTIN_LIBRARY(bld, libname):
135 obj_target = libname + '.objlist'
137 # first create a target for building the object files for this library
138 # by separating in this way, we avoid recompiling the C files
139 # separately for the install library and the build library
140 bld.SAMBA_SUBSYSTEM(obj_target,
143 public_deps = public_deps,
145 public_headers = public_headers,
146 header_path = header_path,
149 autoproto = autoproto,
150 depends_on = depends_on,
151 hide_symbols = hide_symbols,
152 pyext = (target_type == "PYTHON"),
153 local_include = local_include)
155 if BUILTIN_LIBRARY(bld, libname):
158 if not SET_TARGET_TYPE(bld, libname, target_type):
161 # the library itself will depend on that object target
162 deps += ' ' + public_deps
164 deps.append(obj_target)
166 if target_type == 'PYTHON' or realname or not is_bundled:
167 # Sanitize the library name
168 bundled_name = libname.lower().replace('_', '-')
169 while bundled_name.startswith("lib"):
170 bundled_name = bundled_name[3:]
172 bundled_name = BUNDLED_NAME(bld, libname, bundled_extension)
174 features = 'cc cshlib symlink_lib install_lib'
175 if target_type == 'PYTHON':
178 features += ' pyembed'
180 features += ' abi_check'
183 abi_file = os.path.join(bld.curdir, abi_file)
185 bld.SET_BUILD_GROUP(group)
189 target = bundled_name,
190 samba_cflags = CURRENT_CFLAGS(bld, libname, cflags),
191 depends_on = depends_on,
193 samba_includes = includes,
194 local_include = local_include,
197 samba_inst_path = install_path,
199 samba_realname = realname,
200 samba_install = install,
202 abi_match = abi_match,
203 is_bundled = is_bundled
206 if realname and not link_name:
207 link_name = 'shared/%s' % realname
210 t.link_name = link_name
212 if pc_files is not None:
213 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
215 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
216 bld.MANPAGES(manpages)
219 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
222 #################################################################
223 def SAMBA_BINARY(bld, binname, source,
233 use_global_deps=True,
244 '''define a Samba binary'''
247 SET_TARGET_TYPE(bld, binname, 'DISABLED')
250 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
253 features = 'cc cprogram symlink_bin install_bin'
255 features += ' pyembed'
257 obj_target = binname + '.objlist'
259 source = bld.EXPAND_VARIABLES(source, vars=vars)
260 source = unique_list(TO_LIST(source))
262 # first create a target for building the object files for this binary
263 # by separating in this way, we avoid recompiling the C files
264 # separately for the install binary and the build binary
265 bld.SAMBA_SUBSYSTEM(obj_target,
271 autoproto = autoproto,
272 subsystem_name = subsystem_name,
273 local_include = local_include,
274 use_hostcc = use_hostcc,
276 use_global_deps= use_global_deps)
278 bld.SET_BUILD_GROUP(group)
280 # the binary itself will depend on that object target
282 deps.append(obj_target)
288 samba_cflags = CURRENT_CFLAGS(bld, binname, cflags),
290 samba_includes = includes,
291 local_include = local_include,
292 samba_modules = modules,
294 samba_subsystem= subsystem_name,
296 samba_inst_path= install_path,
297 samba_install = install
300 # setup the subsystem_name as an alias for the real
301 # binary name, so it can be found when expanding
302 # subsystem dependencies
303 if subsystem_name is not None:
304 bld.TARGET_ALIAS(subsystem_name, binname)
306 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
307 bld.MANPAGES(manpages)
309 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
312 #################################################################
313 def SAMBA_MODULE(bld, modname, source,
319 autoproto_extra_source='',
322 internal_module=True,
328 '''define a Samba module.'''
330 source = bld.EXPAND_VARIABLES(source, vars=vars)
332 if internal_module or BUILTIN_LIBRARY(bld, modname):
333 # treat internal modules as subsystems for now
334 if subsystem is not None:
335 deps += ' ' + subsystem
337 bld.SAMBA_SUBSYSTEM(modname, source,
341 autoproto_extra_source=autoproto_extra_source,
343 local_include=local_include,
346 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
350 SET_TARGET_TYPE(bld, modname, 'DISABLED')
353 modnames = [modname] + TO_LIST(aliases)
354 for modname in modnames:
355 obj_target = modname + '.objlist'
358 if subsystem is not None:
359 deps += ' ' + subsystem
360 while realname.startswith("lib"+subsystem+"_"):
361 realname = realname[len("lib"+subsystem+"_"):]
362 while realname.startswith(subsystem+"_"):
363 realname = realname[len(subsystem+"_"):]
365 realname = bld.env.shlib_PATTERN % realname
366 while realname.startswith("lib"):
367 realname = realname[len("lib"):]
369 build_link_name = "modules/%s/%s" % (subsystem, realname)
372 cflags += " -D%s=samba_init_module" % init_function
374 bld.SAMBA_LIBRARY(modname,
379 autoproto = autoproto,
380 local_include=local_include,
382 link_name=build_link_name,
383 install_path="${MODULESDIR}/%s" % subsystem,
387 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
390 #################################################################
391 def SAMBA_SUBSYSTEM(bld, modname, source,
400 init_function_sentinal=None,
402 autoproto_extra_source='',
405 local_include_first=True,
409 use_global_deps=True,
413 '''define a Samba subsystem'''
416 SET_TARGET_TYPE(bld, modname, 'DISABLED')
419 # remember empty subsystems, so we can strip the dependencies
420 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
421 SET_TARGET_TYPE(bld, modname, 'EMPTY')
424 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
427 source = bld.EXPAND_VARIABLES(source, vars=vars)
428 source = unique_list(TO_LIST(source))
430 deps += ' ' + public_deps
432 bld.SET_BUILD_GROUP(group)
442 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
443 depends_on = depends_on,
444 samba_deps = TO_LIST(deps),
445 samba_includes = includes,
446 local_include = local_include,
447 local_include_first = local_include_first,
448 samba_subsystem= subsystem_name,
449 samba_use_hostcc = use_hostcc,
450 samba_use_global_deps = use_global_deps
453 if cflags_end is not None:
454 t.samba_cflags.extend(TO_LIST(cflags_end))
456 if autoproto is not None:
457 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
458 if public_headers is not None:
459 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
463 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
466 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
467 group='generators', enabled=True,
472 '''A generic source generator target'''
474 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
480 bld.SET_BUILD_GROUP(group)
483 source=bld.EXPAND_VARIABLES(source, vars=vars),
485 shell=isinstance(rule, str),
494 if public_headers is not None:
495 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
497 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
502 def SETUP_BUILD_GROUPS(bld):
503 '''setup build groups used to ensure that the different build
504 phases happen consecutively'''
505 bld.p_ln = bld.srcnode # we do want to see all targets!
506 bld.env['USING_BUILD_GROUPS'] = True
507 bld.add_group('setup')
508 bld.add_group('build_compiler_source')
509 bld.add_group('base_libraries')
510 bld.add_group('generators')
511 bld.add_group('compiler_prototypes')
512 bld.add_group('compiler_libraries')
513 bld.add_group('build_compilers')
514 bld.add_group('build_source')
515 bld.add_group('prototypes')
516 bld.add_group('main')
517 bld.add_group('binaries')
518 bld.add_group('final')
519 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
522 def SET_BUILD_GROUP(bld, group):
523 '''set the current build group'''
524 if not 'USING_BUILD_GROUPS' in bld.env:
527 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
532 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
533 """use timestamps instead of file contents for deps
534 this currently doesn't work"""
535 def h_file(filename):
537 st = os.stat(filename)
538 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
540 m.update(str(st.st_mtime))
541 m.update(str(st.st_size))
544 Utils.h_file = h_file
548 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
549 shell=True, color='PINK', ext_in='.bin')
552 @feature('copy_script')
553 @before('apply_link')
554 def copy_script(self):
555 tsk = self.create_task('copy_script', self.allnodes[0])
556 tsk.env.TARGET = self.target
558 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
559 '''used to copy scripts from the source tree into the build directory
560 for use by selftest'''
562 source = bld.path.ant_glob(pattern)
564 bld.SET_BUILD_GROUP('build_source')
565 for s in TO_LIST(source):
567 if installname != None:
569 target = os.path.join(installdir, iname)
570 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
572 t = bld(features='copy_script',
577 t.env.LINK_TARGET = target
579 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
582 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
583 python_fixup=False, destname=None, base_name=None):
585 destdir = bld.EXPAND_VARIABLES(destdir)
589 destname = os.path.basename(destname)
590 dest = os.path.join(destdir, destname)
592 # fixup the python path it will use to find Samba modules
593 inst_file = file + '.inst'
594 if bld.env["PYTHONDIR"] not in sys.path:
595 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
597 # Eliminate updating sys.path if the target python dir is already
599 regex = "s|sys.path.insert.*bin/python.*$||g"
600 bld.SAMBA_GENERATOR('python_%s' % destname,
601 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
606 file = os.path.join(base_name, file)
607 bld.install_as(dest, file, chmod=chmod)
610 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
611 python_fixup=False, destname=None, base_name=None):
612 '''install a set of files'''
613 for f in TO_LIST(files):
614 install_file(bld, destdir, f, chmod=chmod, flat=flat,
615 python_fixup=python_fixup, destname=destname,
617 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
620 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
621 python_fixup=False, exclude=None, trim_path=None):
622 '''install a set of files matching a wildcard pattern'''
623 files=TO_LIST(bld.path.ant_glob(pattern))
627 files2.append(os_path_relpath(f, trim_path))
632 if fnmatch.fnmatch(f, exclude):
634 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
635 python_fixup=python_fixup, base_name=trim_path)
636 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
639 def INSTALL_DIRS(bld, destdir, dirs):
640 '''install a set of directories'''
641 destdir = bld.EXPAND_VARIABLES(destdir)
642 dirs = bld.EXPAND_VARIABLES(dirs)
643 for d in TO_LIST(dirs):
644 bld.install_dir(os.path.join(destdir, d))
645 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
648 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
649 class header_task(Task.Task):
651 The public headers (the one installed on the system) have both
652 different paths and contents, so the rename is not enough.
654 Intermediate .inst.h files are created because path manipulation
655 may be slow. The substitution is thus performed only once.
660 vars = ['INCLUDEDIR', 'HEADER_DEPS']
663 txt = self.inputs[0].read(self.env)
665 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
666 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
668 # use a regexp to substitute the #include lines in the files
669 map = self.generator.bld.hnodemap
670 dirnodes = self.generator.bld.hnodedirs
675 # pokemon headers: gotta catch'em all!
677 if s.startswith('bin/default'):
678 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
680 Logs.warn('could not find the public header for %r' % s)
684 Logs.warn('could not find the public header replacement for build header %r' % s)
686 # this part is more difficult since the path may be relative to anything
687 for dirnode in dirnodes:
688 node = dirnode.find_resource(s)
694 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
696 Logs.warn('-> could not find the public header for %r' % s)
698 return "#include <%s>" % fin
701 txt = re_header.sub(repl, txt)
703 # and write the output file
706 f = open(self.outputs[0].abspath(self.env), 'w')
712 @TaskGen.feature('pubh')
713 def make_public_headers(self):
715 collect the public headers to process and to install, then
716 create the substitutions (name and contents)
719 if not self.bld.is_install:
720 # install time only (lazy)
724 # hnodedirs: list of folders for searching the headers
725 # hnodemap: node ids and replacement string (node objects are unique)
727 self.bld.hnodedirs.append(self.path)
728 except AttributeError:
729 self.bld.hnodemap = {}
730 self.bld.hnodedirs = [self.bld.srcnode, self.path]
732 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
733 node = self.bld.srcnode.find_dir(k)
735 self.bld.hnodedirs.append(node)
737 header_path = getattr(self, 'header_path', None) or ''
739 for x in self.to_list(self.headers):
741 # too complicated, but what was the original idea?
742 if isinstance(header_path, list):
744 for (p1, dir) in header_path:
745 lst = self.to_list(p1)
747 if fnmatch.fnmatch(x, p2):
755 inst_path = header_path
759 if x.find(':') != -1:
764 inn = self.path.find_resource(name)
767 raise ValueError("could not find the public header %r in %r" % (name, self.path))
768 out = inn.change_ext('.inst.h')
769 self.create_task('header', inn, out)
775 inst_path = inst_path + '/'
776 inst_path = inst_path + dest
778 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
780 self.bld.hnodemap[inn.id] = inst_path
782 # create a hash (not md5) to make sure the headers are re-created if something changes
784 lst = list(self.bld.hnodemap.keys())
787 val = hash((val, k, self.bld.hnodemap[k]))
788 self.bld.env.HEADER_DEPS = val
790 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
791 '''install some headers
793 header_path may either be a string that is added to the INCLUDEDIR,
794 or it can be a dictionary of wildcard patterns which map to destination
795 directories relative to INCLUDEDIR
797 bld.SET_BUILD_GROUP('final')
798 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
800 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
803 def subst_at_vars(task):
804 '''substiture @VAR@ style variables in a file'''
805 src = task.inputs[0].srcpath(task.env)
806 tgt = task.outputs[0].bldpath(task.env)
812 a = re.split('(@\w+@)', s)
815 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
817 if re.match('@\w+@', v):
819 if not vname in task.env and vname.upper() in task.env:
820 vname = vname.upper()
821 if not vname in task.env:
822 Logs.error("Unknown substitution %s in %s" % (v, task.name))
824 v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
825 # now we back substitute the allowed pc vars
826 for (b, m) in back_sub:
829 if not b in done_var:
830 # we don't want to substitute the first usage
836 contents = ''.join(out)
838 s = f.write(contents)
844 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
845 '''install some pkg_config pc files'''
846 dest = '${PKGCONFIGDIR}'
847 dest = bld.EXPAND_VARIABLES(dest)
848 for f in TO_LIST(pc_files):
849 base=os.path.basename(f)
850 t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
855 t.env.PACKAGE_VERSION = vnum
856 INSTALL_FILES(bld, dest, f, flat=True, destname=base)
857 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
860 def MANPAGES(bld, manpages):
861 '''build and install manual pages'''
862 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
863 for m in manpages.split():
865 bld.SAMBA_GENERATOR(m,
869 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
871 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
872 Build.BuildContext.MANPAGES = MANPAGES
875 #############################################################
876 # give a nicer display when building different types of files
877 def progress_display(self, msg, fname):
878 col1 = Logs.colors(self.color)
879 col2 = Logs.colors.NORMAL
880 total = self.position[1]
882 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
883 return fs % (self.position[0], self.position[1], col1, fname, col2)
885 def link_display(self):
886 if Options.options.progress_bar != 0:
887 return Task.Task.old_display(self)
888 fname = self.outputs[0].bldpath(self.env)
889 return progress_display(self, 'Linking', fname)
890 Task.TaskBase.classes['cc_link'].display = link_display
892 def samba_display(self):
893 if Options.options.progress_bar != 0:
894 return Task.Task.old_display(self)
896 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
897 if self.name in targets:
898 target_type = targets[self.name]
899 type_map = { 'GENERATOR' : 'Generating',
900 'PROTOTYPE' : 'Generating'
902 if target_type in type_map:
903 return progress_display(self, type_map[target_type], self.name)
905 fname = self.inputs[0].bldpath(self.env)
906 if fname[0:3] == '../':
908 ext_loc = fname.rfind('.')
910 return Task.Task.old_display(self)
911 ext = fname[ext_loc:]
913 ext_map = { '.idl' : 'Compiling IDL',
914 '.et' : 'Compiling ERRTABLE',
915 '.asn1': 'Compiling ASN1',
918 return progress_display(self, ext_map[ext], fname)
919 return Task.Task.old_display(self)
921 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
922 Task.TaskBase.classes['Task'].display = samba_display