-
Notifications
You must be signed in to change notification settings - Fork 2
Expand file tree
/
Copy pathbuild.zig
More file actions
292 lines (270 loc) · 11.4 KB
/
Copy pathbuild.zig
File metadata and controls
292 lines (270 loc) · 11.4 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
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
//! Zig 构建系统配置文件
//!
//! 定义项目的构建流程,包括:
//! - 共享库(JNI 桥接):编译为动态链接库供 Java 层通过 JNI 加载
//! - 可执行文件:独立运行的播放器程序(用于桌面测试)
//! - 测试:各模块的单元测试
//!
//! 构建依赖:
//! - miniaudio:跨平台音频引擎(C 库,通过 @cImport 引入)
//! - 平台特定的系统库(见 linkPlatformLibs)
const std = @import("std");
/// 项目构建入口。
///
/// 整体流程:
/// 1. 解析目标平台和优化选项
/// 2. 创建 miniaudio 模块(C 头文件翻译)
/// 3. 构建共享库(JNI 桥接)
/// 4. 构建可执行文件
/// 5. 配置运行步骤
/// 6. 配置测试步骤
pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const miniaudio_mod = createMiniaudioModule(b, target, optimize);
// 平台工具模块(跨平台休眠、时间戳等)
const platform_mod = b.createModule(.{
.root_source_file = b.path("src/platform.zig"),
});
// 歌词类型模块(共享库和测试共享)
const lyrics_types_mod = b.createModule(.{
.root_source_file = b.path("src/lyrics/types.zig"),
});
// 共享库(JNI 桥接)
// 编译为动态链接库(libzmusic.so / zmusic.dll),供 Java 层通过 System.loadLibrary 加载
const shared_lib = b.addLibrary(.{
.linkage = .dynamic,
.name = "zmusic",
.root_module = b.createModule(.{
.root_source_file = b.path("src/jni/bridge.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
}),
});
configureModule(b, shared_lib.root_module, target, miniaudio_mod, platform_mod);
// callback 模块作为独立 import,供 bridge.zig 导入事件回调机制
shared_lib.root_module.addImport("callback", b.createModule(.{
.root_source_file = b.path("src/jni/callback.zig"),
}));
// Player 模块(bridge.zig 通过 @import("player") 引入)
//
// 由于共享库根模块路径为 src/jni/,无法通过 ../player.zig 相对导入,
// 因此将 player.zig 作为独立命名模块引入,并配置其依赖的命名导入。
//
// player.zig 已修改为通过 @import("lyrics_types") 命名导入获取歌词类型定义,
// 而非相对导入 lyrics/types.zig,避免 types.zig 同时属于 player 模块和
// lyrics_types 模块(Zig 不允许同一文件属于多个模块)。
const player_mod_for_lib = b.createModule(.{
.root_source_file = b.path("src/player.zig"),
});
player_mod_for_lib.addImport("miniaudio", miniaudio_mod);
player_mod_for_lib.addImport("platform", platform_mod);
player_mod_for_lib.addImport("lyrics_types", lyrics_types_mod);
shared_lib.root_module.addImport("player", player_mod_for_lib);
b.installArtifact(shared_lib);
// 可执行文件
// 独立运行的播放器程序,用于桌面环境下的开发测试
const exe = b.addExecutable(.{
.name = "zmusic-player",
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
}),
});
configureModule(b, exe.root_module, target, miniaudio_mod, platform_mod);
// player.zig 使用 @import("lyrics_types"),exe 通过 main.zig 相对导入 player.zig,
// 因此 exe 模块也需要 lyrics_types 命名导入
exe.root_module.addImport("lyrics_types", lyrics_types_mod);
b.installArtifact(exe);
// 运行
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_step.dependOn(&run_cmd.step);
// 测试
const test_step = b.step("test", "运行单元测试");
// 主模块单元测试
const main_tests = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path("src/main.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
}),
});
configureModule(b, main_tests.root_module, target, miniaudio_mod, platform_mod);
main_tests.root_module.addImport("lyrics_types", lyrics_types_mod);
test_step.dependOn(&b.addRunArtifact(main_tests).step);
// 歌词模块(应用和测试共享)
// 歌词解析器模块
// 解析器依赖类型定义模块(lyrics_types_mod 已在共享库部分创建)
const lyrics_parser_mod = b.createModule(.{
.root_source_file = b.path("src/lyrics/parser.zig"),
});
// 解析器依赖类型定义模块
lyrics_parser_mod.addImport("lyrics_types", lyrics_types_mod);
addModuleTest(b, test_step, target, optimize, miniaudio_mod, platform_mod, "tests/test_lyrics.zig", &.{
.{ "lyrics_parser", lyrics_parser_mod },
.{ "lyrics", lyrics_types_mod },
});
const queue_mod = b.createModule(.{
.root_source_file = b.path("src/queue/playlist.zig"),
});
queue_mod.addImport("platform", platform_mod);
addModuleTest(b, test_step, target, optimize, miniaudio_mod, platform_mod, "tests/test_queue.zig", &.{
.{ "queue", queue_mod },
});
// Player 模块(需要 miniaudio C 源文件,因为 player.zig 的 deinit/stop 引用了 miniaudio 符号)
const player_test_mod = b.createModule(.{
.root_source_file = b.path("src/player.zig"),
});
player_test_mod.addImport("miniaudio", miniaudio_mod);
player_test_mod.addImport("platform", platform_mod);
player_test_mod.addImport("lyrics_types", lyrics_types_mod);
const player_test = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path("tests/test_player.zig"),
.target = target,
.optimize = optimize,
.link_libc = true,
}),
});
player_test.root_module.addImport("miniaudio", miniaudio_mod);
player_test.root_module.addImport("platform", platform_mod);
player_test.root_module.addImport("player", player_test_mod);
addMiniaudioCSources(b, player_test.root_module);
linkPlatformLibs(b, player_test.root_module, target);
test_step.dependOn(&b.addRunArtifact(player_test).step);
}
/// 创建 miniaudio 绑定模块。
///
/// 通过 Zig 的 @cImport 机制自动翻译 C 头文件,生成可在 Zig 中直接调用的
/// 类型安全绑定,无需手写 FFI 桥接代码。
fn createMiniaudioModule(
b: *std.Build,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
) *std.Build.Module {
const translate = b.addTranslateC(.{
.root_source_file = b.path("vendor/miniaudio/miniaudio.h"),
.target = target,
.optimize = optimize,
});
return translate.createModule();
}
/// 为构建模块应用通用配置。
///
/// 所有需要音频能力的模块(共享库、可执行文件、测试)都通过此函数统一配置,
/// 确保 miniaudio 导入、C 源文件和平台链接库的一致性。
fn configureModule(
b: *std.Build,
mod: *std.Build.Module,
target: std.Build.ResolvedTarget,
miniaudio_mod: *std.Build.Module,
platform_mod: *std.Build.Module,
) void {
mod.addImport("miniaudio", miniaudio_mod);
mod.addImport("platform", platform_mod);
addMiniaudioCSources(b, mod);
linkPlatformLibs(b, mod, target);
}
/// 创建并注册模块级测试。
///
/// 封装测试创建的通用逻辑:创建测试可执行文件、添加依赖模块、
/// 链接平台库,最后挂载到总测试步骤下。
fn addModuleTest(
b: *std.Build,
test_step: *std.Build.Step,
target: std.Build.ResolvedTarget,
optimize: std.builtin.OptimizeMode,
miniaudio_mod: *std.Build.Module,
platform_mod: *std.Build.Module,
test_path: []const u8,
imports: []const struct { []const u8, *std.Build.Module },
) void {
const t = b.addTest(.{
.root_module = b.createModule(.{
.root_source_file = b.path(test_path),
.target = target,
.optimize = optimize,
.link_libc = true,
}),
});
t.root_module.addImport("miniaudio", miniaudio_mod);
t.root_module.addImport("platform", platform_mod);
for (imports) |imp| {
t.root_module.addImport(imp[0], imp[1]);
}
linkPlatformLibs(b, t.root_module, target);
test_step.dependOn(&b.addRunArtifact(t).step);
}
/// 添加 miniaudio 的 C 源文件。
///
/// miniaudio 是纯 C 库,虽然通过 @cImport 翻译了头文件获得了类型定义和函数声明,
/// 但实际的实现代码(miniaudio.c)仍需作为 C 源文件参与编译和链接。
fn addMiniaudioCSources(b: *std.Build, mod: *std.Build.Module) void {
mod.addCSourceFile(.{
.file = b.path("vendor/miniaudio/miniaudio.c"),
.flags = &.{},
});
mod.addIncludePath(b.path("vendor/miniaudio"));
}
/// 链接各平台所需的系统库。
///
/// miniaudio 在不同操作系统上依赖不同的底层音频 API,需要链接对应的系统库:
///
/// - Linux:
/// - pthread:POSIX 线程库,用于异步音频回调
/// - m:数学库,音频处理中的数学运算
/// - dl:动态链接库,用于运行时加载音频驱动
///
/// - Windows:
/// - winmm:Windows 多媒体 API
/// - ole32:COM 基础库,部分音频接口依赖 COM
/// - uuid:UUID 生成,COM 组件标识
///
/// - macOS:
/// - CoreAudio:核心音频服务
/// - AudioToolbox:高级音频工具箱(编解码、格式转换等)
/// - CoreFoundation:基础框架,提供数据类型和运行时支持
fn linkPlatformLibs(b: *std.Build, mod: *std.Build.Module, target: std.Build.ResolvedTarget) void {
switch (target.result.os.tag) {
.linux => {
mod.linkSystemLibrary("pthread", .{});
mod.linkSystemLibrary("m", .{});
mod.linkSystemLibrary("dl", .{});
},
.windows => {
mod.linkSystemLibrary("winmm", .{});
mod.linkSystemLibrary("ole32", .{});
mod.linkSystemLibrary("uuid", .{});
},
.macos => {
// macOS SDK 路径获取(优先级从高到低):
// 1. getSdk:macOS 主机自动检测
// 2. SDKROOT 环境变量:交叉编译时手动指定
// 3. --sysroot 构建参数
const sdk = sdk_blk: {
break :sdk_blk std.zig.system.darwin.getSdk(b.allocator, b.graph.io, &target.result) orelse b.graph.environ_map.get("SDKROOT") orelse b.sysroot;
};
if (sdk) |path| {
mod.addFrameworkPath(.{ .cwd_relative = b.pathJoin(&.{ path, "System/Library/Frameworks" }) });
mod.addSystemIncludePath(.{ .cwd_relative = b.pathJoin(&.{ path, "usr/include" }) });
mod.addLibraryPath(.{ .cwd_relative = b.pathJoin(&.{ path, "usr/lib" }) });
// Zig 交叉编译 macOS 时 -lc 链接的是 Zig 自带 libc,不含 iconv。
// 需要显式链接系统的 libiconv(SDK 的 usr/lib/libiconv.tbd)。
mod.linkSystemLibrary("iconv", .{});
mod.linkFramework("CoreAudio", .{});
mod.linkFramework("AudioToolbox", .{});
mod.linkFramework("CoreFoundation", .{});
}
},
else => {},
}
}