This commit is contained in:
Elaina 2025-11-21 11:37:59 -06:00 committed by GitHub
commit a3ba70bc58
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
2 changed files with 156 additions and 0 deletions

View file

@ -13,6 +13,7 @@ const Allocator = mem.Allocator;
const Target = std.Target;
const process = std.process;
const EnvMap = std.process.EnvMap;
const Thread = std.Thread;
const File = fs.File;
const Sha256 = std.crypto.hash.sha2.Sha256;
const Build = @This();
@ -97,6 +98,8 @@ pkg_hash: []const u8,
/// A mapping from dependency names to package hashes.
available_deps: AvailableDeps,
compile_commands: ?*CompileCommands = null,
release_mode: ReleaseMode,
build_id: ?std.zig.BuildId = null,
@ -109,6 +112,132 @@ pub const ReleaseMode = enum {
small,
};
pub const CompileCommands = struct {
step: Step,
entries: ArrayList(CompileCommandsEntry),
mutex: Thread.Mutex,
output_file: LazyPath,
generated_file: GeneratedFile,
pub const base_id: Step.Id = .custom;
pub fn create(owner: *Build, output_file: LazyPath) *CompileCommands {
const self = owner.allocator.create(CompileCommands) catch @panic("OOM");
self.* = .{
.step = Step.init(.{
.id = base_id,
.name = owner.fmt("compile_commands.json -> {s}", .{output_file.getDisplayName()}),
.owner = owner,
.makeFn = make,
}),
.entries = ArrayList(CompileCommandsEntry).init(owner.allocator),
.mutex = .{},
.output_file = output_file.dupe(owner),
.generated_file = .{ .step = undefined },
};
self.generated_file.step = &self.step;
// Set this as the global compile commands database
owner.compile_commands = self;
// Add dependencies from the output file's LazyPath
output_file.addStepDependencies(&self.step);
return self;
}
pub fn append(self: *CompileCommands, entry: CompileCommandsEntry) !void {
self.mutex.lock();
defer self.mutex.unlock();
try self.entries.append(entry);
}
pub fn getEntries(self: *CompileCommands) []CompileCommandsEntry {
self.mutex.lock();
defer self.mutex.unlock();
return self.entries.items;
}
/// Returns a LazyPath representing the generated compile_commands.json file
pub fn getOutput(self: *CompileCommands) LazyPath {
return .{ .generated = .{ .file = &self.generated_file } };
}
fn make(build_step: *Step, options: Step.MakeOptions) anyerror!void {
_ = options;
const self: *CompileCommands = @fieldParentPtr("step", build_step);
const b = build_step.owner;
const gpa = b.allocator;
// Create a temporary buffer for the JSON content
var buffer = std.ArrayList(u8).init(gpa);
defer buffer.deinit();
var writer = buffer.writer();
try writer.writeAll("[");
const entries = self.getEntries();
var temp = std.ArrayList(u8).init(gpa);
defer temp.deinit();
for (entries, 0..) |entry, i| {
if (i != 0) try writer.writeAll(",");
try writer.writeAll("\n {\n");
try writer.writeAll(" \"directory\": \"");
try std.json.encodeJsonStringChars(entry.working_directory, .{}, writer);
try writer.writeAll("\",\n");
try writer.writeAll(" \"file\": \"");
const full_path = b.pathJoin(&.{ entry.working_directory, entry.relative_path });
try std.json.encodeJsonStringChars(full_path, .{}, writer);
try writer.writeAll("\",\n");
try writer.writeAll(" \"command\": \"");
temp.clearRetainingCapacity();
var temp_writer = temp.writer();
// Write the compiler command
try temp_writer.writeAll("zig cc");
for (entry.flags) |flag| {
try temp_writer.writeAll(" ");
try temp_writer.writeAll(flag);
}
try temp_writer.writeAll(" ");
try temp_writer.writeAll(entry.relative_path);
try std.json.encodeJsonStringChars(temp.items, .{}, writer);
try writer.writeAll("\"\n }");
}
try writer.writeAll("\n]\n");
// Now write the content to the output file based on its type
const output_path = self.output_file.getPath2(b, build_step);
// Ensure directory exists
if (std.fs.path.dirname(output_path)) |dirname| {
try b.build_root.handle.makePath(dirname);
}
// Write the file
try b.build_root.handle.writeFile(.{ .sub_path = output_path, .data = buffer.items });
// Set the generated file path for LazyPath access
self.generated_file.path = try b.build_root.join(gpa, &.{output_path});
}
};
pub const CompileCommandsEntry = struct {
module: *Module,
working_directory: []const u8,
relative_path: []const u8,
flags: []const []const u8,
};
/// Shared state among all Build instances.
/// Settings that are here rather than in Build are not configurable per-package.
pub const Graph = struct {
@ -405,6 +534,7 @@ fn createChildOnly(
.named_lazy_paths = .init(allocator),
.pkg_hash = pkg_hash,
.available_deps = pkg_deps,
.compile_commands = parent.compile_commands,
.release_mode = parent.release_mode,
};
try child.top_level_steps.put(allocator, child.install_tls.step.name, &child.install_tls);
@ -2237,6 +2367,10 @@ pub fn runBuild(b: *Build, build_zig: anytype) anyerror!void {
}
}
pub fn addCompileCommands(b: *Build, output_file: LazyPath) *CompileCommands {
return CompileCommands.create(b, output_file);
}
/// A file that is generated by a build step.
/// This struct is an interface that is meant to be used with `@fieldParentPtr` to implement the actual path logic.
pub const GeneratedFile = struct {

View file

@ -1327,6 +1327,16 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
try zig_args.append(lang.internalIdentifier());
}
if (b.compile_commands) |compdb| {
const path = c_source_file.file.getPath3(mod.owner, step);
try compdb.append(.{
.module = compile.root_module,
.working_directory = b.pathResolve(&.{path.root_dir.path orelse "."}),
.relative_path = path.sub_path,
.flags = c_source_file.flags,
});
}
try zig_args.append(c_source_file.file.getPath2(mod.owner, step));
if (c_source_file.language != null) {
@ -1354,6 +1364,18 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
}
const root_path = c_source_files.root.getPath2(mod.owner, step);
if (b.compile_commands) |compdb| {
for (c_source_files.files) |file| {
try compdb.append(.{
.module = compile.root_module,
.working_directory = root_path,
.relative_path = file,
.flags = c_source_files.flags,
});
}
}
for (c_source_files.files) |file| {
try zig_args.append(b.pathJoin(&.{ root_path, file }));
}