libxce_dep's sources:[gen_srcs] handed every consumer target the generated .c output, so 143 targets each compiled controller.capnp.c independently: a 9.5s / 293MB-RSS / 3.3MB-object TU under the asan profile, ~35% of a clean build's serial work and ~470MB of duplicate objects on disk. Index the dep's sources to gen_srcs[1] (the .h output) instead: the custom_target edge still orders every consumer's compiles after codegen (bug-1798's guarantee, unchanged), .h sources are never compiled, and the .c is compiled exactly once inside libxce. libxce_src keeps the unindexed custom_target for the same reason it always did. Verified: fresh asan tree builds green (1934 steps vs 2076), meson test 134/134 including the three --wrap interposition tests; tree size 2.0G -> 1.3G. Co-Authored-By: Claude Code <noreply@anthropic.com>
304 lines
13 KiB
Meson
304 lines
13 KiB
Meson
fs = import('fs')
|
|
|
|
capnp = find_program('capnp')
|
|
packcc = find_program('packcc')
|
|
# capnp's `-o c:` plugin lookup execs 'capnpc-c' via PATH -- the meson
|
|
# program override (wrap subproject) does not extend PATH, so wrap the
|
|
# command with env and prepend the overridden binary's directory. Listing
|
|
# capnpc_c in the command (as a no-op first token via env's PATH arg) is not
|
|
# enough for ordering; the custom target below also depends on it.
|
|
capnpc_c = find_program('capnpc-c')
|
|
|
|
schema_dir = meson.current_source_dir() / '..' / 'schema'
|
|
|
|
gen_srcs = custom_target(
|
|
'controllercapnp.[ch]',
|
|
output:['controller.capnp.c', 'controller.capnp.h'],
|
|
input: ['../schema/controller.capnp', capnpc_c],
|
|
# ASAN_OPTIONS=detect_leaks=0: capnpc-c (the code generator) deliberately
|
|
# never frees its compile-time structures -- a one-shot tool whose process
|
|
# exits immediately. Under the asan profile the wrap-subproject binary is
|
|
# built with -fsanitize=address, and LSAN's default end-of-process leak
|
|
# check then fails the generation step (exit 1, ~2MB reported across
|
|
# capnpc-c.c allocations) -- de-nix build path only; the legacy nix build
|
|
# never propagated -fsanitize to the subproject. Same exemption
|
|
# convention as every other "one-shot, never-torn-down" boundary in this
|
|
# repo (test('libxce test', ...) above uses the identical env var).
|
|
command: [env_program, 'PATH=' + fs.parent(capnpc_c.full_path()) + ':/usr/bin:/bin',
|
|
'ASAN_OPTIONS=detect_leaks=0',
|
|
capnp, 'compile','-o' ,'c:lib', '-I', schema_dir, '--src-prefix', schema_dir,
|
|
'@INPUT0@']
|
|
)
|
|
|
|
section_parser = custom_target(
|
|
'xce resreq section parser',
|
|
output: ['section.c', 'section.h'],
|
|
input: ['section.peg'],
|
|
command: [
|
|
packcc,
|
|
'-l',
|
|
'-o',
|
|
'lib/section',
|
|
'@INPUT@'
|
|
],
|
|
build_by_default: true)
|
|
|
|
rusage_parser = custom_target(
|
|
'xce resreq rusage parser',
|
|
output: ['rusage.c', 'rusage.h'],
|
|
input: ['rusage.peg'],
|
|
command: [
|
|
packcc,
|
|
'-l',
|
|
'-o',
|
|
'lib/rusage',
|
|
'@INPUT@'
|
|
],
|
|
build_by_default: true)
|
|
|
|
xce_resreq_parser = custom_target(
|
|
'xce resreq xce_resreq parser',
|
|
output: ['xce_resreq.c', 'xce_resreq.h'],
|
|
input: ['xce_resreq.peg'],
|
|
command: [
|
|
packcc,
|
|
'-l',
|
|
'-o',
|
|
'lib/xce_resreq',
|
|
'@INPUT@'
|
|
],
|
|
build_by_default: true)
|
|
|
|
xce_select_parser = custom_target(
|
|
'xce select expression parser',
|
|
output: ['xce_select_peg.c', 'xce_select_peg.h'],
|
|
input: ['xce_select.peg'],
|
|
command: [
|
|
packcc,
|
|
'-l',
|
|
'-o',
|
|
'lib/xce_select_peg',
|
|
'@INPUT@'
|
|
],
|
|
build_by_default: true)
|
|
|
|
libxce_src = [ # bug-1798: pass the whole gen_srcs custom_target (both
|
|
# controller.capnp.c AND controller.capnp.h), not just
|
|
# gen_srcs[0] -- several files below (utils.c and others)
|
|
# reach controller.capnp.h only via #include, which ninja
|
|
# cannot see. Indexing to gen_srcs[0] dropped that header
|
|
# from this target's declared outputs entirely, so a
|
|
# from-scratch build had no dependency edge forcing capnp
|
|
# codegen to finish before those files compiled,
|
|
# intermittently failing with "controller.capnp.h: No such
|
|
# file or directory" (recurring since 2026-07-16). Passing
|
|
# the full custom_target compiles only its .c output (as
|
|
# before) while ordering the ENTIRE target's build after
|
|
# BOTH outputs are generated.
|
|
gen_srcs,
|
|
# bug-1798 (same class): each packcc parser custom_target
|
|
# below also produces a paired .h output (section.h,
|
|
# rusage.h, xce_resreq.h, xce_select_peg.h) that only
|
|
# resreq.c #includes -- a separate file from each parser's
|
|
# own generated .c. Indexing to [0] dropped every one of
|
|
# these headers from libxce_src, leaving resreq.c's compile
|
|
# with no declared dependency on any of the four codegen
|
|
# steps. Passing each custom_target unindexed compiles only
|
|
# its .c output (as before) while ordering this whole
|
|
# target's build after both of its outputs.
|
|
section_parser,
|
|
rusage_parser,
|
|
xce_resreq_parser,
|
|
xce_select_parser,
|
|
'client.c',
|
|
'client_failover.c',
|
|
'dataq.c',
|
|
'hook.c',
|
|
'htab.c',
|
|
'kvmgr.c',
|
|
'mylock.c',
|
|
'netio.c',
|
|
'request.c',
|
|
'threshold.c',
|
|
'utils.c',
|
|
'vtaskmgr.c',
|
|
'window.c',
|
|
'xceapi.c',
|
|
'xcectx.c',
|
|
'xcelist.c',
|
|
'xlist.c',
|
|
'xstring.c',
|
|
'zarray.c',
|
|
'wrap.c',
|
|
'idgen.c',
|
|
'delay.c',
|
|
'ordq.c',
|
|
'preason.c',
|
|
'xcealloc.c',
|
|
'misc.c',
|
|
'group.c',
|
|
'xcelimit.c',
|
|
'xvec.c',
|
|
'helper.c',
|
|
'resreq.c',
|
|
'xce_select.c',
|
|
'bitset.c',
|
|
'kv.c',
|
|
'jobj.c',
|
|
'qobj.c',
|
|
'resobj.c',
|
|
'wobj.c',
|
|
'configobj.c',
|
|
'cluster.c',
|
|
'raft/raft.c',
|
|
]
|
|
|
|
incdir = include_directories('../include')
|
|
build_inc = include_directories('.')
|
|
|
|
capnpc_c_prog = find_program('capnpc-c')
|
|
|
|
# debug('capnpc_c_prog %s\n', capnpc_c_prog)
|
|
|
|
thread_dep = dependency('threads')
|
|
c_capnproto_dep = dependency('c-capnproto')
|
|
tinylog_dep = dependency('tinylog')
|
|
unity_dep = dependency('unity')
|
|
ssl_dep = dependency('openssl', version: '>=3.0')
|
|
|
|
libxce_deps = [c_capnproto_dep, ssl_dep, tinylog_dep, thread_dep, librtti_dep]
|
|
|
|
#libxce_shared = shared_library('xce',
|
|
# libxce_src,
|
|
# include_directories: [incdir, build_inc],
|
|
# dependencies: libxce_deps,
|
|
# install: true)
|
|
|
|
libxce = static_library('xce',
|
|
libxce_src,
|
|
include_directories: [incdir, build_inc],
|
|
dependencies: libxce_deps,
|
|
install: true)
|
|
|
|
|
|
|
|
# bug-1798: `sources: [gen_srcs]` forces every consumer of libxce_dep
|
|
# (controller/worker/scheduler, none of which otherwise declare any
|
|
# dependency on this codegen step even though several of their own .c
|
|
# files #include "controller.capnp.h" directly via a raw -Ilib path) to
|
|
# build gen_srcs' outputs before compiling their own sources -- closing
|
|
# the same missing-dependency-edge race as the libxce_src fix above, but
|
|
# for consumers OUTSIDE the libxce target itself. link_with alone does not
|
|
# propagate this: it only orders the LINK step after libxce is built, not
|
|
# a consumer's own COMPILE steps against a header libxce never installs.
|
|
#
|
|
# build-time dedup: this used to pass the WHOLE custom_target, which made
|
|
# every consumer target compile controller.capnp.c itself -- 143 separate
|
|
# compiles of a 9.5s/293MB-RSS/3.3MB-object TU (asan profile), ~35% of a
|
|
# full clean build's serial work and ~470MB of duplicate .o files on disk.
|
|
# Indexing to gen_srcs[1] (the .h output only) keeps everything bug-1798
|
|
# needed: a custom_target output still carries the dependency edge on the
|
|
# whole codegen step (custom_target always builds ALL of its outputs
|
|
# atomically), and a .h source is never compiled. The .c output is compiled
|
|
# exactly once, inside libxce above, and every consumer links the archive.
|
|
libxce_dep = declare_dependency(link_with: libxce, sources: [gen_srcs[1]],
|
|
dependencies: libxce_deps)
|
|
# libxce_shared_dep = declare_dependency(link_with: libxce_shared)
|
|
|
|
libxce_test_src = ['testsuite/test.c',
|
|
'testsuite/test_error.c',
|
|
'testsuite/test_htab.c',
|
|
'testsuite/test_htab_int.c',
|
|
'testsuite/test_utils.c',
|
|
'testsuite/test_xstring.c',
|
|
'testsuite/test_zarray.c',
|
|
'testsuite/test_config.c',
|
|
'testsuite/test_idgen.c',
|
|
'testsuite/test_jobsel.c',
|
|
'testsuite/test_delay.c',
|
|
'testsuite/test_ordq.c',
|
|
'testsuite/test_preason.c',
|
|
'testsuite/test_window.c',
|
|
'testsuite/test_kvmgr.c',
|
|
'testsuite/test_group.c',
|
|
'testsuite/test_xlist.c',
|
|
'testsuite/test_limit.c',
|
|
'testsuite/test_xvec.c',
|
|
'testsuite/test_resreq.c',
|
|
'testsuite/test_resreq_fingerprint.c',
|
|
'testsuite/test_resreq_task_count.c',
|
|
'testsuite/test_resmgr_fixture.c',
|
|
'testsuite/test_select.c',
|
|
'testsuite/test_bitset.c',
|
|
'testsuite/test_kv.c',
|
|
'testsuite/test_jobj.c',
|
|
'testsuite/test_jobj_hardening.c',
|
|
'testsuite/test_wobj.c',
|
|
'testsuite/test_qobj.c',
|
|
'testsuite/test_request.c',
|
|
'testsuite/test_job_state_wire.c',
|
|
'testsuite/test_cbuf.c',
|
|
'testsuite/test_zarray_cow.c',
|
|
'testsuite/test_kvmgr_capture.c',
|
|
'testsuite/test_zarray_reserve.c',
|
|
'testsuite/test_kvmgr_reserve.c',
|
|
'testsuite/test_hooks.c',
|
|
'testsuite/test_cluster.c',
|
|
'testsuite/test_cluster_parse.c',
|
|
'testsuite/test_libxce_retry.c',
|
|
'testsuite/test_netio_on_connected.c',
|
|
'testsuite/test_netio_on_fini.c',
|
|
'testsuite/test_netio_reconnect_gen.c',
|
|
'testsuite/test_netio_arm_failure.c',
|
|
'testsuite/test_netio_close_propagation.c',
|
|
'testsuite/test_netio_bounded_connect.c',
|
|
'testsuite/test_netio_batch_dispatch_uaf.c',
|
|
'testsuite/test_netio_err_queue.c',
|
|
'testsuite/test_netio_nodelay.c',
|
|
'testsuite/test_request_send_propagation.c',
|
|
'testsuite/test_raft.c',
|
|
'testsuite/test_wal_wire.c',
|
|
'testsuite/test_ctrlstatus.c',
|
|
'testsuite/test_vtask_admission.c',
|
|
'testsuite/test_vtask_enq_race.c',
|
|
'testsuite/test_vtask_pkt_handoff.c',
|
|
'testsuite/test_ctx_reconnect_quiesce.c',
|
|
'testsuite/test_vtask_order.c',
|
|
'testsuite/test_vtaskmgr_teardown.c',
|
|
'testsuite/test_xcealloc.c',
|
|
'raft/raft_sim.c',
|
|
]
|
|
|
|
libxce_test_deps = [libxce_deps, librtti_dep, unity_dep, libxce_dep]
|
|
libxce_test = executable('xcelib_test',
|
|
libxce_test_src,
|
|
# <unity.h> and <unity/unity.h> both resolve through
|
|
# unity_dep itself: the wrap-path override (or the nix
|
|
# pc under the legacy build) carries the include roots
|
|
# for both layouts.
|
|
include_directories: [incdir, build_inc],
|
|
dependencies: libxce_test_deps,
|
|
c_args: ['-DXCE_TESTING'],
|
|
install: true)
|
|
|
|
test('libxce test', libxce_test, timeout: 120,
|
|
env: ['ASAN_OPTIONS=detect_leaks=0']) # suite convention — fixtures not torn down
|
|
|
|
# bug-1882 regression: `-Wl,--wrap=strdup` linker symbol interposition (same
|
|
# idiom as bug-1843's sched_record_lifecycle_test `--wrap=realloc`) so the
|
|
# tests can make a chosen strdup() inside make_alloc()/make_host_alloc()
|
|
# fail. Its own binary, so xcelib_test's other tests never see the wrap.
|
|
# libxce-alloc-preason-equality: it also wraps vasprintf (and the
|
|
# _FORTIFY_SOURCE __vasprintf_chk variant) to fail the equality functions'
|
|
# description allocation.
|
|
libxce_alloc_oom_test = executable('xcealloc_oom_test',
|
|
['testsuite/test_xcealloc_oom.c'],
|
|
include_directories: [incdir, build_inc],
|
|
dependencies: libxce_test_deps,
|
|
c_args: ['-DXCE_TESTING'],
|
|
link_args: ['-Wl,--wrap=strdup',
|
|
'-Wl,--wrap=vasprintf',
|
|
'-Wl,--wrap=__vasprintf_chk'],
|
|
install: false)
|
|
|
|
test('libxce alloc OOM test', libxce_alloc_oom_test, timeout: 60)
|