1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
|
# Cursed, but I don't think there's another way to get this environment variable.
lix_suffix = run_command('bash', '-c', 'echo -n "$VERSION_SUFFIX"', check : true).stdout().strip()
lix_version_parts = meson.project_version().split('.')
lix_major = lix_version_parts[0]
lix_minor = lix_version_parts[1]
lix_patch = lix_version_parts[2].replace(lix_suffix, '')
# These variables (aside from LSOF) are created pseudo-dynamically, near the beginning of
# the top-level meson.build. Aside from prefix itself, each of these was
# made into an absolute path by joining it with prefix, unless it was already
# an absolute path (which is the default for store-dir, state-dir, and log-dir).
cpp_str_defines = {
'LSOF': lsof.full_path(),
'NIX_PREFIX': prefix,
'NIX_STORE_DIR': store_dir,
'NIX_DATA_DIR': datadir,
'NIX_STATE_DIR': state_dir / 'nix',
'NIX_LOG_DIR': log_dir,
'NIX_CONF_DIR': sysconfdir / 'nix',
'NIX_BIN_DIR': bindir,
'NIX_MAN_DIR': mandir,
}
embedded_sandbox_headers = []
if enable_embedded_sandbox_shell
hexdump = find_program('hexdump', required : true, native : true)
embedded_sandbox_shell_gen = custom_target(
'embedded-sandbox-shell.gen.hh',
command : [
hexdump,
'-v',
'-e',
'1/1 "0x%x," "\n"'
],
input : busybox.full_path(),
output : 'embedded-sandbox-shell.gen.hh',
capture : true,
feed : true,
)
embedded_sandbox_headers += embedded_sandbox_shell_gen
cpp_str_defines += {
'SANDBOX_SHELL': '__embedded_sandbox_shell__'
}
elif busybox.found()
cpp_str_defines += {
'SANDBOX_SHELL': busybox.full_path()
}
endif
if pasta.found()
cpp_str_defines += {
'PASTA_PATH': pasta.full_path(),
}
endif
cpp_args = []
foreach name, value : cpp_str_defines
cpp_args += [
'-D' + name + '=' + '"' + value + '"'
]
endforeach
builtin_dep_closure_set = []
foreach path : builtin_dep_closure
# replace all `)"` substrings with `))" R"("`, ie split the `)"` into two chars and
# have the preprocessor concatenate our literals at the break. this way we can just
# use raw strings to represent all paths without worrying about any other escaping.
builtin_dep_closure_set += [ 'R"(' + path.replace(')"', '))" R"("') + ')"' ]
endforeach
configdata.set('HAVE_STRUCT_DIRENT_D_TYPE', 1) # FIXME: actually check this for solaris
configdata.set('LIX_MAJOR', lix_major)
configdata.set('LIX_MINOR', lix_minor)
configdata.set('LIX_PATCH', lix_patch)
configdata.set_quoted('PACKAGE_NAME', meson.project_name())
configdata.set_quoted('PACKAGE_VERSION', meson.project_version())
configdata.set_quoted('PACKAGE_TARNAME', meson.project_name())
configdata.set_quoted('PACKAGE_STRING', meson.project_name() + ' ' + meson.project_version())
configdata.set_quoted('SYSTEM', host_system)
configdata.set_quoted('LIX_LIBEXEC_DIR', libexecdir / 'lix')
configdata.set('BUILTIN_DEP_CLOSURE', ', '.join(builtin_dep_closure_set))
config_h = configure_file(
configuration : configdata,
output : 'config.h',
)
install_headers(config_h, subdir : 'lix')
# Subcomponents: these link into artifacts themselves, and have interdependencies.
if not wasm_eval_only
subdir('lix-doc')
endif
if not wasm_eval_only
# We manage libutil's Rust components here, because subdir(rust-monocrate)
# needs libutil's Rust components, and subdir(libutil) needs rust-monocrate.
# FIXME?: Should libutil-rs just be in a separate directory from libutil, then?
libutil_rs_sources = files(
'libutil/lib.rs',
)
libutil_rs = static_library(
'lixutil_rs',
sources : libutil_rs_sources,
rust_args : [
# Will be empty if we're linking statically.
rust_dynamic_args,
# Will no-op if we're linking statically.
'-C', f'link-arg=-Wl,@soname_arg@,liblixutil_rs.@dylib_suffix@',
],
)
libutil_rs_gen_h = custom_target(
'libutil-rs.gen.hh',
input : [
files('libutil/cbindgen.toml'),
libutil_rs_sources,
],
output : ['libutil-rs.gen.hh'],
command : [
cbindgen,
'--config',
'@INPUT0@',
'--output',
'@OUTPUT0@',
'--',
'@INPUT1@',
],
)
# For rust-monocrate.
liblixutil_rs_internal = declare_dependency(
link_with : libutil_rs,
)
subdir('rust-monocrate')
# NOW we can create the dependency object anything that actually wants to use liblixutil_rs,
# because liblixutil_rs is secretly rust-monocrate in a trenchcoat.
# `library('lixutil')` and `executable('liblixutil-tests')` depend on this.
liblixutil_rs = declare_dependency(
link_with : rust_monocrate,
sources : [libutil_rs_gen_h],
)
else
# In the WASM build, no Rust components.
liblixutil_rs = declare_dependency()
endif
# we only collect sources and generated headers here. real headers are collected and
# installed by each subdir due to how meson generates paths for headers it installs.
liblix_sources = []
liblix_generated_headers = embedded_sandbox_headers
subdir('libutil')
subdir('libstore')
if not wasm_eval_only
subdir('libfetchers')
else
# For the WASM build, only generate the libfetchers settings header —
# libexpr's flake code needs it to compile. No libfetchers sources are compiled.
# Use subdir() so the output lands in build-wasm/lix/libfetchers/ matching the include path.
subdir('libfetchers')
endif
subdir('libexpr')
if not wasm_eval_only
subdir('libmain')
subdir('libcmd')
# libexec utilities should depend only on libutil, but meh.
subdir('libexec')
endif
if not wasm_eval_only
# FIXME: not using the pkg-config module because it creates way too many deps
# while meson migration is in progress, and we want to not include boost here
foreach lib : [ 'util', 'store', 'fetchers', 'expr', 'main', 'cmd' ]
configure_file(
input : f'lib@lib@/lix-@lib@.pc.in',
output : f'lix-@lib@.pc',
install_dir : libdir / 'pkgconfig',
configuration : {
'prefix' : prefix,
'libdir' : libdir,
'includedir' : includedir,
'PACKAGE_VERSION' : meson.project_version(),
},
)
endforeach
endif
if wasm_eval_only
# WASM evaluator build: minimal dependency set.
# KJ/capnp excluded — WasmStore uses synchronous host imports.
# openssl excluded — hash.cc uses wasm-crypto-impl.cc instead of libcrypto.so.
# boost: include headers only (partial_dependency) to avoid -pthread from boost's pkg-config.
boost_headers_only = boost.partial_dependency(compile_args : true, includes : true, link_args : false, links : false)
dependencies = [
boost_headers_only,
nlohmann_json,
pegtl,
toml11,
liblixutil_rs,
]
else
dependencies = [
aws_s3,
aws_sdk,
aws_sdk_transfer,
boehm,
boost,
brotli,
capnp_rpc,
cpuid,
curl,
editline,
kj,
libarchive,
libatomic,
lowdown,
ncurses,
nlohmann_json,
openssl,
seccomp,
sqlite,
toml11,
# NOTE: this is secretly rust-monocrate in a trenchcoat.
# See that directory's README for more details.
liblixutil_rs,
]
if host_machine.system() == 'freebsd'
dependencies += [ libprocstat ]
endif
endif
if wasm_eval_only
# WASM does not support shared libraries; build as static archive
# that gets linked into the final libnixexpr.wasm executable.
liblix_all = static_library(
'lix',
liblix_sources,
liblix_generated_headers,
dependencies : dependencies,
include_directories : [ '..' ],
cpp_args : cpp_args,
cpp_pch : cpp_pch,
install : false,
)
else
liblix_all = library(
'lix',
liblix_sources,
liblix_generated_headers,
dependencies : dependencies,
# `..` makes the `lix/` subtree of the repo available for includes,
# mirroring our header directory structure once they are installed.
include_directories : [ '..' ],
cpp_args : cpp_args,
cpp_pch : cpp_pch,
install : true,
# FIXME(Qyriad): is this right?
install_rpath : libdir,
)
endif
liblix = declare_dependency(
include_directories : include_directories('..'),
dependencies : dependencies,
link_with : liblix_all,
)
meson.override_dependency('lix', liblix)
if wasm_eval_only
# Build the WASM evaluator module (libnixexpr.wasm).
# This is a WASI-compatible standalone WASM binary that the gonix Go host
# loads via wazero and calls into for Nix expression evaluation.
executable(
'libnixexpr',
# wasm-exports.cc: nix_* C entry points for Go/wazero.
# wasm-codec.cc: codec wire-format encoder for the Nix AST.
files('libexpr/wasm-exports.cc', 'libexpr/wasm-codec.cc'),
dependencies : [liblix],
include_directories : [ '..' ],
cpp_args : cpp_args,
# -sSTANDALONE_WASM=1: WASI-compatible output, no JS runtime required.
# -fexceptions: Emscripten EH (invoke_* + __cxa_* ABI); Go host provides
# the invoke_* stubs that call the __wasm_invoke_* trampolines below.
# --no-entry: reactor binary (exports _initialize instead of _start).
link_args : [
'-sSTANDALONE_WASM=1',
'-fexceptions',
'--no-entry',
'-sUSE_PTHREADS=0',
'-sEXPORTED_FUNCTIONS=_nix_init,_nix_builtin_names,_nix_eval_expr,_nix_parse_expr,_nix_force_value,_nix_get_attr,_nix_get_attr_names,_nix_list_length,_nix_list_get,_nix_value_type,_nix_value_ptr,_nix_get_string,_nix_get_int,_nix_get_float,_nix_get_bool,_nix_free_value,_nix_last_error,_malloc,_free',
'-g3',
'-sASSERTIONS=1',
# lix_host_get_config is provided by the Go host at runtime as a WASM import.
'-sERROR_ON_UNDEFINED_SYMBOLS=0',
# printString in derivations.cc uses a 64KB inline small_vector buffer on the stack.
# The default 64KB WASM stack is not enough; use 1MB.
'-sSTACK_SIZE=1048576',
# Allow heap growth so malloc doesn't fail during evaluation.
'-sALLOW_MEMORY_GROWTH=1',
# Start with 64MB heap (enough for typical eval workloads).
'-sINITIAL_MEMORY=67108864',
],
name_suffix : 'wasm',
install : false,
)
# Standalone C++ EH test binary — no Lix dependency, just libc++/libc++abi.
# Used by gonix/wasm/eh_test.go to verify the Go host's EH implementation.
executable(
'wasm-eh-tests',
files('libexpr/wasm-eh-tests.cc'),
include_directories : [ '..' ],
cpp_args : cpp_args,
link_args : [
'-sSTANDALONE_WASM=1',
'-fexceptions',
'--no-entry',
'-sUSE_PTHREADS=0',
'-sEXPORTED_FUNCTIONS=_eh_test_simple,_eh_test_rethrow,_eh_test_catch_hierarchy,_eh_test_nested_try,_eh_test_exception_ptr,_eh_test_dtor_throw,_eh_test_multi_catch,_eh_test_catch_and_resume,_eh_test_propagate_through,_eh_test_exception_ptr_rethrow_chain,_eh_test_uncaught_depth_stable,_malloc,_free',
'-g3',
'-sASSERTIONS=1',
'-sERROR_ON_UNDEFINED_SYMBOLS=0',
'-sSTACK_SIZE=1048576',
'-sALLOW_MEMORY_GROWTH=1',
'-sINITIAL_MEMORY=16777216',
],
name_suffix : 'wasm',
install : false,
)
else
# The rest of the subdirectories aren't separate components,
# just source files in another directory, so we process them here.
# Static library that just sets default ASan options. It needs to be included
# in every executable.
asanoptions = static_library(
'libasanoptions',
files('asan-options/asan-options.cc'),
)
libasanoptions = declare_dependency(
link_whole: asanoptions
)
# Legacy commands.
subdir('legacy')
# Finally, the nix command itself, which all of the other commands are implmented in terms of
# as a multicall binary.
subdir('nix')
endif
|