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)
194 features = 'cc cshlib symlink_lib install_lib'
195 if target_type == 'PYTHON':
198 # this is quite strange. we should add pyext feature for pyext
199 # but that breaks the build. This may be a bug in the waf python tool
200 features += ' pyembed'
203 features += ' abi_check'
205 if bld.env.HAVE_LD_VERSION_SCRIPT:
206 vscript = "%s.vscript" % libname
208 version = "PRIVATE_%s_%s" % (Utils.g_module.APPNAME, Utils.g_module.VERSION)
209 vscript_abi_directory = None
211 version = "%s_%s" % (libname, vnum)
212 vscript_abi_directory = abi_directory
215 vscript_abi_directory = None
217 bld.ABI_VSCRIPT(libname, vscript_abi_directory, version, vscript)
218 ldflags.append("-Wl,--version-script=%s/%s" % (bld.path.abspath(bld.env), vscript))
219 fullname = bld.env.shlib_PATTERN % bundled_name
220 bld.add_manual_dependency(bld.path.find_or_declare(fullname), bld.path.find_or_declare(vscript))
222 bld.SET_BUILD_GROUP(group)
226 target = bundled_name,
227 depends_on = depends_on,
230 samba_includes = includes,
231 local_include = local_include,
235 samba_inst_path = install_path,
237 samba_realname = realname,
238 samba_install = install,
239 abi_directory = "%s/%s" % (bld.path.abspath(), abi_directory),
240 abi_match = abi_match,
241 private_library = private_library,
242 grouping_library=grouping_library
245 if realname and not link_name:
246 link_name = 'shared/%s' % realname
249 t.link_name = link_name
251 if pc_files is not None:
252 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
254 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
255 bld.MANPAGES(manpages)
258 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
261 #################################################################
262 def SAMBA_BINARY(bld, binname, source,
272 use_global_deps=True,
283 '''define a Samba binary'''
286 SET_TARGET_TYPE(bld, binname, 'DISABLED')
289 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
292 features = 'cc cprogram symlink_bin install_bin'
294 features += ' pyembed'
296 obj_target = binname + '.objlist'
298 source = bld.EXPAND_VARIABLES(source, vars=vars)
299 source = unique_list(TO_LIST(source))
301 if group == 'binaries':
302 subsystem_group = 'main'
304 subsystem_group = group
306 # first create a target for building the object files for this binary
307 # by separating in this way, we avoid recompiling the C files
308 # separately for the install binary and the build binary
309 bld.SAMBA_SUBSYSTEM(obj_target,
314 group = subsystem_group,
315 autoproto = autoproto,
316 subsystem_name = subsystem_name,
317 local_include = local_include,
318 use_hostcc = use_hostcc,
320 use_global_deps= use_global_deps)
322 bld.SET_BUILD_GROUP(group)
324 # the binary itself will depend on that object target
326 deps.append(obj_target)
333 samba_includes = includes,
334 local_include = local_include,
335 samba_modules = modules,
337 samba_subsystem= subsystem_name,
339 samba_inst_path= install_path,
340 samba_install = install
343 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
344 bld.MANPAGES(manpages)
346 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
349 #################################################################
350 def SAMBA_MODULE(bld, modname, source,
355 module_init_name='samba_init_module',
357 autoproto_extra_source='',
359 internal_module=True,
365 '''define a Samba module.'''
367 source = bld.EXPAND_VARIABLES(source, vars=vars)
369 if internal_module or BUILTIN_LIBRARY(bld, modname):
370 bld.SAMBA_SUBSYSTEM(modname, source,
374 autoproto_extra_source=autoproto_extra_source,
376 local_include=local_include,
379 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
383 SET_TARGET_TYPE(bld, modname, 'DISABLED')
386 obj_target = modname + '.objlist'
389 if subsystem is not None:
390 deps += ' ' + subsystem
391 while realname.startswith("lib"+subsystem+"_"):
392 realname = realname[len("lib"+subsystem+"_"):]
393 while realname.startswith(subsystem+"_"):
394 realname = realname[len(subsystem+"_"):]
396 realname = bld.make_libname(realname)
397 while realname.startswith("lib"):
398 realname = realname[len("lib"):]
400 build_link_name = "modules/%s/%s" % (subsystem, realname)
403 cflags += " -D%s=%s" % (init_function, module_init_name)
405 bld.SAMBA_LIBRARY(modname,
410 autoproto = autoproto,
411 local_include=local_include,
413 link_name=build_link_name,
414 install_path="${MODULESDIR}/%s" % subsystem,
419 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
422 #################################################################
423 def SAMBA_SUBSYSTEM(bld, modname, source,
432 init_function_sentinal=None,
434 autoproto_extra_source='',
437 local_include_first=True,
441 use_global_deps=True,
445 '''define a Samba subsystem'''
448 SET_TARGET_TYPE(bld, modname, 'DISABLED')
451 # remember empty subsystems, so we can strip the dependencies
452 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
453 SET_TARGET_TYPE(bld, modname, 'EMPTY')
456 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
459 source = bld.EXPAND_VARIABLES(source, vars=vars)
460 source = unique_list(TO_LIST(source))
462 deps += ' ' + public_deps
464 bld.SET_BUILD_GROUP(group)
474 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
475 depends_on = depends_on,
476 samba_deps = TO_LIST(deps),
477 samba_includes = includes,
478 local_include = local_include,
479 local_include_first = local_include_first,
480 samba_subsystem= subsystem_name,
481 samba_use_hostcc = use_hostcc,
482 samba_use_global_deps = use_global_deps
485 if cflags_end is not None:
486 t.samba_cflags.extend(TO_LIST(cflags_end))
488 if autoproto is not None:
489 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
490 if public_headers is not None:
491 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
495 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
498 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
499 group='generators', enabled=True,
504 '''A generic source generator target'''
506 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
512 bld.SET_BUILD_GROUP(group)
515 source=bld.EXPAND_VARIABLES(source, vars=vars),
517 shell=isinstance(rule, str),
521 samba_type='GENERATOR',
528 if public_headers is not None:
529 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
531 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
536 def SETUP_BUILD_GROUPS(bld):
537 '''setup build groups used to ensure that the different build
538 phases happen consecutively'''
539 bld.p_ln = bld.srcnode # we do want to see all targets!
540 bld.env['USING_BUILD_GROUPS'] = True
541 bld.add_group('setup')
542 bld.add_group('build_compiler_source')
543 bld.add_group('vscripts')
544 bld.add_group('base_libraries')
545 bld.add_group('generators')
546 bld.add_group('compiler_prototypes')
547 bld.add_group('compiler_libraries')
548 bld.add_group('build_compilers')
549 bld.add_group('build_source')
550 bld.add_group('prototypes')
551 bld.add_group('main')
552 bld.add_group('symbolcheck')
553 bld.add_group('libraries')
554 bld.add_group('binaries')
555 bld.add_group('syslibcheck')
556 bld.add_group('final')
557 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
560 def SET_BUILD_GROUP(bld, group):
561 '''set the current build group'''
562 if not 'USING_BUILD_GROUPS' in bld.env:
565 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
570 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
571 """use timestamps instead of file contents for deps
572 this currently doesn't work"""
573 def h_file(filename):
575 st = os.stat(filename)
576 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
578 m.update(str(st.st_mtime))
579 m.update(str(st.st_size))
582 Utils.h_file = h_file
586 t = Task.simple_task_type('copy_script', 'rm -f "${LINK_TARGET}" && ln -s "${SRC[0].abspath(env)}" ${LINK_TARGET}',
587 shell=True, color='PINK', ext_in='.bin')
590 @feature('copy_script')
591 @before('apply_link')
592 def copy_script(self):
593 tsk = self.create_task('copy_script', self.allnodes[0])
594 tsk.env.TARGET = self.target
596 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
597 '''used to copy scripts from the source tree into the build directory
598 for use by selftest'''
600 source = bld.path.ant_glob(pattern)
602 bld.SET_BUILD_GROUP('build_source')
603 for s in TO_LIST(source):
605 if installname != None:
607 target = os.path.join(installdir, iname)
608 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
610 t = bld(features='copy_script',
615 t.env.LINK_TARGET = target
617 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
620 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
621 python_fixup=False, destname=None, base_name=None):
623 destdir = bld.EXPAND_VARIABLES(destdir)
627 destname = os.path.basename(destname)
628 dest = os.path.join(destdir, destname)
630 # fixup the python path it will use to find Samba modules
631 inst_file = file + '.inst'
632 if bld.env["PYTHONDIR"] not in sys.path:
633 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
635 # Eliminate updating sys.path if the target python dir is already
637 regex = "s|sys.path.insert.*bin/python.*$||g"
638 bld.SAMBA_GENERATOR('python_%s' % destname,
639 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
644 file = os.path.join(base_name, file)
645 bld.install_as(dest, file, chmod=chmod)
648 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
649 python_fixup=False, destname=None, base_name=None):
650 '''install a set of files'''
651 for f in TO_LIST(files):
652 install_file(bld, destdir, f, chmod=chmod, flat=flat,
653 python_fixup=python_fixup, destname=destname,
655 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
658 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
659 python_fixup=False, exclude=None, trim_path=None):
660 '''install a set of files matching a wildcard pattern'''
661 files=TO_LIST(bld.path.ant_glob(pattern))
665 files2.append(os_path_relpath(f, trim_path))
670 if fnmatch.fnmatch(f, exclude):
672 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
673 python_fixup=python_fixup, base_name=trim_path)
674 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
677 def INSTALL_DIRS(bld, destdir, dirs):
678 '''install a set of directories'''
679 destdir = bld.EXPAND_VARIABLES(destdir)
680 dirs = bld.EXPAND_VARIABLES(dirs)
681 for d in TO_LIST(dirs):
682 bld.install_dir(os.path.join(destdir, d))
683 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
686 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
687 class header_task(Task.Task):
689 The public headers (the one installed on the system) have both
690 different paths and contents, so the rename is not enough.
692 Intermediate .inst.h files are created because path manipulation
693 may be slow. The substitution is thus performed only once.
698 vars = ['INCLUDEDIR', 'HEADER_DEPS']
701 txt = self.inputs[0].read(self.env)
703 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
704 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
706 # use a regexp to substitute the #include lines in the files
707 map = self.generator.bld.hnodemap
708 dirnodes = self.generator.bld.hnodedirs
713 # pokemon headers: gotta catch'em all!
715 if s.startswith('bin/default'):
716 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
718 Logs.warn('could not find the public header for %r' % s)
722 Logs.warn('could not find the public header replacement for build header %r' % s)
724 # this part is more difficult since the path may be relative to anything
725 for dirnode in dirnodes:
726 node = dirnode.find_resource(s)
732 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
734 Logs.warn('-> could not find the public header for %r' % s)
736 return "#include <%s>" % fin
739 txt = re_header.sub(repl, txt)
741 # and write the output file
744 f = open(self.outputs[0].abspath(self.env), 'w')
750 @TaskGen.feature('pubh')
751 def make_public_headers(self):
753 collect the public headers to process and to install, then
754 create the substitutions (name and contents)
757 if not self.bld.is_install:
758 # install time only (lazy)
762 # hnodedirs: list of folders for searching the headers
763 # hnodemap: node ids and replacement string (node objects are unique)
765 self.bld.hnodedirs.append(self.path)
766 except AttributeError:
767 self.bld.hnodemap = {}
768 self.bld.hnodedirs = [self.bld.srcnode, self.path]
770 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
771 node = self.bld.srcnode.find_dir(k)
773 self.bld.hnodedirs.append(node)
775 header_path = getattr(self, 'header_path', None) or ''
777 for x in self.to_list(self.headers):
779 # too complicated, but what was the original idea?
780 if isinstance(header_path, list):
782 for (p1, dir) in header_path:
783 lst = self.to_list(p1)
785 if fnmatch.fnmatch(x, p2):
793 inst_path = header_path
797 if x.find(':') != -1:
802 inn = self.path.find_resource(name)
805 raise ValueError("could not find the public header %r in %r" % (name, self.path))
806 out = inn.change_ext('.inst.h')
807 self.create_task('header', inn, out)
813 inst_path = inst_path + '/'
814 inst_path = inst_path + dest
816 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
818 self.bld.hnodemap[inn.id] = inst_path
820 # create a hash (not md5) to make sure the headers are re-created if something changes
822 lst = list(self.bld.hnodemap.keys())
825 val = hash((val, k, self.bld.hnodemap[k]))
826 self.bld.env.HEADER_DEPS = val
828 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
829 '''install some headers
831 header_path may either be a string that is added to the INCLUDEDIR,
832 or it can be a dictionary of wildcard patterns which map to destination
833 directories relative to INCLUDEDIR
835 bld.SET_BUILD_GROUP('final')
836 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
838 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
841 def MANPAGES(bld, manpages):
842 '''build and install manual pages'''
843 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
844 for m in manpages.split():
846 bld.SAMBA_GENERATOR(m,
850 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
852 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
853 Build.BuildContext.MANPAGES = MANPAGES
856 #############################################################
857 # give a nicer display when building different types of files
858 def progress_display(self, msg, fname):
859 col1 = Logs.colors(self.color)
860 col2 = Logs.colors.NORMAL
861 total = self.position[1]
863 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
864 return fs % (self.position[0], self.position[1], col1, fname, col2)
866 def link_display(self):
867 if Options.options.progress_bar != 0:
868 return Task.Task.old_display(self)
869 fname = self.outputs[0].bldpath(self.env)
870 return progress_display(self, 'Linking', fname)
871 Task.TaskBase.classes['cc_link'].display = link_display
873 def samba_display(self):
874 if Options.options.progress_bar != 0:
875 return Task.Task.old_display(self)
877 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
878 if self.name in targets:
879 target_type = targets[self.name]
880 type_map = { 'GENERATOR' : 'Generating',
881 'PROTOTYPE' : 'Generating'
883 if target_type in type_map:
884 return progress_display(self, type_map[target_type], self.name)
886 if len(self.inputs) == 0:
887 return Task.Task.old_display(self)
889 fname = self.inputs[0].bldpath(self.env)
890 if fname[0:3] == '../':
892 ext_loc = fname.rfind('.')
894 return Task.Task.old_display(self)
895 ext = fname[ext_loc:]
897 ext_map = { '.idl' : 'Compiling IDL',
898 '.et' : 'Compiling ERRTABLE',
899 '.asn1': 'Compiling ASN1',
902 return progress_display(self, ext_map[ext], fname)
903 return Task.Task.old_display(self)
905 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
906 Task.TaskBase.classes['Task'].display = samba_display
911 def apply_bundle_remove_dynamiclib_patch(self):
912 if self.env['MACBUNDLE'] or getattr(self,'mac_bundle',False):
913 if not getattr(self,'vnum',None):
915 self.env['LINKFLAGS'].remove('-dynamiclib')
916 self.env['LINKFLAGS'].remove('-single_module')