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,
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 needs_python = needs_python,
154 hide_symbols = hide_symbols,
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 if target_type == 'PYTHON' or realname or not is_bundled:
169 # Sanitize the library name
170 bundled_name = libname.lower().replace('_', '-')
171 while bundled_name.startswith("lib"):
172 bundled_name = bundled_name[3:]
174 bundled_name = BUNDLED_NAME(bld, libname, bundled_extension)
176 features = 'cc cshlib symlink_lib install_lib'
177 if target_type == 'PYTHON':
180 features += ' pyembed'
182 features += ' abi_check'
185 abi_file = os.path.join(bld.curdir, abi_file)
187 bld.SET_BUILD_GROUP(group)
191 target = bundled_name,
192 samba_cflags = CURRENT_CFLAGS(bld, libname, cflags),
193 depends_on = depends_on,
195 samba_includes = includes,
196 local_include = local_include,
199 samba_inst_path = install_path,
201 samba_realname = realname,
202 samba_install = install,
204 abi_match = abi_match,
205 is_bundled = is_bundled
208 if realname and not link_name:
209 link_name = 'shared/%s' % realname
212 t.link_name = link_name
214 if pc_files is not None:
215 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
217 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
218 bld.MANPAGES(manpages)
221 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
224 #################################################################
225 def SAMBA_BINARY(bld, binname, source,
235 use_global_deps=True,
246 '''define a Samba binary'''
249 SET_TARGET_TYPE(bld, binname, 'DISABLED')
252 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
255 features = 'cc cprogram symlink_bin install_bin'
257 features += ' pyembed'
259 obj_target = binname + '.objlist'
261 source = bld.EXPAND_VARIABLES(source, vars=vars)
262 source = unique_list(TO_LIST(source))
264 # first create a target for building the object files for this binary
265 # by separating in this way, we avoid recompiling the C files
266 # separately for the install binary and the build binary
267 bld.SAMBA_SUBSYSTEM(obj_target,
273 autoproto = autoproto,
274 subsystem_name = subsystem_name,
275 needs_python = needs_python,
276 local_include = local_include,
277 use_hostcc = use_hostcc,
278 use_global_deps= use_global_deps)
280 bld.SET_BUILD_GROUP(group)
282 # the binary itself will depend on that object target
284 deps.append(obj_target)
290 samba_cflags = CURRENT_CFLAGS(bld, binname, cflags),
292 samba_includes = includes,
293 local_include = local_include,
294 samba_modules = modules,
296 samba_subsystem= subsystem_name,
298 samba_inst_path= install_path,
299 samba_install = install
302 # setup the subsystem_name as an alias for the real
303 # binary name, so it can be found when expanding
304 # subsystem dependencies
305 if subsystem_name is not None:
306 bld.TARGET_ALIAS(subsystem_name, binname)
308 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
309 bld.MANPAGES(manpages)
311 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
314 #################################################################
315 def SAMBA_MODULE(bld, modname, source,
321 autoproto_extra_source='',
324 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)
371 bld.SAMBA_LIBRARY(modname,
376 autoproto = autoproto,
377 local_include=local_include,
379 link_name=build_link_name,
380 install_path="${MODULESDIR}/%s" % subsystem
383 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
386 #################################################################
387 def SAMBA_SUBSYSTEM(bld, modname, source,
396 init_function_sentinal=None,
398 autoproto_extra_source='',
401 local_include_first=True,
405 use_global_deps=True,
409 '''define a Samba subsystem'''
412 SET_TARGET_TYPE(bld, modname, 'DISABLED')
415 # remember empty subsystems, so we can strip the dependencies
416 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
417 SET_TARGET_TYPE(bld, modname, 'EMPTY')
420 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
423 source = bld.EXPAND_VARIABLES(source, vars=vars)
424 source = unique_list(TO_LIST(source))
426 deps += ' ' + public_deps
428 bld.SET_BUILD_GROUP(group)
438 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
439 depends_on = depends_on,
440 samba_deps = TO_LIST(deps),
441 samba_includes = includes,
442 local_include = local_include,
443 local_include_first = local_include_first,
444 samba_subsystem= subsystem_name,
445 samba_use_hostcc = use_hostcc,
446 samba_use_global_deps = use_global_deps
449 if cflags_end is not None:
450 t.samba_cflags.extend(TO_LIST(cflags_end))
452 if autoproto is not None:
453 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
454 if public_headers is not None:
455 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
459 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
462 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
463 group='generators', enabled=True,
468 '''A generic source generator target'''
470 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
476 bld.SET_BUILD_GROUP(group)
479 source=bld.EXPAND_VARIABLES(source, vars=vars),
481 shell=isinstance(rule, str),
490 if public_headers is not None:
491 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
493 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
498 def SETUP_BUILD_GROUPS(bld):
499 '''setup build groups used to ensure that the different build
500 phases happen consecutively'''
501 bld.p_ln = bld.srcnode # we do want to see all targets!
502 bld.env['USING_BUILD_GROUPS'] = True
503 bld.add_group('setup')
504 bld.add_group('build_compiler_source')
505 bld.add_group('base_libraries')
506 bld.add_group('generators')
507 bld.add_group('compiler_prototypes')
508 bld.add_group('compiler_libraries')
509 bld.add_group('build_compilers')
510 bld.add_group('build_source')
511 bld.add_group('prototypes')
512 bld.add_group('main')
513 bld.add_group('binaries')
514 bld.add_group('final')
515 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
518 def SET_BUILD_GROUP(bld, group):
519 '''set the current build group'''
520 if not 'USING_BUILD_GROUPS' in bld.env:
523 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
528 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
529 """use timestamps instead of file contents for deps
530 this currently doesn't work"""
531 def h_file(filename):
533 st = os.stat(filename)
534 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
536 m.update(str(st.st_mtime))
537 m.update(str(st.st_size))
540 Utils.h_file = h_file
544 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
545 shell=True, color='PINK', ext_in='.bin')
548 @feature('copy_script')
549 @before('apply_link')
550 def copy_script(self):
551 tsk = self.create_task('copy_script', self.allnodes[0])
552 tsk.env.TARGET = self.target
554 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
555 '''used to copy scripts from the source tree into the build directory
556 for use by selftest'''
558 source = bld.path.ant_glob(pattern)
560 bld.SET_BUILD_GROUP('build_source')
561 for s in TO_LIST(source):
563 if installname != None:
565 target = os.path.join(installdir, iname)
566 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
568 t = bld(features='copy_script',
573 t.env.LINK_TARGET = target
575 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
578 def install_file(bld, destdir, file, chmod=0644, flat=False,
579 python_fixup=False, destname=None, base_name=None):
581 destdir = bld.EXPAND_VARIABLES(destdir)
585 destname = os.path.basename(destname)
586 dest = os.path.join(destdir, destname)
588 # fixup the python path it will use to find Samba modules
589 inst_file = file + '.inst'
590 if bld.env["PYTHONDIR"] not in sys.path:
591 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
593 # Eliminate updating sys.path if the target python dir is already
595 regex = "s|sys.path.insert.*bin/python.*$||g"
596 bld.SAMBA_GENERATOR('python_%s' % destname,
597 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
602 file = os.path.join(base_name, file)
603 bld.install_as(dest, file, chmod=chmod)
606 def INSTALL_FILES(bld, destdir, files, chmod=0644, flat=False,
607 python_fixup=False, destname=None, base_name=None):
608 '''install a set of files'''
609 for f in TO_LIST(files):
610 install_file(bld, destdir, f, chmod=chmod, flat=flat,
611 python_fixup=python_fixup, destname=destname,
613 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
616 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=0644, flat=False,
617 python_fixup=False, exclude=None, trim_path=None):
618 '''install a set of files matching a wildcard pattern'''
619 files=TO_LIST(bld.path.ant_glob(pattern))
623 files2.append(os_path_relpath(f, trim_path))
628 if fnmatch.fnmatch(f, exclude):
630 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
631 python_fixup=python_fixup, base_name=trim_path)
632 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
635 def INSTALL_DIRS(bld, destdir, dirs):
636 '''install a set of directories'''
637 destdir = bld.EXPAND_VARIABLES(destdir)
638 dirs = bld.EXPAND_VARIABLES(dirs)
639 for d in TO_LIST(dirs):
640 bld.install_dir(os.path.join(destdir, d))
641 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
644 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
645 class header_task(Task.Task):
647 The public headers (the one installed on the system) have both
648 different paths and contents, so the rename is not enough.
650 Intermediate .inst.h files are created because path manipulation
651 may be slow. The substitution is thus performed only once.
656 vars = ['INCLUDEDIR', 'HEADER_DEPS']
659 txt = self.inputs[0].read(self.env)
661 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
662 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
664 # use a regexp to substitute the #include lines in the files
665 map = self.generator.bld.hnodemap
666 dirnodes = self.generator.bld.hnodedirs
671 # pokemon headers: gotta catch'em all!
673 if s.startswith('bin/default'):
674 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
676 Logs.warn('could not find the public header for %r' % s)
680 Logs.warn('could not find the public header replacement for build header %r' % s)
682 # this part is more difficult since the path may be relative to anything
683 for dirnode in dirnodes:
684 node = dirnode.find_resource(s)
690 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
692 Logs.warn('-> could not find the public header for %r' % s)
694 return "#include <%s>" % fin
697 txt = re_header.sub(repl, txt)
699 # and write the output file
702 f = open(self.outputs[0].abspath(self.env), 'w')
708 @TaskGen.feature('pubh')
709 def make_public_headers(self):
711 collect the public headers to process and to install, then
712 create the substitutions (name and contents)
715 if not self.bld.is_install:
716 # install time only (lazy)
720 # hnodedirs: list of folders for searching the headers
721 # hnodemap: node ids and replacement string (node objects are unique)
723 self.bld.hnodedirs.append(self.path)
724 except AttributeError:
725 self.bld.hnodemap = {}
726 self.bld.hnodedirs = [self.bld.srcnode, self.path]
728 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
729 node = self.bld.srcnode.find_dir(k)
731 self.bld.hnodedirs.append(node)
733 header_path = getattr(self, 'header_path', None) or ''
735 for x in self.to_list(self.headers):
737 # too complicated, but what was the original idea?
738 if isinstance(header_path, list):
740 for (p1, dir) in header_path:
741 lst = self.to_list(p1)
743 if fnmatch.fnmatch(x, p2):
751 inst_path = header_path
755 if x.find(':') != -1:
760 inn = self.path.find_resource(name)
763 raise ValueError("could not find the public header %r in %r" % (name, self.path))
764 out = inn.change_ext('.inst.h')
765 self.create_task('header', inn, out)
771 inst_path = inst_path + '/'
772 inst_path = inst_path + dest
774 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
776 self.bld.hnodemap[inn.id] = inst_path
778 # create a hash (not md5) to make sure the headers are re-created if something changes
780 lst = list(self.bld.hnodemap.keys())
783 val = hash((val, k, self.bld.hnodemap[k]))
784 self.bld.env.HEADER_DEPS = val
786 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
787 '''install some headers
789 header_path may either be a string that is added to the INCLUDEDIR,
790 or it can be a dictionary of wildcard patterns which map to destination
791 directories relative to INCLUDEDIR
793 bld.SET_BUILD_GROUP('final')
794 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
796 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
799 def subst_at_vars(task):
800 '''substiture @VAR@ style variables in a file'''
801 src = task.inputs[0].srcpath(task.env)
802 tgt = task.outputs[0].bldpath(task.env)
808 a = re.split('(@\w+@)', s)
811 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
813 if re.match('@\w+@', v):
815 if not vname in task.env and vname.upper() in task.env:
816 vname = vname.upper()
817 if not vname in task.env:
818 Logs.error("Unknown substitution %s in %s" % (v, task.name))
820 v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
821 # now we back substitute the allowed pc vars
822 for (b, m) in back_sub:
825 if not b in done_var:
826 # we don't want to substitute the first usage
832 contents = ''.join(out)
834 s = f.write(contents)
840 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
841 '''install some pkg_config pc files'''
842 dest = '${PKGCONFIGDIR}'
843 dest = bld.EXPAND_VARIABLES(dest)
844 for f in TO_LIST(pc_files):
845 base=os.path.basename(f)
846 t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
851 t.env.PACKAGE_VERSION = vnum
852 INSTALL_FILES(bld, dest, f, flat=True, destname=base)
853 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
856 def MANPAGES(bld, manpages):
857 '''build and install manual pages'''
858 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
859 for m in manpages.split():
861 bld.SAMBA_GENERATOR(m,
865 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
867 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
868 Build.BuildContext.MANPAGES = MANPAGES
871 #############################################################
872 # give a nicer display when building different types of files
873 def progress_display(self, msg, fname):
874 col1 = Logs.colors(self.color)
875 col2 = Logs.colors.NORMAL
876 total = self.position[1]
878 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
879 return fs % (self.position[0], self.position[1], col1, fname, col2)
881 def link_display(self):
882 if Options.options.progress_bar != 0:
883 return Task.Task.old_display(self)
884 fname = self.outputs[0].bldpath(self.env)
885 return progress_display(self, 'Linking', fname)
886 Task.TaskBase.classes['cc_link'].display = link_display
888 def samba_display(self):
889 if Options.options.progress_bar != 0:
890 return Task.Task.old_display(self)
892 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
893 if self.name in targets:
894 target_type = targets[self.name]
895 type_map = { 'GENERATOR' : 'Generating',
896 'PROTOTYPE' : 'Generating'
898 if target_type in type_map:
899 return progress_display(self, type_map[target_type], self.name)
901 fname = self.inputs[0].bldpath(self.env)
902 if fname[0:3] == '../':
904 ext_loc = fname.rfind('.')
906 return Task.Task.old_display(self)
907 ext = fname[ext_loc:]
909 ext_map = { '.idl' : 'Compiling IDL',
910 '.et' : 'Compiling ERRTABLE',
911 '.asn1': 'Compiling ASN1',
914 return progress_display(self, ext_map[ext], fname)
915 return Task.Task.old_display(self)
917 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
918 Task.TaskBase.classes['Task'].display = samba_display