-
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
// Copyright 2025 Shota FUJI
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// SPDX-License-Identifier: Apache-2.0
//! This file defines how `zig build` command behaves.
//! Run `zig build --help` for available subcommands and options.
//!
//! Learn more at
//! https://ziglang.org/learn/build-system/
const std = @import("std");
const BuildError = error{
NonValaSourceInSourceListError,
};
pub fn build(b: *std.Build) !void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});
const freelog = b.option(bool, "freelog", "Enable logging memory releases") orelse false;
const compile_gschema = b.option(bool, "compile-gschema", "Compile gschema XML file for local run") orelse false;
const core = core: {
const dep = b.dependency("core", .{
.target = target,
.optimize = optimize,
.freelog = freelog,
});
break :core dep.artifact("plac_glib");
};
// Vala source codes to compile.
const vala_sources: [][]const u8 = vala_sources: {
var list = std.ArrayList([]const u8).init(b.allocator);
var dir = try std.fs.cwd().openDir("src", .{ .iterate = true });
defer dir.close();
var walker = try dir.walk(b.allocator);
defer walker.deinit();
while (try walker.next()) |entry| {
const ext = std.fs.path.extension(entry.basename);
if (std.mem.eql(u8, ".vala", ext)) {
try list.append(b.dupe(entry.path));
}
}
break :vala_sources try list.toOwnedSlice();
};
// System libraries to link.
const system_libraries = [_][]const u8{
"gtk4",
"libadwaita-1",
// Neither GTK4 nor Libadwaita provides SVG rendering. We need to use librsvg for
// that purpose, whilst undocumented.
"librsvg-2.0",
"gee-0.8",
};
// A directory containing C source files compiled from Vala source code.
// If you use this LazyPath in your artifact, the artifact automatically depends on
// this Vala to C complication step. You don't have to manually `a.dependOn()`.
const vala_cdir = vala: {
const valac = b.addSystemCommand(&.{"valac"});
// Tell Vala compiler to emit C rather than compile using system C compiler.
valac.addArg("--ccode");
// Vala compiler uses GResource XML to statically check attributes.
valac.addArg("--gresources");
valac.addFileArg(b.path("data/gresource.xml"));
valac.addArg("--vapidir");
valac.addDirectoryArg(core.getEmittedIncludeTree());
// Tell Vala what system libraries to use. Perhaps type checking things?
for (system_libraries) |lib| {
valac.addArgs(&.{ "--pkg", lib });
}
valac.addArgs(&.{ "--pkg", "posix" });
valac.addArgs(&.{ "--pkg", "plac" });
// Tell Vala to emit C source files under the output directory.
// The directory usually is Zig cache directory (like ".zig-cache/o/xxx").
// For example, when there is "src/Foo.vala", this step produces
// ".zig-cache/o/hash-or-something-idk/vala/src/Foo.c"
valac.addArg("--directory");
const dir = valac.addOutputDirectoryArg("vala");
// Let valac emit C source code without "src/" prefix.
valac.addArg("--basedir");
valac.addDirectoryArg(b.path("src"));
// Vala compiler takes a list of Vala source files, then process them all.
for (vala_sources) |src| {
valac.addFileArg(b.path(b.pathJoin(&.{ "src", src })));
}
const t = b.addInstallDirectory(.{
.source_dir = dir,
.install_dir = .{ .custom = "src" },
.install_subdir = "",
});
const step = b.step("valac", "Compile C source code from Vala files for debugging purpose");
step.dependOn(&t.step);
break :vala dir;
};
const gresouce_c = gresource: {
const compiler = b.addSystemCommand(&.{"glib-compile-resources"});
compiler.addArg("--sourcedir");
compiler.addDirectoryArg(b.path("data"));
compiler.addFileArg(b.path("data/gresource.xml"));
compiler.addArg("--target");
const built_c = compiler.addOutputFileArg("gresource.c");
compiler.addArg("--dependency-file");
_ = compiler.addDepFileOutputArg("resource.d");
compiler.addArg("--generate-source");
break :gresource built_c;
};
const gschema_compiled = gschema: {
const compiler = b.addSystemCommand(&.{"glib-compile-schemas"});
compiler.addArg("--targetdir");
const out = compiler.addOutputDirectoryArg("compiled");
compiler.addArg("--strict");
compiler.addDirectoryArg(b.path("data/"));
compiler.addFileInput(b.path("data/jp.pocka.plac.gtk-adwaita.gschema.xml"));
const t = b.addInstallFile(out.path(b, "gschemas.compiled"), "share/glib-2.0/schemas/gschemas.compiled");
break :gschema t;
};
// An executable.
const exe = exe: {
const exe = b.addExecutable(.{
.name = "plac",
.target = target,
.optimize = optimize,
});
// This is a standard C application, so libc is required.
exe.linkLibC();
// Vala does not bundle system libraries (of course): we have to tell the
// linker.
for (system_libraries) |lib| {
exe.linkSystemLibrary(lib);
}
exe.linkLibrary(core);
exe.addCSourceFile(.{ .file = gresouce_c });
// At this point, build does not run yet—we can't enumerate C source
// directory. Since we already have a list of Vala source code, we can
// build a list of paths of to-be-generated C source file.
for (vala_sources) |src| {
// Basically unreachable. Don't put non Vala source into `vala_sources`.
if (!std.mem.endsWith(u8, src, ".vala")) {
return BuildError.NonValaSourceInSourceListError;
}
// Looks fragile but it works. Even if it produced incorrect paths,
// filesystem and Zig compiler will tell us a file is missing.
const rel_path = b.fmt("{s}.c", .{src[0..(src.len - 5)]});
// src = "src/Foo.vala"
// rel_path = "src/Foo.c"
// file = "(step cache directory)/vala/src/Foo.c"
exe.addCSourceFile(.{
.file = try vala_cdir.join(b.allocator, rel_path),
});
}
break :exe exe;
};
// Install a main executable file on "zig build" without subcommand.
b.installArtifact(exe);
// Install gschema file
b.installFile("data/jp.pocka.plac.gtk-adwaita.gschema.xml", "share/glib-2.0/schemas/jp.pocka.plac.gtk-adwaita.gschema.xml");
// Install compiled gschema file
if (compile_gschema) {
b.getInstallStep().dependOn(&gschema_compiled.step);
}
}