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, # and 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)