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)
186 Logs.error("vnum is invalid for private libraries")
188 vnum = bld.env.PACKAGE_VERSION
190 features = 'cc cshlib symlink_lib install_lib'
191 if target_type == 'PYTHON':
194 # this is quite strange. we should add pyext feature for pyext
195 # but that breaks the build. This may be a bug in the waf python tool
196 features += ' pyembed'
198 features += ' abi_check'
201 abi_file = os.path.join(bld.curdir, abi_file)
203 bld.SET_BUILD_GROUP(group)
207 target = bundled_name,
208 depends_on = depends_on,
210 samba_includes = includes,
211 local_include = local_include,
214 samba_inst_path = install_path,
216 samba_realname = realname,
217 samba_install = install,
219 abi_match = abi_match,
220 private_library = private_library,
221 grouping_library=grouping_library
224 if realname and not link_name:
225 link_name = 'shared/%s' % realname
228 t.link_name = link_name
230 if pc_files is not None:
231 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
233 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
234 bld.MANPAGES(manpages)
237 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
240 #################################################################
241 def SAMBA_BINARY(bld, binname, source,
251 use_global_deps=True,
262 '''define a Samba binary'''
265 SET_TARGET_TYPE(bld, binname, 'DISABLED')
268 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
271 features = 'cc cprogram symlink_bin install_bin'
273 features += ' pyembed'
275 obj_target = binname + '.objlist'
277 source = bld.EXPAND_VARIABLES(source, vars=vars)
278 source = unique_list(TO_LIST(source))
280 # first create a target for building the object files for this binary
281 # by separating in this way, we avoid recompiling the C files
282 # separately for the install binary and the build binary
283 bld.SAMBA_SUBSYSTEM(obj_target,
289 autoproto = autoproto,
290 subsystem_name = subsystem_name,
291 local_include = local_include,
292 use_hostcc = use_hostcc,
294 use_global_deps= use_global_deps)
296 bld.SET_BUILD_GROUP(group)
298 # the binary itself will depend on that object target
300 deps.append(obj_target)
307 samba_includes = includes,
308 local_include = local_include,
309 samba_modules = modules,
311 samba_subsystem= subsystem_name,
313 samba_inst_path= install_path,
314 samba_install = install
317 # setup the subsystem_name as an alias for the real
318 # binary name, so it can be found when expanding
319 # subsystem dependencies
320 if subsystem_name is not None:
321 bld.TARGET_ALIAS(subsystem_name, binname)
323 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
324 bld.MANPAGES(manpages)
326 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
329 #################################################################
330 def SAMBA_MODULE(bld, modname, source,
336 autoproto_extra_source='',
339 internal_module=True,
345 '''define a Samba module.'''
347 source = bld.EXPAND_VARIABLES(source, vars=vars)
349 if internal_module or BUILTIN_LIBRARY(bld, modname):
350 # treat internal modules as subsystems for now
351 if subsystem is not None:
352 deps += ' ' + subsystem
354 bld.SAMBA_SUBSYSTEM(modname, source,
358 autoproto_extra_source=autoproto_extra_source,
360 local_include=local_include,
363 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
367 SET_TARGET_TYPE(bld, modname, 'DISABLED')
370 if aliases is not None:
371 # if we have aliases, then create a private base library, and a set
372 # of modules on top of that library
374 cflags += " -D%s=samba_init_module" % init_function
376 basename = modname + '-base'
377 bld.SAMBA_LIBRARY(basename,
381 autoproto = autoproto,
382 local_include=local_include,
388 aliases = TO_LIST(aliases)
389 aliases.append(modname)
391 for alias in aliases:
392 bld.SAMBA_MODULE(alias,
394 internal_module=False,
396 init_function=init_function,
401 obj_target = modname + '.objlist'
404 if subsystem is not None:
405 deps += ' ' + subsystem
406 while realname.startswith("lib"+subsystem+"_"):
407 realname = realname[len("lib"+subsystem+"_"):]
408 while realname.startswith(subsystem+"_"):
409 realname = realname[len(subsystem+"_"):]
411 realname = bld.make_libname(realname)
412 while realname.startswith("lib"):
413 realname = realname[len("lib"):]
415 build_link_name = "modules/%s/%s" % (subsystem, realname)
418 cflags += " -D%s=samba_init_module" % init_function
420 bld.SAMBA_LIBRARY(modname,
425 autoproto = autoproto,
426 local_include=local_include,
428 link_name=build_link_name,
429 install_path="${MODULESDIR}/%s" % subsystem,
434 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
437 #################################################################
438 def SAMBA_SUBSYSTEM(bld, modname, source,
447 init_function_sentinal=None,
449 autoproto_extra_source='',
452 local_include_first=True,
456 use_global_deps=True,
460 '''define a Samba subsystem'''
463 SET_TARGET_TYPE(bld, modname, 'DISABLED')
466 # remember empty subsystems, so we can strip the dependencies
467 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
468 SET_TARGET_TYPE(bld, modname, 'EMPTY')
471 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
474 source = bld.EXPAND_VARIABLES(source, vars=vars)
475 source = unique_list(TO_LIST(source))
477 deps += ' ' + public_deps
479 bld.SET_BUILD_GROUP(group)
489 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
490 depends_on = depends_on,
491 samba_deps = TO_LIST(deps),
492 samba_includes = includes,
493 local_include = local_include,
494 local_include_first = local_include_first,
495 samba_subsystem= subsystem_name,
496 samba_use_hostcc = use_hostcc,
497 samba_use_global_deps = use_global_deps
500 if cflags_end is not None:
501 t.samba_cflags.extend(TO_LIST(cflags_end))
503 if autoproto is not None:
504 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
505 if public_headers is not None:
506 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
510 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
513 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
514 group='generators', enabled=True,
519 '''A generic source generator target'''
521 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
527 bld.SET_BUILD_GROUP(group)
530 source=bld.EXPAND_VARIABLES(source, vars=vars),
532 shell=isinstance(rule, str),
541 if public_headers is not None:
542 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
544 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
549 def SETUP_BUILD_GROUPS(bld):
550 '''setup build groups used to ensure that the different build
551 phases happen consecutively'''
552 bld.p_ln = bld.srcnode # we do want to see all targets!
553 bld.env['USING_BUILD_GROUPS'] = True
554 bld.add_group('setup')
555 bld.add_group('build_compiler_source')
556 bld.add_group('base_libraries')
557 bld.add_group('generators')
558 bld.add_group('compiler_prototypes')
559 bld.add_group('compiler_libraries')
560 bld.add_group('build_compilers')
561 bld.add_group('build_source')
562 bld.add_group('prototypes')
563 bld.add_group('main')
564 bld.add_group('binaries')
565 bld.add_group('final')
566 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
569 def SET_BUILD_GROUP(bld, group):
570 '''set the current build group'''
571 if not 'USING_BUILD_GROUPS' in bld.env:
574 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
579 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
580 """use timestamps instead of file contents for deps
581 this currently doesn't work"""
582 def h_file(filename):
584 st = os.stat(filename)
585 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
587 m.update(str(st.st_mtime))
588 m.update(str(st.st_size))
591 Utils.h_file = h_file
595 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
596 shell=True, color='PINK', ext_in='.bin')
599 @feature('copy_script')
600 @before('apply_link')
601 def copy_script(self):
602 tsk = self.create_task('copy_script', self.allnodes[0])
603 tsk.env.TARGET = self.target
605 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
606 '''used to copy scripts from the source tree into the build directory
607 for use by selftest'''
609 source = bld.path.ant_glob(pattern)
611 bld.SET_BUILD_GROUP('build_source')
612 for s in TO_LIST(source):
614 if installname != None:
616 target = os.path.join(installdir, iname)
617 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
619 t = bld(features='copy_script',
624 t.env.LINK_TARGET = target
626 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
629 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
630 python_fixup=False, destname=None, base_name=None):
632 destdir = bld.EXPAND_VARIABLES(destdir)
636 destname = os.path.basename(destname)
637 dest = os.path.join(destdir, destname)
639 # fixup the python path it will use to find Samba modules
640 inst_file = file + '.inst'
641 if bld.env["PYTHONDIR"] not in sys.path:
642 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
644 # Eliminate updating sys.path if the target python dir is already
646 regex = "s|sys.path.insert.*bin/python.*$||g"
647 bld.SAMBA_GENERATOR('python_%s' % destname,
648 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
653 file = os.path.join(base_name, file)
654 bld.install_as(dest, file, chmod=chmod)
657 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
658 python_fixup=False, destname=None, base_name=None):
659 '''install a set of files'''
660 for f in TO_LIST(files):
661 install_file(bld, destdir, f, chmod=chmod, flat=flat,
662 python_fixup=python_fixup, destname=destname,
664 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
667 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
668 python_fixup=False, exclude=None, trim_path=None):
669 '''install a set of files matching a wildcard pattern'''
670 files=TO_LIST(bld.path.ant_glob(pattern))
674 files2.append(os_path_relpath(f, trim_path))
679 if fnmatch.fnmatch(f, exclude):
681 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
682 python_fixup=python_fixup, base_name=trim_path)
683 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
686 def INSTALL_DIRS(bld, destdir, dirs):
687 '''install a set of directories'''
688 destdir = bld.EXPAND_VARIABLES(destdir)
689 dirs = bld.EXPAND_VARIABLES(dirs)
690 for d in TO_LIST(dirs):
691 bld.install_dir(os.path.join(destdir, d))
692 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
695 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
696 class header_task(Task.Task):
698 The public headers (the one installed on the system) have both
699 different paths and contents, so the rename is not enough.
701 Intermediate .inst.h files are created because path manipulation
702 may be slow. The substitution is thus performed only once.
707 vars = ['INCLUDEDIR', 'HEADER_DEPS']
710 txt = self.inputs[0].read(self.env)
712 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
713 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
715 # use a regexp to substitute the #include lines in the files
716 map = self.generator.bld.hnodemap
717 dirnodes = self.generator.bld.hnodedirs
722 # pokemon headers: gotta catch'em all!
724 if s.startswith('bin/default'):
725 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
727 Logs.warn('could not find the public header for %r' % s)
731 Logs.warn('could not find the public header replacement for build header %r' % s)
733 # this part is more difficult since the path may be relative to anything
734 for dirnode in dirnodes:
735 node = dirnode.find_resource(s)
741 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
743 Logs.warn('-> could not find the public header for %r' % s)
745 return "#include <%s>" % fin
748 txt = re_header.sub(repl, txt)
750 # and write the output file
753 f = open(self.outputs[0].abspath(self.env), 'w')
759 @TaskGen.feature('pubh')
760 def make_public_headers(self):
762 collect the public headers to process and to install, then
763 create the substitutions (name and contents)
766 if not self.bld.is_install:
767 # install time only (lazy)
771 # hnodedirs: list of folders for searching the headers
772 # hnodemap: node ids and replacement string (node objects are unique)
774 self.bld.hnodedirs.append(self.path)
775 except AttributeError:
776 self.bld.hnodemap = {}
777 self.bld.hnodedirs = [self.bld.srcnode, self.path]
779 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
780 node = self.bld.srcnode.find_dir(k)
782 self.bld.hnodedirs.append(node)
784 header_path = getattr(self, 'header_path', None) or ''
786 for x in self.to_list(self.headers):
788 # too complicated, but what was the original idea?
789 if isinstance(header_path, list):
791 for (p1, dir) in header_path:
792 lst = self.to_list(p1)
794 if fnmatch.fnmatch(x, p2):
802 inst_path = header_path
806 if x.find(':') != -1:
811 inn = self.path.find_resource(name)
814 raise ValueError("could not find the public header %r in %r" % (name, self.path))
815 out = inn.change_ext('.inst.h')
816 self.create_task('header', inn, out)
822 inst_path = inst_path + '/'
823 inst_path = inst_path + dest
825 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
827 self.bld.hnodemap[inn.id] = inst_path
829 # create a hash (not md5) to make sure the headers are re-created if something changes
831 lst = list(self.bld.hnodemap.keys())
834 val = hash((val, k, self.bld.hnodemap[k]))
835 self.bld.env.HEADER_DEPS = val
837 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
838 '''install some headers
840 header_path may either be a string that is added to the INCLUDEDIR,
841 or it can be a dictionary of wildcard patterns which map to destination
842 directories relative to INCLUDEDIR
844 bld.SET_BUILD_GROUP('final')
845 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
847 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
850 def subst_at_vars(task):
851 '''substiture @VAR@ style variables in a file'''
852 src = task.inputs[0].srcpath(task.env)
853 tgt = task.outputs[0].bldpath(task.env)
859 a = re.split('(@\w+@)', s)
862 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
864 if re.match('@\w+@', v):
866 if not vname in task.env and vname.upper() in task.env:
867 vname = vname.upper()
868 if not vname in task.env:
869 Logs.error("Unknown substitution %s in %s" % (v, task.name))
871 v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
872 # now we back substitute the allowed pc vars
873 for (b, m) in back_sub:
876 if not b in done_var:
877 # we don't want to substitute the first usage
883 contents = ''.join(out)
885 s = f.write(contents)
890 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
891 '''install some pkg_config pc files'''
892 dest = '${PKGCONFIGDIR}'
893 dest = bld.EXPAND_VARIABLES(dest)
894 for f in TO_LIST(pc_files):
895 base=os.path.basename(f)
896 t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
901 t.env.PACKAGE_VERSION = vnum
902 INSTALL_FILES(bld, dest, f, flat=True, destname=base)
903 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
906 def MANPAGES(bld, manpages):
907 '''build and install manual pages'''
908 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
909 for m in manpages.split():
911 bld.SAMBA_GENERATOR(m,
915 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
917 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
918 Build.BuildContext.MANPAGES = MANPAGES
921 #############################################################
922 # give a nicer display when building different types of files
923 def progress_display(self, msg, fname):
924 col1 = Logs.colors(self.color)
925 col2 = Logs.colors.NORMAL
926 total = self.position[1]
928 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
929 return fs % (self.position[0], self.position[1], col1, fname, col2)
931 def link_display(self):
932 if Options.options.progress_bar != 0:
933 return Task.Task.old_display(self)
934 fname = self.outputs[0].bldpath(self.env)
935 return progress_display(self, 'Linking', fname)
936 Task.TaskBase.classes['cc_link'].display = link_display
938 def samba_display(self):
939 if Options.options.progress_bar != 0:
940 return Task.Task.old_display(self)
942 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
943 if self.name in targets:
944 target_type = targets[self.name]
945 type_map = { 'GENERATOR' : 'Generating',
946 'PROTOTYPE' : 'Generating'
948 if target_type in type_map:
949 return progress_display(self, type_map[target_type], self.name)
951 fname = self.inputs[0].bldpath(self.env)
952 if fname[0:3] == '../':
954 ext_loc = fname.rfind('.')
956 return Task.Task.old_display(self)
957 ext = fname[ext_loc:]
959 ext_map = { '.idl' : 'Compiling IDL',
960 '.et' : 'Compiling ERRTABLE',
961 '.asn1': 'Compiling ASN1',
964 return progress_display(self, ext_map[ext], fname)
965 return Task.Task.old_display(self)
967 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
968 Task.TaskBase.classes['Task'].display = samba_display