glfw: remove xcode_frameworks submodule; Sdk type

Signed-off-by: Stephen Gutekanst <stephen@hexops.com>
This commit is contained in:
Stephen Gutekanst 2023-07-06 23:45:18 -07:00
parent 3ef11aec02
commit 98418ac4dc
2 changed files with 325 additions and 229 deletions

View file

@ -54,11 +54,7 @@ Then in your `build.zig` add:
```zig ```zig
... ...
const glfw = @import("libs/mach-glfw/build.zig").Sdk(.{ const glfw = @import("libs/mach-glfw/build.zig");
// TODO(build-system): This cannot be imported with the Zig package manager
// error: TarUnsupportedFileType
.xcode_frameworks = @import("libs/mach-glfw/libs/xcode-frameworks/build.zig"),
});
pub fn build(b: *Build) !void { pub fn build(b: *Build) !void {
... ...

168
build.zig
View file

@ -6,20 +6,12 @@ pub fn build(b: *Build) !void {
const optimize = b.standardOptimizeOption(.{}); const optimize = b.standardOptimizeOption(.{});
const target = b.standardTargetOptions(.{}); const target = b.standardTargetOptions(.{});
const glfw = Sdk(.{
// TODO(build-system): This cannot be imported with the Zig package manager
// error: TarUnsupportedFileType
.xcode_frameworks = @import("libs/xcode-frameworks/build.zig"),
});
const test_step = b.step("test", "Run library tests"); const test_step = b.step("test", "Run library tests");
test_step.dependOn(&(try glfw.testStep(b, optimize, target)).step); test_step.dependOn(&(try testStep(b, optimize, target)).step);
test_step.dependOn(&(try glfw.testStepShared(b, optimize, target)).step); test_step.dependOn(&(try testStepShared(b, optimize, target)).step);
} }
pub fn Sdk(comptime deps: anytype) type { pub fn testStep(b: *Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) !*std.build.RunStep {
return struct {
pub fn testStep(b: *Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) !*std.build.RunStep {
const main_tests = b.addTest(.{ const main_tests = b.addTest(.{
.name = "glfw-tests", .name = "glfw-tests",
.root_source_file = .{ .path = sdkPath("/src/main.zig") }, .root_source_file = .{ .path = sdkPath("/src/main.zig") },
@ -30,9 +22,9 @@ pub fn Sdk(comptime deps: anytype) type {
try link(b, main_tests, .{}); try link(b, main_tests, .{});
b.installArtifact(main_tests); b.installArtifact(main_tests);
return b.addRunArtifact(main_tests); return b.addRunArtifact(main_tests);
} }
pub fn testStepShared(b: *Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) !*std.build.RunStep { pub fn testStepShared(b: *Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget) !*std.build.RunStep {
const main_tests = b.addTest(.{ const main_tests = b.addTest(.{
.name = "glfw-tests-shared", .name = "glfw-tests-shared",
.root_source_file = .{ .path = sdkPath("/src/main.zig") }, .root_source_file = .{ .path = sdkPath("/src/main.zig") },
@ -43,9 +35,9 @@ pub fn Sdk(comptime deps: anytype) type {
try link(b, main_tests, .{ .shared = true }); try link(b, main_tests, .{ .shared = true });
b.installArtifact(main_tests); b.installArtifact(main_tests);
return b.addRunArtifact(main_tests); return b.addRunArtifact(main_tests);
} }
pub const Options = struct { pub const Options = struct {
/// Not supported on macOS. /// Not supported on macOS.
vulkan: bool = true, vulkan: bool = true,
@ -68,19 +60,19 @@ pub fn Sdk(comptime deps: anytype) type {
shared: bool = false, shared: bool = false,
install_libs: bool = false, install_libs: bool = false,
}; };
var _module: ?*std.build.Module = null; var _module: ?*std.build.Module = null;
pub fn module(b: *std.Build) *std.build.Module { pub fn module(b: *std.Build) *std.build.Module {
if (_module) |m| return m; if (_module) |m| return m;
_module = b.createModule(.{ _module = b.createModule(.{
.source_file = .{ .path = sdkPath("/src/main.zig") }, .source_file = .{ .path = sdkPath("/src/main.zig") },
}); });
return _module.?; return _module.?;
} }
pub fn link(b: *Build, step: *std.build.CompileStep, options: Options) !void { pub fn link(b: *Build, step: *std.build.CompileStep, options: Options) !void {
if (options.shared) step.defineCMacro("GLFW_DLL", null); if (options.shared) step.defineCMacro("GLFW_DLL", null);
const lib = try buildLibrary(b, step.optimize, step.target, options); const lib = try buildLibrary(b, step.optimize, step.target, options);
step.linkLibrary(lib); step.linkLibrary(lib);
@ -95,11 +87,11 @@ pub fn Sdk(comptime deps: anytype) type {
// .optimize = step.optimize, // .optimize = step.optimize,
// }).artifact("xcode-frameworks")); // }).artifact("xcode-frameworks"));
// @import("xcode_frameworks").addPaths(step); // @import("xcode_frameworks").addPaths(step);
deps.xcode_frameworks.addPaths(step); xcode_frameworks.addPaths(b, step);
}
} }
}
fn buildLibrary(b: *Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget, options: Options) !*std.build.CompileStep { fn buildLibrary(b: *Build, optimize: std.builtin.OptimizeMode, target: std.zig.CrossTarget, options: Options) !*std.build.CompileStep {
// TODO(build-system): https://github.com/hexops/mach/issues/229#issuecomment-1100958939 // TODO(build-system): https://github.com/hexops/mach/issues/229#issuecomment-1100958939
ensureDependencySubmodule(b.allocator, "upstream") catch return error.CannotEnsureDependency; ensureDependencySubmodule(b.allocator, "upstream") catch return error.CannotEnsureDependency;
@ -119,14 +111,14 @@ pub fn Sdk(comptime deps: anytype) type {
b.installArtifact(lib); b.installArtifact(lib);
return lib; return lib;
} }
fn addGLFWIncludes(step: *std.build.CompileStep) void { fn addGLFWIncludes(step: *std.build.CompileStep) void {
step.addIncludePath(sdkPath("/upstream/glfw/include")); step.addIncludePath(sdkPath("/upstream/glfw/include"));
step.addIncludePath(sdkPath("/src")); step.addIncludePath(sdkPath("/src"));
} }
fn addGLFWSources(b: *Build, lib: *std.build.CompileStep, options: Options) std.mem.Allocator.Error!void { fn addGLFWSources(b: *Build, lib: *std.build.CompileStep, options: Options) std.mem.Allocator.Error!void {
const include_glfw_src = comptime "-I" ++ sdkPath("/upstream/glfw/src"); const include_glfw_src = comptime "-I" ++ sdkPath("/upstream/glfw/src");
switch (lib.target_info.target.os.tag) { switch (lib.target_info.target.os.tag) {
.windows => lib.addCSourceFiles(&.{ .windows => lib.addCSourceFiles(&.{
@ -164,9 +156,9 @@ pub fn Sdk(comptime deps: anytype) type {
lib.addCSourceFiles(sources.items, flags.items); lib.addCSourceFiles(sources.items, flags.items);
}, },
} }
} }
fn linkGLFWDependencies(b: *Build, step: *std.build.CompileStep, options: Options) void { fn linkGLFWDependencies(b: *Build, step: *std.build.CompileStep, options: Options) void {
if (step.target_info.target.os.tag == .windows) { if (step.target_info.target.os.tag == .windows) {
step.linkLibrary(b.dependency("direct3d_headers", .{ step.linkLibrary(b.dependency("direct3d_headers", .{
.target = step.target, .target = step.target,
@ -204,7 +196,7 @@ pub fn Sdk(comptime deps: anytype) type {
// .optimize = step.optimize, // .optimize = step.optimize,
// }).artifact("xcode-frameworks")); // }).artifact("xcode-frameworks"));
// @import("xcode_frameworks").addPaths(step); // @import("xcode_frameworks").addPaths(step);
deps.xcode_frameworks.addPaths(step); xcode_frameworks.addPaths(b, step);
} }
switch (step.target_info.target.os.tag) { switch (step.target_info.target.os.tag) {
.windows => { .windows => {
@ -240,9 +232,9 @@ pub fn Sdk(comptime deps: anytype) type {
} }
}, },
} }
} }
fn ensureDependencySubmodule(allocator: std.mem.Allocator, path: []const u8) !void { fn ensureDependencySubmodule(allocator: std.mem.Allocator, path: []const u8) !void {
if (std.process.getEnvVarOwned(allocator, "NO_ENSURE_SUBMODULES")) |no_ensure_submodules| { if (std.process.getEnvVarOwned(allocator, "NO_ENSURE_SUBMODULES")) |no_ensure_submodules| {
defer allocator.free(no_ensure_submodules); defer allocator.free(no_ensure_submodules);
if (std.mem.eql(u8, no_ensure_submodules, "true")) return; if (std.mem.eql(u8, no_ensure_submodules, "true")) return;
@ -253,14 +245,122 @@ pub fn Sdk(comptime deps: anytype) type {
child.stdout = std.io.getStdOut(); child.stdout = std.io.getStdOut();
_ = try child.spawnAndWait(); _ = try child.spawnAndWait();
}
fn sdkPath(comptime suffix: []const u8) []const u8 {
if (suffix[0] != '/') @compileError("suffix must be an absolute path");
return comptime blk: {
const root_dir = std.fs.path.dirname(@src().file) orelse ".";
break :blk root_dir ++ suffix;
};
}
// TODO(build-system): This is a workaround that we copy anywhere xcode_frameworks needs to be used.
// With the Zig package manager, it should be possible to remove this entirely and instead just
// write:
//
// ```
// step.linkLibrary(b.dependency("xcode_frameworks", .{
// .target = step.target,
// .optimize = step.optimize,
// }).artifact("xcode-frameworks"));
// @import("xcode_frameworks").addPaths(step);
// ```
//
// However, today this package cannot be imported with the Zig package manager due to `error: TarUnsupportedFileType`
// which would be fixed by https://github.com/ziglang/zig/pull/15382 - so instead for now you must
// copy+paste this struct into your `build.zig` and write:
//
// ```
// try xcode_frameworks.addPaths(b, step);
// ```
const xcode_frameworks = struct {
pub fn addPaths(b: *std.Build, step: *std.build.CompileStep) void {
// branch: mach
ensureGitRepoCloned(b.allocator, "https://github.com/hexops/xcode-frameworks", "723aa55e9752c8c6c25d3413722b5fe13d72ac4f", "zig-cache/xcode_frameworks") catch |err| @panic(@errorName(err));
step.addFrameworkPath("zig-cache/xcode_frameworks/Frameworks");
step.addSystemIncludePath("zig-cache/xcode_frameworks/include");
step.addLibraryPath("zig-cache/xcode_frameworks/lib");
} }
fn sdkPath(comptime suffix: []const u8) []const u8 { fn xcodeSdkPath(comptime suffix: []const u8) []const u8 {
if (suffix[0] != '/') @compileError("suffix must be an absolute path"); if (suffix[0] != '/') @compileError("suffix must be an absolute path");
return comptime blk: { return comptime blk: {
const root_dir = std.fs.path.dirname(@src().file) orelse "."; const root_dir = std.fs.path.dirname(@src().file) orelse ".";
break :blk root_dir ++ suffix; break :blk root_dir ++ suffix;
}; };
} }
fn ensureGitRepoCloned(allocator: std.mem.Allocator, clone_url: []const u8, revision: []const u8, rel_dir: []const u8) !void {
if (isEnvVarTruthy(allocator, "NO_ENSURE_SUBMODULES") or isEnvVarTruthy(allocator, "NO_ENSURE_GIT")) {
return;
}
ensureGit(allocator);
if (std.fs.cwd().realpathAlloc(allocator, rel_dir)) |dir| {
const current_revision = try getCurrentGitRevision(allocator, dir);
if (!std.mem.eql(u8, current_revision, revision)) {
// Reset to the desired revision
exec(allocator, &[_][]const u8{ "git", "fetch" }, dir) catch |err| std.debug.print("warning: failed to 'git fetch' in {s}: {s}\n", .{ dir, @errorName(err) });
try exec(allocator, &[_][]const u8{ "git", "checkout", "--quiet", "--force", revision }, dir);
try exec(allocator, &[_][]const u8{ "git", "submodule", "update", "--init", "--recursive" }, dir);
}
return;
} else |err| return switch (err) {
error.FileNotFound => {
std.log.info("cloning required dependency..\ngit clone {s} {s}..\n", .{ clone_url, rel_dir });
try exec(allocator, &[_][]const u8{ "git", "clone", "-c", "core.longpaths=true", clone_url, rel_dir }, xcodeSdkPath("/"));
try exec(allocator, &[_][]const u8{ "git", "checkout", "--quiet", "--force", revision }, rel_dir);
try exec(allocator, &[_][]const u8{ "git", "submodule", "update", "--init", "--recursive" }, rel_dir);
return;
},
else => err,
}; };
} }
fn exec(allocator: std.mem.Allocator, argv: []const []const u8, cwd: []const u8) !void {
var child = std.ChildProcess.init(argv, allocator);
child.cwd = cwd;
_ = try child.spawnAndWait();
}
fn getCurrentGitRevision(allocator: std.mem.Allocator, cwd: []const u8) ![]const u8 {
const result = try std.ChildProcess.exec(.{ .allocator = allocator, .argv = &.{ "git", "rev-parse", "HEAD" }, .cwd = cwd });
allocator.free(result.stderr);
if (result.stdout.len > 0) return result.stdout[0 .. result.stdout.len - 1]; // trim newline
return result.stdout;
}
fn ensureGit(allocator: std.mem.Allocator) void {
const argv = &[_][]const u8{ "git", "--version" };
const result = std.ChildProcess.exec(.{
.allocator = allocator,
.argv = argv,
.cwd = ".",
}) catch { // e.g. FileNotFound
std.log.err("mach: error: 'git --version' failed. Is git not installed?", .{});
std.process.exit(1);
};
defer {
allocator.free(result.stderr);
allocator.free(result.stdout);
}
if (result.term.Exited != 0) {
std.log.err("mach: error: 'git --version' failed. Is git not installed?", .{});
std.process.exit(1);
}
}
fn isEnvVarTruthy(allocator: std.mem.Allocator, name: []const u8) bool {
if (std.process.getEnvVarOwned(allocator, name)) |truthy| {
defer allocator.free(truthy);
if (std.mem.eql(u8, truthy, "true")) return true;
return false;
} else |_| {
return false;
}
}
};