1 # Copyright the NTPsec project contributors
3 # SPDX-License-Identifier: BSD-2-Clause
5 from __future__ import print_function
7 from datetime import datetime
14 from waflib import Build
15 from waflib import Context
16 from waflib import Scripting
17 from waflib import Utils
18 from waflib.Build import (BuildContext, CleanContext, InstallContext,
19 StepContext, ListContext)
20 from waflib.Context import BOTH
21 from waflib.Errors import WafError
22 from waflib.Logs import pprint
23 from waflib.Tools import waf_unit_test
25 # Avoid writing .pyc files in wafhelpers/
26 sys.dont_write_bytecode = True
28 from wafhelpers.options import options_cmd
29 from wafhelpers.probes import probe_header, probe_function
30 from wafhelpers.test import test_write_log, test_print_log
46 "Be helpful, give a usage"
49 build Build the project
51 configure Configure the project
53 install Install the project
54 loccount Show SLOC count of the source tree
55 uninstall Uninstall the project
61 options_cmd(ctx, config)
62 ctx.load('asciidoc', tooldir='wafhelpers/')
67 ctx.load('asciidoc', tooldir='wafhelpers/')
69 class oc(Build.BuildContext):
72 def exec_command(self, cmd, **kw):
75 err, out = self.cmd_and_log(cmd, **kw)
77 self.logger.debug('WafError')
79 if (len(out) and any(word in out for word
80 in ['err', 'err:', 'error', 'error:',
81 'ignored', 'illegal', 'unknown',
82 'unrecognized', 'warning'])):
83 self.logger.debug('noooo %r' % out)
86 self.logger.debug('noooo %r' % err)
93 def msg_setting(name, val):
94 pprint("NORMAL", " %-30s: " % name, sep="")
97 srcnode = ctx.srcnode.abspath()
98 bldnode = ctx.bldnode.abspath()
100 ctx.run_build_cls = 'check'
101 ctx.load('waf', tooldir='wafhelpers/')
102 ctx.load('waf_unit_test')
105 with open("VERSION", "r") as f:
106 ntpsec_release = f.read().split(" ")[0].strip()
108 ctx.env.OPT_STORE = config["OPT_STORE"]
111 # Wipe out and override flags with those from the commandline
112 for flag in ctx.env.OPT_STORE:
113 if flag == "--undefine":
114 for sym in ctx.env.OPT_STORE[flag]:
116 elif flag == "--define":
117 for symval in ctx.env.OPT_STORE[flag]:
118 (sym, val) = symval.split("=")
120 ctx.define(sym, int(val))
124 opt = flag.replace("--", "").upper()
125 opt_map[opt] = ctx.env.OPT_STORE[flag]
127 ctx.env['ntpc'] = ctx.options.enable_pylib
128 ctx.env['ntpcver'] = '1.1.0'
130 msg("--- Configuring host ---")
131 ctx.setenv('host', ctx.env.derive())
133 ctx.load('compiler_c')
134 ctx.start_msg('Checking compiler version')
135 ctx.end_msg("%s" % ".".join(ctx.env.CC_VERSION))
137 # Some distros do not have /sbin in the PATH for non-root users. We honor
138 # the real PATH first, but append the sbin directories.
140 "ldconfig", var="BIN_LDCONFIG", mandatory=False,
141 path_list=(os.environ.get('PATH','').split(os.pathsep) +
142 ["/sbin", "/usr/sbin", "/usr/local/sbin"]))
144 # Ensure m4 is present, or bison will fail with SIGPIPE
145 ctx.find_program('m4')
149 ctx.env[opt] = opt_map[opt]
151 # Not needed to build. Used by utility scripts.
152 ctx.find_program("awk", var="BIN_AWK", mandatory=False)
153 ctx.find_program("sh", var="BIN_SH", mandatory=False)
156 package='systemd', variables=['systemdsystemunitdir'],
157 uselib_store='SYSTEMD', mandatory=False,
158 msg="Checking for systemd")
159 if ctx.env.SYSTEMD_systemdsystemunitdir:
160 ctx.start_msg("systemd unit directory:")
161 ctx.end_msg(ctx.env.SYSTEMD_systemdsystemunitdir)
163 ctx.env.BIN_GIT = False
164 if os.path.exists(".git"):
165 ctx.find_program("git", var="BIN_GIT", mandatory=False)
167 build_desc = ctx.options.build_desc.strip()
169 build_desc = ' ' + build_desc
171 # 'tag', '7', and 'deadbeef' are fill ins for
172 # a previous tag (always dropped), commits since that tag,
173 # the short commit hash. I can see 5 'git describe' outputs
174 # buildbots and prepush should get: tag-7-gdeadbeef
175 # working developers should get: tag-7-gdeadbeef-dirty
176 # patched shallow builders should get: gdeadbeef-dirty
177 # other shallow builder should get: gdeadbeef
178 # the thorium poisoned get errors and burst into flame
179 # 1-2 tokens gets appended verbatim
180 # 3-4 gets the first token dropped and the rest added
181 # I have never seen 5+ tokens, we should be safe
182 cmd = ctx.env.BIN_GIT + shlex.split("describe --tags --dirty --always")
183 git_short_hash = ctx.cmd_and_log(cmd).strip().split('-')
184 clip = 1 if len(git_short_hash) > 2 else 0
185 git_short_hash = '-'.join(git_short_hash[clip:])
187 ctx.env.NTPSEC_VERSION = "%s+" % ntpsec_release
188 ctx.env.NTPSEC_VERSION_EXTENDED = ("%s+%s%s" %
193 ctx.env.NTPSEC_VERSION = "%s" % ntpsec_release
194 ctx.env.NTPSEC_VERSION_EXTENDED = ("%s%s" % (ntpsec_release,
196 ctx.define("NTPSEC_VERSION", ctx.env.NTPSEC_VERSION)
197 ctx.define("NTPSEC_VERSION_EXTENDED", ctx.env.NTPSEC_VERSION_EXTENDED)
199 # We require some things that C99 doesn't enable, like pthreads.
200 # These flags get propagated to both the host and main parts of the build.
203 # If ==1, like _POSIX_SOURCE;
204 # if >=2 add IEEE Std 1003.2;
205 # if >=199309L, add IEEE Std 1003.1b-1993;
206 # if >=199506L, add IEEE Std 1003.1c-1995;
207 # if >=200112L, all of IEEE 1003.1-2004
208 # if >=200809L, all of IEEE 1003.1-2008
210 # FIXME: We'd like this to be -D_POSIX_C_SOURCE=200809L -D_XOPEN_SOURCE=700
211 # rather than -D_GNU_SOURCE, but that runs into problems in two places:
212 # (1) The ISC net handling stuff, where struct in6_addr’ loses a member
213 # named s6_addr32 that the macros need, and (2) three BSD functions
214 # related to chroot jailing in the sandbox code.
216 # Note that _POSIX_C_SOURCE >= 199506L and _GNU_SOURCE both turn on
217 # _POSIX_PTHREAD_SEMANTICS and _REENTRANT
219 ctx.env.CFLAGS = ["-std=c99", "-D_GNU_SOURCE"] + ctx.env.CFLAGS
221 msg("--- Configuring main ---")
222 ctx.setenv("main", ctx.env.derive())
224 from wafhelpers.check_sizeof import check_sizeof
227 ctx.env[opt] = opt_map[opt]
229 if ctx.options.cross_compiler:
230 ctx.env.ENABLE_CROSS = True
232 ctx.start_msg("Using Cross compiler CC:")
233 # ctx.get_cc_version(ctx.env.CC, gcc=True)
234 ctx.end_msg(ctx.options.cross_compiler)
236 ctx.env.CC = shlex.split(ctx.options.cross_compiler)
237 ctx.env.LINK_CC = shlex.split(ctx.options.cross_compiler)
239 if ctx.env["CROSS-CFLAGS"]:
240 # Lexically split each part of the CFLAGS, then chain the lists
241 iter = [shlex.split(x) for x in opt_map["CROSS-CFLAGS"]]
242 ctx.env.CFLAGS = list(itertools.chain.from_iterable(iter))
244 if ctx.env["CROSS-LDFLAGS"]:
245 # Lexically split each part of the LDFLAGS, then chain the lists
246 iter = [shlex.split(x) for x in opt_map["CROSS-LDFLAGS"]]
247 ctx.env.LDFLAGS = list(itertools.chain.from_iterable(iter))
250 from wafhelpers.refclock import refclock_map
251 print("ID Description")
252 print("~~ ~~~~~~~~~~~")
253 for id in refclock_map:
254 print("%-5s %s" % (id, refclock_map[id]["descr"]))
258 # These are required by various refclocks
259 # needs to be tested before CFLAGS are set
260 if ctx.check_endianness() == "big":
261 ctx.define("WORDS_BIGENDIAN", 1)
263 if ctx.options.enable_leap_testing:
264 ctx.define("ENABLE_LEAP_TESTING", 1,
265 comment="Enable leap seconds on other than 1st of month.")
267 # check for some libs first. some options, like stack protector,
268 # may depend on some libs, like -lssp
269 ctx.check_cc(lib="m", comment="Math library")
270 ctx.check_cc(lib="rt", mandatory=False, comment="realtime library")
271 ctx.check_cc(lib="pthread", mandatory=False, comment="threads library")
272 ctx.check_cc(lib="execinfo", mandatory=False,
273 comment="BSD backtrace library")
274 ret = ctx.check_cc(lib="bsd", mandatory=False,
275 comment="BSD compatibility library")
277 ctx.env.LDFLAGS += ["-lbsd"]
279 # -lssp and -lssp_nonshared may be needed by older gcc to
280 # support "-fstack-protector-all"
281 ret = ctx.check_cc(lib="ssp", mandatory=False,
284 ctx.env.LDFLAGS += ["-lssp"]
286 ret = ctx.check_cc(lib="ssp_nonshared", mandatory=False,
287 comment="libssp_nonshared")
289 ctx.env.LDFLAGS += ["-lssp_nonshared"]
293 ('PIE', '-pie -fPIE'),
294 # this quiets most of macOS warnings on -fpie
295 ('unused', '-Qunused-arguments'),
296 # This is a useless warning on any architecture with a barrel
297 # shifter, which includes Intel and ARM and basically
298 # everything nowadays. Even so, we'd enable it out of
299 # perfectionism, but the GCC directives that ought to be
300 # useful for forcing structure alignment in order to suppress
301 # it locally don't seem to be working quite right.
302 # ('w_cast_align', "-Wcast-align"),
303 ('w_cast_qual', "-Wcast-qual"),
304 ('w_disabled_optimization', "-Wdisabled-optimization"),
305 ('w_float_equal', "-Wfloat-equal"),
306 ('w_format', '-Wformat'),
307 ('w_format_security', '-Wformat-security'),
309 ('w_format_signedness', '-Wformat-signedness'),
310 ('w_implicit_function_declaration', "-Wimplicit-function-declaration"),
311 ('w_init_self', '-Winit-self'),
312 ('w_invalid_pch', '-Winvalid-pch'),
313 ('w_missing_declarations', '-Wmissing-declarations'),
314 ('w_multichar', '-Wmultichar'),
315 ('w_packed', '-Wpacked'),
316 ('w_pointer_arith', '-Wpointer-arith'),
317 ('w_shadow', '-Wshadow'),
319 ('w_suggest_attribute_noreturn', "-Wsuggest-attribute=noreturn"),
320 ('w_write_strings', '-Wwrite-strings'),
323 # Check which linker flags are supported
324 ld_hardening_flags = [
325 ('f_stack_protector_all', '-fstack-protector-all'),
326 ("z_now", "-Wl,-z,now"), # no deferred symbol resolution
329 # we prepend our options to CFLAGS, this allows user provided
330 # CFLAGS to override our computed CFLAGS
331 if not ctx.options.disable_debug_gdb:
332 ctx.env.CFLAGS = ["-g"] + ctx.env.CFLAGS
333 ctx.define("USEBACKTRACE", "1", quote=False)
337 ('LTO', '-flto'), # link time optimization
339 ld_hardening_flags += [
340 ('stripall', "-Wl,--strip-all"), # Strip binaries
343 if ctx.options.enable_debug:
344 ctx.define("DEBUG", 1, comment="Enable debug mode")
345 ctx.env.BISONFLAGS += ["--debug"]
347 if ctx.options.enable_warnings:
348 # turn on some annoying warnings
350 # "-Wall", # for masochists
351 # "-Waggregate-return", # breaks ldiv(), ntpcal_daysplit(), etc.
352 # "-Wcast-align", # fails on RasPi, needs fixing.
353 # "-Wbad-function-cast", # ntpd casts long<->double a lot
354 # "-Wformat-nonliteral", # complains about a used feature
355 "-Winline", # some OS have inline issues.
356 # "-Wmissing-format-attribute", # false positives
357 # "-Wnested-externs", # incompatible w/ Unity...
358 # "-Wpadded", # duck... over 3k warnings
359 # "-Wredundant-decls", # incompatible w/ Unity
360 "-Wswitch-default", # warns on missing switch-default
361 # old Bison triggers this
362 "-Wswitch-enum", # warns on missing enum case handler
365 ('w_implicit_fallthru', "-Wimplicit-fallthrough=3"),
366 # Fails on Solaris, OpenBSD 6, and RasPi
367 # Complains about a Bison bug
368 # Cannot be suppressed
369 # ('w_sign_conversion', "-Wsign-conversion"),
370 # fails on clang, lots of false positives and Unity complaints
371 # ('w_suggest_attribute_const', "-Wsuggest-attribute=const"),
372 # fails on clang, lot's of false positives and Unity complaints
373 # ('w_suggest_attribute_pure', "-Wsuggest-attribute=pure"),
377 # -O1 will turn on -D_FORTIFY_SOURCE=2 for us
381 "-Wmissing-prototypes",
382 "-Wstrict-prototypes",
387 # gotta be tricky to test for -Wsuggest-attribute=const
390 int main(int argc, char **argv) {
391 (void)argc; (void)argv;
397 # check if C compiler supports some flags
398 old_run_build_cls = ctx.run_build_cls
399 ctx.run_build_cls = 'oc'
400 for (name, ccflag) in cc_test_flags:
401 ctx.check(cflags=ccflag,
402 define_name='HAS_' + name,
405 msg='Checking if C compiler supports ' + ccflag,
408 ctx.run_build_cls = old_run_build_cls
411 ctx.env.CFLAGS = ["-fPIC"] + ctx.env.CFLAGS
414 ctx.env.LINKFLAGS_NTPD += [
417 ctx.env.CFLAGS_bin = ["-fPIE", "-pie"] + ctx.env.CFLAGS
418 ld_hardening_flags += [
419 ('relro', "-Wl,-z,relro"), # hardening, marks some read only,
422 if ctx.env.HAS_unused:
423 ctx.env.CFLAGS = ['-Qunused-arguments'] + ctx.env.CFLAGS
425 # XXX: -flto currently breaks link of ntpd
426 if ctx.env.HAS_LTO and False:
427 ctx.env.CFLAGS = ["-flto"] + ctx.env.CFLAGS
429 # debug warnings that are not available with all compilers
430 if ctx.env.HAS_w_implicit_fallthru:
431 ctx.env.CFLAGS = ['-Wimplicit-fallthrough=3'] + ctx.env.CFLAGS
432 if ctx.env.HAS_w_suggest_attribute_const:
433 ctx.env.CFLAGS = ['-Wsuggest-attribute=const'] + ctx.env.CFLAGS
434 if ctx.env.HAS_w_suggest_attribute_noreturn:
435 ctx.env.CFLAGS = ['-Wsuggest-attribute=noreturn'] + ctx.env.CFLAGS
436 if ctx.env.HAS_w_suggest_attribute_pure:
437 ctx.env.CFLAGS = ['-Wsuggest-attribute=pure'] + ctx.env.CFLAGS
438 if ctx.env.HAS_w_format_security:
439 ctx.env.CFLAGS = ['-Wformat-security'] + ctx.env.CFLAGS
440 if ctx.env.HAS_w_format_signedness:
441 ctx.env.CFLAGS = ['-Wformat-signedness'] + ctx.env.CFLAGS
442 # should be before other -Wformat-* in CFLAGS
443 if ctx.env.HAS_w_format:
444 ctx.env.CFLAGS = ['-Wformat'] + ctx.env.CFLAGS
445 if ctx.env.HAS_w_float_equal:
446 ctx.env.CFLAGS = ['-Wfloat-equal'] + ctx.env.CFLAGS
447 if ctx.env.HAS_w_init_self:
448 ctx.env.CFLAGS = ['-Winit-self'] + ctx.env.CFLAGS
449 if ctx.env.HAS_w_write_strings:
450 ctx.env.CFLAGS = ['-Wwrite-strings'] + ctx.env.CFLAGS
451 if ctx.env.HAS_w_pointer_arith:
452 ctx.env.CFLAGS = ['-Wpointer-arith'] + ctx.env.CFLAGS
453 if ctx.env.HAS_w_invalid_pch:
454 ctx.env.CFLAGS = ['-Winvalid-pch'] + ctx.env.CFLAGS
455 if ctx.env.HAS_w_implicit_function_declaration:
456 ctx.env.CFLAGS = ['-Wimplicit-function-declaration'] + ctx.env.CFLAGS
457 if ctx.env.HAS_w_disabled_optimization:
458 ctx.env.CFLAGS = ['-Wdisabled-optimization'] + ctx.env.CFLAGS
459 # if ctx.env.HAS_w_cast_align:
460 # ctx.env.CFLAGS = ['-Wcast-align'] + ctx.env.CFLAGS
461 if ctx.env.HAS_w_missing_declarations:
462 ctx.env.CFLAGS = ['-Wmissing-declarations'] + ctx.env.CFLAGS
463 if ctx.env.HAS_w_cast_qual:
464 ctx.env.CFLAGS = ['-Wcast-qual'] + ctx.env.CFLAGS
465 if ctx.env.HAS_w_packed:
466 ctx.env.CFLAGS = ['-Wpacked'] + ctx.env.CFLAGS
467 if ctx.env.HAS_w_shadow:
468 ctx.env.CFLAGS = ['-Wshadow'] + ctx.env.CFLAGS
469 # if ctx.env.HAS_w_sign_conversion:
470 # ctx.env.CFLAGS = ['-Wsign-conversion'] + ctx.env.CFLAGS
471 if ctx.env.HAS_f_stack_protector_all:
472 ctx.env.CFLAGS = ['-fstack-protector-all'] + ctx.env.CFLAGS
474 # old gcc takes -z,relro, but then barfs if -fPIE available and used.
475 # ("relro", "-Wl,-z,relro"), # marks some sections read only
476 old_run_build_cls = ctx.run_build_cls
477 ctx.run_build_cls = 'oc'
478 for (name, ldflag) in ld_hardening_flags:
479 ctx.check(define_name='HAS_' + name,
483 msg='Checking if linker supports ' + ldflag,
485 if ctx.env['HAS_' + name]:
486 ctx.env.LDFLAGS += [ldflag]
488 ctx.run_build_cls = old_run_build_cls
490 if ctx.env.CC_NAME == "sun":
491 # we are sun, placeholder
493 elif ctx.env.CC_NAME == "clang":
494 # used on macOS, FreeBSD,
495 # FORTIFY needs LTO to work well
496 if ctx.env.DEST_OS not in ["darwin", "freebsd"]:
497 # -flto and friends breaks tests on macOS
500 # "-fsanitize=cfi", # hardening
501 # "-fsanitize=safe-stack", # hardening
504 "-Wl,-z,relro", # hardening, marks some section read only,
506 # else: # gcc, probably
508 # Exclude Unity's support for printing floating point numbers
509 # since it triggers warnings
511 ctx.env.CFLAGS = ['-DUNITY_EXCLUDE_FLOAT_PRINT'] + ctx.env.CFLAGS
514 if ctx.env.DEST_OS in ["freebsd"]:
515 ctx.env.INCLUDES = ["/usr/local/include"]
516 ctx.env.LIBPATH = ["/usr/local/lib"]
517 if os.path.isdir("/usr/local/ssl/"):
518 # This assumes OpenSSL is the only thing that was in /usr/local/
519 ctx.env.INCLUDES = ["/usr/local/ssl/include"]
520 ctx.env.LIBPATH = ["/usr/local/ssl/lib"]
521 elif ctx.env.DEST_OS == "netbsd" and os.path.isdir("/usr/pkg/include"):
522 ctx.env.INCLUDES = ["/usr/pkg/include"]
523 ctx.env.LIBPATH = ["/usr/pkg/lib"]
524 ctx.env.LDFLAGS += ["-rpath=/usr/pkg/lib"]
525 if os.path.isdir("/usr/local/ssl/"):
526 # This assumes OpenSSL is the only thing that was in /usr/pkg/
527 ctx.env.INCLUDES = ["/usr/local/ssl/include"]
528 ctx.env.LIBPATH = ["/usr/local/ssl/lib"]
529 elif ctx.env.DEST_OS == "linux" and os.path.isdir("/usr/local/ssl/"):
530 # This supports building OpenSSL from source
531 # That allows using OpenSSL 1.1.1 on older CentOS
532 # or testing pre-release versions of OpenSSL
534 ctx.env.INCLUDES = ["/usr/local/ssl/include"]
535 if os.path.isdir("/usr/local/ssl/lib64/"):
536 ctx.env.LIBPATH = ["/usr/local/ssl/lib64"]
538 ctx.env.LIBPATH = ["/usr/local/ssl/lib"]
539 elif ctx.env.DEST_OS == "darwin":
541 if os.path.isdir("/opt/local/include"):
542 ctx.env.INCLUDES = ["/opt/local/include"]
543 if os.path.isdir("/opt/local/lib"):
544 ctx.env.LIBPATH = ["/opt/local/lib"]
545 # OS X needs this for IPv6
546 ctx.define("__APPLE_USE_RFC_3542", 1,
547 comment="Needed for IPv6 support")
548 elif ctx.env.DEST_OS == "sunos":
549 # Declare compatibility with the POSIX.1-2001 standard, and any
550 # headers/interfaces not in conflict with that standard
551 ctx.define("_POSIX_C_SOURCE", "200112L", quote=False)
552 ctx.define("__EXTENSIONS__", "1", quote=False)
554 # Borrowed from waf-1.9, when type_name and field_name were valid keywords
556 int main(int argc, char **argv) {
557 (void)argc; (void)argv;
558 if ((%(type_name)s *) 0) return 0;
559 if (sizeof (%(type_name)s)) return 0;
566 int main(int argc, char **argv) {
568 (void)argc; (void)argv;
569 off = (char*) &((%(type_name)s*)0)->%(field_name)s;
570 return (size_t) off < sizeof(%(type_name)s);
574 def to_header(header_name):
575 return ''.join(['#include <%s>\n' %
576 x for x in Utils.to_list(header_name)])
579 ("struct if_laddrconf", ["sys/types.h", "net/if6.h"], False),
580 ("struct if_laddrreq", ["sys/types.h", "net/if6.h"], False),
581 ("struct timex", ["sys/time.h", "sys/timex.h"], True),
582 ("struct ntptimeval", ["sys/time.h", "sys/timex.h"], False),
584 for (s, h, r) in structures:
586 fragment=to_header(h) + SNIP_TYPE % {'type_name': s},
587 msg='Checking for type %s' % s,
588 define_name=ctx.have_define(s.upper()),
593 ("struct timex", "time_tick", ["sys/time.h", "sys/timex.h"]),
594 ("struct timex", "modes", ["sys/time.h", "sys/timex.h"]),
595 ("struct ntptimeval", "time.tv_nsec", ["sys/time.h", "sys/timex.h"]),
596 ("struct ntptimeval", "tai", ["sys/time.h", "sys/timex.h"]),
597 # first in glibc 2.12
599 for (s, f, h) in structure_fields:
601 fragment=(to_header(h) + SNIP_FIELD %
602 {'type_name': s, 'field_name': f}),
603 msg='Checking for field %s in %s' % (f, s),
604 define_name=ctx.have_define((s + '_' + f).upper()),
608 # mostly used by timespecops.h
610 ("time.h", "time_t"),
614 for header, sizeof in sorted(sizeofs, key=lambda x: x[1:]):
615 check_sizeof(ctx, header, sizeof)
617 # Parts of attic need libssl
618 if not ctx.options.disable_nts or ctx.options.enable_attic:
619 # Check via pkg-config first, then fall back to a direct search
620 if not ctx.check_cfg(
621 package='libssl', uselib_store='SSL',
622 args=['--cflags', '--libs'],
623 msg="Checking for OpenSSL/libssl (via pkg-config)",
624 define_name='', mandatory=False,
626 ctx.check_cc(msg="Checking for OpenSSL's ssl library",
627 lib="ssl", mandatory=True)
629 # Check via pkg-config first, then fall back to a direct search
630 if not ctx.check_cfg(
631 package='libcrypto', uselib_store='CRYPTO',
632 args=['--cflags', '--libs'],
633 msg="Checking for OpenSSL/libcrypto (via pkg-config)",
634 define_name='', mandatory=False,
636 ctx.check_cc(msg="Checking for OpenSSL's crypto library",
637 lib="crypto", mandatory=True)
639 # Optional functions. Do all function checks here, otherwise
640 # we're likely to duplicate them.
641 optional_functions = (
642 ('_Unwind_Backtrace', ["unwind.h"]),
643 ('adjtimex', ["sys/time.h", "sys/timex.h"]),
644 ('backtrace_symbols_fd', ["execinfo.h"]),
645 ('ntp_adjtime', ["sys/time.h", "sys/timex.h"]), # BSD
646 ('ntp_gettime', ["sys/time.h", "sys/timex.h"]), # BSD
647 ('res_init', ["netinet/in.h", "arpa/nameser.h", "resolv.h"]),
648 ('strlcpy', ["string.h"]),
649 ('strlcat', ["string.h"]),
650 ('timegm', ["time.h"]),
651 # Hack. It's not a function, but this works.
652 ('PRIV_NTP_ADJTIME', ["sys/priv.h"]) # FreeBSD
654 for ft in optional_functions:
655 probe_function(ctx, function=ft[0], prerequisites=ft[1])
657 # This area is still work in progress
658 # Need to disable making symbols
659 # but not until killing off HAVE_TIMER_CREATE
661 # Sanity checks to give a sensible error message
662 required_functions = (
663 # Check for ancient version of OpenSSL.
664 ('EVP_MD_CTX_new', ["openssl/evp.h"], "CRYPTO", False),
665 # MacOS doesn't have timer_create ??
666 ('timer_create', ["signal.h", "time.h"], "RT", False),
667 # Very old versions of OpenSSL don't have cmac.h
668 # We could add ifdefs, but old crypto is deprecated in favor of CMAC
669 # and so far, all the systems that we want to support are new enough.
670 ('CMAC_CTX_new', ["openssl/cmac.h"], "CRYPTO", True),
671 # Next should be above, but it needs a library
672 # EVP_PKEY_new_CMAC_key added in OpenSSL 1.1.1
673 ('EVP_PKEY_new_CMAC_key', ["openssl/cmac.h"], "CRYPTO", False))
674 for ft in required_functions:
675 probe_function(ctx, function=ft[0],
676 prerequisites=ft[1], use=ft[2],
679 # check for BSD versions outside of libc
680 if not ctx.get_define("HAVE_STRLCAT"):
681 ret = probe_function(ctx, function='strlcat',
682 prerequisites=['bsd/string.h'])
684 ctx.define("HAVE_STRLCAT", 1, comment="Using bsd/strlcat")
686 if not ctx.get_define("HAVE_STRLCPY"):
687 ret = probe_function(ctx, function='strlcpy',
688 prerequisites=['bsd/string.h'])
690 ctx.define("HAVE_STRLCPY", 1, comment="Using bsd/strlcpy")
692 # Nobody uses the symbol, but this seems like a good sanity check.
693 ctx.check_cc(header_name="stdbool.h", mandatory=True,
694 comment="Sanity check.")
696 # This is a list of every optional include header in the
697 # codebase that is guarded by a directly corresponding HAVE_*_H symbol.
699 # In some cases one HAVE symbol controls inclusion of more
700 # than one header. In these cases only the one header name
701 # matching the pattern of the HAVE_*_H symbol name is listed
702 # here, so we can invert the relationship to generate tests
703 # for all the symbols.
705 # Some of these are cruft from ancient big-iron systems and should
709 ("arpa/nameser.h", ["sys/types.h"]),
710 "bsd/string.h", # bsd emulation
711 ("ifaddrs.h", ["sys/types.h"]),
712 ("linux/if_addr.h", ["sys/socket.h"]),
713 ("linux/rtnetlink.h", ["sys/socket.h"]),
716 ("net/route.h", ["sys/types.h", "sys/socket.h", "net/if.h"]),
717 "openssl/opensslv.h", # just for wafhelper OpenSSL
720 "sys/clockctl.h", # NetBSD
722 "sys/modem.h", # Apple
724 ("sys/sysctl.h", ["sys/types.h"]),
725 ("timepps.h", ["inttypes.h"]),
726 ("sys/timepps.h", ["inttypes.h", "sys/time.h"]),
727 ("sys/timex.h", ["sys/time.h"]),
729 for hdr in optional_headers:
730 if isinstance(hdr, str):
731 if ctx.check_cc(header_name=hdr, mandatory=False,
732 comment="<%s> header" % hdr):
736 if probe_header(ctx, hdr, prereqs):
738 if os.path.exists("/usr/include/" + hdr):
740 print("Compilation check failed but include exists %s" % hdr)
742 if ((ctx.get_define("HAVE_TIMEPPS_H") or
743 ctx.get_define("HAVE_SYS_TIMEPPS_H"))):
744 ctx.define("HAVE_PPSAPI", 1, comment="Enable the PPS API")
746 # Check for Solaris capabilities
747 if ctx.get_define("HAVE_PRIV_H") and ctx.env.DEST_OS == "sunos":
748 ctx.define("HAVE_SOLARIS_PRIVS", 1,
749 comment="Enable Solaris Privileges (Solaris only)")
751 from wafhelpers.check_sockaddr import check_sockaddr
754 from wafhelpers.check_strerror import check_strerror
757 # Check for Solaris's service configuration facility library
758 ctx.check_cc(header_name="libscf.h", lib="scf", mandatory=False,
761 # Some systems don't have sys/timex.h eg OS X, OpenBSD...
762 if ctx.get_define("HAVE_SYS_TIMEX_H"):
763 ctx.env.HEADER_SYS_TIMEX_H = True
765 if ctx.options.refclocks:
766 from wafhelpers.refclock import refclock_config
768 # timegm needed by refclock_nmea, it's not in POSIX
769 # It's in Linux, FreeBSD, and NetBSD
770 if not ctx.get_define("HAVE_TIMEGM") and ctx.get_define("CLOCK_NMEA"):
771 ctx.fatal("Refclock NMEA needs timegm")
772 # We should provide an implementation.
773 # Like we do for BSD string functions.
775 # NetBSD (used to) need to recreate sockets on changed routing.
776 # Perhaps it still does. If so, this should be set. The autoconf
777 # build set it "if the OS clears cached routes when more specifics
779 # ctx.define("OS_MISSES_SPECIFIC_ROUTE_UPDATES", 1)
781 if ctx.options.enable_leap_smear:
782 ctx.define("ENABLE_LEAP_SMEAR", 1,
783 comment="Enable experimental leap smearing code")
785 if ctx.options.enable_mssntp:
786 ctx.define("ENABLE_MSSNTP", 1,
787 comment="Enable MS-SNTP extensions "
788 " https://msdn.microsoft.com/en-us/library/cc212930.aspx")
790 if ctx.options.enable_attic:
791 ctx.env.ENABLE_ATTIC = True
793 if ctx.options.disable_nts:
794 ctx.env.DISABLE_NTS = True
795 ctx.define("DISABLE_NTS", 1,
796 comment="Disable NTS")
798 if not ctx.options.disable_droproot:
799 ctx.define("ENABLE_DROPROOT", 1,
800 comment="Drop root after initialising")
801 if ctx.options.enable_early_droproot:
802 ctx.define("ENABLE_EARLY_DROPROOT", 1,
803 comment="Enable early drop root")
804 if ctx.options.disable_fuzz:
805 pprint("YELLOW", "--disable-fuzz is now standard. Clock fuzzing is gone.")
807 # SO_REUSEADDR socket option is needed to open a socket on an
808 # interface when the port number is already in use on another
809 # interface. Linux needs this, NetBSD does not, status on
810 # other platforms is unknown. It is probably harmless to
811 # have it on everywhere.
812 ctx.define("NEED_REUSEADDR_FOR_IFADDRBIND", 1,
813 comment="Whether SO_REUSEADDR is needed to open "
814 "same sockets on alternate interfaces, required "
817 # Check for directory separator
818 if ctx.env.DEST_OS == "win32":
823 ctx.define("DIR_SEP", "'%s'" % sep, quote=False,
824 comment="Directory separator used")
826 if ctx.get_define("HAVE_SYS_SYSCTL_H"):
827 ctx.define("HAVE_IFLIST_SYSCTL", 1,
828 comment="Whether sysctl interface exists")
830 # Header/library checks
832 if not ctx.options.disable_droproot and ctx.env.DEST_OS == "linux":
833 ctx.check_cc(header_name="sys/prctl.h", mandatory=False)
834 ctx.check_cc(header_name="sys/capability.h", mandatory=False)
835 ctx.check_cc(lib="cap", comment="Capability library", mandatory=False)
837 if ((ctx.get_define("HAVE_SYS_CAPABILITY_H") and
838 ctx.get_define("HAVE_SYS_PRCTL_H") and ctx.env.LIB_CAP)):
839 ctx.define("HAVE_LINUX_CAPABILITY", 1)
841 if ctx.options.enable_seccomp:
842 if ctx.env.DEST_OS != "linux":
843 ctx.fatal("seccomp is only supported on Linux")
845 # Check via pkg-config first, then fall back to a direct search
846 if not ctx.check_cfg(
847 package='libseccomp', args=['--libs', '--cflags'],
848 uselib_store='SECCOMP', define_name='HAVE_SECCOMP_H',
851 ctx.check_cc(header_name="seccomp.h")
852 ctx.check_cc(lib="seccomp")
854 if not ctx.options.disable_mdns_registration:
855 ctx.check_cc(header_name="dns_sd.h", lib="dns_sd", mandatory=False,
856 uselib_store="DNS_SD")
858 # Solaris needs -lsocket and -lnsl for socket code
859 if ctx.env.DEST_OS == "sunos":
860 ctx.check(features="c cshlib", lib="socket", mandatory=False)
861 ctx.check(features="c cshlib", lib="nsl", mandatory=False)
863 if ctx.options.enable_classic_mode:
864 ctx.define("ENABLE_CLASSIC_MODE", 1)
866 ctx.undefine("ENABLE_CLASSIC_MODE")
868 # Ugly hack to examine config symbols
869 for sym in ctx.env.DEFINES:
870 if sym.startswith("NTP_SIZEOF_TIME_T="):
871 timesize = int(sym.split("=")[1])
873 msg("WARNING: This system has a 32-bit time_t.")
874 msg("WARNING: Your ntpd will fail on 2038-01-19T03:14:07Z.")
876 if not ctx.env.DISABLE_NTS:
877 from wafhelpers.openssl import check_libssl_tls13
878 from wafhelpers.openssl import check_openssl_bad_version
879 from wafhelpers.openssl import dump_openssl_version
880 check_libssl_tls13(ctx)
881 check_openssl_bad_version(ctx)
882 dump_openssl_version(ctx)
884 # before write_config()
885 if ctx.is_defined("HAVE_LINUX_CAPABILITY"):
886 droproot_type = "Linux"
887 elif ctx.is_defined("HAVE_SOLARIS_PRIVS"):
888 droproot_type = "Solaris"
889 elif ctx.is_defined("HAVE_SYS_CLOCKCTL_H"):
890 droproot_type = "NetBSD"
891 elif ctx.is_defined("HAVE_PRIV_NTP_ADJTIME"):
892 droproot_type = "FreeBSD"
894 droproot_type = "None"
896 # write_config() removes symbols
897 ctx.start_msg("Writing configuration header:")
898 ctx.write_config_header("config.h")
899 ctx.end_msg("config.h", "PINK")
908 msg_setting("CC", " ".join(ctx.env.CC))
909 msg_setting("CFLAGS", " ".join(ctx.env.CFLAGS))
910 msg_setting("LDFLAGS", " ".join(ctx.env.LDFLAGS))
911 msg_setting("LINKFLAGS_NTPD", " ".join(ctx.env.LINKFLAGS_NTPD))
912 msg_setting("PREFIX", ctx.env.PREFIX)
913 msg_setting("LIBDIR", ctx.env.LIBDIR)
914 msg_setting("Droproot Support", droproot_type)
915 msg_setting("Debug Support", yesno(ctx.options.enable_debug))
916 msg_setting("Refclocks", ", ".join(sorted(ctx.env.REFCLOCK_LIST)))
917 msg_setting("Build Docs", yesno(ctx.env.BUILD_DOC))
918 msg_setting("Build Manpages", yesno(ctx.env.BUILD_MAN))
921 ctx.env.PYSHEBANG = ctx.options.pyshebang
922 msg_setting("PYSHEBANG", ctx.env.PYSHEBANG)
923 # Convert the Python directories to absolute paths.
924 # This makes them behave the same as PREFIX.
925 ctx.env.PYTHONDIR = os.path.abspath(ctx.env.PYTHONDIR)
926 ctx.env.PYTHONARCHDIR = os.path.abspath(ctx.env.PYTHONARCHDIR)
927 msg_setting("PYTHONDIR", ctx.env.PYTHONDIR)
928 msg_setting("PYTHONARCHDIR", ctx.env.PYTHONARCHDIR)
931 class check(BuildContext):
937 """Run binary check, use after tests."""
938 from wafhelpers.bin_test import cmd_bin_test
942 def bin_test_summary(ctx):
943 """Display results of binary check, use after tests."""
944 from wafhelpers.bin_test import bin_test_summary
945 bin_test_summary(ctx)
949 ("build", BuildContext),
950 ("clean", CleanContext),
951 ("install", InstallContext),
952 ("step", StepContext),
953 ("list", ListContext),
954 # ("check", BuildContext)
957 for v in ["host", "main"]:
958 # the reason for creating these subclasses is just for __doc__ below...
959 for cmd, cls in variant_cmd:
961 __doc__ = "%s %s" % (cmd, v)
962 cmd = "%s_%s" % (cmd, v)
966 def init_handler(ctx):
968 if cmd == 'init_handler':
971 def make_context(name):
972 for x in Context.classes:
973 if x.cmd == name and x.fun != 'init_handler':
975 ctx.fatal('No class for %r' % cmd)
977 # By default we want to iterate over each variant.
978 for v in ["host", "main"]:
979 obj = make_context(cmd)
981 pprint("YELLOW", "--- %sing %s ---" % (cmd, v))
986 ("install", "init_handler", None),
987 ("uninstall", "init_handler", None),
988 ("build", "init_handler", None),
989 ("clean", "init_handler", None),
990 ("list", "init_handler", None),
991 ("step", "init_handler", None),
995 for command, func, descr in commands:
996 class tmp1(Context.Context):
1002 'install uninstall build clean list step'
1004 execute = Scripting.autoconfigure(Context.Context.execute)
1008 def afterparty(ctx):
1009 # Make magic links to support in-tree testing.
1011 # The idea is that all directories where the Python tools live should
1012 # have an 'ntp' symlink so they can import Python modules from the pylib
1015 # Note that this setup is applied to the build tree, not the
1016 # source tree. Only the build-tree copies of the programs are
1018 for x in ("ntpclients", "tests/pylib",):
1019 # List used to be longer...
1020 path_build = ctx.bldnode.make_node("pylib")
1021 path_source = ctx.bldnode.make_node(x + "/ntp")
1022 relpath = (("../" * (x.count("/")+1)) +
1023 path_build.path_from(ctx.bldnode))
1024 if ctx.cmd in ('install', 'build'):
1025 if ((not path_source.exists() or
1026 os.readlink(path_source.abspath()) != relpath)):
1028 os.remove(path_source.abspath())
1031 os.symlink(relpath, path_source.abspath())
1034 python_scripts = set([
1035 "ntpclients/ntpdig.py",
1036 "ntpclients/ntpkeygen.py",
1037 "ntpclients/ntpq.py",
1038 "ntpclients/ntpsweep.py",
1039 "ntpclients/ntptrace.py",
1040 "ntpclients/ntpwait.py",
1041 "ntpclients/ntpsnmpd.py",
1046 from waflib.Logs import verbose
1047 ctx.load('waf', tooldir='wafhelpers/')
1048 ctx.load('asciidoc', tooldir='wafhelpers/')
1050 if ctx.variant == "host":
1054 if ctx.cmd == "build":
1055 # It's a waf gotcha that if there are object files (including
1056 # .pyc and .pyo files) in a source directory, compilation to
1057 # the build directory never happens. This is how we foil that.
1058 ctx.add_pre_fun(lambda ctx: ctx.exec_command("rm -f pylib/*.py[co]"))
1059 # Start purging ntp.ntpc files from build dir
1060 # so old extension won't clobber FFI or reverse
1061 bldnode = ctx.bldnode.make_node('pylib')
1063 target3 = bldnode.ant_glob('*ntpc*')
1065 ctx.exec_command("rm -f %s" % _.abspath())
1066 # Finish purging ntp.ntpc
1069 if ctx.env.REFCLOCK_GENERIC or ctx.env.REFCLOCK_TRIMBLE:
1070 # required by the generic and Trimble refclocks
1071 ctx.recurse("libparse")
1072 ctx.recurse("libntp")
1073 if not ctx.env.DISABLE_NTS:
1074 ctx.recurse("libaes_siv")
1076 ctx.recurse("ntpfrob")
1077 ctx.recurse("ntptime")
1078 ctx.recurse("pylib")
1079 if ctx.env.ENABLE_ATTIC:
1080 ctx.recurse("attic")
1082 ctx.recurse("tests")
1084 if ctx.env['PYTHON_ARGPARSE']:
1085 python_scripts.add("ntpclients/ntplogtemp.py")
1086 python_scripts.add("ntpclients/ntpviz.py")
1087 if ctx.env['PYTHON_ARGPARSE'] and ctx.env['PYTHON_GPS']:
1088 python_scripts.add("ntpclients/ntploggps.py")
1089 if ctx.env['PYTHON_CURSES']:
1090 python_scripts.add("ntpclients/ntpmon.py")
1092 # Make sure the python scripts compile, but don't install them
1095 source=python_scripts,
1099 scripts = ["ntpclients/ntpleapfetch"] + list(python_scripts)
1104 target=[x.replace('.py', '') for x in scripts],
1106 install_path='${BINDIR}',
1109 ctx.add_post_fun(afterparty)
1110 if ctx.cmd == 'clean':
1113 if ctx.env['PYTHON_ARGPARSE']:
1114 ctx.manpage(1, "ntpclients/ntplogtemp-man.adoc")
1115 ctx.manpage(1, "ntpclients/ntpviz-man.adoc")
1116 if ctx.env['PYTHON_ARGPARSE'] and ctx.env['PYTHON_GPS']:
1117 ctx.manpage(1, "ntpclients/ntploggps-man.adoc")
1118 if ctx.env['PYTHON_CURSES']:
1119 ctx.manpage(1, "ntpclients/ntpmon-man.adoc")
1120 ctx.manpage(1, "ntpclients/ntpdig-man.adoc")
1121 ctx.manpage(1, "ntpclients/ntpq-man.adoc")
1122 ctx.manpage(1, "ntpclients/ntpsweep-man.adoc")
1123 ctx.manpage(1, "ntpclients/ntptrace-man.adoc")
1124 ctx.manpage(8, "ntpclients/ntpkeygen-man.adoc")
1125 ctx.manpage(8, "ntpclients/ntpleapfetch-man.adoc")
1126 ctx.manpage(8, "ntpclients/ntpwait-man.adoc")
1127 ctx.manpage(8, "ntpclients/ntpsnmpd-man.adoc")
1129 # Skip running unit tests on a cross compile build
1130 from waflib import Options
1131 if not ctx.env.ENABLE_CROSS:
1132 # Force re-running of tests. Same as 'waf --alltests'
1133 if ctx.cmd == "check":
1134 ctx.options.all_tests = True
1136 # Print log if -v is supplied
1138 ctx.add_post_fun(test_print_log)
1139 elif Options.options.no_tests:
1143 ctx.add_post_fun(bin_test)
1145 # Write test log to a file
1146 ctx.add_post_fun(test_write_log)
1148 # Print a summary at the end
1149 ctx.add_post_fun(waf_unit_test.summary)
1150 ctx.add_post_fun(waf_unit_test.set_exit_code)
1151 ctx.add_post_fun(bin_test_summary)
1153 pprint("YELLOW", "Unit test runner skipped on a cross-compiled build.")
1154 Options.options.no_tests = True
1156 if ctx.cmd == "build":
1157 if "PYTHONPATH" not in os.environ:
1158 print("--- PYTHONPATH is not set, "
1159 "loading the Python ntp library may be troublesome ---")
1160 elif ctx.env.PYTHONARCHDIR not in os.environ["PYTHONPATH"]:
1161 print("--- PYTHONARCHDIR not in PYTHONPATH, "
1162 "loading the Python ntp library may be troublesome ---")
1165 # Miscellaneous utility productions
1170 "Get a report on configuration symbols not accounted for."
1171 ctx.exec_command("ifdex -X build/config.h -X devel/ifdex-ignores .")
1174 # See https://gitlab.com/esr/loccount
1176 "Report the SLOC count of the source tree."
1177 ctx.exec_command("loccount -x=build .")
1181 "Create standalone binaries from Python scripts."
1182 ctx.exec_command("for prog in " + " ".join(python_scripts) +
1183 "; do cxfreeze $prog; done")
1187 "Report references without anchors in the documentation."
1188 ctx.exec_command("devel/linkcheck docs/")
1190 # The following sets edit modes for GNU EMACS