Skip to content

Commit 2e8c837

Browse files
fix d8 step to work with "zig build --watch" when modifying Java files (#72)
fix d8 step to work with "zig build --watch" when modifying Java files Fixes #71
1 parent 2095f42 commit 2e8c837

File tree

4 files changed

+90
-45
lines changed

4 files changed

+90
-45
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
11
.zig-cache
22
zig-out
3+
zig-pkg

examples/sdl2/third-party/sdl2/build.zig.zon

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
.{
2-
.name = "sdl",
2+
.name = .sdl,
33
.version = "2.32.10",
44
.dependencies = .{
55
.sdl2 = .{
@@ -14,4 +14,5 @@
1414
"build.zig.zon",
1515
"src",
1616
},
17+
.fingerprint = 0xec638ccb4e5e0ae0,
1718
}

src/androidbuild/apk.zig

Lines changed: 49 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const getAndroidTriple = androidbuild.getAndroidTriple;
1515
const runNameContext = androidbuild.runNameContext;
1616
const printErrorsAndExit = androidbuild.printErrorsAndExit;
1717

18+
const Allocator = std.mem.Allocator;
1819
const Target = std.Target;
1920
const Step = std.Build.Step;
2021
const ResolvedTarget = std.Build.ResolvedTarget;
@@ -219,7 +220,7 @@ pub fn addInstallApk(apk: *Apk) *Step.InstallFile {
219220
};
220221
}
221222

222-
fn doInstallApk(apk: *Apk) std.mem.Allocator.Error!*Step.InstallFile {
223+
fn doInstallApk(apk: *Apk) Allocator.Error!*Step.InstallFile {
223224
const b = apk.b;
224225

225226
const key_store: KeyStore = apk.key_store orelse .empty;
@@ -299,12 +300,14 @@ fn doInstallApk(apk: *Apk) std.mem.Allocator.Error!*Step.InstallFile {
299300
};
300301

301302
// ie. "$ANDROID_HOME/Sdk/platforms/android-{api_level}/android.jar"
302-
const root_jar = b.pathResolve(&[_][]const u8{
303-
apk.sdk.android_sdk_path,
304-
"platforms",
305-
b.fmt("android-{d}", .{@intFromEnum(apk.api_level)}),
306-
"android.jar",
307-
});
303+
const root_jar: LazyPath = .{
304+
.cwd_relative = b.pathResolve(&[_][]const u8{
305+
apk.sdk.android_sdk_path,
306+
"platforms",
307+
b.fmt("android-{d}", .{@intFromEnum(apk.api_level)}),
308+
"android.jar",
309+
}),
310+
};
308311

309312
// Make resources.apk from:
310313
// - resources.flat.zip (created from "aapt2 compile")
@@ -319,11 +322,13 @@ fn doInstallApk(apk: *Apk) std.mem.Allocator.Error!*Step.InstallFile {
319322
const aapt2link = b.addSystemCommand(&[_][]const u8{
320323
apk.build_tools.aapt2,
321324
"link",
322-
"-I", // add an existing package to base include set
323-
root_jar,
324325
});
325326
aapt2link.setName(runNameContext("aapt2 link"));
326327

328+
// Add '-I android_sdk/platforms/android_version/android.jar'
329+
aapt2link.addArg("-I");
330+
aapt2link.addFileArg(root_jar);
331+
327332
if (b.verbose) {
328333
aapt2link.addArg("-v");
329334
}
@@ -552,13 +557,20 @@ fn doInstallApk(apk: *Apk) std.mem.Allocator.Error!*Step.InstallFile {
552557
// Source: https://github.com/libsdl-org/SDL/blob/release-2.30.7/android-project/app/src/main/java/org/libsdl/app/SDLActivity.java#L2045
553558
"-encoding",
554559
"utf8",
555-
"-cp",
556-
root_jar,
557-
// NOTE(jae): 2024-09-19
558-
// Debug issues with the SDL.java classes
559-
// "-Xlint:deprecation",
560560
});
561561
javac_cmd.setName(runNameContext("javac"));
562+
563+
// Add root jar
564+
javac_cmd.addArg("-cp");
565+
javac_cmd.addFileArg(root_jar);
566+
567+
// NOTE(jae): 2026-03-01
568+
// If we have verbose logging on, telling us about deprecated Java files
569+
if (b.verbose) {
570+
javac_cmd.addArg("-Xlint:deprecation");
571+
}
572+
573+
// Output directory
562574
javac_cmd.addArg("-d");
563575
const java_classes_output_dir = javac_cmd.addOutputDirectoryArg("android_classes");
564576

@@ -574,23 +586,8 @@ fn doInstallApk(apk: *Apk) std.mem.Allocator.Error!*Step.InstallFile {
574586
});
575587
d8.setName(runNameContext("d8"));
576588

577-
// Prepend JDK bin path so d8 can always find "java", etc
578-
if (apk.sdk.jdk_path.len > 0) {
579-
var env_map = d8.getEnvMap();
580-
const path = env_map.get("PATH") orelse &[0]u8{};
581-
const new_path = try std.mem.join(b.allocator, &[1]u8{std.fs.path.delimiter}, &.{
582-
b.fmt("{s}/bin", .{apk.sdk.jdk_path}),
583-
path,
584-
});
585-
try env_map.put("PATH", new_path);
586-
}
587-
588-
// ie. android_sdk/platforms/android-{api-level}/android.jar
589-
d8.addArg("--lib");
590-
d8.addArg(root_jar);
591-
592-
d8.addArg("--output");
593-
const dex_output_dir = d8.addOutputDirectoryArg("android_dex");
589+
// Add JDK bin path so d8 can always find "java", etc
590+
try apk.updatePathWithJdk(d8);
594591

595592
// NOTE(jae): 2024-09-22
596593
// As per documentation for d8, we may want to specific the minimum API level we want
@@ -599,7 +596,14 @@ fn doInstallApk(apk: *Apk) std.mem.Allocator.Error!*Step.InstallFile {
599596
// d8.addArg(number_as_string);
600597

601598
// add each output *.class file
602-
D8Glob.create(b, d8, java_classes_output_dir);
599+
D8Glob.create(b, d8, java_classes_output_dir, root_jar);
600+
601+
// ie. android_sdk/platforms/android-{api-level}/android.jar
602+
d8.addArg("--lib");
603+
d8.addFileArg(root_jar);
604+
605+
d8.addArg("--output");
606+
const dex_output_dir = d8.addOutputDirectoryArg("android_dex");
603607
const dex_file = dex_output_dir.path(b, "classes.dex");
604608

605609
// Append classes.dex to apk
@@ -949,18 +953,23 @@ fn updateSharedLibraryOptions(artifact: *std.Build.Step.Compile) void {
949953
}
950954

951955
/// Prepend JDK bin path so "d8", "apksigner", etc can always find "java"
952-
fn updatePathWithJdk(apk: *Apk, run: *std.Build.Step.Run) !void {
956+
fn updatePathWithJdk(apk: *Apk, run: *std.Build.Step.Run) Allocator.Error!void {
953957
if (apk.sdk.jdk_path.len == 0) return;
954958

955959
const b = apk.b;
956960

957-
var env_map = run.getEnvMap();
958-
const path = env_map.get("PATH") orelse &[0]u8{};
959-
const new_path = try std.mem.join(b.allocator, &[1]u8{std.fs.path.delimiter}, &.{
960-
b.fmt("{s}/bin", .{apk.sdk.jdk_path}),
961-
path,
962-
});
963-
try env_map.put("PATH", new_path);
961+
if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 15) {
962+
// Deprecated path: Add "java" path to env
963+
var env_map = run.getEnvMap();
964+
const path = env_map.get("PATH") orelse &[0]u8{};
965+
const new_path = try std.mem.join(b.allocator, &[1]u8{std.fs.path.delimiter}, &.{
966+
b.fmt("{s}/bin", .{apk.sdk.jdk_path}),
967+
path,
968+
});
969+
try env_map.put("PATH", new_path);
970+
} else {
971+
run.addPathDir(b.pathJoin(&.{ apk.sdk.jdk_path, "bin" }));
972+
}
964973
}
965974

966975
const Apk = @This();

src/androidbuild/d8glob.zig

Lines changed: 38 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,18 @@ run: *Build.Step.Run,
2121
/// The directory that will contain the files to glob
2222
dir: LazyPath,
2323

24+
/// Track the files added to the Run step for --watch
25+
argv_range: ?ArgRange,
26+
2427
const file_ext = ".class";
2528

29+
const ArgRange = struct {
30+
start: usize,
31+
end: usize,
32+
};
33+
2634
/// Creates a D8Glob step which is used to collect all *.class output files after a javac process generates them
27-
pub fn create(owner: *std.Build, run: *Run, dir: LazyPath) void {
35+
pub fn create(owner: *std.Build, run: *Run, dir: LazyPath, root_jar: LazyPath) void {
2836
const glob = owner.allocator.create(@This()) catch @panic("OOM");
2937
glob.* = .{
3038
.step = Step.init(.{
@@ -35,19 +43,31 @@ pub fn create(owner: *std.Build, run: *Run, dir: LazyPath) void {
3543
}),
3644
.run = run,
3745
.dir = dir,
46+
.argv_range = null,
3847
};
3948
// Run step relies on this finishing
4049
run.step.dependOn(&glob.step);
4150
// If dir is generated then this will wait for that dir to generate
4251
dir.addStepDependencies(&glob.step);
52+
// If root_jar changes, then this should trigger
53+
root_jar.addStepDependencies(&glob.step);
4354
}
4455

45-
fn make(step: *Step, _: Build.Step.MakeOptions) !void {
56+
fn make(step: *Step, options: Build.Step.MakeOptions) !void {
57+
_ = options;
4658
const b = step.owner;
4759
const arena = b.allocator;
60+
const gpa = b.allocator;
4861
const glob: *@This() = @fieldParentPtr("step", step);
62+
4963
const d8 = glob.run;
5064

65+
// Triggers on --watch if a Java file is modified.
66+
// For example: ZigSDLActivity.java
67+
if (glob.argv_range) |argv_range| {
68+
try d8.argv.replaceRange(d8.step.owner.allocator, argv_range.start, argv_range.end - argv_range.start, &.{});
69+
}
70+
5171
const search_dir = if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 15)
5272
glob.dir.getPath3(b, step)
5373
else
@@ -76,6 +96,7 @@ fn make(step: *Step, _: Build.Step.MakeOptions) !void {
7696
else
7797
dir.close(b.graph.io);
7898

99+
var optional_argv_start: ?usize = null;
79100
var walker = try dir.walk(arena);
80101
defer walker.deinit();
81102
while (if (builtin.zig_version.major == 0 and builtin.zig_version.minor <= 15)
@@ -92,17 +113,30 @@ fn make(step: *Step, _: Build.Step.MakeOptions) !void {
92113
// - !std.mem.containsAtLeast(u8, entry.basename, 1, "$") and
93114
// - !std.mem.containsAtLeast(u8, entry.basename, 1, "_API")
94115
if (std.mem.endsWith(u8, entry.path, file_ext)) {
116+
const absolute_file_path = try search_dir.root_dir.join(gpa, &.{ search_dir.sub_path, entry.path });
117+
const relative_to_dir_path = absolute_file_path[search_dir.sub_path.len + 1 ..];
95118
// NOTE(jae): 2024-09-22
96119
// We set the current working directory to "glob.Dir" and then make arguments be
97120
// relative to that directory.
98121
//
99122
// This is to avoid the Java error "command line too long" that can occur with d8
100-
d8.addArg(entry.path);
123+
if (optional_argv_start == null) {
124+
optional_argv_start = d8.argv.items.len;
125+
}
101126
d8.addFileInput(LazyPath{
102-
.cwd_relative = try search_dir.root_dir.join(b.allocator, &.{ search_dir.sub_path, entry.path }),
127+
.cwd_relative = absolute_file_path,
103128
});
129+
d8.addArg(relative_to_dir_path);
104130
}
105131
}
132+
133+
// Track arguments added to "d8" so that we can remove them if "make" is re-run in --watch mode
134+
if (optional_argv_start) |argv_start| {
135+
glob.argv_range = .{
136+
.start = argv_start,
137+
.end = d8.argv.items.len,
138+
};
139+
}
106140
}
107141

108142
const D8Glob = @This();

0 commit comments

Comments
 (0)