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 features = 'cc cshlib symlink_lib install_lib'
200 if target_type == 'PYTHON':
203 # this is quite strange. we should add pyext feature for pyext
204 # but that breaks the build. This may be a bug in the waf python tool
205 features += ' pyembed'
208 features += ' abi_check'
210 if bld.env.HAVE_LD_VERSION_SCRIPT:
211 vscript = "%s.vscript" % libname
213 version = "%s_%s" % (Utils.g_module.APPNAME, Utils.g_module.VERSION)
215 version = "%s_%s" % (libname, vnum)
219 bld.ABI_VSCRIPT(libname, abi_directory, version, vscript)
220 ldflags.append("-Wl,--version-script=%s/%s" % (bld.path.abspath(bld.env), vscript))
221 fullname = bld.env.shlib_PATTERN % bundled_name
222 bld.add_manual_dependency(bld.path.find_or_declare(fullname), bld.path.find_or_declare(vscript))
224 bld.SET_BUILD_GROUP(group)
228 target = bundled_name,
229 depends_on = depends_on,
232 samba_includes = includes,
233 local_include = local_include,
237 samba_inst_path = install_path,
239 samba_realname = realname,
240 samba_install = install,
241 abi_directory = "%s/%s" % (bld.path.abspath(), abi_directory),
242 abi_match = abi_match,
243 private_library = private_library,
244 grouping_library=grouping_library
247 if realname and not link_name:
248 link_name = 'shared/%s' % realname
251 t.link_name = link_name
253 if pc_files is not None:
254 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
256 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
257 bld.MANPAGES(manpages)
260 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
263 #################################################################
264 def SAMBA_BINARY(bld, binname, source,
274 use_global_deps=True,
285 '''define a Samba binary'''
288 SET_TARGET_TYPE(bld, binname, 'DISABLED')
291 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
294 features = 'cc cprogram symlink_bin install_bin'
296 features += ' pyembed'
298 obj_target = binname + '.objlist'
300 source = bld.EXPAND_VARIABLES(source, vars=vars)
301 source = unique_list(TO_LIST(source))
303 if group == 'binaries':
304 subsystem_group = 'main'
306 subsystem_group = group
308 # first create a target for building the object files for this binary
309 # by separating in this way, we avoid recompiling the C files
310 # separately for the install binary and the build binary
311 bld.SAMBA_SUBSYSTEM(obj_target,
316 group = subsystem_group,
317 autoproto = autoproto,
318 subsystem_name = subsystem_name,
319 local_include = local_include,
320 use_hostcc = use_hostcc,
322 use_global_deps= use_global_deps)
324 bld.SET_BUILD_GROUP(group)
326 # the binary itself will depend on that object target
328 deps.append(obj_target)
335 samba_includes = includes,
336 local_include = local_include,
337 samba_modules = modules,
339 samba_subsystem= subsystem_name,
341 samba_inst_path= install_path,
342 samba_install = install
345 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
346 bld.MANPAGES(manpages)
348 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
351 #################################################################
352 def SAMBA_MODULE(bld, modname, source,
357 module_init_name='samba_init_module',
359 autoproto_extra_source='',
361 internal_module=True,
367 '''define a Samba module.'''
369 source = bld.EXPAND_VARIABLES(source, vars=vars)
371 if internal_module or BUILTIN_LIBRARY(bld, modname):
372 bld.SAMBA_SUBSYSTEM(modname, source,
376 autoproto_extra_source=autoproto_extra_source,
378 local_include=local_include,
381 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
385 SET_TARGET_TYPE(bld, modname, 'DISABLED')
388 obj_target = modname + '.objlist'
391 if subsystem is not None:
392 deps += ' ' + subsystem
393 while realname.startswith("lib"+subsystem+"_"):
394 realname = realname[len("lib"+subsystem+"_"):]
395 while realname.startswith(subsystem+"_"):
396 realname = realname[len(subsystem+"_"):]
398 realname = bld.make_libname(realname)
399 while realname.startswith("lib"):
400 realname = realname[len("lib"):]
402 build_link_name = "modules/%s/%s" % (subsystem, realname)
405 cflags += " -D%s=%s" % (init_function, module_init_name)
407 bld.SAMBA_LIBRARY(modname,
412 autoproto = autoproto,
413 local_include=local_include,
415 link_name=build_link_name,
416 install_path="${MODULESDIR}/%s" % subsystem,
421 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
424 #################################################################
425 def SAMBA_SUBSYSTEM(bld, modname, source,
434 init_function_sentinal=None,
436 autoproto_extra_source='',
439 local_include_first=True,
443 use_global_deps=True,
447 '''define a Samba subsystem'''
450 SET_TARGET_TYPE(bld, modname, 'DISABLED')
453 # remember empty subsystems, so we can strip the dependencies
454 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
455 SET_TARGET_TYPE(bld, modname, 'EMPTY')
458 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
461 source = bld.EXPAND_VARIABLES(source, vars=vars)
462 source = unique_list(TO_LIST(source))
464 deps += ' ' + public_deps
466 bld.SET_BUILD_GROUP(group)
476 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
477 depends_on = depends_on,
478 samba_deps = TO_LIST(deps),
479 samba_includes = includes,
480 local_include = local_include,
481 local_include_first = local_include_first,
482 samba_subsystem= subsystem_name,
483 samba_use_hostcc = use_hostcc,
484 samba_use_global_deps = use_global_deps
487 if cflags_end is not None:
488 t.samba_cflags.extend(TO_LIST(cflags_end))
490 if autoproto is not None:
491 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
492 if public_headers is not None:
493 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
497 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
500 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
501 group='generators', enabled=True,
506 '''A generic source generator target'''
508 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
514 bld.SET_BUILD_GROUP(group)
517 source=bld.EXPAND_VARIABLES(source, vars=vars),
519 shell=isinstance(rule, str),
523 samba_type='GENERATOR',
530 if public_headers is not None:
531 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
533 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
538 def SETUP_BUILD_GROUPS(bld):
539 '''setup build groups used to ensure that the different build
540 phases happen consecutively'''
541 bld.p_ln = bld.srcnode # we do want to see all targets!
542 bld.env['USING_BUILD_GROUPS'] = True
543 bld.add_group('setup')
544 bld.add_group('build_compiler_source')
545 bld.add_group('vscripts')
546 bld.add_group('base_libraries')
547 bld.add_group('generators')
548 bld.add_group('compiler_prototypes')
549 bld.add_group('compiler_libraries')
550 bld.add_group('build_compilers')
551 bld.add_group('build_source')
552 bld.add_group('prototypes')
553 bld.add_group('main')
554 bld.add_group('symbolcheck')
555 bld.add_group('libraries')
556 bld.add_group('binaries')
557 bld.add_group('syslibcheck')
558 bld.add_group('final')
559 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
562 def SET_BUILD_GROUP(bld, group):
563 '''set the current build group'''
564 if not 'USING_BUILD_GROUPS' in bld.env:
567 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
572 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
573 """use timestamps instead of file contents for deps
574 this currently doesn't work"""
575 def h_file(filename):
577 st = os.stat(filename)
578 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
580 m.update(str(st.st_mtime))
581 m.update(str(st.st_size))
584 Utils.h_file = h_file
588 t = Task.simple_task_type('copy_script', 'rm -f "${LINK_TARGET}" && ln -s "${SRC[0].abspath(env)}" ${LINK_TARGET}',
589 shell=True, color='PINK', ext_in='.bin')
592 @feature('copy_script')
593 @before('apply_link')
594 def copy_script(self):
595 tsk = self.create_task('copy_script', self.allnodes[0])
596 tsk.env.TARGET = self.target
598 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
599 '''used to copy scripts from the source tree into the build directory
600 for use by selftest'''
602 source = bld.path.ant_glob(pattern)
604 bld.SET_BUILD_GROUP('build_source')
605 for s in TO_LIST(source):
607 if installname != None:
609 target = os.path.join(installdir, iname)
610 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
612 t = bld(features='copy_script',
617 t.env.LINK_TARGET = target
619 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
622 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
623 python_fixup=False, destname=None, base_name=None):
625 destdir = bld.EXPAND_VARIABLES(destdir)
629 destname = os.path.basename(destname)
630 dest = os.path.join(destdir, destname)
632 # fixup the python path it will use to find Samba modules
633 inst_file = file + '.inst'
634 if bld.env["PYTHONDIR"] not in sys.path:
635 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
637 # Eliminate updating sys.path if the target python dir is already
639 regex = "s|sys.path.insert.*bin/python.*$||g"
640 bld.SAMBA_GENERATOR('python_%s' % destname,
641 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
646 file = os.path.join(base_name, file)
647 bld.install_as(dest, file, chmod=chmod)
650 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
651 python_fixup=False, destname=None, base_name=None):
652 '''install a set of files'''
653 for f in TO_LIST(files):
654 install_file(bld, destdir, f, chmod=chmod, flat=flat,
655 python_fixup=python_fixup, destname=destname,
657 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
660 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
661 python_fixup=False, exclude=None, trim_path=None):
662 '''install a set of files matching a wildcard pattern'''
663 files=TO_LIST(bld.path.ant_glob(pattern))
667 files2.append(os_path_relpath(f, trim_path))
672 if fnmatch.fnmatch(f, exclude):
674 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
675 python_fixup=python_fixup, base_name=trim_path)
676 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
679 def INSTALL_DIRS(bld, destdir, dirs):
680 '''install a set of directories'''
681 destdir = bld.EXPAND_VARIABLES(destdir)
682 dirs = bld.EXPAND_VARIABLES(dirs)
683 for d in TO_LIST(dirs):
684 bld.install_dir(os.path.join(destdir, d))
685 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
688 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
689 class header_task(Task.Task):
691 The public headers (the one installed on the system) have both
692 different paths and contents, so the rename is not enough.
694 Intermediate .inst.h files are created because path manipulation
695 may be slow. The substitution is thus performed only once.
700 vars = ['INCLUDEDIR', 'HEADER_DEPS']
703 txt = self.inputs[0].read(self.env)
705 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
706 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
708 # use a regexp to substitute the #include lines in the files
709 map = self.generator.bld.hnodemap
710 dirnodes = self.generator.bld.hnodedirs
715 # pokemon headers: gotta catch'em all!
717 if s.startswith('bin/default'):
718 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
720 Logs.warn('could not find the public header for %r' % s)
724 Logs.warn('could not find the public header replacement for build header %r' % s)
726 # this part is more difficult since the path may be relative to anything
727 for dirnode in dirnodes:
728 node = dirnode.find_resource(s)
734 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
736 Logs.warn('-> could not find the public header for %r' % s)
738 return "#include <%s>" % fin
741 txt = re_header.sub(repl, txt)
743 # and write the output file
746 f = open(self.outputs[0].abspath(self.env), 'w')
752 @TaskGen.feature('pubh')
753 def make_public_headers(self):
755 collect the public headers to process and to install, then
756 create the substitutions (name and contents)
759 if not self.bld.is_install:
760 # install time only (lazy)
764 # hnodedirs: list of folders for searching the headers
765 # hnodemap: node ids and replacement string (node objects are unique)
767 self.bld.hnodedirs.append(self.path)
768 except AttributeError:
769 self.bld.hnodemap = {}
770 self.bld.hnodedirs = [self.bld.srcnode, self.path]
772 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
773 node = self.bld.srcnode.find_dir(k)
775 self.bld.hnodedirs.append(node)
777 header_path = getattr(self, 'header_path', None) or ''
779 for x in self.to_list(self.headers):
781 # too complicated, but what was the original idea?
782 if isinstance(header_path, list):
784 for (p1, dir) in header_path:
785 lst = self.to_list(p1)
787 if fnmatch.fnmatch(x, p2):
795 inst_path = header_path
799 if x.find(':') != -1:
804 inn = self.path.find_resource(name)
807 raise ValueError("could not find the public header %r in %r" % (name, self.path))
808 out = inn.change_ext('.inst.h')
809 self.create_task('header', inn, out)
815 inst_path = inst_path + '/'
816 inst_path = inst_path + dest
818 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
820 self.bld.hnodemap[inn.id] = inst_path
822 # create a hash (not md5) to make sure the headers are re-created if something changes
824 lst = list(self.bld.hnodemap.keys())
827 val = hash((val, k, self.bld.hnodemap[k]))
828 self.bld.env.HEADER_DEPS = val
830 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
831 '''install some headers
833 header_path may either be a string that is added to the INCLUDEDIR,
834 or it can be a dictionary of wildcard patterns which map to destination
835 directories relative to INCLUDEDIR
837 bld.SET_BUILD_GROUP('final')
838 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
840 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
843 def MANPAGES(bld, manpages):
844 '''build and install manual pages'''
845 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
846 for m in manpages.split():
848 bld.SAMBA_GENERATOR(m,
852 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
854 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
855 Build.BuildContext.MANPAGES = MANPAGES
858 #############################################################
859 # give a nicer display when building different types of files
860 def progress_display(self, msg, fname):
861 col1 = Logs.colors(self.color)
862 col2 = Logs.colors.NORMAL
863 total = self.position[1]
865 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
866 return fs % (self.position[0], self.position[1], col1, fname, col2)
868 def link_display(self):
869 if Options.options.progress_bar != 0:
870 return Task.Task.old_display(self)
871 fname = self.outputs[0].bldpath(self.env)
872 return progress_display(self, 'Linking', fname)
873 Task.TaskBase.classes['cc_link'].display = link_display
875 def samba_display(self):
876 if Options.options.progress_bar != 0:
877 return Task.Task.old_display(self)
879 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
880 if self.name in targets:
881 target_type = targets[self.name]
882 type_map = { 'GENERATOR' : 'Generating',
883 'PROTOTYPE' : 'Generating'
885 if target_type in type_map:
886 return progress_display(self, type_map[target_type], self.name)
888 if len(self.inputs) == 0:
889 return Task.Task.old_display(self)
891 fname = self.inputs[0].bldpath(self.env)
892 if fname[0:3] == '../':
894 ext_loc = fname.rfind('.')
896 return Task.Task.old_display(self)
897 ext = fname[ext_loc:]
899 ext_map = { '.idl' : 'Compiling IDL',
900 '.et' : 'Compiling ERRTABLE',
901 '.asn1': 'Compiling ASN1',
904 return progress_display(self, ext_map[ext], fname)
905 return Task.Task.old_display(self)
907 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
908 Task.TaskBase.classes['Task'].display = samba_display
913 def apply_bundle_remove_dynamiclib_patch(self):
914 if self.env['MACBUNDLE'] or getattr(self,'mac_bundle',False):
915 if not getattr(self,'vnum',None):
917 self.env['LINKFLAGS'].remove('-dynamiclib')
918 self.env['LINKFLAGS'].remove('-single_module')