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'
206 if bld.env.HAVE_LD_VERSION_SCRIPT:
208 version = "%s_%s" % (Utils.g_module.APPNAME, Utils.g_module.VERSION)
210 version = "%s_%s" % (libname, vnum)
214 vscript = "%s.vscript" % libname
215 bld.ABI_VSCRIPT(libname, abi_directory, version, vscript)
216 fullname = bld.env.shlib_PATTERN % bundled_name
217 bld.add_manual_dependency(bld.path.find_or_declare(fullname), bld.path.find_or_declare(vscript))
218 if Options.is_install:
219 # also make the .inst file depend on the vscript
220 instname = bld.env.shlib_PATTERN % (bundled_name + '.inst')
221 bld.add_manual_dependency(bld.path.find_or_declare(instname), bld.path.find_or_declare(vscript))
222 vscript = os.path.join(bld.path.abspath(bld.env), vscript)
224 bld.SET_BUILD_GROUP(group)
228 target = bundled_name,
229 depends_on = depends_on,
230 samba_ldflags = ldflags,
232 samba_includes = includes,
233 version_script = vscript,
234 local_include = local_include,
238 samba_inst_path = install_path,
240 samba_realname = realname,
241 samba_install = install,
242 abi_directory = "%s/%s" % (bld.path.abspath(), abi_directory),
243 abi_match = abi_match,
244 private_library = private_library,
245 grouping_library=grouping_library
248 if realname and not link_name:
249 link_name = 'shared/%s' % realname
252 t.link_name = link_name
254 if pc_files is not None:
255 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
257 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
258 bld.MANPAGES(manpages)
261 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
264 #################################################################
265 def SAMBA_BINARY(bld, binname, source,
275 use_global_deps=True,
286 '''define a Samba binary'''
289 SET_TARGET_TYPE(bld, binname, 'DISABLED')
292 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
295 features = 'cc cprogram symlink_bin install_bin'
297 features += ' pyembed'
299 obj_target = binname + '.objlist'
301 source = bld.EXPAND_VARIABLES(source, vars=vars)
302 source = unique_list(TO_LIST(source))
304 if group == 'binaries':
305 subsystem_group = 'main'
307 subsystem_group = group
309 # first create a target for building the object files for this binary
310 # by separating in this way, we avoid recompiling the C files
311 # separately for the install binary and the build binary
312 bld.SAMBA_SUBSYSTEM(obj_target,
317 group = subsystem_group,
318 autoproto = autoproto,
319 subsystem_name = subsystem_name,
320 local_include = local_include,
321 use_hostcc = use_hostcc,
323 use_global_deps= use_global_deps)
325 bld.SET_BUILD_GROUP(group)
327 # the binary itself will depend on that object target
329 deps.append(obj_target)
336 samba_includes = includes,
337 local_include = local_include,
338 samba_modules = modules,
340 samba_subsystem= subsystem_name,
342 samba_inst_path= install_path,
343 samba_install = install,
344 samba_ldflags = TO_LIST(ldflags)
347 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
348 bld.MANPAGES(manpages)
350 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
353 #################################################################
354 def SAMBA_MODULE(bld, modname, source,
359 module_init_name='samba_init_module',
361 autoproto_extra_source='',
363 internal_module=True,
369 '''define a Samba module.'''
371 source = bld.EXPAND_VARIABLES(source, vars=vars)
373 if internal_module or BUILTIN_LIBRARY(bld, modname):
374 bld.SAMBA_SUBSYSTEM(modname, source,
378 autoproto_extra_source=autoproto_extra_source,
380 local_include=local_include,
383 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
387 SET_TARGET_TYPE(bld, modname, 'DISABLED')
390 obj_target = modname + '.objlist'
393 if subsystem is not None:
394 deps += ' ' + subsystem
395 while realname.startswith("lib"+subsystem+"_"):
396 realname = realname[len("lib"+subsystem+"_"):]
397 while realname.startswith(subsystem+"_"):
398 realname = realname[len(subsystem+"_"):]
400 realname = bld.make_libname(realname)
401 while realname.startswith("lib"):
402 realname = realname[len("lib"):]
404 build_link_name = "modules/%s/%s" % (subsystem, realname)
407 cflags += " -D%s=%s" % (init_function, module_init_name)
409 bld.SAMBA_LIBRARY(modname,
414 autoproto = autoproto,
415 local_include=local_include,
417 link_name=build_link_name,
418 install_path="${MODULESDIR}/%s" % subsystem,
423 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
426 #################################################################
427 def SAMBA_SUBSYSTEM(bld, modname, source,
436 init_function_sentinal=None,
438 autoproto_extra_source='',
441 local_include_first=True,
445 use_global_deps=True,
449 '''define a Samba subsystem'''
452 SET_TARGET_TYPE(bld, modname, 'DISABLED')
455 # remember empty subsystems, so we can strip the dependencies
456 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
457 SET_TARGET_TYPE(bld, modname, 'EMPTY')
460 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
463 source = bld.EXPAND_VARIABLES(source, vars=vars)
464 source = unique_list(TO_LIST(source))
466 deps += ' ' + public_deps
468 bld.SET_BUILD_GROUP(group)
478 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
479 depends_on = depends_on,
480 samba_deps = TO_LIST(deps),
481 samba_includes = includes,
482 local_include = local_include,
483 local_include_first = local_include_first,
484 samba_subsystem= subsystem_name,
485 samba_use_hostcc = use_hostcc,
486 samba_use_global_deps = use_global_deps
489 if cflags_end is not None:
490 t.samba_cflags.extend(TO_LIST(cflags_end))
492 if autoproto is not None:
493 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
494 if public_headers is not None:
495 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
499 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
502 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
503 group='generators', enabled=True,
508 '''A generic source generator target'''
510 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
516 bld.SET_BUILD_GROUP(group)
519 source=bld.EXPAND_VARIABLES(source, vars=vars),
521 shell=isinstance(rule, str),
525 samba_type='GENERATOR',
532 if public_headers is not None:
533 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
535 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
540 def SETUP_BUILD_GROUPS(bld):
541 '''setup build groups used to ensure that the different build
542 phases happen consecutively'''
543 bld.p_ln = bld.srcnode # we do want to see all targets!
544 bld.env['USING_BUILD_GROUPS'] = True
545 bld.add_group('setup')
546 bld.add_group('build_compiler_source')
547 bld.add_group('vscripts')
548 bld.add_group('base_libraries')
549 bld.add_group('generators')
550 bld.add_group('compiler_prototypes')
551 bld.add_group('compiler_libraries')
552 bld.add_group('build_compilers')
553 bld.add_group('build_source')
554 bld.add_group('prototypes')
555 bld.add_group('main')
556 bld.add_group('symbolcheck')
557 bld.add_group('libraries')
558 bld.add_group('binaries')
559 bld.add_group('syslibcheck')
560 bld.add_group('final')
561 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
564 def SET_BUILD_GROUP(bld, group):
565 '''set the current build group'''
566 if not 'USING_BUILD_GROUPS' in bld.env:
569 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
574 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
575 """use timestamps instead of file contents for deps
576 this currently doesn't work"""
577 def h_file(filename):
579 st = os.stat(filename)
580 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
582 m.update(str(st.st_mtime))
583 m.update(str(st.st_size))
586 Utils.h_file = h_file
590 t = Task.simple_task_type('copy_script', 'rm -f "${LINK_TARGET}" && ln -s "${SRC[0].abspath(env)}" ${LINK_TARGET}',
591 shell=True, color='PINK', ext_in='.bin')
594 @feature('copy_script')
595 @before('apply_link')
596 def copy_script(self):
597 tsk = self.create_task('copy_script', self.allnodes[0])
598 tsk.env.TARGET = self.target
600 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
601 '''used to copy scripts from the source tree into the build directory
602 for use by selftest'''
604 source = bld.path.ant_glob(pattern)
606 bld.SET_BUILD_GROUP('build_source')
607 for s in TO_LIST(source):
609 if installname != None:
611 target = os.path.join(installdir, iname)
612 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
614 t = bld(features='copy_script',
619 t.env.LINK_TARGET = target
621 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
624 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
625 python_fixup=False, destname=None, base_name=None):
627 destdir = bld.EXPAND_VARIABLES(destdir)
631 destname = os.path.basename(destname)
632 dest = os.path.join(destdir, destname)
634 # fixup the python path it will use to find Samba modules
635 inst_file = file + '.inst'
636 if bld.env["PYTHONDIR"] not in sys.path:
637 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
639 # Eliminate updating sys.path if the target python dir is already
641 regex = "s|sys.path.insert.*bin/python.*$||g"
642 bld.SAMBA_GENERATOR('python_%s' % destname,
643 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
648 file = os.path.join(base_name, file)
649 bld.install_as(dest, file, chmod=chmod)
652 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
653 python_fixup=False, destname=None, base_name=None):
654 '''install a set of files'''
655 for f in TO_LIST(files):
656 install_file(bld, destdir, f, chmod=chmod, flat=flat,
657 python_fixup=python_fixup, destname=destname,
659 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
662 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
663 python_fixup=False, exclude=None, trim_path=None):
664 '''install a set of files matching a wildcard pattern'''
665 files=TO_LIST(bld.path.ant_glob(pattern))
669 files2.append(os_path_relpath(f, trim_path))
674 if fnmatch.fnmatch(f, exclude):
676 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
677 python_fixup=python_fixup, base_name=trim_path)
678 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
681 def INSTALL_DIRS(bld, destdir, dirs):
682 '''install a set of directories'''
683 destdir = bld.EXPAND_VARIABLES(destdir)
684 dirs = bld.EXPAND_VARIABLES(dirs)
685 for d in TO_LIST(dirs):
686 bld.install_dir(os.path.join(destdir, d))
687 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
690 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
691 class header_task(Task.Task):
693 The public headers (the one installed on the system) have both
694 different paths and contents, so the rename is not enough.
696 Intermediate .inst.h files are created because path manipulation
697 may be slow. The substitution is thus performed only once.
702 vars = ['INCLUDEDIR', 'HEADER_DEPS']
705 txt = self.inputs[0].read(self.env)
707 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
708 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
710 # use a regexp to substitute the #include lines in the files
711 map = self.generator.bld.hnodemap
712 dirnodes = self.generator.bld.hnodedirs
717 # pokemon headers: gotta catch'em all!
719 if s.startswith('bin/default'):
720 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
722 Logs.warn('could not find the public header for %r' % s)
726 Logs.warn('could not find the public header replacement for build header %r' % s)
728 # this part is more difficult since the path may be relative to anything
729 for dirnode in dirnodes:
730 node = dirnode.find_resource(s)
736 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
738 Logs.warn('-> could not find the public header for %r' % s)
740 return "#include <%s>" % fin
743 txt = re_header.sub(repl, txt)
745 # and write the output file
748 f = open(self.outputs[0].abspath(self.env), 'w')
754 @TaskGen.feature('pubh')
755 def make_public_headers(self):
757 collect the public headers to process and to install, then
758 create the substitutions (name and contents)
761 if not self.bld.is_install:
762 # install time only (lazy)
766 # hnodedirs: list of folders for searching the headers
767 # hnodemap: node ids and replacement string (node objects are unique)
769 self.bld.hnodedirs.append(self.path)
770 except AttributeError:
771 self.bld.hnodemap = {}
772 self.bld.hnodedirs = [self.bld.srcnode, self.path]
774 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
775 node = self.bld.srcnode.find_dir(k)
777 self.bld.hnodedirs.append(node)
779 header_path = getattr(self, 'header_path', None) or ''
781 for x in self.to_list(self.headers):
783 # too complicated, but what was the original idea?
784 if isinstance(header_path, list):
786 for (p1, dir) in header_path:
787 lst = self.to_list(p1)
789 if fnmatch.fnmatch(x, p2):
797 inst_path = header_path
801 if x.find(':') != -1:
806 inn = self.path.find_resource(name)
809 raise ValueError("could not find the public header %r in %r" % (name, self.path))
810 out = inn.change_ext('.inst.h')
811 self.create_task('header', inn, out)
817 inst_path = inst_path + '/'
818 inst_path = inst_path + dest
820 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
822 self.bld.hnodemap[inn.id] = inst_path
824 # create a hash (not md5) to make sure the headers are re-created if something changes
826 lst = list(self.bld.hnodemap.keys())
829 val = hash((val, k, self.bld.hnodemap[k]))
830 self.bld.env.HEADER_DEPS = val
832 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
833 '''install some headers
835 header_path may either be a string that is added to the INCLUDEDIR,
836 or it can be a dictionary of wildcard patterns which map to destination
837 directories relative to INCLUDEDIR
839 bld.SET_BUILD_GROUP('final')
840 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
842 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
845 def MANPAGES(bld, manpages):
846 '''build and install manual pages'''
847 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
848 for m in manpages.split():
850 bld.SAMBA_GENERATOR(m,
854 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
856 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
857 Build.BuildContext.MANPAGES = MANPAGES
860 #############################################################
861 # give a nicer display when building different types of files
862 def progress_display(self, msg, fname):
863 col1 = Logs.colors(self.color)
864 col2 = Logs.colors.NORMAL
865 total = self.position[1]
867 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
868 return fs % (self.position[0], self.position[1], col1, fname, col2)
870 def link_display(self):
871 if Options.options.progress_bar != 0:
872 return Task.Task.old_display(self)
873 fname = self.outputs[0].bldpath(self.env)
874 return progress_display(self, 'Linking', fname)
875 Task.TaskBase.classes['cc_link'].display = link_display
877 def samba_display(self):
878 if Options.options.progress_bar != 0:
879 return Task.Task.old_display(self)
881 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
882 if self.name in targets:
883 target_type = targets[self.name]
884 type_map = { 'GENERATOR' : 'Generating',
885 'PROTOTYPE' : 'Generating'
887 if target_type in type_map:
888 return progress_display(self, type_map[target_type], self.name)
890 if len(self.inputs) == 0:
891 return Task.Task.old_display(self)
893 fname = self.inputs[0].bldpath(self.env)
894 if fname[0:3] == '../':
896 ext_loc = fname.rfind('.')
898 return Task.Task.old_display(self)
899 ext = fname[ext_loc:]
901 ext_map = { '.idl' : 'Compiling IDL',
902 '.et' : 'Compiling ERRTABLE',
903 '.asn1': 'Compiling ASN1',
906 return progress_display(self, ext_map[ext], fname)
907 return Task.Task.old_display(self)
909 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
910 Task.TaskBase.classes['Task'].display = samba_display
915 def apply_bundle_remove_dynamiclib_patch(self):
916 if self.env['MACBUNDLE'] or getattr(self,'mac_bundle',False):
917 if not getattr(self,'vnum',None):
919 self.env['LINKFLAGS'].remove('-dynamiclib')
920 self.env['LINKFLAGS'].remove('-single_module')