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
34 # some systems have broken threading in python
35 if os.environ.get('WAF_NOTHREADS') == '1':
40 os.putenv('PYTHONUNBUFFERED', '1')
43 if Constants.HEXVERSION < 0x105019:
45 Please use the version of waf that comes with Samba, not
46 a system installed version. See http://wiki.samba.org/index.php/Waf
49 Alternatively, please use ./autogen-waf.sh, and then
50 run ./configure and make as usual. That will call the right version of waf.
56 def SAMBA_BUILD_ENV(conf):
57 '''create the samba build environment'''
58 conf.env.BUILD_DIRECTORY = conf.blddir
59 mkdir_p(os.path.join(conf.blddir, LIB_PATH))
60 mkdir_p(os.path.join(conf.blddir, LIB_PATH, "private"))
61 mkdir_p(os.path.join(conf.blddir, "modules"))
62 mkdir_p(os.path.join(conf.blddir, 'python/samba/dcerpc'))
63 # this allows all of the bin/shared and bin/python targets
64 # to be expressed in terms of build directory paths
65 mkdir_p(os.path.join(conf.blddir, 'default'))
66 for p in ['python','shared', 'modules']:
67 link_target = os.path.join(conf.blddir, 'default/' + p)
68 if not os.path.lexists(link_target):
69 os.symlink('../' + p, link_target)
71 # get perl to put the blib files in the build directory
72 blib_bld = os.path.join(conf.blddir, 'default/pidl/blib')
73 blib_src = os.path.join(conf.srcdir, 'pidl/blib')
74 mkdir_p(blib_bld + '/man1')
75 mkdir_p(blib_bld + '/man3')
76 if os.path.islink(blib_src):
78 elif os.path.exists(blib_src):
79 shutil.rmtree(blib_src)
82 def ADD_INIT_FUNCTION(bld, subsystem, target, init_function):
83 '''add an init_function to the list for a subsystem'''
84 if init_function is None:
86 bld.ASSERT(subsystem is not None, "You must specify a subsystem for init_function '%s'" % init_function)
87 cache = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
88 if not subsystem in cache:
90 cache[subsystem].append( { 'TARGET':target, 'INIT_FUNCTION':init_function } )
91 Build.BuildContext.ADD_INIT_FUNCTION = ADD_INIT_FUNCTION
95 #################################################################
96 def SAMBA_LIBRARY(bld, libname, source,
107 external_library=False,
118 target_type='LIBRARY',
119 bundled_extension=True,
125 private_library=False,
126 grouping_library=False,
128 '''define a Samba library'''
131 SET_TARGET_TYPE(bld, libname, 'DISABLED')
134 source = bld.EXPAND_VARIABLES(source, vars=vars)
136 # remember empty libraries, so we can strip the dependencies
137 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
138 SET_TARGET_TYPE(bld, libname, 'EMPTY')
141 if BUILTIN_LIBRARY(bld, libname):
144 obj_target = libname + '.objlist'
146 if group == 'libraries':
147 subsystem_group = 'main'
149 subsystem_group = group
151 # first create a target for building the object files for this library
152 # by separating in this way, we avoid recompiling the C files
153 # separately for the install library and the build library
154 bld.SAMBA_SUBSYSTEM(obj_target,
157 public_deps = public_deps,
159 public_headers = public_headers,
160 header_path = header_path,
162 group = subsystem_group,
163 autoproto = autoproto,
164 depends_on = depends_on,
165 hide_symbols = hide_symbols,
166 pyext = pyext or (target_type == "PYTHON"),
167 local_include = local_include)
169 if BUILTIN_LIBRARY(bld, libname):
172 if not SET_TARGET_TYPE(bld, libname, target_type):
175 # the library itself will depend on that object target
176 deps += ' ' + public_deps
178 deps.append(obj_target)
180 realname = bld.map_shlib_extension(realname, python=(target_type=='PYTHON'))
181 link_name = bld.map_shlib_extension(link_name, python=(target_type=='PYTHON'))
183 # we don't want any public libraries without version numbers
184 if not private_library and vnum is None and soname is None and target_type != 'PYTHON' and not realname:
185 raise Utils.WafError("public library '%s' must have a vnum" % libname)
187 if target_type == 'PYTHON' or realname or not private_library:
188 bundled_name = libname.replace('_', '-')
190 bundled_name = PRIVATE_NAME(bld, libname, bundled_extension, private_library)
192 ldflags = TO_LIST(ldflags)
196 Logs.error("vnum is invalid for private libraries")
199 version = "%s_%s" % (Utils.g_module.APPNAME, Utils.g_module.VERSION)
201 version = "%s_%s" % (Utils.g_module.APPNAME, Utils.g_module.VERSION.split(".")[0])
203 features = 'cc cshlib symlink_lib install_lib'
204 if target_type == 'PYTHON':
207 # this is quite strange. we should add pyext feature for pyext
208 # but that breaks the build. This may be a bug in the waf python tool
209 features += ' pyembed'
212 features += ' abi_check'
213 if bld.env.HAVE_LD_VERSION_SCRIPT:
214 vscript = "%s.vscript" % libname
215 bld.ABI_VSCRIPT(libname, abi_directory, vnum, vscript)
216 ldflags.append("-Wl,--version-script=%s/%s" % (bld.path.abspath(bld.env), vscript))
218 bld.SET_BUILD_GROUP(group)
222 target = bundled_name,
223 depends_on = depends_on,
226 samba_includes = includes,
227 local_include = local_include,
231 samba_inst_path = install_path,
233 samba_realname = realname,
234 samba_install = install,
235 abi_directory = abi_directory,
236 abi_match = abi_match,
237 private_library = private_library,
238 grouping_library=grouping_library
241 if realname and not link_name:
242 link_name = 'shared/%s' % realname
245 t.link_name = link_name
247 if pc_files is not None:
248 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
250 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
251 bld.MANPAGES(manpages)
254 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
257 #################################################################
258 def SAMBA_BINARY(bld, binname, source,
268 use_global_deps=True,
279 '''define a Samba binary'''
282 SET_TARGET_TYPE(bld, binname, 'DISABLED')
285 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
288 features = 'cc cprogram symlink_bin install_bin'
290 features += ' pyembed'
292 obj_target = binname + '.objlist'
294 source = bld.EXPAND_VARIABLES(source, vars=vars)
295 source = unique_list(TO_LIST(source))
297 if group == 'binaries':
298 subsystem_group = 'main'
300 subsystem_group = group
302 # first create a target for building the object files for this binary
303 # by separating in this way, we avoid recompiling the C files
304 # separately for the install binary and the build binary
305 bld.SAMBA_SUBSYSTEM(obj_target,
310 group = subsystem_group,
311 autoproto = autoproto,
312 subsystem_name = subsystem_name,
313 local_include = local_include,
314 use_hostcc = use_hostcc,
316 use_global_deps= use_global_deps)
318 bld.SET_BUILD_GROUP(group)
320 # the binary itself will depend on that object target
322 deps.append(obj_target)
329 samba_includes = includes,
330 local_include = local_include,
331 samba_modules = modules,
333 samba_subsystem= subsystem_name,
335 samba_inst_path= install_path,
336 samba_install = install
339 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
340 bld.MANPAGES(manpages)
342 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
345 #################################################################
346 def SAMBA_MODULE(bld, modname, source,
351 module_init_name='samba_init_module',
353 autoproto_extra_source='',
355 internal_module=True,
361 '''define a Samba module.'''
363 source = bld.EXPAND_VARIABLES(source, vars=vars)
365 if internal_module or BUILTIN_LIBRARY(bld, modname):
366 bld.SAMBA_SUBSYSTEM(modname, source,
370 autoproto_extra_source=autoproto_extra_source,
372 local_include=local_include,
375 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
379 SET_TARGET_TYPE(bld, modname, 'DISABLED')
382 obj_target = modname + '.objlist'
385 if subsystem is not None:
386 deps += ' ' + subsystem
387 while realname.startswith("lib"+subsystem+"_"):
388 realname = realname[len("lib"+subsystem+"_"):]
389 while realname.startswith(subsystem+"_"):
390 realname = realname[len(subsystem+"_"):]
392 realname = bld.make_libname(realname)
393 while realname.startswith("lib"):
394 realname = realname[len("lib"):]
396 build_link_name = "modules/%s/%s" % (subsystem, realname)
399 cflags += " -D%s=%s" % (init_function, module_init_name)
401 bld.SAMBA_LIBRARY(modname,
406 autoproto = autoproto,
407 local_include=local_include,
409 link_name=build_link_name,
410 install_path="${MODULESDIR}/%s" % subsystem,
415 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
418 #################################################################
419 def SAMBA_SUBSYSTEM(bld, modname, source,
428 init_function_sentinal=None,
430 autoproto_extra_source='',
433 local_include_first=True,
437 use_global_deps=True,
441 '''define a Samba subsystem'''
444 SET_TARGET_TYPE(bld, modname, 'DISABLED')
447 # remember empty subsystems, so we can strip the dependencies
448 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
449 SET_TARGET_TYPE(bld, modname, 'EMPTY')
452 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
455 source = bld.EXPAND_VARIABLES(source, vars=vars)
456 source = unique_list(TO_LIST(source))
458 deps += ' ' + public_deps
460 bld.SET_BUILD_GROUP(group)
470 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
471 depends_on = depends_on,
472 samba_deps = TO_LIST(deps),
473 samba_includes = includes,
474 local_include = local_include,
475 local_include_first = local_include_first,
476 samba_subsystem= subsystem_name,
477 samba_use_hostcc = use_hostcc,
478 samba_use_global_deps = use_global_deps
481 if cflags_end is not None:
482 t.samba_cflags.extend(TO_LIST(cflags_end))
484 if autoproto is not None:
485 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
486 if public_headers is not None:
487 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
491 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
494 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
495 group='generators', enabled=True,
500 '''A generic source generator target'''
502 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
508 bld.SET_BUILD_GROUP(group)
511 source=bld.EXPAND_VARIABLES(source, vars=vars),
513 shell=isinstance(rule, str),
517 samba_type='GENERATOR',
524 if public_headers is not None:
525 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
527 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
532 def SETUP_BUILD_GROUPS(bld):
533 '''setup build groups used to ensure that the different build
534 phases happen consecutively'''
535 bld.p_ln = bld.srcnode # we do want to see all targets!
536 bld.env['USING_BUILD_GROUPS'] = True
537 bld.add_group('setup')
538 bld.add_group('build_compiler_source')
539 bld.add_group('vscripts')
540 bld.add_group('base_libraries')
541 bld.add_group('generators')
542 bld.add_group('compiler_prototypes')
543 bld.add_group('compiler_libraries')
544 bld.add_group('build_compilers')
545 bld.add_group('build_source')
546 bld.add_group('prototypes')
547 bld.add_group('main')
548 bld.add_group('symbolcheck')
549 bld.add_group('libraries')
550 bld.add_group('binaries')
551 bld.add_group('syslibcheck')
552 bld.add_group('final')
553 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
556 def SET_BUILD_GROUP(bld, group):
557 '''set the current build group'''
558 if not 'USING_BUILD_GROUPS' in bld.env:
561 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
566 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
567 """use timestamps instead of file contents for deps
568 this currently doesn't work"""
569 def h_file(filename):
571 st = os.stat(filename)
572 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
574 m.update(str(st.st_mtime))
575 m.update(str(st.st_size))
578 Utils.h_file = h_file
582 t = Task.simple_task_type('copy_script', 'rm -f "${LINK_TARGET}" && ln -s "${SRC[0].abspath(env)}" ${LINK_TARGET}',
583 shell=True, color='PINK', ext_in='.bin')
586 @feature('copy_script')
587 @before('apply_link')
588 def copy_script(self):
589 tsk = self.create_task('copy_script', self.allnodes[0])
590 tsk.env.TARGET = self.target
592 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
593 '''used to copy scripts from the source tree into the build directory
594 for use by selftest'''
596 source = bld.path.ant_glob(pattern)
598 bld.SET_BUILD_GROUP('build_source')
599 for s in TO_LIST(source):
601 if installname != None:
603 target = os.path.join(installdir, iname)
604 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
606 t = bld(features='copy_script',
611 t.env.LINK_TARGET = target
613 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
616 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
617 python_fixup=False, destname=None, base_name=None):
619 destdir = bld.EXPAND_VARIABLES(destdir)
623 destname = os.path.basename(destname)
624 dest = os.path.join(destdir, destname)
626 # fixup the python path it will use to find Samba modules
627 inst_file = file + '.inst'
628 if bld.env["PYTHONDIR"] not in sys.path:
629 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
631 # Eliminate updating sys.path if the target python dir is already
633 regex = "s|sys.path.insert.*bin/python.*$||g"
634 bld.SAMBA_GENERATOR('python_%s' % destname,
635 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
640 file = os.path.join(base_name, file)
641 bld.install_as(dest, file, chmod=chmod)
644 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
645 python_fixup=False, destname=None, base_name=None):
646 '''install a set of files'''
647 for f in TO_LIST(files):
648 install_file(bld, destdir, f, chmod=chmod, flat=flat,
649 python_fixup=python_fixup, destname=destname,
651 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
654 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
655 python_fixup=False, exclude=None, trim_path=None):
656 '''install a set of files matching a wildcard pattern'''
657 files=TO_LIST(bld.path.ant_glob(pattern))
661 files2.append(os_path_relpath(f, trim_path))
666 if fnmatch.fnmatch(f, exclude):
668 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
669 python_fixup=python_fixup, base_name=trim_path)
670 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
673 def INSTALL_DIRS(bld, destdir, dirs):
674 '''install a set of directories'''
675 destdir = bld.EXPAND_VARIABLES(destdir)
676 dirs = bld.EXPAND_VARIABLES(dirs)
677 for d in TO_LIST(dirs):
678 bld.install_dir(os.path.join(destdir, d))
679 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
682 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
683 class header_task(Task.Task):
685 The public headers (the one installed on the system) have both
686 different paths and contents, so the rename is not enough.
688 Intermediate .inst.h files are created because path manipulation
689 may be slow. The substitution is thus performed only once.
694 vars = ['INCLUDEDIR', 'HEADER_DEPS']
697 txt = self.inputs[0].read(self.env)
699 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
700 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
702 # use a regexp to substitute the #include lines in the files
703 map = self.generator.bld.hnodemap
704 dirnodes = self.generator.bld.hnodedirs
709 # pokemon headers: gotta catch'em all!
711 if s.startswith('bin/default'):
712 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
714 Logs.warn('could not find the public header for %r' % s)
718 Logs.warn('could not find the public header replacement for build header %r' % s)
720 # this part is more difficult since the path may be relative to anything
721 for dirnode in dirnodes:
722 node = dirnode.find_resource(s)
728 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
730 Logs.warn('-> could not find the public header for %r' % s)
732 return "#include <%s>" % fin
735 txt = re_header.sub(repl, txt)
737 # and write the output file
740 f = open(self.outputs[0].abspath(self.env), 'w')
746 @TaskGen.feature('pubh')
747 def make_public_headers(self):
749 collect the public headers to process and to install, then
750 create the substitutions (name and contents)
753 if not self.bld.is_install:
754 # install time only (lazy)
758 # hnodedirs: list of folders for searching the headers
759 # hnodemap: node ids and replacement string (node objects are unique)
761 self.bld.hnodedirs.append(self.path)
762 except AttributeError:
763 self.bld.hnodemap = {}
764 self.bld.hnodedirs = [self.bld.srcnode, self.path]
766 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
767 node = self.bld.srcnode.find_dir(k)
769 self.bld.hnodedirs.append(node)
771 header_path = getattr(self, 'header_path', None) or ''
773 for x in self.to_list(self.headers):
775 # too complicated, but what was the original idea?
776 if isinstance(header_path, list):
778 for (p1, dir) in header_path:
779 lst = self.to_list(p1)
781 if fnmatch.fnmatch(x, p2):
789 inst_path = header_path
793 if x.find(':') != -1:
798 inn = self.path.find_resource(name)
801 raise ValueError("could not find the public header %r in %r" % (name, self.path))
802 out = inn.change_ext('.inst.h')
803 self.create_task('header', inn, out)
809 inst_path = inst_path + '/'
810 inst_path = inst_path + dest
812 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
814 self.bld.hnodemap[inn.id] = inst_path
816 # create a hash (not md5) to make sure the headers are re-created if something changes
818 lst = list(self.bld.hnodemap.keys())
821 val = hash((val, k, self.bld.hnodemap[k]))
822 self.bld.env.HEADER_DEPS = val
824 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
825 '''install some headers
827 header_path may either be a string that is added to the INCLUDEDIR,
828 or it can be a dictionary of wildcard patterns which map to destination
829 directories relative to INCLUDEDIR
831 bld.SET_BUILD_GROUP('final')
832 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
834 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
837 def MANPAGES(bld, manpages):
838 '''build and install manual pages'''
839 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
840 for m in manpages.split():
842 bld.SAMBA_GENERATOR(m,
846 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
848 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
849 Build.BuildContext.MANPAGES = MANPAGES
852 #############################################################
853 # give a nicer display when building different types of files
854 def progress_display(self, msg, fname):
855 col1 = Logs.colors(self.color)
856 col2 = Logs.colors.NORMAL
857 total = self.position[1]
859 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
860 return fs % (self.position[0], self.position[1], col1, fname, col2)
862 def link_display(self):
863 if Options.options.progress_bar != 0:
864 return Task.Task.old_display(self)
865 fname = self.outputs[0].bldpath(self.env)
866 return progress_display(self, 'Linking', fname)
867 Task.TaskBase.classes['cc_link'].display = link_display
869 def samba_display(self):
870 if Options.options.progress_bar != 0:
871 return Task.Task.old_display(self)
873 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
874 if self.name in targets:
875 target_type = targets[self.name]
876 type_map = { 'GENERATOR' : 'Generating',
877 'PROTOTYPE' : 'Generating'
879 if target_type in type_map:
880 return progress_display(self, type_map[target_type], self.name)
882 if len(self.inputs) == 0:
883 return Task.Task.old_display(self)
885 fname = self.inputs[0].bldpath(self.env)
886 if fname[0:3] == '../':
888 ext_loc = fname.rfind('.')
890 return Task.Task.old_display(self)
891 ext = fname[ext_loc:]
893 ext_map = { '.idl' : 'Compiling IDL',
894 '.et' : 'Compiling ERRTABLE',
895 '.asn1': 'Compiling ASN1',
898 return progress_display(self, ext_map[ext], fname)
899 return Task.Task.old_display(self)
901 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
902 Task.TaskBase.classes['Task'].display = samba_display
907 def apply_bundle_remove_dynamiclib_patch(self):
908 if self.env['MACBUNDLE'] or getattr(self,'mac_bundle',False):
909 if not getattr(self,'vnum',None):
911 self.env['LINKFLAGS'].remove('-dynamiclib')
912 self.env['LINKFLAGS'].remove('-single_module')