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
32 # some systems have broken threading in python
33 if os.environ.get('WAF_NOTHREADS') == '1':
38 os.putenv('PYTHONUNBUFFERED', '1')
41 if Constants.HEXVERSION < 0x105019:
43 Please use the version of waf that comes with Samba, not
44 a system installed version. See http://wiki.samba.org/index.php/Waf
47 Alternatively, please use ./autogen-waf.sh, and then
48 run ./configure and make as usual. That will call the right version of waf.
54 def SAMBA_BUILD_ENV(conf):
55 '''create the samba build environment'''
56 conf.env.BUILD_DIRECTORY = conf.blddir
57 mkdir_p(os.path.join(conf.blddir, LIB_PATH))
58 mkdir_p(os.path.join(conf.blddir, "modules"))
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', 'modules']:
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,
103 external_library=False,
114 target_type='LIBRARY',
115 bundled_extension=True,
121 private_library=False,
122 grouping_library=False,
124 '''define a Samba library'''
127 SET_TARGET_TYPE(bld, libname, 'DISABLED')
130 source = bld.EXPAND_VARIABLES(source, vars=vars)
132 # remember empty libraries, so we can strip the dependencies
133 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
134 SET_TARGET_TYPE(bld, libname, 'EMPTY')
137 if BUILTIN_LIBRARY(bld, libname):
140 obj_target = libname + '.objlist'
142 if group == 'libraries':
143 subsystem_group = 'main'
145 subsystem_group = group
147 # first create a target for building the object files for this library
148 # by separating in this way, we avoid recompiling the C files
149 # separately for the install library and the build library
150 bld.SAMBA_SUBSYSTEM(obj_target,
153 public_deps = public_deps,
155 public_headers = public_headers,
156 header_path = header_path,
158 group = subsystem_group,
159 autoproto = autoproto,
160 depends_on = depends_on,
161 hide_symbols = hide_symbols,
162 pyext = pyext or (target_type == "PYTHON"),
163 local_include = local_include)
165 if BUILTIN_LIBRARY(bld, libname):
168 if not SET_TARGET_TYPE(bld, libname, target_type):
171 # the library itself will depend on that object target
172 deps += ' ' + public_deps
174 deps.append(obj_target)
176 realname = bld.map_shlib_extension(realname, python=(target_type=='PYTHON'))
177 link_name = bld.map_shlib_extension(link_name, python=(target_type=='PYTHON'))
179 # we don't want any public libraries without version numbers
180 if not private_library and vnum is None and soname is None and target_type != 'PYTHON' and not realname:
181 raise Utils.WafError("public library '%s' must have a vnum" % libname)
183 if target_type == 'PYTHON' or realname or not private_library:
184 bundled_name = libname.replace('_', '-')
186 bundled_name = PRIVATE_NAME(bld, libname, bundled_extension, private_library)
190 Logs.error("vnum is invalid for private libraries")
192 vnum = Utils.g_module.VERSION
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'
202 features += ' abi_check'
205 abi_file = os.path.join(bld.curdir, abi_file)
207 bld.SET_BUILD_GROUP(group)
211 target = bundled_name,
212 depends_on = depends_on,
214 samba_includes = includes,
215 local_include = local_include,
219 samba_inst_path = install_path,
221 samba_realname = realname,
222 samba_install = install,
224 abi_match = abi_match,
225 private_library = private_library,
226 grouping_library=grouping_library
229 if realname and not link_name:
230 link_name = 'shared/%s' % realname
233 t.link_name = link_name
235 if pc_files is not None:
236 bld.PKG_CONFIG_FILES(pc_files, vnum=vnum)
238 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
239 bld.MANPAGES(manpages)
242 Build.BuildContext.SAMBA_LIBRARY = SAMBA_LIBRARY
245 #################################################################
246 def SAMBA_BINARY(bld, binname, source,
256 use_global_deps=True,
267 '''define a Samba binary'''
270 SET_TARGET_TYPE(bld, binname, 'DISABLED')
273 if not SET_TARGET_TYPE(bld, binname, 'BINARY'):
276 features = 'cc cprogram symlink_bin install_bin'
278 features += ' pyembed'
280 obj_target = binname + '.objlist'
282 source = bld.EXPAND_VARIABLES(source, vars=vars)
283 source = unique_list(TO_LIST(source))
285 if group == 'binaries':
286 subsystem_group = 'main'
288 subsystem_group = group
290 # first create a target for building the object files for this binary
291 # by separating in this way, we avoid recompiling the C files
292 # separately for the install binary and the build binary
293 bld.SAMBA_SUBSYSTEM(obj_target,
298 group = subsystem_group,
299 autoproto = autoproto,
300 subsystem_name = subsystem_name,
301 local_include = local_include,
302 use_hostcc = use_hostcc,
304 use_global_deps= use_global_deps)
306 bld.SET_BUILD_GROUP(group)
308 # the binary itself will depend on that object target
310 deps.append(obj_target)
317 samba_includes = includes,
318 local_include = local_include,
319 samba_modules = modules,
321 samba_subsystem= subsystem_name,
323 samba_inst_path= install_path,
324 samba_install = install
327 # setup the subsystem_name as an alias for the real
328 # binary name, so it can be found when expanding
329 # subsystem dependencies
330 if subsystem_name is not None:
331 bld.TARGET_ALIAS(subsystem_name, binname)
333 if manpages is not None and 'XSLTPROC_MANPAGES' in bld.env and bld.env['XSLTPROC_MANPAGES']:
334 bld.MANPAGES(manpages)
336 Build.BuildContext.SAMBA_BINARY = SAMBA_BINARY
339 #################################################################
340 def SAMBA_MODULE(bld, modname, source,
346 autoproto_extra_source='',
349 internal_module=True,
355 '''define a Samba module.'''
357 source = bld.EXPAND_VARIABLES(source, vars=vars)
359 if internal_module or BUILTIN_LIBRARY(bld, modname):
360 bld.SAMBA_SUBSYSTEM(modname, source,
364 autoproto_extra_source=autoproto_extra_source,
366 local_include=local_include,
369 bld.ADD_INIT_FUNCTION(subsystem, modname, init_function)
373 SET_TARGET_TYPE(bld, modname, 'DISABLED')
376 if aliases is not None:
377 # if we have aliases, then create a private base library, and a set
378 # of modules on top of that library
380 cflags += " -D%s=samba_init_module" % init_function
382 basename = modname + '-base'
383 bld.SAMBA_LIBRARY(basename,
387 autoproto = autoproto,
388 local_include=local_include,
394 aliases = TO_LIST(aliases)
395 aliases.append(modname)
397 for alias in aliases:
398 bld.SAMBA_MODULE(alias,
400 internal_module=False,
402 init_function=init_function,
407 obj_target = modname + '.objlist'
410 if subsystem is not None:
411 deps += ' ' + subsystem
412 while realname.startswith("lib"+subsystem+"_"):
413 realname = realname[len("lib"+subsystem+"_"):]
414 while realname.startswith(subsystem+"_"):
415 realname = realname[len(subsystem+"_"):]
417 realname = bld.make_libname(realname)
418 while realname.startswith("lib"):
419 realname = realname[len("lib"):]
421 build_link_name = "modules/%s/%s" % (subsystem, realname)
424 cflags += " -D%s=samba_init_module" % init_function
426 bld.SAMBA_LIBRARY(modname,
431 autoproto = autoproto,
432 local_include=local_include,
434 link_name=build_link_name,
435 install_path="${MODULESDIR}/%s" % subsystem,
440 Build.BuildContext.SAMBA_MODULE = SAMBA_MODULE
443 #################################################################
444 def SAMBA_SUBSYSTEM(bld, modname, source,
453 init_function_sentinal=None,
455 autoproto_extra_source='',
458 local_include_first=True,
462 use_global_deps=True,
466 '''define a Samba subsystem'''
469 SET_TARGET_TYPE(bld, modname, 'DISABLED')
472 # remember empty subsystems, so we can strip the dependencies
473 if ((source == '') or (source == [])) and deps == '' and public_deps == '':
474 SET_TARGET_TYPE(bld, modname, 'EMPTY')
477 if not SET_TARGET_TYPE(bld, modname, 'SUBSYSTEM'):
480 source = bld.EXPAND_VARIABLES(source, vars=vars)
481 source = unique_list(TO_LIST(source))
483 deps += ' ' + public_deps
485 bld.SET_BUILD_GROUP(group)
495 samba_cflags = CURRENT_CFLAGS(bld, modname, cflags, hide_symbols=hide_symbols),
496 depends_on = depends_on,
497 samba_deps = TO_LIST(deps),
498 samba_includes = includes,
499 local_include = local_include,
500 local_include_first = local_include_first,
501 samba_subsystem= subsystem_name,
502 samba_use_hostcc = use_hostcc,
503 samba_use_global_deps = use_global_deps
506 if cflags_end is not None:
507 t.samba_cflags.extend(TO_LIST(cflags_end))
509 if autoproto is not None:
510 bld.SAMBA_AUTOPROTO(autoproto, source + TO_LIST(autoproto_extra_source))
511 if public_headers is not None:
512 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
516 Build.BuildContext.SAMBA_SUBSYSTEM = SAMBA_SUBSYSTEM
519 def SAMBA_GENERATOR(bld, name, rule, source='', target='',
520 group='generators', enabled=True,
525 '''A generic source generator target'''
527 if not SET_TARGET_TYPE(bld, name, 'GENERATOR'):
533 bld.SET_BUILD_GROUP(group)
536 source=bld.EXPAND_VARIABLES(source, vars=vars),
538 shell=isinstance(rule, str),
547 if public_headers is not None:
548 bld.PUBLIC_HEADERS(public_headers, header_path=header_path)
550 Build.BuildContext.SAMBA_GENERATOR = SAMBA_GENERATOR
555 def SETUP_BUILD_GROUPS(bld):
556 '''setup build groups used to ensure that the different build
557 phases happen consecutively'''
558 bld.p_ln = bld.srcnode # we do want to see all targets!
559 bld.env['USING_BUILD_GROUPS'] = True
560 bld.add_group('setup')
561 bld.add_group('build_compiler_source')
562 bld.add_group('base_libraries')
563 bld.add_group('generators')
564 bld.add_group('compiler_prototypes')
565 bld.add_group('compiler_libraries')
566 bld.add_group('build_compilers')
567 bld.add_group('build_source')
568 bld.add_group('prototypes')
569 bld.add_group('main')
570 bld.add_group('symbolcheck')
571 bld.add_group('libraries')
572 bld.add_group('binaries')
573 bld.add_group('syslibcheck')
574 bld.add_group('final')
575 Build.BuildContext.SETUP_BUILD_GROUPS = SETUP_BUILD_GROUPS
578 def SET_BUILD_GROUP(bld, group):
579 '''set the current build group'''
580 if not 'USING_BUILD_GROUPS' in bld.env:
583 Build.BuildContext.SET_BUILD_GROUP = SET_BUILD_GROUP
588 def ENABLE_TIMESTAMP_DEPENDENCIES(conf):
589 """use timestamps instead of file contents for deps
590 this currently doesn't work"""
591 def h_file(filename):
593 st = os.stat(filename)
594 if stat.S_ISDIR(st[stat.ST_MODE]): raise IOError('not a file')
596 m.update(str(st.st_mtime))
597 m.update(str(st.st_size))
600 Utils.h_file = h_file
604 t = Task.simple_task_type('copy_script', 'rm -f ${LINK_TARGET} && ln -s ${SRC[0].abspath(env)} ${LINK_TARGET}',
605 shell=True, color='PINK', ext_in='.bin')
608 @feature('copy_script')
609 @before('apply_link')
610 def copy_script(self):
611 tsk = self.create_task('copy_script', self.allnodes[0])
612 tsk.env.TARGET = self.target
614 def SAMBA_SCRIPT(bld, name, pattern, installdir, installname=None):
615 '''used to copy scripts from the source tree into the build directory
616 for use by selftest'''
618 source = bld.path.ant_glob(pattern)
620 bld.SET_BUILD_GROUP('build_source')
621 for s in TO_LIST(source):
623 if installname != None:
625 target = os.path.join(installdir, iname)
626 tgtdir = os.path.dirname(os.path.join(bld.srcnode.abspath(bld.env), '..', target))
628 t = bld(features='copy_script',
633 t.env.LINK_TARGET = target
635 Build.BuildContext.SAMBA_SCRIPT = SAMBA_SCRIPT
638 def install_file(bld, destdir, file, chmod=MODE_644, flat=False,
639 python_fixup=False, destname=None, base_name=None):
641 destdir = bld.EXPAND_VARIABLES(destdir)
645 destname = os.path.basename(destname)
646 dest = os.path.join(destdir, destname)
648 # fixup the python path it will use to find Samba modules
649 inst_file = file + '.inst'
650 if bld.env["PYTHONDIR"] not in sys.path:
651 regex = "s|\(sys.path.insert.*\)bin/python\(.*\)$|\\1${PYTHONDIR}\\2|g"
653 # Eliminate updating sys.path if the target python dir is already
655 regex = "s|sys.path.insert.*bin/python.*$||g"
656 bld.SAMBA_GENERATOR('python_%s' % destname,
657 rule="sed '%s' < ${SRC} > ${TGT}" % regex,
662 file = os.path.join(base_name, file)
663 bld.install_as(dest, file, chmod=chmod)
666 def INSTALL_FILES(bld, destdir, files, chmod=MODE_644, flat=False,
667 python_fixup=False, destname=None, base_name=None):
668 '''install a set of files'''
669 for f in TO_LIST(files):
670 install_file(bld, destdir, f, chmod=chmod, flat=flat,
671 python_fixup=python_fixup, destname=destname,
673 Build.BuildContext.INSTALL_FILES = INSTALL_FILES
676 def INSTALL_WILDCARD(bld, destdir, pattern, chmod=MODE_644, flat=False,
677 python_fixup=False, exclude=None, trim_path=None):
678 '''install a set of files matching a wildcard pattern'''
679 files=TO_LIST(bld.path.ant_glob(pattern))
683 files2.append(os_path_relpath(f, trim_path))
688 if fnmatch.fnmatch(f, exclude):
690 INSTALL_FILES(bld, destdir, files, chmod=chmod, flat=flat,
691 python_fixup=python_fixup, base_name=trim_path)
692 Build.BuildContext.INSTALL_WILDCARD = INSTALL_WILDCARD
695 def INSTALL_DIRS(bld, destdir, dirs):
696 '''install a set of directories'''
697 destdir = bld.EXPAND_VARIABLES(destdir)
698 dirs = bld.EXPAND_VARIABLES(dirs)
699 for d in TO_LIST(dirs):
700 bld.install_dir(os.path.join(destdir, d))
701 Build.BuildContext.INSTALL_DIRS = INSTALL_DIRS
704 re_header = re.compile('#include[ \t]*"([^"]+)"', re.I | re.M)
705 class header_task(Task.Task):
707 The public headers (the one installed on the system) have both
708 different paths and contents, so the rename is not enough.
710 Intermediate .inst.h files are created because path manipulation
711 may be slow. The substitution is thus performed only once.
716 vars = ['INCLUDEDIR', 'HEADER_DEPS']
719 txt = self.inputs[0].read(self.env)
721 # hard-coded string, but only present in samba4 (I promise, you won't feel a thing)
722 txt = txt.replace('#if _SAMBA_BUILD_ == 4', '#if 1\n')
724 # use a regexp to substitute the #include lines in the files
725 map = self.generator.bld.hnodemap
726 dirnodes = self.generator.bld.hnodedirs
731 # pokemon headers: gotta catch'em all!
733 if s.startswith('bin/default'):
734 node = self.generator.bld.srcnode.find_resource(s.replace('bin/default/', ''))
736 Logs.warn('could not find the public header for %r' % s)
740 Logs.warn('could not find the public header replacement for build header %r' % s)
742 # this part is more difficult since the path may be relative to anything
743 for dirnode in dirnodes:
744 node = dirnode.find_resource(s)
750 Logs.warn('could not find the public header replacement for source header %r %r' % (s, node))
752 Logs.warn('-> could not find the public header for %r' % s)
754 return "#include <%s>" % fin
757 txt = re_header.sub(repl, txt)
759 # and write the output file
762 f = open(self.outputs[0].abspath(self.env), 'w')
768 @TaskGen.feature('pubh')
769 def make_public_headers(self):
771 collect the public headers to process and to install, then
772 create the substitutions (name and contents)
775 if not self.bld.is_install:
776 # install time only (lazy)
780 # hnodedirs: list of folders for searching the headers
781 # hnodemap: node ids and replacement string (node objects are unique)
783 self.bld.hnodedirs.append(self.path)
784 except AttributeError:
785 self.bld.hnodemap = {}
786 self.bld.hnodedirs = [self.bld.srcnode, self.path]
788 for k in 'source4 source4/include lib/talloc lib/tevent/ source4/lib/ldb/include/'.split():
789 node = self.bld.srcnode.find_dir(k)
791 self.bld.hnodedirs.append(node)
793 header_path = getattr(self, 'header_path', None) or ''
795 for x in self.to_list(self.headers):
797 # too complicated, but what was the original idea?
798 if isinstance(header_path, list):
800 for (p1, dir) in header_path:
801 lst = self.to_list(p1)
803 if fnmatch.fnmatch(x, p2):
811 inst_path = header_path
815 if x.find(':') != -1:
820 inn = self.path.find_resource(name)
823 raise ValueError("could not find the public header %r in %r" % (name, self.path))
824 out = inn.change_ext('.inst.h')
825 self.create_task('header', inn, out)
831 inst_path = inst_path + '/'
832 inst_path = inst_path + dest
834 self.bld.install_as('${INCLUDEDIR}/%s' % inst_path, out, self.env)
836 self.bld.hnodemap[inn.id] = inst_path
838 # create a hash (not md5) to make sure the headers are re-created if something changes
840 lst = list(self.bld.hnodemap.keys())
843 val = hash((val, k, self.bld.hnodemap[k]))
844 self.bld.env.HEADER_DEPS = val
846 def PUBLIC_HEADERS(bld, public_headers, header_path=None):
847 '''install some headers
849 header_path may either be a string that is added to the INCLUDEDIR,
850 or it can be a dictionary of wildcard patterns which map to destination
851 directories relative to INCLUDEDIR
853 bld.SET_BUILD_GROUP('final')
854 ret = bld(features=['pubh'], headers=public_headers, header_path=header_path)
856 Build.BuildContext.PUBLIC_HEADERS = PUBLIC_HEADERS
859 def subst_at_vars(task):
860 '''substiture @VAR@ style variables in a file'''
861 src = task.inputs[0].srcpath(task.env)
862 tgt = task.outputs[0].bldpath(task.env)
868 a = re.split('(@\w+@)', s)
871 back_sub = [ ('PREFIX', '${prefix}'), ('EXEC_PREFIX', '${exec_prefix}')]
873 if re.match('@\w+@', v):
875 if not vname in task.env and vname.upper() in task.env:
876 vname = vname.upper()
877 if not vname in task.env:
878 Logs.error("Unknown substitution %s in %s" % (v, task.name))
880 v = SUBST_VARS_RECURSIVE(task.env[vname], task.env)
881 # now we back substitute the allowed pc vars
882 for (b, m) in back_sub:
885 if not b in done_var:
886 # we don't want to substitute the first usage
892 contents = ''.join(out)
894 s = f.write(contents)
899 def PKG_CONFIG_FILES(bld, pc_files, vnum=None):
900 '''install some pkg_config pc files'''
901 dest = '${PKGCONFIGDIR}'
902 dest = bld.EXPAND_VARIABLES(dest)
903 for f in TO_LIST(pc_files):
904 base=os.path.basename(f)
905 t = bld.SAMBA_GENERATOR('PKGCONFIG_%s' % base,
910 t.env.PACKAGE_VERSION = vnum
911 INSTALL_FILES(bld, dest, f, flat=True, destname=base)
912 Build.BuildContext.PKG_CONFIG_FILES = PKG_CONFIG_FILES
915 def MANPAGES(bld, manpages):
916 '''build and install manual pages'''
917 bld.env.MAN_XSL = 'http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl'
918 for m in manpages.split():
920 bld.SAMBA_GENERATOR(m,
924 rule='${XSLTPROC} -o ${TGT} --nonet ${MAN_XSL} ${SRC}'
926 bld.INSTALL_FILES('${MANDIR}/man%s' % m[-1], m, flat=True)
927 Build.BuildContext.MANPAGES = MANPAGES
930 #############################################################
931 # give a nicer display when building different types of files
932 def progress_display(self, msg, fname):
933 col1 = Logs.colors(self.color)
934 col2 = Logs.colors.NORMAL
935 total = self.position[1]
937 fs = '[%%%dd/%%%dd] %s %%s%%s%%s\n' % (n, n, msg)
938 return fs % (self.position[0], self.position[1], col1, fname, col2)
940 def link_display(self):
941 if Options.options.progress_bar != 0:
942 return Task.Task.old_display(self)
943 fname = self.outputs[0].bldpath(self.env)
944 return progress_display(self, 'Linking', fname)
945 Task.TaskBase.classes['cc_link'].display = link_display
947 def samba_display(self):
948 if Options.options.progress_bar != 0:
949 return Task.Task.old_display(self)
951 targets = LOCAL_CACHE(self, 'TARGET_TYPE')
952 if self.name in targets:
953 target_type = targets[self.name]
954 type_map = { 'GENERATOR' : 'Generating',
955 'PROTOTYPE' : 'Generating'
957 if target_type in type_map:
958 return progress_display(self, type_map[target_type], self.name)
960 if len(self.inputs) == 0:
961 return Task.Task.old_display(self)
963 fname = self.inputs[0].bldpath(self.env)
964 if fname[0:3] == '../':
966 ext_loc = fname.rfind('.')
968 return Task.Task.old_display(self)
969 ext = fname[ext_loc:]
971 ext_map = { '.idl' : 'Compiling IDL',
972 '.et' : 'Compiling ERRTABLE',
973 '.asn1': 'Compiling ASN1',
976 return progress_display(self, ext_map[ext], fname)
977 return Task.Task.old_display(self)
979 Task.TaskBase.classes['Task'].old_display = Task.TaskBase.classes['Task'].display
980 Task.TaskBase.classes['Task'].display = samba_display