Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
111 changes: 106 additions & 5 deletions lib/std/Build/Step/Compile.zig
Original file line number Diff line number Diff line change
Expand Up @@ -382,7 +382,7 @@ pub fn create(owner: *std.Build, options: Options) *Compile {
pub fn installHeader(cs: *Compile, src_path: []const u8, dest_rel_path: []const u8) void {
const b = cs.step.owner;
const install_file = b.addInstallHeaderFile(src_path, dest_rel_path);
b.getInstallStep().dependOn(&install_file.step);
cs.step.dependOn(&install_file.step);
cs.installed_headers.append(&install_file.step) catch @panic("OOM");
}

Expand All @@ -404,7 +404,7 @@ pub fn installConfigHeader(
dest_rel_path,
);
install_file.step.dependOn(&config_header.step);
b.getInstallStep().dependOn(&install_file.step);
cs.step.dependOn(&install_file.step);
cs.installed_headers.append(&install_file.step) catch @panic("OOM");
}

Expand All @@ -426,14 +426,13 @@ pub fn installHeadersDirectoryOptions(
) void {
const b = cs.step.owner;
const install_dir = b.addInstallDirectory(options);
b.getInstallStep().dependOn(&install_dir.step);
cs.step.dependOn(&install_dir.step);
cs.installed_headers.append(&install_dir.step) catch @panic("OOM");
}

pub fn installLibraryHeaders(cs: *Compile, l: *Compile) void {
assert(l.kind == .lib);
const b = cs.step.owner;
const install_step = b.getInstallStep();
// Copy each element from installed_headers, modifying the builder
// to be the new parent's builder.
for (l.installed_headers.items) |step| {
Expand All @@ -448,7 +447,7 @@ pub fn installLibraryHeaders(cs: *Compile, l: *Compile) void {
else => unreachable,
};
cs.installed_headers.append(step_copy) catch @panic("OOM");
install_step.dependOn(step_copy);
cs.step.dependOn(step_copy);
}
cs.installed_headers.appendSlice(l.installed_headers.items) catch @panic("OOM");
}
Expand Down Expand Up @@ -1887,3 +1886,105 @@ fn moduleNeedsCliArg(mod: *const Module) bool {
else => continue,
} else false;
}

// https://github.com/ziglang/zig/issues/17204
test "installed header dependencies" {
var arena = std.heap.ArenaAllocator.init(std.testing.allocator);
defer arena.deinit();

var graph: std.Build.Graph = .{
.arena = arena.allocator(),
.cache = .{
.gpa = arena.allocator(),
.manifest_dir = std.fs.cwd(),
},
.zig_exe = "test",
.env_map = std.process.EnvMap.init(arena.allocator()),
.global_cache_root = .{ .path = "test", .handle = std.fs.cwd() },
};

var builder = try std.Build.create(
&graph,
.{ .path = "test", .handle = std.fs.cwd() },
.{ .path = "test", .handle = std.fs.cwd() },
&.{},
);

const config_header = builder.addConfigHeader(.{}, .{});

const foo = builder.addStaticLibrary(.{
.name = "foo",
.root_source_file = .{ .path = "foo.zig" },
.optimize = .Debug,
.target = builder.resolveTargetQuery(.{
.cpu_arch = .aarch64,
.os_tag = .linux,
.abi = .gnu,
}),
});

// Install some headers as part of the foo library and verify that
// it declares a dependency on each header installation step.
{
foo.installHeader("foo.h", "foo.h");
foo.installConfigHeader(config_header, .{
.dest_rel_path = "config.h",
});
foo.installHeadersDirectoryOptions(.{
.source_dir = .{ .path = "test/include" },
.install_dir = .header,
.install_subdir = "test",
});

const foo_step = foo.step;
for (foo.installed_headers.items) |header_step| {
const found = for (foo_step.dependencies.items) |dep| {
if (dep == header_step) {
break true;
}
} else false;

errdefer std.debug.print("direct: missing dependency: {s}\n", .{header_step.name});
try std.testing.expect(found);
}
}

// Create another library, install foo's headers into it, and verify that
// it declares a dependency on each header installation step.
const bar = builder.addStaticLibrary(.{
.name = "bar",
.root_source_file = .{ .path = "bar.zig" },
.optimize = .Debug,
.target = builder.resolveTargetQuery(.{
.cpu_arch = .aarch64,
.os_tag = .linux,
.abi = .gnu,
}),
});

{
bar.installLibraryHeaders(foo);

const foo_step = foo.step;
const bar_step = bar.step;

// installLibraryHeaders adds new steps for the headers,
// but also a copy of those from the original library.
//
// So each header could be depended on by either foo or bar.
var all_dependencies = std.ArrayList(*std.Build.Step).init(arena.allocator());
try all_dependencies.appendSlice(foo_step.dependencies.items);
try all_dependencies.appendSlice(bar_step.dependencies.items);

for (bar.installed_headers.items) |header_step| {
const found = for (all_dependencies.items) |dep| {
if (dep == header_step) {
break true;
}
} else false;

errdefer std.debug.print("transitive: missing dependency: {s}\n", .{header_step.name});
try std.testing.expect(found);
}
}
}
35 changes: 35 additions & 0 deletions test/standalone/install_header_dep/build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
//! Verify that Step.Compile.installHeader correctly declare a dependency on
//! the Step itself.
//!
//! Test for https://github.com/ziglang/zig/issues/17204.

const std = @import("std");

pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});

const foo = b.addStaticLibrary(.{
.name = "foo",
.root_source_file = .{ .path = "foo.zig" },
.optimize = .Debug,
.target = target,
});
foo.installHeader("foo.h", "foo.h");

const exists_in = b.addExecutable(.{
.name = "exists_in",
.root_source_file = .{ .path = "exists_in.zig" },
.optimize = .Debug,
.target = target,
});

const run = b.addRunArtifact(exists_in);
run.addDirectoryArg(.{ .path = b.getInstallPath(.header, ".") });
run.addArgs(&.{"foo.h"});
run.expectExitCode(0);
run.step.dependOn(&foo.step);

const test_step = b.step("test", "Test it");
b.default_step = test_step;
test_step.dependOn(&run.step);
}
40 changes: 40 additions & 0 deletions test/standalone/install_header_dep/exists_in.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
//! Verifies that a file exists in a directory.
//!
//! Usage:
//!
//! ```
//! exists_in <dir> <path>
//! ```
//!
//! Where `<dir>/<path>` is the full path to the file.

const std = @import("std");

pub fn main() !void {
var arena_state = std.heap.ArenaAllocator.init(std.heap.page_allocator);
const arena = arena_state.allocator();
defer arena_state.deinit();

try run(arena);
}

fn run(allocator: std.mem.Allocator) !void {
var args = try std.process.argsWithAllocator(allocator);
defer args.deinit();
_ = args.next() orelse unreachable; // skip binary name

const dir_path = args.next() orelse {
std.log.err("missing <dir> argument", .{});
return error.BadUsage;
};

const relpath = args.next() orelse {
std.log.err("missing <path> argument", .{});
return error.BadUsage;
};

var dir = try std.fs.cwd().openDir(dir_path, .{});
defer dir.close();

_ = try dir.statFile(relpath);
}
1 change: 1 addition & 0 deletions test/standalone/install_header_dep/foo.h
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Header file
1 change: 1 addition & 0 deletions test/standalone/install_header_dep/foo.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
// Source file