-
Notifications
You must be signed in to change notification settings - Fork 6
Expand file tree
/
Copy pathbuild.zig
More file actions
122 lines (104 loc) · 5.21 KB
/
Copy pathbuild.zig
File metadata and controls
122 lines (104 loc) · 5.21 KB
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
const std = @import("std");
// Single source of truth for the version: the `.version` field in build.zig.zon.
// Injected into the binary via a build option (see configureModule) so mcp.zig's
// `initialize` / serverInfo report exactly what the package declares.
const build_zon = @import("build.zig.zon");
const VERSION = build_zon.version;
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const is_macos = target.result.os.tag == .macos;
// ── Main executable ──
const exe = b.addExecutable(.{
.name = "memxt",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
}),
});
configureModule(b, exe.root_module, is_macos);
b.installArtifact(exe);
// ── Run step ──
const run_cmd = b.addRunArtifact(exe);
run_cmd.step.dependOn(b.getInstallStep());
if (b.args) |args| run_cmd.addArgs(args);
const run_step = b.step("run", "Run memxt");
run_step.dependOn(&run_cmd.step);
// ── Tests ──
const test_step = b.step("test", "Run unit tests");
const unit_tests = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
}),
});
configureModule(b, unit_tests.root_module, is_macos);
test_step.dependOn(&b.addRunArtifact(unit_tests).step);
}
/// Wire up SQLite, sqlite-vec, and the statically-linked llama.cpp/ggml stack
/// for a module. We link the cmake-produced `.a` archives directly (by path)
/// rather than via `linkSystemLibrary`, so we never accidentally pick up a
/// Homebrew dylib and we end up with a genuinely static binary.
fn configureModule(b: *std.Build, mod: *std.Build.Module, is_macos: bool) void {
// Expose the package version to Zig code as `@import("build_options").version`.
const options = b.addOptions();
options.addOption([]const u8, "version", VERSION);
mod.addOptions("build_options", options);
// SQLite is vendored (amalgamation) and compiled in, not linked from the
// system — so the binary is genuinely self-contained (no libsqlite3 dynamic
// dep) and cross-compiles cleanly (no system-library lookup for the target).
mod.addCSourceFile(.{
.file = b.path("lib/sqlite3.c"),
// FTS5 powers hybrid keyword search (identifiers, error codes). Must be
// enabled at amalgamation compile time — it's not on by default.
.flags = &[_][]const u8{ "-O2", "-DSQLITE_THREADSAFE=1", "-DSQLITE_DQS=0", "-DSQLITE_DEFAULT_MEMSTATUS=0", "-DSQLITE_OMIT_DEPRECATED", "-DSQLITE_ENABLE_FTS5" },
});
mod.addCSourceFile(.{
.file = b.path("lib/sqlite-vec.c"),
.flags = &[_][]const u8{ "-O3", "-fomit-frame-pointer", "-DSQLITE_CORE" },
});
// Headers.
mod.addIncludePath(b.path("lib"));
mod.addIncludePath(b.path("lib/llama.cpp/include"));
mod.addIncludePath(b.path("lib/llama.cpp/ggml/include"));
// ── llama.cpp + ggml static archives (cmake output) ──
// Built via: cmake -S lib/llama.cpp -B lib/llama.cpp/build \
// -DCMAKE_BUILD_TYPE=Release -DBUILD_SHARED_LIBS=OFF \
// -DGGML_METAL=ON(macOS)/OFF(linux) -DGGML_BLAS=ON(macOS)/OFF(linux) \
// -DLLAMA_BUILD_TESTS=OFF -DLLAMA_BUILD_EXAMPLES=OFF \
// -DLLAMA_BUILD_SERVER=OFF -DLLAMA_BUILD_TOOLS=OFF
// then: cmake --build lib/llama.cpp/build -j
mod.addObjectFile(b.path("lib/llama.cpp/build/src/libllama.a"));
mod.addObjectFile(b.path("lib/llama.cpp/build/ggml/src/libggml.a"));
mod.addObjectFile(b.path("lib/llama.cpp/build/ggml/src/libggml-base.a"));
mod.addObjectFile(b.path("lib/llama.cpp/build/ggml/src/libggml-cpu.a"));
// C++ runtime: libc++ on both platforms. On Linux we build llama.cpp with
// clang + -stdlib=libc++ in CI so the ABI matches here.
mod.linkSystemLibrary("c++", .{});
if (is_macos) {
// Make the macOS SDK frameworks/libs discoverable. Native builds detect
// these automatically, but cross-compiling (x86_64 on an arm host) does
// not, so we add the SDK paths explicitly.
if (sdkPath(b)) |sdk| {
mod.addFrameworkPath(.{ .cwd_relative = b.fmt("{s}/System/Library/Frameworks", .{sdk}) });
mod.addLibraryPath(.{ .cwd_relative = b.fmt("{s}/usr/lib", .{sdk}) });
}
// macOS: Metal + Accelerate BLAS backends and the frameworks they need.
mod.addObjectFile(b.path("lib/llama.cpp/build/ggml/src/ggml-blas/libggml-blas.a"));
mod.addObjectFile(b.path("lib/llama.cpp/build/ggml/src/ggml-metal/libggml-metal.a"));
mod.linkFramework("Accelerate", .{});
mod.linkFramework("Metal", .{});
mod.linkFramework("MetalKit", .{});
mod.linkFramework("Foundation", .{});
}
}
/// Resolve the active macOS SDK path via `xcrun`, or null if unavailable.
fn sdkPath(b: *std.Build) ?[]const u8 {
const out = b.run(&.{ "xcrun", "--show-sdk-path" });
const trimmed = std.mem.trim(u8, out, " \t\r\n");
return if (trimmed.len == 0) null else trimmed;
}