option-tester: Add many changes
[ntpsec.git] / wscript
blobd4129f20b10d1e0c8db6c6aa63171172e51b2985
1 # Copyright the NTPsec project contributors
3 # SPDX-License-Identifier: BSD-2-Clause
5 from __future__ import print_function
7 import itertools
8 import os
9 import shlex
10 import sys
12 from waflib import Build
13 from waflib import Context
14 from waflib import Scripting
15 from waflib import Utils
16 from waflib.Build import (BuildContext, CleanContext, InstallContext,
17                           StepContext, ListContext)
18 from waflib.Context import BOTH
19 from waflib.Errors import WafError
20 from waflib.Logs import pprint
21 from waflib.Tools import waf_unit_test
23 # Avoid writing .pyc files in wafhelpers/
24 sys.dont_write_bytecode = True
26 from wafhelpers.options import options_cmd
27 from wafhelpers.probes import probe_header, probe_function
28 from wafhelpers.test import test_write_log, test_print_log
31 pprint.__doc__ = None
33 APPNAME = 'ntpsec'
35 out = "build"
37 config = {
38     "out": out,
39     "OPT_STORE": {}
43 def help(ctx):
44     "Be helpful, give a usage"
45     print('''
46 Usage: waf <command>
47     build       Build the project
48     check       Run tests
49     configure   Configure the project
50     dist        Create a release
51     install     Install the project
52     loccount    Show SLOC count of the source tree
53     uninstall   Uninstall the project
55 ''')
58 def options(ctx):
59     options_cmd(ctx, config)
60     ctx.load('asciidoc', tooldir='wafhelpers/')
61     ctx.recurse("pylib")
64 def configure(ctx):
65     ctx.load('asciidoc', tooldir='wafhelpers/')
67     class oc(Build.BuildContext):
68         cmd = 'oc'
70         def exec_command(self, cmd, **kw):
71             kw['output'] = BOTH
72             try:
73                 err, out = self.cmd_and_log(cmd, **kw)
74             except WafError as e:
75                 self.logger.debug('WafError')
76                 return e.returncode
77             if (len(out) and any(word in out for word
78                                  in ['err', 'err:', 'error', 'error:',
79                                      'ignored', 'illegal', 'unknown',
80                                      'unrecognized', 'warning'])):
81                 self.logger.debug('noooo %r' % out)
82                 return 1
83             if err:
84                 self.logger.debug('noooo %r' % err)
85                 return 1
86             return 0
88     def msg(str):
89         pprint("YELLOW", str)
91     def msg_setting(name, val):
92         pprint("NORMAL", "  %-30s: " % name, sep="")
93         pprint("YELLOW", val)
95     srcnode = ctx.srcnode.abspath()
96     bldnode = ctx.bldnode.abspath()
98     ctx.run_build_cls = 'check'
99     ctx.load('waf', tooldir='wafhelpers/')
100     ctx.load('waf_unit_test')
101     ctx.load('gnu_dirs')
103     with open("VERSION", "r") as f:
104         ntpsec_release = f.read().split(" ")[0].strip()
106     ctx.env.OPT_STORE = config["OPT_STORE"]
108     opt_map = {}
109     # Wipe out and override flags with those from the commandline
110     for flag in ctx.env.OPT_STORE:
111         if flag == "--undefine":
112             for sym in ctx.env.OPT_STORE[flag]:
113                 ctx.undefine(sym)
114         elif flag == "--define":
115             for symval in ctx.env.OPT_STORE[flag]:
116                 (sym, val) = symval.split("=")
117                 try:
118                     ctx.define(sym, int(val))
119                 except ValueError:
120                     ctx.define(sym, val)
121         else:
122             opt = flag.replace("--", "").upper()
123             opt_map[opt] = ctx.env.OPT_STORE[flag]
125     ctx.env['ntpc'] = ctx.options.enable_pylib
126     ctx.env['ntpcver'] = '1.1.0'
128     msg("--- Configuring host ---")
129     ctx.setenv('host', ctx.env.derive())
131     ctx.load('compiler_c')
132     ctx.start_msg('Checking compiler version')
133     ctx.end_msg("%s" % ".".join(ctx.env.CC_VERSION))
135     # Some distros do not have /sbin in the PATH for non-root users.  We honor
136     # the real PATH first, but append the sbin directories.
137     ctx.find_program(
138         "ldconfig", var="BIN_LDCONFIG", mandatory=False,
139         path_list=(os.environ.get('PATH', '').split(os.pathsep) +
140             ["/sbin", "/usr/sbin", "/usr/local/sbin"]))
142     # Ensure m4 is present, or bison will fail with SIGPIPE
143     ctx.find_program('m4')
144     ctx.load('bison')
146     for opt in opt_map:
147         ctx.env[opt] = opt_map[opt]
149     # Not needed to build.  Used by utility scripts.
150     ctx.find_program("awk", var="BIN_AWK", mandatory=False)
151     ctx.find_program("sh", var="BIN_SH", mandatory=False)
153     ctx.check_cfg(
154         package='systemd', variables=['systemdsystemunitdir'],
155         uselib_store='SYSTEMD', mandatory=False,
156         msg="Checking for systemd")
157     if ctx.env.SYSTEMD_systemdsystemunitdir:
158         ctx.start_msg("systemd unit directory:")
159         ctx.end_msg(ctx.env.SYSTEMD_systemdsystemunitdir)
161     ctx.env.BIN_GIT = False
162     if os.path.exists(".git"):
163         ctx.find_program("git", var="BIN_GIT", mandatory=False)
165     build_desc = ctx.options.build_desc.strip()
166     if build_desc:
167         build_desc = ' ' + build_desc
168     if ctx.env.BIN_GIT:
169         # 'tag', '7', and 'deadbeef' are fill ins for
170         # a previous tag (always dropped), commits since that tag,
171         # the short commit hash. I can see 5 'git describe' outputs
172         # buildbots and prepush should get: tag-7-gdeadbeef
173         # working developers should get: tag-7-gdeadbeef-dirty
174         # patched shallow builders should get: gdeadbeef-dirty
175         # other shallow builder should get: gdeadbeef
176         # the thorium poisoned get errors and burst into flame
177         # 1-2 tokens gets appended verbatim
178         # 3-4 gets the first token dropped and the rest added
179         # I have never seen 5+ tokens, we should be safe
180         cmd = ctx.env.BIN_GIT + shlex.split("describe --tags --dirty --always")
181         git_short_hash = ctx.cmd_and_log(cmd).strip().split('-')
182         clip = 1 if len(git_short_hash) > 2 else 0
183         git_short_hash = '-'.join(git_short_hash[clip:])
185         ctx.env.NTPSEC_VERSION = "%s+" % ntpsec_release
186         ctx.env.NTPSEC_VERSION_EXTENDED = ("%s+%s%s" %
187                                            (ntpsec_release,
188                                             git_short_hash,
189                                             build_desc))
190     else:
191         ctx.env.NTPSEC_VERSION = "%s" % ntpsec_release
192         ctx.env.NTPSEC_VERSION_EXTENDED = ("%s%s" % (ntpsec_release,
193                                                       build_desc))
194     ctx.define("NTPSEC_VERSION", ctx.env.NTPSEC_VERSION)
195     ctx.define("NTPSEC_VERSION_EXTENDED", ctx.env.NTPSEC_VERSION_EXTENDED)
197     # We require some things that C99 doesn't enable, like pthreads.
198     # These flags get propagated to both the host and main parts of the build.
199     #
200     # _POSIX_C_SOURCE
201     #      If ==1, like _POSIX_SOURCE;
202     #      if >=2 add IEEE Std 1003.2;
203     #      if >=199309L, add IEEE Std 1003.1b-1993;
204     #      if >=199506L, add IEEE Std 1003.1c-1995;
205     #      if >=200112L, all of IEEE 1003.1-2004
206     #      if >=200809L, all of IEEE 1003.1-2008
207     #
208     # FIXME: We'd like this to be -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700
209     # rather than -D_GNU_SOURCE, but that runs into problems in two places:
210     # (1) The ISC net handling stuff, where struct in6_addr’ loses a member
211     # named s6_addr32 that the macros need, and (2) three BSD functions
212     # related to chroot jailing in the sandbox code.
213     #
214     # Note that _POSIX_C_SOURCE >= 199506L and _GNU_SOURCE both turn on
215     # _POSIX_PTHREAD_SEMANTICS and _REENTRANT
216     #
217     ctx.env.CFLAGS = ["-std=c99", "-D_GNU_SOURCE"] + ctx.env.CFLAGS
219     msg("--- Configuring main ---")
220     ctx.setenv("main", ctx.env.derive())
222     from wafhelpers.check_sizeof import check_sizeof, check_timex
224     for opt in opt_map:
225         ctx.env[opt] = opt_map[opt]
227     if ctx.options.cross_compiler:
228         ctx.env.ENABLE_CROSS = True
230         ctx.start_msg("Using Cross compiler CC:")
231 #           ctx.get_cc_version(ctx.env.CC, gcc=True)
232         ctx.end_msg(ctx.options.cross_compiler)
234         ctx.env.CC = shlex.split(ctx.options.cross_compiler)
235         ctx.env.LINK_CC = shlex.split(ctx.options.cross_compiler)
237         if ctx.env["CROSS-CFLAGS"]:
238             # Lexically split each part of the CFLAGS, then chain the lists
239             iter = [shlex.split(x) for x in opt_map["CROSS-CFLAGS"]]
240             ctx.env.CFLAGS = list(itertools.chain.from_iterable(iter))
242         if ctx.env["CROSS-LDFLAGS"]:
243             # Lexically split each part of the LDFLAGS, then chain the lists
244             iter = [shlex.split(x) for x in opt_map["CROSS-LDFLAGS"]]
245             ctx.env.LDFLAGS = list(itertools.chain.from_iterable(iter))
247     if ctx.options.list:
248         from wafhelpers.refclock import refclock_map
249         print("ID    Description")
250         print("~~    ~~~~~~~~~~~")
251         for id in refclock_map:
252             print("%-5s %s" % (id, refclock_map[id]["descr"]))
254         return
256     # These are required by various refclocks
257     # needs to be tested before CFLAGS are set
258     if ctx.check_endianness() == "big":
259         ctx.define("WORDS_BIGENDIAN", 1)
261     if ctx.options.enable_leap_testing:
262         ctx.define("ENABLE_LEAP_TESTING", 1,
263                    comment="Enable leap seconds on other than 1st of month.")
265     # check for some libs first.  some options, like stack protector,
266     # may depend on some libs, like -lssp
267     ctx.check_cc(lib="m", comment="Math library")
268     ctx.check_cc(lib="rt", mandatory=False, comment="realtime library")
269     ctx.check_cc(lib="pthread", mandatory=False, comment="threads library")
270     ctx.check_cc(lib="execinfo", mandatory=False,
271                  comment="BSD backtrace library")
272     ret = ctx.check_cc(lib="bsd", mandatory=False,
273                        comment="BSD compatibility library")
274     if ret:
275         ctx.env.LDFLAGS += ["-lbsd"]
277     # -lssp and -lssp_nonshared may be needed by older gcc to
278     # support "-fstack-protector-all"
279     ret = ctx.check_cc(lib="ssp", mandatory=False,
280                        comment="libssp")
281     if ret:
282         ctx.env.LDFLAGS += ["-lssp"]
284     ret = ctx.check_cc(lib="ssp_nonshared", mandatory=False,
285                        comment="libssp_nonshared")
286     if ret:
287         ctx.env.LDFLAGS += ["-lssp_nonshared"]
289     cc_test_flags = [
290         ('PIC', '-fPIC'),
291         ('PIE', '-pie -fPIE'),
292         # this quiets most of macOS warnings on -fpie
293         ('unused', '-Qunused-arguments'),
294         # This is a useless warning on any architecture with a barrel
295         # shifter, which includes Intel and ARM and basically
296         # everything nowadays. Even so, we'd enable it out of
297         # perfectionism, but the GCC directives that ought to be
298         # useful for forcing structure alignment in order to suppress
299         # it locally don't seem to be working quite right.
300         # ('w_cast_align', "-Wcast-align"),
301         ('w_cast_qual', "-Wcast-qual"),
302         ('w_disabled_optimization', "-Wdisabled-optimization"),
303         ('w_float_equal', "-Wfloat-equal"),
304         ('w_format', '-Wformat'),
305         ('w_format_security', '-Wformat-security'),
306         # fails on OpenBSD 6
307         ('w_format_signedness', '-Wformat-signedness'),
308         ('w_implicit_function_declaration', "-Wimplicit-function-declaration"),
309         ('w_init_self', '-Winit-self'),
310         ('w_invalid_pch', '-Winvalid-pch'),
311         ('w_missing_declarations', '-Wmissing-declarations'),
312         ('w_multichar', '-Wmultichar'),
313         ('w_packed', '-Wpacked'),
314         ('w_pointer_arith', '-Wpointer-arith'),
315         ('w_shadow', '-Wshadow'),
316         # fails on clang
317         ('w_suggest_attribute_noreturn', "-Wsuggest-attribute=noreturn"),
318         ('w_write_strings', '-Wwrite-strings'),
319         ]
321     # Check which linker flags are supported
322     ld_hardening_flags = [
323         ('f_stack_protector_all', '-fstack-protector-all'),
324         ("z_now", "-Wl,-z,now"),     # no deferred symbol resolution
325     ]
327     # we prepend our options to CFLAGS, this allows user provided
328     # CFLAGS to override our computed CFLAGS
329     if not ctx.options.disable_debug_gdb:
330         ctx.env.CFLAGS = ["-g"] + ctx.env.CFLAGS
331         ctx.define("USEBACKTRACE", "1", quote=False)
332     else:
333         # not gdb debugging
334         cc_test_flags += [
335             ('LTO', '-flto'),                   # link time optimization
336             ]
337         ld_hardening_flags += [
338             ('stripall', "-Wl,--strip-all"),    # Strip binaries
339             ]
341     if ctx.options.enable_debug:
342         ctx.define("DEBUG", 1, comment="Enable debug mode")
343         ctx.env.BISONFLAGS += ["--debug"]
345     if ctx.options.enable_warnings:
346         # turn on some annoying warnings
347         ctx.env.CFLAGS = [
348             # "-Wall",                # for masochists
349             # "-Waggregate-return",   # breaks ldiv(), ntpcal_daysplit(),  etc.
350             # "-Wcast-align",         # fails on RasPi, needs fixing.
351             # "-Wbad-function-cast",  # ntpd casts long<->double a lot
352             # "-Wformat-nonliteral",  # complains about a used feature
353             "-Winline",               # some OS have inline issues.
354             # "-Wmissing-format-attribute", # false positives
355             # "-Wnested-externs",     # incompatible w/ Unity...
356             # "-Wpadded",             # duck... over 3k warnings
357             # "-Wredundant-decls",    # incompatible w/ Unity
358             "-Wswitch-default",       # warns on missing switch-default
359                                         # old Bison triggers this
360             "-Wswitch-enum",          # warns on missing enum case handler
361         ] + ctx.env.CFLAGS
362         cc_test_flags += [
363             ('w_implicit_fallthru', "-Wimplicit-fallthrough=3"),
364             # Fails on Solaris, OpenBSD 6, and RasPi
365             # Complains about a Bison bug
366             # Cannot be suppressed
367             # ('w_sign_conversion', "-Wsign-conversion"),
368             # fails on clang, lots of false positives and Unity complaints
369             # ('w_suggest_attribute_const', "-Wsuggest-attribute=const"),
370             # fails on clang, lot's of false positives and Unity complaints
371             # ('w_suggest_attribute_pure', "-Wsuggest-attribute=pure"),
372             ]
374     ctx.env.CFLAGS = [
375         # -O1 will turn on -D_FORTIFY_SOURCE=2 for us
376         "-O1",
377         "-Wall",
378         "-Wextra",
379         "-Wmissing-prototypes",
380         "-Wstrict-prototypes",
381         "-Wundef",
382         "-Wunused",
383         ] + ctx.env.CFLAGS
385     # gotta be tricky to test for -Wsuggest-attribute=const
386     FRAGMENT = '''
387 int tmp;
388 int main(int argc, char **argv) {
389         (void)argc; (void)argv;
390         tmp = argc;
391         return argc;
395     # check if C compiler supports some flags
396     old_run_build_cls = ctx.run_build_cls
397     ctx.run_build_cls = 'oc'
398     for (name, ccflag) in cc_test_flags:
399         ctx.check(cflags=ccflag,
400                   define_name='HAS_' + name,
401                   fragment=FRAGMENT,
402                   mandatory=False,
403                   msg='Checking if C compiler supports ' + ccflag,
404                   run_build_cls='oc')
406     ctx.run_build_cls = old_run_build_cls
408     if ctx.env.HAS_PIC:
409         ctx.env.CFLAGS = ["-fPIC"] + ctx.env.CFLAGS
411     if ctx.env.HAS_PIE:
412         ctx.env.LINKFLAGS_NTPD += [
413             "-pie",
414             ]
415         ctx.env.CFLAGS_bin = ["-fPIE", "-pie"] + ctx.env.CFLAGS
416         ld_hardening_flags += [
417             ('relro', "-Wl,-z,relro"),  # hardening, marks some read only,
418             ]
420     if ctx.env.HAS_unused:
421         ctx.env.CFLAGS = ['-Qunused-arguments'] + ctx.env.CFLAGS
423     # XXX: -flto currently breaks link of ntpd
424     if ctx.env.HAS_LTO and False:
425         ctx.env.CFLAGS = ["-flto"] + ctx.env.CFLAGS
427     # debug warnings that are not available with all compilers
428     if ctx.env.HAS_w_implicit_fallthru:
429         ctx.env.CFLAGS = ['-Wimplicit-fallthrough=3'] + ctx.env.CFLAGS
430     if ctx.env.HAS_w_suggest_attribute_const:
431         ctx.env.CFLAGS = ['-Wsuggest-attribute=const'] + ctx.env.CFLAGS
432     if ctx.env.HAS_w_suggest_attribute_noreturn:
433         ctx.env.CFLAGS = ['-Wsuggest-attribute=noreturn'] + ctx.env.CFLAGS
434     if ctx.env.HAS_w_suggest_attribute_pure:
435         ctx.env.CFLAGS = ['-Wsuggest-attribute=pure'] + ctx.env.CFLAGS
436     if ctx.env.HAS_w_format_security:
437         ctx.env.CFLAGS = ['-Wformat-security'] + ctx.env.CFLAGS
438     if ctx.env.HAS_w_format_signedness:
439         ctx.env.CFLAGS = ['-Wformat-signedness'] + ctx.env.CFLAGS
440     # should be before other -Wformat-* in CFLAGS
441     if ctx.env.HAS_w_format:
442         ctx.env.CFLAGS = ['-Wformat'] + ctx.env.CFLAGS
443     if ctx.env.HAS_w_float_equal:
444         ctx.env.CFLAGS = ['-Wfloat-equal'] + ctx.env.CFLAGS
445     if ctx.env.HAS_w_init_self:
446         ctx.env.CFLAGS = ['-Winit-self'] + ctx.env.CFLAGS
447     if ctx.env.HAS_w_write_strings:
448         ctx.env.CFLAGS = ['-Wwrite-strings'] + ctx.env.CFLAGS
449     if ctx.env.HAS_w_pointer_arith:
450         ctx.env.CFLAGS = ['-Wpointer-arith'] + ctx.env.CFLAGS
451     if ctx.env.HAS_w_invalid_pch:
452         ctx.env.CFLAGS = ['-Winvalid-pch'] + ctx.env.CFLAGS
453     if ctx.env.HAS_w_implicit_function_declaration:
454         ctx.env.CFLAGS = ['-Wimplicit-function-declaration'] + ctx.env.CFLAGS
455     if ctx.env.HAS_w_disabled_optimization:
456         ctx.env.CFLAGS = ['-Wdisabled-optimization'] + ctx.env.CFLAGS
457     # if ctx.env.HAS_w_cast_align:
458     #     ctx.env.CFLAGS = ['-Wcast-align'] + ctx.env.CFLAGS
459     if ctx.env.HAS_w_missing_declarations:
460         ctx.env.CFLAGS = ['-Wmissing-declarations'] + ctx.env.CFLAGS
461     if ctx.env.HAS_w_cast_qual:
462         ctx.env.CFLAGS = ['-Wcast-qual'] + ctx.env.CFLAGS
463     if ctx.env.HAS_w_packed:
464         ctx.env.CFLAGS = ['-Wpacked'] + ctx.env.CFLAGS
465     if ctx.env.HAS_w_shadow:
466         ctx.env.CFLAGS = ['-Wshadow'] + ctx.env.CFLAGS
467     # if ctx.env.HAS_w_sign_conversion:
468     #     ctx.env.CFLAGS = ['-Wsign-conversion'] + ctx.env.CFLAGS
469     if ctx.env.HAS_f_stack_protector_all:
470         ctx.env.CFLAGS = ['-fstack-protector-all'] + ctx.env.CFLAGS
472     # old gcc takes -z,relro, but then barfs if -fPIE available and used.
473     # ("relro", "-Wl,-z,relro"), # marks some sections read only
474     old_run_build_cls = ctx.run_build_cls
475     ctx.run_build_cls = 'oc'
476     for (name, ldflag) in ld_hardening_flags:
477         ctx.check(define_name='HAS_' + name,
478                   fragment=FRAGMENT,
479                   ldflags=ldflag,
480                   mandatory=False,
481                   msg='Checking if linker supports ' + ldflag,
482                   run_build_cls='oc')
483         if ctx.env['HAS_' + name]:
484             ctx.env.LDFLAGS += [ldflag]
486     ctx.run_build_cls = old_run_build_cls
488     if ctx.env.CC_NAME == "sun":
489         # we are sun, placeholder
490         ctx.env.CFLAGS += []
491     elif ctx.env.CC_NAME == "clang":
492         # used on macOS, FreeBSD,
493         # FORTIFY needs LTO to work well
494         if ctx.env.DEST_OS not in ["darwin", "freebsd"]:
495             # -flto and friends breaks tests on macOS
496             # ctx.env.CFLAGS = [
497             #    "-flto"
498             #    "-fsanitize=cfi",           # hardening
499             #    "-fsanitize=safe-stack",    # hardening
500             #    ] + ctx.env.CFLAGS
501             ctx.env.LDFLAGS += [
502                 "-Wl,-z,relro",  # hardening, marks some section read only,
503                 ]
504     # else:  # gcc, probably
506     # Exclude Unity's support for printing floating point numbers
507     # since it triggers warnings
508     # with -Wfloat-equal
509     ctx.env.CFLAGS = ['-DUNITY_EXCLUDE_FLOAT_PRINT'] + ctx.env.CFLAGS
511     # XXX: hack
512     if ctx.env.DEST_OS in ["freebsd"]:
513         ctx.env.INCLUDES = ["/usr/local/include"]
514         ctx.env.LIBPATH = ["/usr/local/lib"]
515         if os.path.isdir("/usr/local/ssl/"):
516             # This assumes OpenSSL is the only thing that was in /usr/local/
517             ctx.env.INCLUDES = ["/usr/local/ssl/include"]
518             ctx.env.LIBPATH = ["/usr/local/ssl/lib"]
519     elif ctx.env.DEST_OS == "netbsd" and os.path.isdir("/usr/pkg/include"):
520         ctx.env.INCLUDES = ["/usr/pkg/include"]
521         ctx.env.LIBPATH = ["/usr/pkg/lib"]
522         ctx.env.LDFLAGS += ["-rpath=/usr/pkg/lib"]
523         if os.path.isdir("/usr/local/ssl/"):
524             # This assumes OpenSSL is the only thing that was in /usr/pkg/
525             ctx.env.INCLUDES = ["/usr/local/ssl/include"]
526             ctx.env.LIBPATH = ["/usr/local/ssl/lib"]
527     elif ctx.env.DEST_OS == "linux" and os.path.isdir("/usr/local/ssl/"):
528         # This supports building OpenSSL from source
529         # That allows using OpenSSL 1.1.1 on older CentOS
530         # or testing pre-release versions of OpenSSL
531         # see HOWTO-OpenSSL
532         ctx.env.INCLUDES = ["/usr/local/ssl/include"]
533         if os.path.isdir("/usr/local/ssl/lib64/"):
534             ctx.env.LIBPATH = ["/usr/local/ssl/lib64"]
535         else:
536             ctx.env.LIBPATH = ["/usr/local/ssl/lib"]
537     elif ctx.env.DEST_OS == "darwin":
538         # macports location
539         if os.path.isdir("/opt/local/include"):
540             ctx.env.INCLUDES = ["/opt/local/include"]
541         if os.path.isdir("/opt/local/lib"):
542             ctx.env.LIBPATH = ["/opt/local/lib"]
543         # OS X needs this for IPv6
544         ctx.define("__APPLE_USE_RFC_3542", 1,
545                    comment="Needed for IPv6 support")
546     elif ctx.env.DEST_OS == "sunos":
547         # Declare compatibility with the POSIX.1-2001 standard, and any
548         # headers/interfaces not in conflict with that standard
549         ctx.define("_POSIX_C_SOURCE", "200112L", quote=False)
550         ctx.define("__EXTENSIONS__", "1", quote=False)
552     # Borrowed from waf-1.9, when type_name and field_name were valid keywords
553     SNIP_TYPE = '''
554     int main(int argc, char **argv) {
555         (void)argc; (void)argv;
556         if ((%(type_name)s *) 0) return 0;
557         if (sizeof (%(type_name)s)) return 0;
558         return 1;
559     }
560     '''
562     SNIP_FIELD = '''
563     #include <stddef.h>
564     int main(int argc, char **argv) {
565         char *off;
566         (void)argc; (void)argv;
567         off = (char*) &((%(type_name)s*)0)->%(field_name)s;
568         return (size_t) off < sizeof(%(type_name)s);
569     }
570     '''
572     def to_header(header_name):
573         return ''.join(['#include <%s>\n' %
574                        x for x in Utils.to_list(header_name)])
576     structures = (
577         ("struct if_laddrconf", ["sys/types.h", "net/if6.h"], False),
578         ("struct if_laddrreq", ["sys/types.h", "net/if6.h"], False),
579         ("struct timex", ["sys/time.h", "sys/timex.h"], True),
580         ("struct ntptimeval", ["sys/time.h", "sys/timex.h"], False),
581     )
582     for (s, h, r) in structures:
583         ctx.check_cc(
584             fragment=to_header(h) + SNIP_TYPE % {'type_name': s},
585             msg='Checking for type %s' % s,
586             define_name=ctx.have_define(s.upper()),
587             mandatory=r,
588         )
590     structure_fields = (
591         ("struct timex", "time_tick", ["sys/time.h", "sys/timex.h"]),
592         ("struct timex", "modes", ["sys/time.h", "sys/timex.h"]),
593         ("struct ntptimeval", "time.tv_nsec", ["sys/time.h", "sys/timex.h"]),
594         ("struct ntptimeval", "tai", ["sys/time.h", "sys/timex.h"]),
595         # first in glibc 2.12
596     )
597     for (s, f, h) in structure_fields:
598         ctx.check_cc(
599             fragment=(to_header(h) + SNIP_FIELD %
600                       {'type_name': s, 'field_name': f}),
601             msg='Checking for field %s in %s' % (f, s),
602             define_name=ctx.have_define((s + '_' + f).upper()),
603             mandatory=False,
604         )
606     # mostly used by timespecops.h
607     # Some are unused, but handy for discovering what a system is doing
608     sizeofs = [
609         ("time.h",      "struct timespec"),
610         ("sys/time.h",  "struct timeval"),
611         ("time.h",      "time_t"),
612         (None,          "long"),
613     ]
615     for header, sizeof in sorted(sizeofs, key=lambda x: x[1:]):
616         check_sizeof(ctx, header, sizeof)
618     check_timex(ctx)
620     # Parts of attic need libssl
621     if not ctx.options.disable_nts or ctx.options.enable_attic:
622         # Check via pkg-config first, then fall back to a direct search
623         if not ctx.check_cfg(
624             package='libssl', uselib_store='SSL',
625             args=['--cflags', '--libs'],
626             msg="Checking for OpenSSL/libssl (via pkg-config)",
627             define_name='', mandatory=False,
628         ):
629             ctx.check_cc(msg="Checking for OpenSSL's ssl library",
630                          lib="ssl", mandatory=True)
632     # Check via pkg-config first, then fall back to a direct search
633     if not ctx.check_cfg(
634         package='libcrypto', uselib_store='CRYPTO',
635         args=['--cflags', '--libs'],
636         msg="Checking for OpenSSL/libcrypto (via pkg-config)",
637         define_name='', mandatory=False,
638     ):
639         ctx.check_cc(msg="Checking for OpenSSL's crypto library",
640                      lib="crypto", mandatory=True)
642     # Optional functions.  Do all function checks here, otherwise
643     # we're likely to duplicate them.
644     optional_functions = (
645         ('_Unwind_Backtrace', ["unwind.h"]),
646         ('adjtimex', ["sys/time.h", "sys/timex.h"]),
647         ('backtrace_symbols_fd', ["execinfo.h"]),
648         ('ntp_adjtime', ["sys/time.h", "sys/timex.h"]),     # BSD
649         ('ntp_gettime', ["sys/time.h", "sys/timex.h"]),     # BSD
650         ('res_init', ["netinet/in.h", "arpa/nameser.h", "resolv.h"]),
651         ('strlcpy', ["string.h"]),
652         ('strlcat', ["string.h"]),
653         ('timegm', ["time.h"]),
654         # Hack.  It's not a function, but this works.
655         ('PRIV_NTP_ADJTIME', ["sys/priv.h"])            # FreeBSD
656     )
657     for ft in optional_functions:
658         probe_function(ctx, function=ft[0], prerequisites=ft[1])
660     # This area is still work in progress
661     # Need to disable making symbols
662     #   but not until killing off HAVE_TIMER_CREATE
664     # Sanity checks to give a sensible error message
665     required_functions = (
666         # Check for ancient version of OpenSSL.
667         ('EVP_MD_CTX_new', ["openssl/evp.h"], "CRYPTO", False),
668         # MacOS doesn't have timer_create ??
669         ('timer_create', ["signal.h", "time.h"], "RT", False),
670         # Very old versions of OpenSSL don't have cmac.h
671         #  We could add ifdefs, but old crypto is deprecated in favor of CMAC
672         #  and so far, all the systems that we want to support are new enough.
673         ('CMAC_CTX_new', ["openssl/cmac.h"], "CRYPTO", True),
674         # Next should be above, but it needs a library
675         # EVP_PKEY_new_CMAC_key added in OpenSSL 1.1.1
676         ('EVP_PKEY_new_CMAC_key', ["openssl/cmac.h"], "CRYPTO", False))
677     for ft in required_functions:
678         probe_function(ctx, function=ft[0],
679                        prerequisites=ft[1], use=ft[2],
680                        mandatory=ft[3])
682     # check for BSD versions outside of libc
683     if not ctx.get_define("HAVE_STRLCAT"):
684         ret = probe_function(ctx, function='strlcat',
685                              prerequisites=['bsd/string.h'])
686         if ret:
687             ctx.define("HAVE_STRLCAT", 1, comment="Using bsd/strlcat")
689     if not ctx.get_define("HAVE_STRLCPY"):
690         ret = probe_function(ctx, function='strlcpy',
691                              prerequisites=['bsd/string.h'])
692         if ret:
693             ctx.define("HAVE_STRLCPY", 1, comment="Using bsd/strlcpy")
695     # Nobody uses the symbol, but this seems like a good sanity check.
696     ctx.check_cc(header_name="stdbool.h", mandatory=True,
697                  comment="Sanity check.")
699     # This is a list of every optional include header in the
700     # codebase that is guarded by a directly corresponding HAVE_*_H symbol.
701     #
702     # In some cases one HAVE symbol controls inclusion of more
703     # than one header.  In these cases only the one header name
704     # matching the pattern of the HAVE_*_H symbol name is listed
705     # here, so we can invert the relationship to generate tests
706     # for all the symbols.
707     #
708     # Some of these are cruft from ancient big-iron systems and should
709     # be removed.
710     optional_headers = (
711         "alloca.h",
712         ("arpa/nameser.h", ["sys/types.h"]),
713         "bsd/string.h",     # bsd emulation
714         ("ifaddrs.h", ["sys/types.h"]),
715         ("linux/if_addr.h", ["sys/socket.h"]),
716         ("linux/rtnetlink.h", ["sys/socket.h"]),
717         "linux/serial.h",
718         "net/if6.h",
719         ("net/route.h", ["sys/types.h", "sys/socket.h", "net/if.h"]),
720         "openssl/opensslv.h",  # just for wafhelper OpenSSL
721         "priv.h",           # Solaris
722         "stdatomic.h",
723         "sys/clockctl.h",   # NetBSD
724         "sys/ioctl.h",
725         "sys/modem.h",      # Apple
726         "sys/sockio.h",
727         ("sys/sysctl.h", ["sys/types.h"]),
728         ("timepps.h", ["inttypes.h"]),
729         ("sys/timepps.h", ["inttypes.h", "sys/time.h"]),
730         ("sys/timex.h", ["sys/time.h"]),
731     )
732     for hdr in optional_headers:
733         if isinstance(hdr, str):
734             if ctx.check_cc(header_name=hdr, mandatory=False,
735                             comment="<%s> header" % hdr):
736                 continue
737         else:
738             (hdr, prereqs) = hdr
739             if probe_header(ctx, hdr, prereqs):
740                 continue
741         if os.path.exists("/usr/include/" + hdr):
742             # Sanity check...
743             print("Compilation check failed but include exists %s" % hdr)
745     if ((ctx.get_define("HAVE_TIMEPPS_H") or
746             ctx.get_define("HAVE_SYS_TIMEPPS_H"))):
747         ctx.define("HAVE_PPSAPI", 1, comment="Enable the PPS API")
749     # Check for Solaris capabilities
750     if ctx.get_define("HAVE_PRIV_H") and ctx.env.DEST_OS == "sunos":
751         ctx.define("HAVE_SOLARIS_PRIVS", 1,
752                    comment="Enable Solaris Privileges (Solaris only)")
754     from wafhelpers.check_sockaddr import check_sockaddr
755     check_sockaddr(ctx)
757     from wafhelpers.check_strerror import check_strerror
758     check_strerror(ctx)
760     # Check for Solaris's service configuration facility library
761     ctx.check_cc(header_name="libscf.h", lib="scf", mandatory=False,
762                  uselib_store="SCF")
764     # Some systems don't have sys/timex.h eg OS X, OpenBSD...
765     if ctx.get_define("HAVE_SYS_TIMEX_H"):
766         ctx.env.HEADER_SYS_TIMEX_H = True
768     if ctx.options.refclocks:
769         from wafhelpers.refclock import refclock_config
770         refclock_config(ctx)
771         # timegm needed by refclock_nmea, it's not in POSIX
772         # It's in Linux, FreeBSD, and NetBSD
773         if not ctx.get_define("HAVE_TIMEGM") and ctx.get_define("CLOCK_NMEA"):
774             ctx.fatal("Refclock NMEA needs timegm")
775             # We should provide an implementation.
776             # Like we do for BSD string functions.
778     # NetBSD (used to) need to recreate sockets on changed routing.
779     # Perhaps it still does. If so, this should be set.  The autoconf
780     # build set it "if the OS clears cached routes when more specifics
781     # become available".
782     # ctx.define("OS_MISSES_SPECIFIC_ROUTE_UPDATES", 1)
784     if ctx.options.enable_leap_smear:
785         ctx.define("ENABLE_LEAP_SMEAR", 1,
786                    comment="Enable experimental leap smearing code")
788     if ctx.options.enable_mssntp:
789         ctx.define("ENABLE_MSSNTP", 1,
790                    comment="Enable MS-SNTP extensions "
791                    " https://msdn.microsoft.com/en-us/library/cc212930.aspx")
793     if ctx.options.enable_attic:
794         ctx.env.ENABLE_ATTIC = True
796     if ctx.options.disable_nts:
797         ctx.env.DISABLE_NTS = True
798         ctx.define("DISABLE_NTS", 1,
799                    comment="Disable NTS")
801     if not ctx.options.disable_droproot:
802         ctx.define("ENABLE_DROPROOT", 1,
803                    comment="Drop root after initialising")
804     if ctx.options.enable_early_droproot:
805         ctx.define("ENABLE_EARLY_DROPROOT", 1,
806                    comment="Enable early drop root")
807     if ctx.options.disable_fuzz:
808         pprint("YELLOW", "--disable-fuzz is now standard.  Clock fuzzing is gone.")
810     # SO_REUSEADDR socket option is needed to open a socket on an
811     # interface when the port number is already in use on another
812     # interface. Linux needs this, NetBSD does not, status on
813     # other platforms is unknown.  It is probably harmless to
814     # have it on everywhere.
815     ctx.define("NEED_REUSEADDR_FOR_IFADDRBIND", 1,
816                comment="Whether SO_REUSEADDR is needed to open "
817                "same sockets on alternate interfaces, required "
818                "by Linux at least")
820     # Check for directory separator
821     if ctx.env.DEST_OS == "win32":
822         sep = "\\"
823     else:
824         sep = "/"
826     ctx.define("DIR_SEP", "'%s'" % sep, quote=False,
827                comment="Directory separator used")
829     if ctx.get_define("HAVE_SYS_SYSCTL_H"):
830         ctx.define("HAVE_IFLIST_SYSCTL", 1,
831                    comment="Whether sysctl interface exists")
833     # Header/library checks
835     if not ctx.options.disable_droproot and ctx.env.DEST_OS == "linux":
836         ctx.check_cc(header_name="sys/prctl.h", mandatory=False)
837         ctx.check_cc(header_name="sys/capability.h", mandatory=False)
838         ctx.check_cc(lib="cap", comment="Capability library", mandatory=False)
840         if ((ctx.get_define("HAVE_SYS_CAPABILITY_H") and
841                 ctx.get_define("HAVE_SYS_PRCTL_H") and ctx.env.LIB_CAP)):
842             ctx.define("HAVE_LINUX_CAPABILITY", 1)
844     if ctx.options.enable_seccomp:
845         if ctx.env.DEST_OS != "linux":
846             ctx.fatal("seccomp is only supported on Linux")
848         # Check via pkg-config first, then fall back to a direct search
849         if not ctx.check_cfg(
850             package='libseccomp', args=['--libs', '--cflags'],
851             uselib_store='SECCOMP', define_name='HAVE_SECCOMP_H',
852             mandatory=False
853         ):
854             ctx.check_cc(header_name="seccomp.h")
855             ctx.check_cc(lib="seccomp")
857     if not ctx.options.disable_mdns_registration:
858         ctx.check_cc(header_name="dns_sd.h", lib="dns_sd", mandatory=False,
859                      uselib_store="DNS_SD")
861     # Solaris needs -lsocket and -lnsl for socket code
862     if ctx.env.DEST_OS == "sunos":
863         ctx.check(features="c cshlib", lib="socket", mandatory=False)
864         ctx.check(features="c cshlib", lib="nsl", mandatory=False)
866     if ctx.options.enable_classic_mode:
867         ctx.define("ENABLE_CLASSIC_MODE", 1)
868     else:
869         ctx.undefine("ENABLE_CLASSIC_MODE")
871     # Ugly hack to examine config symbols
872     for sym in ctx.env.DEFINES:
873         if sym.startswith("NTP_SIZEOF_TIME_T="):
874             timesize = int(sym.split("=")[1])
875             if timesize < 8:
876                 msg("WARNING: This system has a 32-bit time_t.")
877                 msg("WARNING: Your ntpd will fail on 2038-01-19T03:14:07Z.")
879     if not ctx.env.DISABLE_NTS:
880         from wafhelpers.openssl import check_libssl_tls13
881         from wafhelpers.openssl import check_openssl_bad_version
882         from wafhelpers.openssl import dump_openssl_version
883         check_libssl_tls13(ctx)
884         check_openssl_bad_version(ctx)
885         dump_openssl_version(ctx)
887     # before write_config()
888     if ctx.is_defined("HAVE_LINUX_CAPABILITY"):
889         droproot_type = "Linux"
890     elif ctx.is_defined("HAVE_SOLARIS_PRIVS"):
891         droproot_type = "Solaris"
892     elif ctx.is_defined("HAVE_SYS_CLOCKCTL_H"):
893         droproot_type = "NetBSD"
894     elif ctx.is_defined("HAVE_PRIV_NTP_ADJTIME"):
895         droproot_type = "FreeBSD"
896     else:
897         droproot_type = "None"
899     # write_config() removes symbols
900     ctx.start_msg("Writing configuration header:")
901     ctx.write_config_header("config.h")
902     ctx.end_msg("config.h", "PINK")
904     def yesno(x):
905         if x:
906             return "Yes"
907         return "No"
909     msg("")
910     msg("Build Options")
911     msg_setting("CC", " ".join(ctx.env.CC))
912     msg_setting("CFLAGS", " ".join(ctx.env.CFLAGS))
913     msg_setting("LDFLAGS", " ".join(ctx.env.LDFLAGS))
914     msg_setting("LINKFLAGS_NTPD", " ".join(ctx.env.LINKFLAGS_NTPD))
915     msg_setting("PREFIX", ctx.env.PREFIX)
916     msg_setting("LIBDIR", ctx.env.LIBDIR)
917     msg_setting("Droproot Support", droproot_type)
918     msg_setting("Debug Support", yesno(ctx.options.enable_debug))
919     msg_setting("Refclocks", ", ".join(sorted(ctx.env.REFCLOCK_LIST)))
920     msg_setting("Build Docs", yesno(ctx.env.BUILD_DOC))
921     msg_setting("Build Manpages", yesno(ctx.env.BUILD_MAN))
923     ctx.recurse("pylib")
924     ctx.env.PYSHEBANG = ctx.options.pyshebang
925     msg_setting("PYSHEBANG", ctx.env.PYSHEBANG)
926     if 'none' != ctx.env['ntpc']:
927         # Convert the Python directories to absolute paths.
928         # This makes them behave the same as PREFIX.
929         ctx.env.PYTHONDIR = os.path.abspath(ctx.env.PYTHONDIR)
930         ctx.env.PYTHONARCHDIR = os.path.abspath(ctx.env.PYTHONARCHDIR)
931         msg_setting("PYTHONDIR", ctx.env.PYTHONDIR)
932         msg_setting("PYTHONARCHDIR", ctx.env.PYTHONARCHDIR)
935 class check(BuildContext):
936     cmd = 'check'
937     variant = "main"
940 class bin_test(BuildContext):
941     """Run binary check, use after tests."""
942     cmd = 'bin_test'
943     variant = "main"
946 variant_cmd = (
947     ("build", BuildContext),
948     ("clean", CleanContext),
949     ("install", InstallContext),
950     ("step", StepContext),
951     ("list", ListContext),
952     # ("check", BuildContext)
955 for v in ["host", "main"]:
956     # the reason for creating these subclasses is just for __doc__ below...
957     for cmd, cls in variant_cmd:
958         class tmp(cls):
959             __doc__ = "%s %s" % (cmd, v)
960             cmd = "%s_%s" % (cmd, v)
961             variant = v
964 def init_handler(ctx):
965     cmd = ctx.cmd
966     if cmd == 'init_handler':
967         cmd = 'build'
969     def make_context(name):
970         for x in Context.classes:
971             if x.cmd == name and x.fun != 'init_handler':
972                 return x()
973         ctx.fatal('No class for %r' % cmd)
975     # By default we want to iterate over each variant.
976     for v in ["host", "main"]:
977         obj = make_context(cmd)
978         obj.variant = v
979         pprint("YELLOW", "--- %sing %s ---" % (cmd, v))
980         obj.execute()
983 commands = (
984     ("install", "init_handler", None),
985     ("uninstall", "init_handler", None),
986     ("build", "init_handler", None),
987     ("clean", "init_handler", None),
988     ("list", "init_handler", None),
989     ("step", "init_handler", None),
993 for command, func, descr in commands:
994     class tmp1(Context.Context):
995         if descr:
996             __doc__ = descr
997         cmd = command
998         fun = func
999         if ((command in
1000             'install uninstall build clean list step'
1001              )):
1002             execute = Scripting.autoconfigure(Context.Context.execute)
1003 # end borrowed code
1006 def afterparty(ctx):
1007     # Make magic links to support in-tree testing.
1008     #
1009     # The idea is that all directories where the Python tools live should
1010     # have an 'ntp' symlink so they can import Python modules from the pylib
1011     # directory.
1012     #
1013     # Note that this setup is applied to the build tree, not the
1014     # source tree.  Only the build-tree copies of the programs are
1015     # expected to work.
1016     for x in ("ntpclients", "tests/pylib",):
1017         # List used to be longer...
1018         path_build = ctx.bldnode.make_node("pylib")
1019         path_source = ctx.bldnode.make_node(x + "/ntp")
1020         relpath = (("../" * (x.count("/")+1)) +
1021                    path_build.path_from(ctx.bldnode))
1022         if ctx.cmd in ('install', 'build'):
1023             if ((not path_source.exists() or
1024                     os.readlink(path_source.abspath()) != relpath)):
1025                 try:
1026                     os.remove(path_source.abspath())
1027                 except OSError:
1028                     pass
1029                 if 'none' != ctx.env['ntpc']:
1030                     os.symlink(relpath, path_source.abspath())
1033 python_scripts = set([
1034     "ntpclients/ntpdig.py",
1035     "ntpclients/ntpkeygen.py",
1036     "ntpclients/ntpq.py",
1037     "ntpclients/ntpsweep.py",
1038     "ntpclients/ntptrace.py",
1039     "ntpclients/ntpwait.py",
1040     "ntpclients/ntpsnmpd.py",
1044 def build(ctx):
1045     from waflib.Logs import verbose
1046     ctx.load('waf', tooldir='wafhelpers/')
1047     ctx.load('asciidoc', tooldir='wafhelpers/')
1049     if ctx.variant == "host":
1050         ctx.recurse("ntpd")
1051         return
1053     with open(str(ctx.bldnode) + "/VERSION.bld", "w") as fp:
1054         fp.write("ntpsec-" + ctx.env.NTPSEC_VERSION_EXTENDED)
1056     if ctx.cmd == "build":
1057         # It's a waf gotcha that if there are object files (including
1058         # .pyc and .pyo files) in a source directory, compilation to
1059         # the build directory never happens.  This is how we foil that.
1060         ctx.add_pre_fun(lambda ctx: ctx.exec_command("rm -f pylib/*.py[co]"))
1061         # Start purging ntp.ntpc files from build dir
1062         # so old extension won't clobber FFI or reverse
1063         bldnode = ctx.bldnode.make_node('pylib')
1064         bldnode.mkdir()
1065         target3 = bldnode.ant_glob('*ntpc*')
1066         for _ in target3:
1067             ctx.exec_command("rm -f %s" % _.abspath())
1068         # Finish purging ntp.ntpc
1069         ctx.add_group()
1071     if ctx.env.REFCLOCK_GENERIC or ctx.env.REFCLOCK_TRIMBLE:
1072         # required by the generic and Trimble refclocks
1073         ctx.recurse("libparse")
1074     ctx.recurse("libntp")
1075     if not ctx.env.DISABLE_NTS:
1076         ctx.recurse("libaes_siv")
1077     ctx.recurse("ntpd")
1078     ctx.recurse("ntpfrob")
1079     ctx.recurse("ntptime")
1080     ctx.recurse("pylib")
1081     if ctx.env.ENABLE_ATTIC:
1082         ctx.recurse("attic")
1083     ctx.recurse("etc")
1084     ctx.recurse("tests")
1086     if ctx.env['PYTHON_ARGPARSE']:
1087         python_scripts.add("ntpclients/ntplogtemp.py")
1088         python_scripts.add("ntpclients/ntpviz.py")
1089     if ctx.env['PYTHON_ARGPARSE'] and ctx.env['PYTHON_GPS']:
1090         python_scripts.add("ntpclients/ntploggps.py")
1091     if ctx.env['PYTHON_CURSES']:
1092         python_scripts.add("ntpclients/ntpmon.py")
1094     scripts = ["ntpclients/ntpleapfetch"]
1096     if 'none' != ctx.env['ntpc']:
1097         scripts += list(python_scripts)
1098         # Make sure the python scripts compile, but don't install them
1099         ctx(
1100             features="py",
1101             source=python_scripts,
1102             install_path=None,
1103         )
1105     ctx(
1106         features="subst",
1107         source=scripts,
1108         target=[x.replace('.py', '') for x in scripts],
1109         chmod=Utils.O755,
1110         install_path='${BINDIR}',
1111     )
1113     ctx.add_post_fun(afterparty)
1114     if ctx.cmd == 'clean':
1115         afterparty(ctx)
1117     if ctx.env['PYTHON_ARGPARSE']:
1118         ctx.manpage(1, "ntpclients/ntplogtemp-man.adoc")
1119         ctx.manpage(1, "ntpclients/ntpviz-man.adoc")
1120     if ctx.env['PYTHON_ARGPARSE'] and ctx.env['PYTHON_GPS']:
1121         ctx.manpage(1, "ntpclients/ntploggps-man.adoc")
1122     if ctx.env['PYTHON_CURSES']:
1123         ctx.manpage(1, "ntpclients/ntpmon-man.adoc")
1124     ctx.manpage(1, "ntpclients/ntpdig-man.adoc")
1125     ctx.manpage(1, "ntpclients/ntpq-man.adoc")
1126     ctx.manpage(1, "ntpclients/ntpsweep-man.adoc")
1127     ctx.manpage(1, "ntpclients/ntptrace-man.adoc")
1128     ctx.manpage(8, "ntpclients/ntpkeygen-man.adoc")
1129     ctx.manpage(8, "ntpclients/ntpleapfetch-man.adoc")
1130     ctx.manpage(8, "ntpclients/ntpwait-man.adoc")
1131     ctx.manpage(8, "ntpclients/ntpsnmpd-man.adoc")
1133     # Skip running unit tests on a cross compile build
1134     from waflib import Options
1135     if not ctx.env.ENABLE_CROSS:
1136         from wafhelpers.bin_test import cmd_bin_test, bin_test_summary
1137         # Force re-running of tests.  Same as 'waf --alltests'
1138         if ctx.cmd == "check":
1139             ctx.options.all_tests = True
1141             # Print log if -v is supplied
1142             if verbose > 0:
1143                 ctx.add_post_fun(test_print_log)
1144         elif Options.options.no_tests:
1145             return
1147         # Test binaries
1148         ctx.add_post_fun(cmd_bin_test)
1150         # Write test log to a file
1151         ctx.add_post_fun(test_write_log)
1153         # Print a summary at the end
1154         ctx.add_post_fun(waf_unit_test.summary)
1155         ctx.add_post_fun(waf_unit_test.set_exit_code)
1156         ctx.add_post_fun(bin_test_summary)
1157     else:
1158         pprint("YELLOW", "Unit test runner skipped on a cross-compiled build.")
1159         Options.options.no_tests = True
1161     if ctx.cmd == "build":
1162         if "PYTHONPATH" not in os.environ:
1163             print("--- PYTHONPATH is not set, "
1164                   "loading the Python ntp library may be troublesome ---")
1165         elif ctx.env.PYTHONARCHDIR not in os.environ["PYTHONPATH"]:
1166             print("--- PYTHONARCHDIR not in PYTHONPATH, "
1167                   "loading the Python ntp library may be troublesome ---")
1170 # Miscellaneous utility productions
1174 def ifdex(ctx):
1175     "Get a report on configuration symbols not accounted for."
1176     ctx.exec_command("ifdex -X build/config.h -X devel/ifdex-ignores .")
1179 # See https://gitlab.com/esr/loccount
1180 def loccount(ctx):
1181     "Report the SLOC count of the source tree."
1182     ctx.exec_command("loccount -x=build .")
1185 def cxfreeze(ctx):
1186     "Create standalone binaries from Python scripts."
1187     ctx.exec_command("for prog in " + " ".join(python_scripts) +
1188                      "; do cxfreeze $prog; done")
1191 def linkcheck(ctx):
1192     "Report references without anchors in the documentation."
1193     ctx.exec_command("devel/linkcheck docs/")
1195 # The following sets edit modes for GNU EMACS
1196 # Local Variables:
1197 # mode:python
1198 # End:
1199 # end