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
33 # some systems have broken threading in python
34 if os.environ.get('WAF_NOTHREADS') == '1':
39 os.putenv('PYTHONUNBUFFERED', '1')
42 if Constants.HEXVERSION < 0x105019:
44 Please use the version of waf that comes with Samba, not
45 a system installed version. See http://wiki.samba.org/index.php/Waf
48 Alternatively, please use ./autogen-waf.sh, and then
49 run ./configure and make as usual. That will call the right version of waf.
55 def SAMBA_BUILD_ENV(conf):
56 '''create the samba build environment'''
57 conf.env.BUILD_DIRECTORY = conf.blddir
58 mkdir_p(os.path.join(conf.blddir, LIB_PATH))
59 mkdir_p(os.path.join(conf.blddir, LIB_PATH, "private"))
60 mkdir_p(os.path.join(conf.blddir, "modules"))
61 mkdir_p(os.path.join(conf.blddir, 'python/samba/dcerpc'))
62 # this allows all of the bin/shared and bin/python targets
63 # to be expressed in terms of build directory paths
64 mkdir_p(os.path.join(conf.blddir, 'default'))
65 for p in ['python','shared', 'modules']:
66 link_target = os.path.join(conf.blddir, 'default/' + p)
67 if not os.path.lexists(link_target):
68 os.symlink('../' + p, link_target)
70 # get perl to put the blib files in the build directory
71 blib_bld = os.path.join(conf.blddir, 'default/pidl/blib')
72 blib_src = os.path.join(conf.srcdir, 'pidl/blib')
73 mkdir_p(blib_bld + '/man1')
74 mkdir_p(blib_bld + '/man3')
75 if os.path.islink(blib_src):
77 elif os.path.exists(blib_src):
78 shutil.rmtree(blib_src)
81 def ADD_INIT_FUNCTION(bld, subsystem, target, init_function):
82 '''add an init_function to the list for a subsystem'''
83 if init_function is None:
85 bld.ASSERT(subsystem is not None, "You must specify a subsystem for init_function '%s'" % init_function)
86 cache = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
87 if not subsystem in cache:
89 cache[subsystem].append( { 'TARGET':target, 'INIT_FUNCTION':init_function } )
90 Build.BuildContext.ADD_INIT_FUNCTION = ADD_INIT_FUNCTION
94 #################################################################
95 def SAMBA_LIBRARY(bld, libname, source,
105 external_library=False,
116 target_type='LIBRARY',
117 bundled_extension=True,
123 private_library=False,
124 grouping_library=False,
126 '''define a Samba library'''
129 SET_TARGET_TYPE(bld, libname, 'DISABLED')
132 source = bld.EXPAND_VARIABLES(source, vars=vars)
134 # remember empty libraries, so we can strip the dependencies
135 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
136 SET_TARGET_TYPE(bld, libname, 'EMPTY')
139 if BUILTIN_LIBRARY(bld, libname):
142 obj_target = libname + '.objlist'
144 if group == 'libraries':
145 subsystem_group = 'main'
147 subsystem_group = group
149 # first create a target for building the object files for this library
150 # by separating in this way, we avoid recompiling the C files
151 # separately for the install library and the build library
152 bld.SAMBA_SUBSYSTEM(obj_target,
155 public_deps = public_deps,
157 public_headers = public_headers,
158 header_path = header_path,
160 group = subsystem_group,
161 autoproto = autoproto,
162 depends_on = depends_on,
163 hide_symbols = hide_symbols,
164 pyext = pyext or (target_type == "PYTHON"),
165 local_include = local_include)
167 if BUILTIN_LIBRARY(bld, libname):
170 if not SET_TARGET_TYPE(bld, libname, target_type):
173 # the library itself will depend on that object target
174 deps += ' ' + public_deps
176 deps.append(obj_target)
178 realname = bld.map_shlib_extension(realname, python=(target_type=='PYTHON'))
179 link_name = bld.map_shlib_extension(link_name, python=(target_type=='PYTHON'))
181 # we don't want any public libraries without version numbers
182 if not private_library and vnum is None and soname is None and target_type != 'PYTHON' and not realname:
183 raise Utils.WafError("public library '%s' must have a vnum" % libname)
185 if target_type == 'PYTHON' or realname or not private_library:
186 bundled_name = libname.replace('_', '-')
188 bundled_name = PRIVATE_NAME(bld, libname, bundled_extension, private_library)
192 Logs.error("vnum is invalid for private libraries")
194 vnum = Utils.g_module.VERSION
196 features = 'cc cshlib symlink_lib install_lib'
197 if target_type == 'PYTHON':
200 # this is quite strange. we should add pyext feature for pyext
201 # but that breaks the build. This may be a bug in the waf python tool
202 features += ' pyembed'
204 features += ' abi_check'
207 abi_file = os.path.join(bld.curdir, abi_file)
209 bld.SET_BUILD_GROUP(group)
213 target = bundled_name,
214 depends_on = depends_on,
216 samba_includes = includes,
217 local_include = local_include,
221 samba_inst_path = install_path,
223 samba_realname = realname,
224 samba_install = install,
226 abi_match = abi_match,
227 private_library = private_library,
228 grouping_library=grouping_library
231 if realname and not link_name:
232 link_name = 'shared/%s' % realname
235 t.link_name = link_name
237 if pc_files is not None:
238 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
240 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
241 bld.MANPAGES(manpages)
244 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
247 #################################################################
248 def SAMBA_BINARY(bld, binname, source,
258 use_global_deps=True,
269 '''define a Samba binary'''
272 SET_TARGET_TYPE(bld, binname, 'DISABLED')
275 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
278 features = 'cc cprogram symlink_bin install_bin'
280 features += ' pyembed'
282 obj_target = binname + '.objlist'
284 source = bld.EXPAND_VARIABLES(source, vars=vars)
285 source = unique_list(TO_LIST(source))
287 if group == 'binaries':
288 subsystem_group = 'main'
290 subsystem_group = group
292 # first create a target for building the object files for this binary
293 # by separating in this way, we avoid recompiling the C files
294 # separately for the install binary and the build binary
295 bld.SAMBA_SUBSYSTEM(obj_target,
300 group = subsystem_group,
301 autoproto = autoproto,
302 subsystem_name = subsystem_name,
303 local_include = local_include,
304 use_hostcc = use_hostcc,
306 use_global_deps= use_global_deps)
308 bld.SET_BUILD_GROUP(group)
310 # the binary itself will depend on that object target
312 deps.append(obj_target)
319 samba_includes = includes,
320 local_include = local_include,
321 samba_modules = modules,
323 samba_subsystem= subsystem_name,
325 samba_inst_path= install_path,
326 samba_install = install
329 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
330 bld.MANPAGES(manpages)
332 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
335 #################################################################
336 def SAMBA_MODULE(bld, modname, source,
341 module_init_name='samba_init_module',
343 autoproto_extra_source='',
345 internal_module=True,
351 '''define a Samba module.'''
353 source = bld.EXPAND_VARIABLES(source, vars=vars)
355 if internal_module or BUILTIN_LIBRARY(bld, modname):
356 bld.SAMBA_SUBSYSTEM(modname, source,
360 autoproto_extra_source=autoproto_extra_source,
362 local_include=local_include,
365 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
369 SET_TARGET_TYPE(bld, modname, 'DISABLED')
372 obj_target = modname + '.objlist'
375 if subsystem is not None:
376 deps += ' ' + subsystem
377 while realname.startswith("lib"+subsystem+"_"):
378 realname = realname[len("lib"+subsystem+"_"):]
379 while realname.startswith(subsystem+"_"):
380 realname = realname[len(subsystem+"_"):]
382 realname = bld.make_libname(realname)
383 while realname.startswith("lib"):
384 realname = realname[len("lib"):]
386 build_link_name = "modules/%s/%s" % (subsystem, realname)
389 cflags += " -D%s=%s" % (init_function, module_init_name)
391 bld.SAMBA_LIBRARY(modname,
396 autoproto = autoproto,
397 local_include=local_include,
399 link_name=build_link_name,
400 install_path="${MODULESDIR}/%s" % subsystem,
405 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
408 #################################################################
409 def SAMBA_SUBSYSTEM(bld, modname, source,
418 init_function_sentinal=None,
420 autoproto_extra_source='',
423 local_include_first=True,
427 use_global_deps=True,
431 '''define a Samba subsystem'''
434 SET_TARGET_TYPE(bld, modname, 'DISABLED')
437 # remember empty subsystems, so we can strip the dependencies
438 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
439 SET_TARGET_TYPE(bld, modname, 'EMPTY')
442 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
445 source = bld.EXPAND_VARIABLES(source, vars=vars)
446 source = unique_list(TO_LIST(source))
448 deps += ' ' + public_deps
450 bld.SET_BUILD_GROUP(group)
460 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
461 depends_on = depends_on,
462 samba_deps = TO_LIST(deps),
463 samba_includes = includes,
464 local_include = local_include,
465 local_include_first = local_include_first,
466 samba_subsystem= subsystem_name,
467 samba_use_hostcc = use_hostcc,
468 samba_use_global_deps = use_global_deps
471 if cflags_end is not None:
472 t.samba_cflags.extend(TO_LIST(cflags_end))
474 if autoproto is not None:
475 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
476 if public_headers is not None:
477 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
481 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
484 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
485 group='generators', enabled=True,
490 '''A generic source generator target'''
492 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
498 bld.SET_BUILD_GROUP(group)
501 source=bld.EXPAND_VARIABLES(source, vars=vars),
503 shell=isinstance(rule, str),
512 if public_headers is not None:
513 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
515 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
520 def SETUP_BUILD_GROUPS(bld):
521 '''setup build groups used to ensure that the different build
522 phases happen consecutively'''
523 bld.p_ln = bld.srcnode # we do want to see all targets!
524 bld.env['USING_BUILD_GROUPS'] = True
525 bld.add_group('setup')
526 bld.add_group('build_compiler_source')
527 bld.add_group('base_libraries')
528 bld.add_group('generators')
529 bld.add_group('compiler_prototypes')
530 bld.add_group('compiler_libraries')
531 bld.add_group('build_compilers')
532 bld.add_group('build_source')
533 bld.add_group('prototypes')
534 bld.add_group('main')
535 bld.add_group('symbolcheck')
536 bld.add_group('libraries')
537 bld.add_group('binaries')
538 bld.add_group('syslibcheck')
539 bld.add_group('final')
540 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
543 def SET_BUILD_GROUP(bld, group):
544 '''set the current build group'''
545 if not 'USING_BUILD_GROUPS' in bld.env:
548 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
553 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
554 """use timestamps instead of file contents for deps
555 this currently doesn't work"""
556 def h_file(filename):
558 st = os.stat(filename)
559 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
561 m.update(str(st.st_mtime))
562 m.update(str(st.st_size))
565 Utils.h_file = h_file
569 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
570 shell=True, color='PINK', ext_in='.bin')
573 @feature('copy_script')
574 @before('apply_link')
575 def copy_script(self):
576 tsk = self.create_task('copy_script', self.allnodes[0])
577 tsk.env.TARGET = self.target
579 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
580 '''used to copy scripts from the source tree into the build directory
581 for use by selftest'''
583 source = bld.path.ant_glob(pattern)
585 bld.SET_BUILD_GROUP('build_source')
586 for s in TO_LIST(source):
588 if installname != None:
590 target = os.path.join(installdir, iname)
591 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
593 t = bld(features='copy_script',
598 t.env.LINK_TARGET = target
600 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
603 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
604 python_fixup=False, destname=None, base_name=None):
606 destdir = bld.EXPAND_VARIABLES(destdir)
610 destname = os.path.basename(destname)
611 dest = os.path.join(destdir, destname)
613 # fixup the python path it will use to find Samba modules
614 inst_file = file + '.inst'
615 if bld.env["PYTHONDIR"] not in sys.path:
616 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
618 # Eliminate updating sys.path if the target python dir is already
620 regex = "s|sys.path.insert.*bin/python.*$||g"
621 bld.SAMBA_GENERATOR('python_%s' % destname,
622 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
627 file = os.path.join(base_name, file)
628 bld.install_as(dest, file, chmod=chmod)
631 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
632 python_fixup=False, destname=None, base_name=None):
633 '''install a set of files'''
634 for f in TO_LIST(files):
635 install_file(bld, destdir, f, chmod=chmod, flat=flat,
636 python_fixup=python_fixup, destname=destname,
638 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
641 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
642 python_fixup=False, exclude=None, trim_path=None):
643 '''install a set of files matching a wildcard pattern'''
644 files=TO_LIST(bld.path.ant_glob(pattern))
648 files2.append(os_path_relpath(f, trim_path))
653 if fnmatch.fnmatch(f, exclude):
655 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
656 python_fixup=python_fixup, base_name=trim_path)
657 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
660 def INSTALL_DIRS(bld, destdir, dirs):
661 '''install a set of directories'''
662 destdir = bld.EXPAND_VARIABLES(destdir)
663 dirs = bld.EXPAND_VARIABLES(dirs)
664 for d in TO_LIST(dirs):
665 bld.install_dir(os.path.join(destdir, d))
666 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
669 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
670 class header_task(Task.Task):
672 The public headers (the one installed on the system) have both
673 different paths and contents, so the rename is not enough.
675 Intermediate .inst.h files are created because path manipulation
676 may be slow. The substitution is thus performed only once.
681 vars = ['INCLUDEDIR', 'HEADER_DEPS']
684 txt = self.inputs[0].read(self.env)
686 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
687 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
689 # use a regexp to substitute the #include lines in the files
690 map = self.generator.bld.hnodemap
691 dirnodes = self.generator.bld.hnodedirs
696 # pokemon headers: gotta catch'em all!
698 if s.startswith('bin/default'):
699 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
701 Logs.warn('could not find the public header for %r' % s)
705 Logs.warn('could not find the public header replacement for build header %r' % s)
707 # this part is more difficult since the path may be relative to anything
708 for dirnode in dirnodes:
709 node = dirnode.find_resource(s)
715 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
717 Logs.warn('-> could not find the public header for %r' % s)
719 return "#include <%s>" % fin
722 txt = re_header.sub(repl, txt)
724 # and write the output file
727 f = open(self.outputs[0].abspath(self.env), 'w')
733 @TaskGen.feature('pubh')
734 def make_public_headers(self):
736 collect the public headers to process and to install, then
737 create the substitutions (name and contents)
740 if not self.bld.is_install:
741 # install time only (lazy)
745 # hnodedirs: list of folders for searching the headers
746 # hnodemap: node ids and replacement string (node objects are unique)
748 self.bld.hnodedirs.append(self.path)
749 except AttributeError:
750 self.bld.hnodemap = {}
751 self.bld.hnodedirs = [self.bld.srcnode, self.path]
753 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
754 node = self.bld.srcnode.find_dir(k)
756 self.bld.hnodedirs.append(node)
758 header_path = getattr(self, 'header_path', None) or ''
760 for x in self.to_list(self.headers):
762 # too complicated, but what was the original idea?
763 if isinstance(header_path, list):
765 for (p1, dir) in header_path:
766 lst = self.to_list(p1)
768 if fnmatch.fnmatch(x, p2):
776 inst_path = header_path
780 if x.find(':') != -1:
785 inn = self.path.find_resource(name)
788 raise ValueError("could not find the public header %r in %r" % (name, self.path))
789 out = inn.change_ext('.inst.h')
790 self.create_task('header', inn, out)
796 inst_path = inst_path + '/'
797 inst_path = inst_path + dest
799 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
801 self.bld.hnodemap[inn.id] = inst_path
803 # create a hash (not md5) to make sure the headers are re-created if something changes
805 lst = list(self.bld.hnodemap.keys())
808 val = hash((val, k, self.bld.hnodemap[k]))
809 self.bld.env.HEADER_DEPS = val
811 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
812 '''install some headers
814 header_path may either be a string that is added to the INCLUDEDIR,
815 or it can be a dictionary of wildcard patterns which map to destination
816 directories relative to INCLUDEDIR
818 bld.SET_BUILD_GROUP('final')
819 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
821 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
824 def MANPAGES(bld, manpages):
825 '''build and install manual pages'''
826 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
827 for m in manpages.split():
829 bld.SAMBA_GENERATOR(m,
833 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
835 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
836 Build.BuildContext.MANPAGES = MANPAGES
839 #############################################################
840 # give a nicer display when building different types of files
841 def progress_display(self, msg, fname):
842 col1 = Logs.colors(self.color)
843 col2 = Logs.colors.NORMAL
844 total = self.position[1]
846 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
847 return fs % (self.position[0], self.position[1], col1, fname, col2)
849 def link_display(self):
850 if Options.options.progress_bar != 0:
851 return Task.Task.old_display(self)
852 fname = self.outputs[0].bldpath(self.env)
853 return progress_display(self, 'Linking', fname)
854 Task.TaskBase.classes['cc_link'].display = link_display
856 def samba_display(self):
857 if Options.options.progress_bar != 0:
858 return Task.Task.old_display(self)
860 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
861 if self.name in targets:
862 target_type = targets[self.name]
863 type_map = { 'GENERATOR' : 'Generating',
864 'PROTOTYPE' : 'Generating'
866 if target_type in type_map:
867 return progress_display(self, type_map[target_type], self.name)
869 if len(self.inputs) == 0:
870 return Task.Task.old_display(self)
872 fname = self.inputs[0].bldpath(self.env)
873 if fname[0:3] == '../':
875 ext_loc = fname.rfind('.')
877 return Task.Task.old_display(self)
878 ext = fname[ext_loc:]
880 ext_map = { '.idl' : 'Compiling IDL',
881 '.et' : 'Compiling ERRTABLE',
882 '.asn1': 'Compiling ASN1',
885 return progress_display(self, ext_map[ext], fname)
886 return Task.Task.old_display(self)
888 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
889 Task.TaskBase.classes['Task'].display = samba_display
894 def apply_bundle_remove_dynamiclib_patch(self):
895 if self.env['MACBUNDLE'] or getattr(self,'mac_bundle',False):
896 if not getattr(self,'vnum',None):
898 self.env['LINKFLAGS'].remove('-dynamiclib')
899 self.env['LINKFLAGS'].remove('-single_module')