1 # Copyright the NTPsec project contributors
3 # SPDX-License-Identifier: BSD-2-Clause
5 from __future__ import print_function
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
44 "Be helpful, give a usage"
47 build Build the project
49 configure Configure the project
51 install Install the project
52 loccount Show SLOC count of the source tree
53 uninstall Uninstall the project
59 options_cmd(ctx, config)
60 ctx.load('asciidoc', tooldir='wafhelpers/')
65 ctx.load('asciidoc', tooldir='wafhelpers/')
67 class oc(Build.BuildContext):
70 def exec_command(self, cmd, **kw):
73 err, out = self.cmd_and_log(cmd, **kw)
75 self.logger.debug('WafError')
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)
84 self.logger.debug('noooo %r' % err)
91 def msg_setting(name, val):
92 pprint("NORMAL", " %-30s: " % name, sep="")
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')
103 with open("VERSION", "r") as f:
104 ntpsec_release = f.read().split(" ")[0].strip()
106 ctx.env.OPT_STORE = config["OPT_STORE"]
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]:
114 elif flag == "--define":
115 for symval in ctx.env.OPT_STORE[flag]:
116 (sym, val) = symval.split("=")
118 ctx.define(sym, int(val))
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.
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')
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)
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()
167 build_desc = ' ' + build_desc
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" %
191 ctx.env.NTPSEC_VERSION = "%s" % ntpsec_release
192 ctx.env.NTPSEC_VERSION_EXTENDED = ("%s%s" % (ntpsec_release,
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.
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
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.
214 # Note that _POSIX_C_SOURCE >= 199506L and _GNU_SOURCE both turn on
215 # _POSIX_PTHREAD_SEMANTICS and _REENTRANT
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
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))
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"]))
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")
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,
282 ctx.env.LDFLAGS += ["-lssp"]
284 ret = ctx.check_cc(lib="ssp_nonshared", mandatory=False,
285 comment="libssp_nonshared")
287 ctx.env.LDFLAGS += ["-lssp_nonshared"]
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'),
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'),
317 ('w_suggest_attribute_noreturn', "-Wsuggest-attribute=noreturn"),
318 ('w_write_strings', '-Wwrite-strings'),
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
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)
335 ('LTO', '-flto'), # link time optimization
337 ld_hardening_flags += [
338 ('stripall', "-Wl,--strip-all"), # Strip binaries
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
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
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"),
375 # -O1 will turn on -D_FORTIFY_SOURCE=2 for us
379 "-Wmissing-prototypes",
380 "-Wstrict-prototypes",
385 # gotta be tricky to test for -Wsuggest-attribute=const
388 int main(int argc, char **argv) {
389 (void)argc; (void)argv;
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,
403 msg='Checking if C compiler supports ' + ccflag,
406 ctx.run_build_cls = old_run_build_cls
409 ctx.env.CFLAGS = ["-fPIC"] + ctx.env.CFLAGS
412 ctx.env.LINKFLAGS_NTPD += [
415 ctx.env.CFLAGS_bin = ["-fPIE", "-pie"] + ctx.env.CFLAGS
416 ld_hardening_flags += [
417 ('relro', "-Wl,-z,relro"), # hardening, marks some read only,
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,
481 msg='Checking if linker supports ' + ldflag,
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
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
498 # "-fsanitize=cfi", # hardening
499 # "-fsanitize=safe-stack", # hardening
502 "-Wl,-z,relro", # hardening, marks some section read only,
504 # else: # gcc, probably
506 # Exclude Unity's support for printing floating point numbers
507 # since it triggers warnings
509 ctx.env.CFLAGS = ['-DUNITY_EXCLUDE_FLOAT_PRINT'] + ctx.env.CFLAGS
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
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"]
536 ctx.env.LIBPATH = ["/usr/local/ssl/lib"]
537 elif ctx.env.DEST_OS == "darwin":
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
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;
564 int main(int argc, char **argv) {
566 (void)argc; (void)argv;
567 off = (char*) &((%(type_name)s*)0)->%(field_name)s;
568 return (size_t) off < sizeof(%(type_name)s);
572 def to_header(header_name):
573 return ''.join(['#include <%s>\n' %
574 x for x in Utils.to_list(header_name)])
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),
582 for (s, h, r) in structures:
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()),
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
597 for (s, f, h) in structure_fields:
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()),
606 # mostly used by timespecops.h
607 # Some are unused, but handy for discovering what a system is doing
609 ("time.h", "struct timespec"),
610 ("sys/time.h", "struct timeval"),
611 ("time.h", "time_t"),
615 for header, sizeof in sorted(sizeofs, key=lambda x: x[1:]):
616 check_sizeof(ctx, header, sizeof)
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,
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,
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
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],
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'])
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'])
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.
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.
708 # Some of these are cruft from ancient big-iron systems and should
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"]),
719 ("net/route.h", ["sys/types.h", "sys/socket.h", "net/if.h"]),
720 "openssl/opensslv.h", # just for wafhelper OpenSSL
723 "sys/clockctl.h", # NetBSD
725 "sys/modem.h", # Apple
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"]),
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):
739 if probe_header(ctx, hdr, prereqs):
741 if os.path.exists("/usr/include/" + hdr):
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
757 from wafhelpers.check_strerror import check_strerror
760 # Check for Solaris's service configuration facility library
761 ctx.check_cc(header_name="libscf.h", lib="scf", mandatory=False,
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
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
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 "
820 # Check for directory separator
821 if ctx.env.DEST_OS == "win32":
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',
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)
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])
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"
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")
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))
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):
940 class bin_test(BuildContext):
941 """Run binary check, use after tests."""
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:
959 __doc__ = "%s %s" % (cmd, v)
960 cmd = "%s_%s" % (cmd, v)
964 def init_handler(ctx):
966 if cmd == 'init_handler':
969 def make_context(name):
970 for x in Context.classes:
971 if x.cmd == name and x.fun != 'init_handler':
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)
979 pprint("YELLOW", "--- %sing %s ---" % (cmd, v))
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):
1000 'install uninstall build clean list step'
1002 execute = Scripting.autoconfigure(Context.Context.execute)
1006 def afterparty(ctx):
1007 # Make magic links to support in-tree testing.
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
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
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)):
1026 os.remove(path_source.abspath())
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",
1045 from waflib.Logs import verbose
1046 ctx.load('waf', tooldir='wafhelpers/')
1047 ctx.load('asciidoc', tooldir='wafhelpers/')
1049 if ctx.variant == "host":
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')
1065 target3 = bldnode.ant_glob('*ntpc*')
1067 ctx.exec_command("rm -f %s" % _.abspath())
1068 # Finish purging ntp.ntpc
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")
1078 ctx.recurse("ntpfrob")
1079 ctx.recurse("ntptime")
1080 ctx.recurse("pylib")
1081 if ctx.env.ENABLE_ATTIC:
1082 ctx.recurse("attic")
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
1101 source=python_scripts,
1108 target=[x.replace('.py', '') for x in scripts],
1110 install_path='${BINDIR}',
1113 ctx.add_post_fun(afterparty)
1114 if ctx.cmd == 'clean':
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
1143 ctx.add_post_fun(test_print_log)
1144 elif Options.options.no_tests:
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)
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
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
1181 "Report the SLOC count of the source tree."
1182 ctx.exec_command("loccount -x=build .")
1186 "Create standalone binaries from Python scripts."
1187 ctx.exec_command("for prog in " + " ".join(python_scripts) +
1188 "; do cxfreeze $prog; done")
1192 "Report references without anchors in the documentation."
1193 ctx.exec_command("devel/linkcheck docs/")
1195 # The following sets edit modes for GNU EMACS