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
9 # bring in the other samba modules
10 from samba_optimisation import *
11 from samba_utils import *
12 from samba_version import *
13 from samba_autoconf import *
14 from samba_patterns import *
15 from samba_pidl import *
16 from samba_errtable import *
17 from samba_asn1 import *
18 from samba_autoproto import *
19 from samba_python import *
20 from samba_deps import *
21 from samba_bundled import *
23 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 < 0x105016:
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, 'python/samba/dcerpc'))
60 # this allows all of the bin/shared and bin/python targets
61 # to be expressed in terms of build directory paths
62 mkdir_p(os.path.join(conf.blddir, 'default'))
63 for p in ['python','shared']:
64 link_target = os.path.join(conf.blddir, 'default/' + p)
65 if not os.path.lexists(link_target):
66 os.symlink('../' + p, link_target)
68 # get perl to put the blib files in the build directory
69 blib_bld = os.path.join(conf.blddir, 'default/pidl/blib')
70 blib_src = os.path.join(conf.srcdir, 'pidl/blib')
71 mkdir_p(blib_bld + '/man1')
72 mkdir_p(blib_bld + '/man3')
73 if os.path.islink(blib_src):
75 elif os.path.exists(blib_src):
76 shutil.rmtree(blib_src)
79 def ADD_INIT_FUNCTION(bld, subsystem, target, init_function):
80 '''add an init_function to the list for a subsystem'''
81 if init_function is None:
83 bld.ASSERT(subsystem is not None, "You must specify a subsystem for init_function '%s'" % init_function)
84 cache = LOCAL_CACHE(bld, 'INIT_FUNCTIONS')
85 if not subsystem in cache:
87 cache[subsystem].append( { 'TARGET':target, 'INIT_FUNCTION':init_function } )
88 Build.BuildContext.ADD_INIT_FUNCTION = ADD_INIT_FUNCTION
92 #################################################################
93 def SAMBA_LIBRARY(bld, libname, source,
102 external_library=False,
112 target_type='LIBRARY',
113 bundled_extension=True,
121 '''define a Samba library'''
124 SET_TARGET_TYPE(bld, libname, 'DISABLED')
127 source = bld.EXPAND_VARIABLES(source, vars=vars)
129 # remember empty libraries, so we can strip the dependencies
130 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
131 SET_TARGET_TYPE(bld, libname, 'EMPTY')
134 if target_type != 'PYTHON' and BUILTIN_LIBRARY(bld, libname):
137 obj_target = libname + '.objlist'
139 # first create a target for building the object files for this library
140 # by separating in this way, we avoid recompiling the C files
141 # separately for the install library and the build library
142 bld.SAMBA_SUBSYSTEM(obj_target,
145 public_deps = public_deps,
147 public_headers = public_headers,
148 header_path = header_path,
151 autoproto = autoproto,
152 depends_on = depends_on,
153 needs_python = needs_python,
154 hide_symbols = hide_symbols,
155 local_include = local_include)
157 if libname == obj_target:
160 if not SET_TARGET_TYPE(bld, libname, target_type):
163 # the library itself will depend on that object target
164 deps += ' ' + public_deps
166 deps.append(obj_target)
168 if target_type == 'PYTHON' or realname or not is_bundled:
169 # Sanitize the library name
170 bundled_name = libname.lower().replace('_', '-')
171 while bundled_name.startswith("lib"):
172 bundled_name = bundled_name[3:]
174 bundled_name = BUNDLED_NAME(bld, libname, bundled_extension)
176 features = 'cc cshlib symlink_lib install_lib'
177 if target_type == 'PYTHON':
180 features += ' pyembed'
182 features += ' abi_check'
185 abi_file = os.path.join(bld.curdir, abi_file)
187 bld.SET_BUILD_GROUP(group)
191 target = bundled_name,
192 samba_cflags = CURRENT_CFLAGS(bld, libname, cflags),
193 depends_on = depends_on,
195 samba_includes = includes,
196 local_include = local_include,
199 samba_inst_path = install_path,
201 samba_realname = realname,
202 samba_install = install,
204 abi_match = abi_match
207 if realname and not link_name:
208 link_name = 'shared/%s' % realname
211 t.link_name = link_name
213 if pc_files is not None:
214 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
216 if manpages is not None and 'XSLTPROC' in bld.env and bld.env['XSLTPROC']:
217 bld.MANPAGES(manpages)
220 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
223 #################################################################
224 def SAMBA_BINARY(bld, binname, source,
234 use_global_deps=True,
245 '''define a Samba binary'''
248 SET_TARGET_TYPE(bld, binname, 'DISABLED')
251 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
254 features = 'cc cprogram symlink_bin install_bin'
256 features += ' pyembed'
258 obj_target = binname + '.objlist'
260 source = bld.EXPAND_VARIABLES(source, vars=vars)
261 source = unique_list(TO_LIST(source))
263 # first create a target for building the object files for this binary
264 # by separating in this way, we avoid recompiling the C files
265 # separately for the install binary and the build binary
266 bld.SAMBA_SUBSYSTEM(obj_target,
272 autoproto = autoproto,
273 subsystem_name = subsystem_name,
274 needs_python = needs_python,
275 local_include = local_include,
276 use_hostcc = use_hostcc,
277 use_global_deps= use_global_deps)
279 bld.SET_BUILD_GROUP(group)
281 # the binary itself will depend on that object target
283 deps.append(obj_target)
289 samba_cflags = CURRENT_CFLAGS(bld, binname, cflags),
291 samba_includes = includes,
292 local_include = local_include,
293 samba_modules = modules,
295 samba_subsystem= subsystem_name,
297 samba_inst_path= install_path,
298 samba_install = install
301 # setup the subsystem_name as an alias for the real
302 # binary name, so it can be found when expanding
303 # subsystem dependencies
304 if subsystem_name is not None:
305 bld.TARGET_ALIAS(subsystem_name, binname)
307 if manpages is not None and 'XSLTPROC' in bld.env and bld.env['XSLTPROC']:
308 bld.MANPAGES(manpages)
310 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
313 #################################################################
314 def SAMBA_MODULE(bld, modname, source,
320 autoproto_extra_source='',
323 internal_module=True,
327 '''define a Samba module.'''
329 # we add the init function regardless of whether the module
330 # is enabled or not, as we need to generate a null list if
332 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
334 if internal_module or BUILTIN_LIBRARY(bld, modname):
335 # treat internal modules as subsystems for now
336 SAMBA_SUBSYSTEM(bld, modname, source,
340 autoproto_extra_source=autoproto_extra_source,
342 local_include=local_include,
347 SET_TARGET_TYPE(bld, modname, 'DISABLED')
350 source = bld.EXPAND_VARIABLES(source, vars=vars)
351 source = unique_list(TO_LIST(source))
353 # remember empty modules, so we can strip the dependencies
354 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
355 SET_TARGET_TYPE(bld, modname, 'EMPTY')
358 if not SET_TARGET_TYPE(bld, modname, 'MODULE'):
361 if subsystem is not None:
362 deps += ' ' + subsystem
364 bld.SET_BUILD_GROUP('main')
369 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags),
370 samba_includes = includes,
371 local_include = local_include,
372 samba_deps = TO_LIST(deps)
375 if autoproto is not None:
376 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
378 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
381 #################################################################
382 def SAMBA_SUBSYSTEM(bld, modname, source,
391 init_function_sentinal=None,
392 heimdal_autoproto=None,
393 heimdal_autoproto_options=None,
394 heimdal_autoproto_private=None,
396 autoproto_extra_source='',
399 local_include_first=True,
403 use_global_deps=True,
407 '''define a Samba subsystem'''
410 SET_TARGET_TYPE(bld, modname, 'DISABLED')
413 # remember empty subsystems, so we can strip the dependencies
414 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
415 SET_TARGET_TYPE(bld, modname, 'EMPTY')
418 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
421 source = bld.EXPAND_VARIABLES(source, vars=vars)
422 source = unique_list(TO_LIST(source))
424 deps += ' ' + public_deps
426 bld.SET_BUILD_GROUP(group)
436 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
437 depends_on = depends_on,
438 samba_deps = TO_LIST(deps),
439 samba_includes = includes,
440 local_include = local_include,
441 local_include_first = local_include_first,
442 samba_subsystem= subsystem_name,
443 samba_use_hostcc = use_hostcc,
444 samba_use_global_deps = use_global_deps
447 if cflags_end is not None:
448 t.samba_cflags.extend(TO_LIST(cflags_end))
450 if heimdal_autoproto is not None:
451 bld.HEIMDAL_AUTOPROTO(heimdal_autoproto, source, options=heimdal_autoproto_options)
452 if heimdal_autoproto_private is not None:
453 bld.HEIMDAL_AUTOPROTO_PRIVATE(heimdal_autoproto_private, source)
454 if autoproto is not None:
455 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
456 if public_headers is not None:
457 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
461 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
464 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
465 group='generators', enabled=True,
469 '''A generic source generator target'''
471 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
477 bld.SET_BUILD_GROUP(group)
480 source=bld.EXPAND_VARIABLES(source, vars=vars),
482 shell=isinstance(rule, str),
488 if public_headers is not None:
489 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
491 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
496 def SETUP_BUILD_GROUPS(bld):
497 '''setup build groups used to ensure that the different build
498 phases happen consecutively'''
499 bld.p_ln = bld.srcnode # we do want to see all targets!
500 bld.env['USING_BUILD_GROUPS'] = True
501 bld.add_group('setup')
502 bld.add_group('build_compiler_source')
503 bld.add_group('base_libraries')
504 bld.add_group('generators')
505 bld.add_group('compiler_prototypes')
506 bld.add_group('compiler_libraries')
507 bld.add_group('build_compilers')
508 bld.add_group('build_source')
509 bld.add_group('prototypes')
510 bld.add_group('main')
511 bld.add_group('binaries')
512 bld.add_group('final')
513 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
516 def SET_BUILD_GROUP(bld, group):
517 '''set the current build group'''
518 if not 'USING_BUILD_GROUPS' in bld.env:
521 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
526 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
527 """use timestamps instead of file contents for deps
528 this currently doesn't work"""
529 def h_file(filename):
531 st = os.stat(filename)
532 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
534 m.update(str(st.st_mtime))
535 m.update(str(st.st_size))
538 Utils.h_file = h_file
542 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
543 shell=True, color='PINK', ext_in='.bin')
546 @feature('copy_script')
547 @before('apply_link')
548 def copy_script(self):
549 tsk = self.create_task('copy_script', self.allnodes[0])
550 tsk.env.TARGET = self.target
552 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
553 '''used to copy scripts from the source tree into the build directory
554 for use by selftest'''
556 source = bld.path.ant_glob(pattern)
558 bld.SET_BUILD_GROUP('build_source')
559 for s in TO_LIST(source):
561 if installname != None:
563 target = os.path.join(installdir, iname)
564 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
566 t = bld(features='copy_script',
571 t.env.LINK_TARGET = target
573 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
576 def install_file(bld, destdir, file, chmod=O644, flat=False,
577 python_fixup=False, destname=None, base_name=None):
579 destdir = bld.EXPAND_VARIABLES(destdir)
583 destname = os.path.basename(destname)
584 dest = os.path.join(destdir, destname)
586 # fixup the python path it will use to find Samba modules
587 inst_file = file + '.inst'
588 bld.SAMBA_GENERATOR('python_%s' % destname,
589 rule="sed 's|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g' < ${SRC} > ${TGT}",
594 file = os.path.join(base_name, file)
595 bld.install_as(dest, file, chmod=chmod)
598 def INSTALL_FILES(bld, destdir, files, chmod=O644, flat=False,
599 python_fixup=False, destname=None, base_name=None):
600 '''install a set of files'''
601 for f in TO_LIST(files):
602 install_file(bld, destdir, f, chmod=chmod, flat=flat,
603 python_fixup=python_fixup, destname=destname,
605 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
608 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=O644, flat=False,
609 python_fixup=False, exclude=None, trim_path=None):
610 '''install a set of files matching a wildcard pattern'''
611 files=TO_LIST(bld.path.ant_glob(pattern))
615 files2.append(os_path_relpath(f, trim_path))
620 if fnmatch.fnmatch(f, exclude):
622 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
623 python_fixup=python_fixup, base_name=trim_path)
624 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
627 def INSTALL_DIRS(bld, destdir, dirs):
628 '''install a set of directories'''
629 destdir = bld.EXPAND_VARIABLES(destdir)
630 dirs = bld.EXPAND_VARIABLES(dirs)
631 for d in TO_LIST(dirs):
632 bld.install_dir(os.path.join(destdir, d))
633 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
636 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
637 class header_task(Task.Task):
640 vars = ['INCLUDEDIR', 'HEADER_DEPS']
642 txt = self.inputs[0].read(self.env)
644 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
646 themap = self.generator.bld.subst_table
650 return "#include <%s>" % themap.get(s, s)
653 txt = re_header.sub(repl, txt)
657 f = open(self.outputs[0].abspath(self.env), 'w')
665 initialize the header substitution table
666 for now use the file headermap.txt but in the future we will compute the paths properly
669 if getattr(bld, 'subst_table', None):
670 return bld.subst_table_h
672 node = bld.srcnode.find_resource("source4/headermap.txt")
675 bld.subst_table_h = 0
677 lines = node.read(None)
679 lines = [x.strip().split(': ') for x in lines.split('\n') if x.rfind(': ') > -1]
680 bld.subst_table = dict(lines)
682 # pidl file replacement (all of this is temporary, one step at a time)
683 keyz = list(bld.subst_table.keys())
685 bld.subst_table['bin/default/' + k] = bld.subst_table[k]
687 tp = tuple(bld.subst_table.keys())
688 bld.subst_table_h = hash(tp)
689 return bld.subst_table_h
691 @TaskGen.feature('pubh')
692 def make_public_headers(self):
693 if not self.bld.is_install:
694 # install time only (lazy)
697 self.env['HEADER_DEPS'] = init_subst(self.bld)
698 # adds a dependency and trigger a rebuild if the dict changes
700 header_path = getattr(self, 'header_path', None) or ''
702 for x in self.to_list(self.headers):
704 # too complicated, but what was the original idea?
705 if isinstance(header_path, list):
707 for (p1, dir) in header_path:
708 lst = self.to_list(p1)
710 if fnmatch.fnmatch(x, p2):
718 inst_path = header_path
722 if x.find(':') != -1:
727 inn = self.path.find_resource(name)
729 raise ValueError("could not find the public header %r in %r" % (name, self.path))
730 out = inn.change_ext('.inst.h')
731 self.create_task('header', inn, out)
737 inst_path = inst_path + '/'
738 inst_path = inst_path + dest
740 #print("going to install the headers", inst_path, out)
741 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
743 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
744 '''install some headers
746 header_path may either be a string that is added to the INCLUDEDIR,
747 or it can be a dictionary of wildcard patterns which map to destination
748 directories relative to INCLUDEDIR
750 bld.SET_BUILD_GROUP('final')
751 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
753 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
756 def subst_at_vars(task):
757 '''substiture @VAR@ style variables in a file'''
758 src = task.inputs[0].srcpath(task.env)
759 tgt = task.outputs[0].bldpath(task.env)
765 a = re.split('(@\w+@)', s)
768 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
770 if re.match('@\w+@', v):
772 if not vname in task.env and vname.upper() in task.env:
773 vname = vname.upper()
774 if not vname in task.env:
775 Logs.error("Unknown substitution %s in %s" % (v, task.name))
777 v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
778 # now we back substitute the allowed pc vars
779 for (b, m) in back_sub:
782 if not b in done_var:
783 # we don't want to substitute the first usage
789 contents = ''.join(out)
791 s = f.write(contents)
797 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
798 '''install some pkg_config pc files'''
799 dest = '${PKGCONFIGDIR}'
800 dest = bld.EXPAND_VARIABLES(dest)
801 for f in TO_LIST(pc_files):
802 base=os.path.basename(f)
803 t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
808 t.env.PACKAGE_VERSION = vnum
809 INSTALL_FILES(bld, dest, f, flat=True, destname=base)
810 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
813 def MANPAGES(bld, manpages):
814 '''build and install manual pages'''
815 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
816 for m in manpages.split():
818 bld.SAMBA_GENERATOR(m,
822 rule='${XSLTPROC} -o ${TGT} ${MAN_XSL} ${SRC}'
824 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
825 Build.BuildContext.MANPAGES = MANPAGES
828 #############################################################
829 # give a nicer display when building different types of files
830 def progress_display(self, msg, fname):
831 col1 = Logs.colors(self.color)
832 col2 = Logs.colors.NORMAL
833 total = self.position[1]
835 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
836 return fs % (self.position[0], self.position[1], col1, fname, col2)
838 def link_display(self):
839 if Options.options.progress_bar != 0:
840 return Task.Task.old_display(self)
841 fname = self.outputs[0].bldpath(self.env)
842 return progress_display(self, 'Linking', fname)
843 Task.TaskBase.classes['cc_link'].display = link_display
845 def samba_display(self):
846 if Options.options.progress_bar != 0:
847 return Task.Task.old_display(self)
849 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
850 if self.name in targets:
851 target_type = targets[self.name]
852 type_map = { 'GENERATOR' : 'Generating',
853 'PROTOTYPE' : 'Generating'
855 if target_type in type_map:
856 return progress_display(self, type_map[target_type], self.name)
858 fname = self.inputs[0].bldpath(self.env)
859 if fname[0:3] == '../':
861 ext_loc = fname.rfind('.')
863 return Task.Task.old_display(self)
864 ext = fname[ext_loc:]
866 ext_map = { '.idl' : 'Compiling IDL',
867 '.et' : 'Compiling ERRTABLE',
868 '.asn1': 'Compiling ASN1',
871 return progress_display(self, ext_map[ext], fname)
872 return Task.Task.old_display(self)
874 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
875 Task.TaskBase.classes['Task'].display = samba_display