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,
112 target_type='LIBRARY',
113 bundled_extension=True,
119 private_library=False,
120 grouping_library=False,
122 '''define a Samba library'''
125 SET_TARGET_TYPE(bld, libname, 'DISABLED')
128 source = bld.EXPAND_VARIABLES(source, vars=vars)
130 # remember empty libraries, so we can strip the dependencies
131 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
132 SET_TARGET_TYPE(bld, libname, 'EMPTY')
135 if BUILTIN_LIBRARY(bld, libname):
138 obj_target = libname + '.objlist'
140 # first create a target for building the object files for this library
141 # by separating in this way, we avoid recompiling the C files
142 # separately for the install library and the build library
143 bld.SAMBA_SUBSYSTEM(obj_target,
146 public_deps = public_deps,
148 public_headers = public_headers,
149 header_path = header_path,
152 autoproto = autoproto,
153 depends_on = depends_on,
154 hide_symbols = hide_symbols,
155 pyext = pyext or (target_type == "PYTHON"),
156 local_include = local_include)
158 if BUILTIN_LIBRARY(bld, libname):
161 if not SET_TARGET_TYPE(bld, libname, target_type):
164 # the library itself will depend on that object target
165 deps += ' ' + public_deps
167 deps.append(obj_target)
169 realname = bld.map_shlib_extension(realname, python=(target_type=='PYTHON'))
170 link_name = bld.map_shlib_extension(link_name, python=(target_type=='PYTHON'))
172 # we don't want any public libraries without version numbers
173 if not private_library and vnum is None and target_type != 'PYTHON' and not realname:
174 raise Utils.WafError("public library '%s' must have a vnum" % libname)
176 if target_type == 'PYTHON' or realname or not private_library:
177 # Sanitize the library name
178 bundled_name = libname.lower().replace('_', '-')
179 while bundled_name.startswith("lib"):
180 bundled_name = bundled_name[3:]
182 bundled_name = PRIVATE_NAME(bld, libname, bundled_extension, private_library)
184 features = 'cc cshlib symlink_lib install_lib'
185 if target_type == 'PYTHON':
188 # this is quite strange. we should add pyext feature for pyext
189 # but that breaks the build. This may be a bug in the waf python tool
190 features += ' pyembed'
192 features += ' abi_check'
195 abi_file = os.path.join(bld.curdir, abi_file)
197 bld.SET_BUILD_GROUP(group)
201 target = bundled_name,
202 depends_on = depends_on,
204 samba_includes = includes,
205 local_include = local_include,
208 samba_inst_path = install_path,
210 samba_realname = realname,
211 samba_install = install,
213 abi_match = abi_match,
214 private_library = private_library,
215 grouping_library=grouping_library
218 if realname and not link_name:
219 link_name = 'shared/%s' % realname
222 t.link_name = link_name
224 if pc_files is not None:
225 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
227 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
228 bld.MANPAGES(manpages)
231 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
234 #################################################################
235 def SAMBA_BINARY(bld, binname, source,
245 use_global_deps=True,
256 '''define a Samba binary'''
259 SET_TARGET_TYPE(bld, binname, 'DISABLED')
262 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
265 features = 'cc cprogram symlink_bin install_bin'
267 features += ' pyembed'
269 obj_target = binname + '.objlist'
271 source = bld.EXPAND_VARIABLES(source, vars=vars)
272 source = unique_list(TO_LIST(source))
274 # first create a target for building the object files for this binary
275 # by separating in this way, we avoid recompiling the C files
276 # separately for the install binary and the build binary
277 bld.SAMBA_SUBSYSTEM(obj_target,
283 autoproto = autoproto,
284 subsystem_name = subsystem_name,
285 local_include = local_include,
286 use_hostcc = use_hostcc,
288 use_global_deps= use_global_deps)
290 bld.SET_BUILD_GROUP(group)
292 # the binary itself will depend on that object target
294 deps.append(obj_target)
301 samba_includes = includes,
302 local_include = local_include,
303 samba_modules = modules,
305 samba_subsystem= subsystem_name,
307 samba_inst_path= install_path,
308 samba_install = install
311 # setup the subsystem_name as an alias for the real
312 # binary name, so it can be found when expanding
313 # subsystem dependencies
314 if subsystem_name is not None:
315 bld.TARGET_ALIAS(subsystem_name, binname)
317 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
318 bld.MANPAGES(manpages)
320 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
323 #################################################################
324 def SAMBA_MODULE(bld, modname, source,
330 autoproto_extra_source='',
333 internal_module=True,
339 '''define a Samba module.'''
341 source = bld.EXPAND_VARIABLES(source, vars=vars)
343 if internal_module or BUILTIN_LIBRARY(bld, modname):
344 # treat internal modules as subsystems for now
345 if subsystem is not None:
346 deps += ' ' + subsystem
348 bld.SAMBA_SUBSYSTEM(modname, source,
352 autoproto_extra_source=autoproto_extra_source,
354 local_include=local_include,
357 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
361 SET_TARGET_TYPE(bld, modname, 'DISABLED')
364 if aliases is not None:
365 # if we have aliases, then create a private base library, and a set
366 # of modules on top of that library
368 cflags += " -D%s=samba_init_module" % init_function
370 basename = modname + '-base'
371 bld.SAMBA_LIBRARY(basename,
375 autoproto = autoproto,
376 local_include=local_include,
382 aliases = TO_LIST(aliases)
383 aliases.append(modname)
385 for alias in aliases:
386 bld.SAMBA_MODULE(alias,
388 internal_module=False,
390 init_function=init_function,
395 obj_target = modname + '.objlist'
398 if subsystem is not None:
399 deps += ' ' + subsystem
400 while realname.startswith("lib"+subsystem+"_"):
401 realname = realname[len("lib"+subsystem+"_"):]
402 while realname.startswith(subsystem+"_"):
403 realname = realname[len(subsystem+"_"):]
405 realname = bld.make_libname(realname)
406 while realname.startswith("lib"):
407 realname = realname[len("lib"):]
409 build_link_name = "modules/%s/%s" % (subsystem, realname)
412 cflags += " -D%s=samba_init_module" % init_function
414 bld.SAMBA_LIBRARY(modname,
419 autoproto = autoproto,
420 local_include=local_include,
422 link_name=build_link_name,
423 install_path="${MODULESDIR}/%s" % subsystem,
428 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
431 #################################################################
432 def SAMBA_SUBSYSTEM(bld, modname, source,
441 init_function_sentinal=None,
443 autoproto_extra_source='',
446 local_include_first=True,
450 use_global_deps=True,
454 '''define a Samba subsystem'''
457 SET_TARGET_TYPE(bld, modname, 'DISABLED')
460 # remember empty subsystems, so we can strip the dependencies
461 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
462 SET_TARGET_TYPE(bld, modname, 'EMPTY')
465 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
468 source = bld.EXPAND_VARIABLES(source, vars=vars)
469 source = unique_list(TO_LIST(source))
471 deps += ' ' + public_deps
473 bld.SET_BUILD_GROUP(group)
483 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
484 depends_on = depends_on,
485 samba_deps = TO_LIST(deps),
486 samba_includes = includes,
487 local_include = local_include,
488 local_include_first = local_include_first,
489 samba_subsystem= subsystem_name,
490 samba_use_hostcc = use_hostcc,
491 samba_use_global_deps = use_global_deps
494 if cflags_end is not None:
495 t.samba_cflags.extend(TO_LIST(cflags_end))
497 if autoproto is not None:
498 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
499 if public_headers is not None:
500 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
504 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
507 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
508 group='generators', enabled=True,
513 '''A generic source generator target'''
515 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
521 bld.SET_BUILD_GROUP(group)
524 source=bld.EXPAND_VARIABLES(source, vars=vars),
526 shell=isinstance(rule, str),
535 if public_headers is not None:
536 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
538 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
543 def SETUP_BUILD_GROUPS(bld):
544 '''setup build groups used to ensure that the different build
545 phases happen consecutively'''
546 bld.p_ln = bld.srcnode # we do want to see all targets!
547 bld.env['USING_BUILD_GROUPS'] = True
548 bld.add_group('setup')
549 bld.add_group('build_compiler_source')
550 bld.add_group('base_libraries')
551 bld.add_group('generators')
552 bld.add_group('compiler_prototypes')
553 bld.add_group('compiler_libraries')
554 bld.add_group('build_compilers')
555 bld.add_group('build_source')
556 bld.add_group('prototypes')
557 bld.add_group('main')
558 bld.add_group('binaries')
559 bld.add_group('final')
560 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
563 def SET_BUILD_GROUP(bld, group):
564 '''set the current build group'''
565 if not 'USING_BUILD_GROUPS' in bld.env:
568 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
573 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
574 """use timestamps instead of file contents for deps
575 this currently doesn't work"""
576 def h_file(filename):
578 st = os.stat(filename)
579 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
581 m.update(str(st.st_mtime))
582 m.update(str(st.st_size))
585 Utils.h_file = h_file
589 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
590 shell=True, color='PINK', ext_in='.bin')
593 @feature('copy_script')
594 @before('apply_link')
595 def copy_script(self):
596 tsk = self.create_task('copy_script', self.allnodes[0])
597 tsk.env.TARGET = self.target
599 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
600 '''used to copy scripts from the source tree into the build directory
601 for use by selftest'''
603 source = bld.path.ant_glob(pattern)
605 bld.SET_BUILD_GROUP('build_source')
606 for s in TO_LIST(source):
608 if installname != None:
610 target = os.path.join(installdir, iname)
611 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
613 t = bld(features='copy_script',
618 t.env.LINK_TARGET = target
620 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
623 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
624 python_fixup=False, destname=None, base_name=None):
626 destdir = bld.EXPAND_VARIABLES(destdir)
630 destname = os.path.basename(destname)
631 dest = os.path.join(destdir, destname)
633 # fixup the python path it will use to find Samba modules
634 inst_file = file + '.inst'
635 if bld.env["PYTHONDIR"] not in sys.path:
636 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
638 # Eliminate updating sys.path if the target python dir is already
640 regex = "s|sys.path.insert.*bin/python.*$||g"
641 bld.SAMBA_GENERATOR('python_%s' % destname,
642 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
647 file = os.path.join(base_name, file)
648 bld.install_as(dest, file, chmod=chmod)
651 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
652 python_fixup=False, destname=None, base_name=None):
653 '''install a set of files'''
654 for f in TO_LIST(files):
655 install_file(bld, destdir, f, chmod=chmod, flat=flat,
656 python_fixup=python_fixup, destname=destname,
658 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
661 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
662 python_fixup=False, exclude=None, trim_path=None):
663 '''install a set of files matching a wildcard pattern'''
664 files=TO_LIST(bld.path.ant_glob(pattern))
668 files2.append(os_path_relpath(f, trim_path))
673 if fnmatch.fnmatch(f, exclude):
675 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
676 python_fixup=python_fixup, base_name=trim_path)
677 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
680 def INSTALL_DIRS(bld, destdir, dirs):
681 '''install a set of directories'''
682 destdir = bld.EXPAND_VARIABLES(destdir)
683 dirs = bld.EXPAND_VARIABLES(dirs)
684 for d in TO_LIST(dirs):
685 bld.install_dir(os.path.join(destdir, d))
686 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
689 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
690 class header_task(Task.Task):
692 The public headers (the one installed on the system) have both
693 different paths and contents, so the rename is not enough.
695 Intermediate .inst.h files are created because path manipulation
696 may be slow. The substitution is thus performed only once.
701 vars = ['INCLUDEDIR', 'HEADER_DEPS']
704 txt = self.inputs[0].read(self.env)
706 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
707 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
709 # use a regexp to substitute the #include lines in the files
710 map = self.generator.bld.hnodemap
711 dirnodes = self.generator.bld.hnodedirs
716 # pokemon headers: gotta catch'em all!
718 if s.startswith('bin/default'):
719 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
721 Logs.warn('could not find the public header for %r' % s)
725 Logs.warn('could not find the public header replacement for build header %r' % s)
727 # this part is more difficult since the path may be relative to anything
728 for dirnode in dirnodes:
729 node = dirnode.find_resource(s)
735 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
737 Logs.warn('-> could not find the public header for %r' % s)
739 return "#include <%s>" % fin
742 txt = re_header.sub(repl, txt)
744 # and write the output file
747 f = open(self.outputs[0].abspath(self.env), 'w')
753 @TaskGen.feature('pubh')
754 def make_public_headers(self):
756 collect the public headers to process and to install, then
757 create the substitutions (name and contents)
760 if not self.bld.is_install:
761 # install time only (lazy)
765 # hnodedirs: list of folders for searching the headers
766 # hnodemap: node ids and replacement string (node objects are unique)
768 self.bld.hnodedirs.append(self.path)
769 except AttributeError:
770 self.bld.hnodemap = {}
771 self.bld.hnodedirs = [self.bld.srcnode, self.path]
773 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
774 node = self.bld.srcnode.find_dir(k)
776 self.bld.hnodedirs.append(node)
778 header_path = getattr(self, 'header_path', None) or ''
780 for x in self.to_list(self.headers):
782 # too complicated, but what was the original idea?
783 if isinstance(header_path, list):
785 for (p1, dir) in header_path:
786 lst = self.to_list(p1)
788 if fnmatch.fnmatch(x, p2):
796 inst_path = header_path
800 if x.find(':') != -1:
805 inn = self.path.find_resource(name)
808 raise ValueError("could not find the public header %r in %r" % (name, self.path))
809 out = inn.change_ext('.inst.h')
810 self.create_task('header', inn, out)
816 inst_path = inst_path + '/'
817 inst_path = inst_path + dest
819 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
821 self.bld.hnodemap[inn.id] = inst_path
823 # create a hash (not md5) to make sure the headers are re-created if something changes
825 lst = list(self.bld.hnodemap.keys())
828 val = hash((val, k, self.bld.hnodemap[k]))
829 self.bld.env.HEADER_DEPS = val
831 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
832 '''install some headers
834 header_path may either be a string that is added to the INCLUDEDIR,
835 or it can be a dictionary of wildcard patterns which map to destination
836 directories relative to INCLUDEDIR
838 bld.SET_BUILD_GROUP('final')
839 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
841 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
844 def subst_at_vars(task):
845 '''substiture @VAR@ style variables in a file'''
846 src = task.inputs[0].srcpath(task.env)
847 tgt = task.outputs[0].bldpath(task.env)
853 a = re.split('(@\w+@)', s)
856 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
858 if re.match('@\w+@', v):
860 if not vname in task.env and vname.upper() in task.env:
861 vname = vname.upper()
862 if not vname in task.env:
863 Logs.error("Unknown substitution %s in %s" % (v, task.name))
865 v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
866 # now we back substitute the allowed pc vars
867 for (b, m) in back_sub:
870 if not b in done_var:
871 # we don't want to substitute the first usage
877 contents = ''.join(out)
879 s = f.write(contents)
885 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
886 '''install some pkg_config pc files'''
887 dest = '${PKGCONFIGDIR}'
888 dest = bld.EXPAND_VARIABLES(dest)
889 for f in TO_LIST(pc_files):
890 base=os.path.basename(f)
891 t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
896 t.env.PACKAGE_VERSION = vnum
897 INSTALL_FILES(bld, dest, f, flat=True, destname=base)
898 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
901 def MANPAGES(bld, manpages):
902 '''build and install manual pages'''
903 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
904 for m in manpages.split():
906 bld.SAMBA_GENERATOR(m,
910 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
912 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
913 Build.BuildContext.MANPAGES = MANPAGES
916 #############################################################
917 # give a nicer display when building different types of files
918 def progress_display(self, msg, fname):
919 col1 = Logs.colors(self.color)
920 col2 = Logs.colors.NORMAL
921 total = self.position[1]
923 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
924 return fs % (self.position[0], self.position[1], col1, fname, col2)
926 def link_display(self):
927 if Options.options.progress_bar != 0:
928 return Task.Task.old_display(self)
929 fname = self.outputs[0].bldpath(self.env)
930 return progress_display(self, 'Linking', fname)
931 Task.TaskBase.classes['cc_link'].display = link_display
933 def samba_display(self):
934 if Options.options.progress_bar != 0:
935 return Task.Task.old_display(self)
937 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
938 if self.name in targets:
939 target_type = targets[self.name]
940 type_map = { 'GENERATOR' : 'Generating',
941 'PROTOTYPE' : 'Generating'
943 if target_type in type_map:
944 return progress_display(self, type_map[target_type], self.name)
946 fname = self.inputs[0].bldpath(self.env)
947 if fname[0:3] == '../':
949 ext_loc = fname.rfind('.')
951 return Task.Task.old_display(self)
952 ext = fname[ext_loc:]
954 ext_map = { '.idl' : 'Compiling IDL',
955 '.et' : 'Compiling ERRTABLE',
956 '.asn1': 'Compiling ASN1',
959 return progress_display(self, ext_map[ext], fname)
960 return Task.Task.old_display(self)
962 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
963 Task.TaskBase.classes['Task'].display = samba_display