Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JSON compilation database support #22012

Open
wants to merge 1 commit into
base: master
Choose a base branch
from
Open
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
12 changes: 12 additions & 0 deletions lib/compiler/build_runner.zig
Original file line number Diff line number Diff line change
Expand Up @@ -284,6 +284,10 @@ pub fn main() !void {
graph.allow_so_scripts = true;
} else if (mem.eql(u8, arg, "-fno-allow-so-scripts")) {
graph.allow_so_scripts = false;
} else if (mem.eql(u8, arg, "-fcompdb")) {
builder.enable_compdb = true;
} else if (mem.eql(u8, arg, "-fno-compdb")) {
builder.enable_compdb = false;
} else if (mem.eql(u8, arg, "-freference-trace")) {
builder.reference_trace = 256;
} else if (mem.startsWith(u8, arg, "-freference-trace=")) {
Expand Down Expand Up @@ -317,6 +321,9 @@ pub fn main() !void {
try targets.append(arg);
}
}
if (builder.enable_compdb) {
try builder.initCompdb();
}

const stderr = std.io.getStdErr();
const ttyconf = get_tty_conf(color, stderr);
Expand Down Expand Up @@ -617,6 +624,10 @@ fn runStepNames(
}
assert(run.memory_blocked_steps.items.len == 0);

if (b.enable_compdb) {
try b.generateCompdb();
}

var test_skip_count: usize = 0;
var test_fail_count: usize = 0;
var test_pass_count: usize = 0;
Expand Down Expand Up @@ -1257,6 +1268,7 @@ fn usage(b: *std.Build, out_stream: anytype) !void {
\\ --release[=mode] Request release mode, optionally specifying a
\\ preferred optimization mode: fast, safe, small
\\
\\ -fcompdb, -fno-compdb Generate a compile_commands.json file (default: no)
\\ -fdarling, -fno-darling Integration with system-installed Darling to
\\ execute macOS programs on Linux hosts
\\ (default: no)
Expand Down
132 changes: 132 additions & 0 deletions lib/std/Build.zig
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ debug_pkg_config: bool = false,
/// Set to 0 to disable stack collection.
debug_stack_frames_count: u8 = 8,

/// Experimental. Generate a compile_commands.json file.
enable_compdb: bool = false,
/// Experimental. Use system Darling installation to run cross compiled macOS build artifacts.
enable_darling: bool = false,
/// Use system QEMU installation to run cross compiled foreign architecture build artifacts.
Expand Down Expand Up @@ -92,6 +94,8 @@ pkg_hash: []const u8,
/// A mapping from dependency names to package hashes.
available_deps: AvailableDeps,

compile_commands_database: ?*CompileCommandsDatabase = null,

release_mode: ReleaseMode,

pub const ReleaseMode = enum {
Expand All @@ -102,6 +106,16 @@ pub const ReleaseMode = enum {
small,
};

pub const CompileCommandsDatabase = struct {
entries: ArrayList(CompileCommandsEntry),
};
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 {
Expand Down Expand Up @@ -383,6 +397,7 @@ fn createChildOnly(
.debug_log_scopes = parent.debug_log_scopes,
.debug_compile_errors = parent.debug_compile_errors,
.debug_pkg_config = parent.debug_pkg_config,
.enable_compdb = parent.enable_compdb,
.enable_darling = parent.enable_darling,
.enable_qemu = parent.enable_qemu,
.enable_rosetta = parent.enable_rosetta,
Expand All @@ -395,6 +410,7 @@ fn createChildOnly(
.named_lazy_paths = .init(allocator),
.pkg_hash = pkg_hash,
.available_deps = pkg_deps,
.compile_commands_database = parent.compile_commands_database,
.release_mode = parent.release_mode,
};
try child.top_level_steps.put(allocator, child.install_tls.step.name, &child.install_tls);
Expand Down Expand Up @@ -641,6 +657,122 @@ fn determineAndApplyInstallPrefix(b: *Build) error{OutOfMemory}!void {
b.resolveInstallPrefix(install_prefix, .{});
}

pub fn initCompdb(self: *Build) !void {
if (!self.enable_compdb) return;

const database = try self.allocator.create(CompileCommandsDatabase);
database.* = .{
.entries = ArrayList(CompileCommandsEntry).init(self.allocator),
};
self.compile_commands_database = database;
}

pub fn generateCompdb(self: *Build) !void {
if (!self.enable_compdb) return;

var file = try self.build_root.handle.createFile("compile_commands.json", .{});
defer file.close();

var buffered = std.io.bufferedWriter(file.writer());
var writer = buffered.writer();

try writer.writeAll("[");

const entries = self.compile_commands_database.?.entries.items;

var temp = std.ArrayList(u8).init(self.allocator);
defer temp.deinit();

for (entries, 0..) |entry, i| {
try writer.writeAll("{");

try writer.writeAll("\"directory\":");
try std.json.encodeJsonString(entry.working_directory, .{}, writer);
try writer.writeAll(",");

try writer.writeAll("\"arguments\":[");
try writer.writeAll("\"zig\",");
try writer.writeAll("\"cc\",");

for (entry.flags) |flag| {
try std.json.encodeJsonString(flag, .{}, writer);
try writer.writeAll(",");
}
for (entry.module.c_macros.items) |macro_flag| {
try std.json.encodeJsonString(macro_flag, .{}, writer);
try writer.writeAll(",");
}
for (entry.module.include_dirs.items) |include_dir| {
const args: [2][]const u8 = blk: switch (include_dir) {
.path => |include_path| {
break :blk .{ "-I", include_path.getPath2(self, null) };
},
.path_system => |include_path| {
break :blk .{ "-isystem", include_path.getPath2(self, null) };
},
.path_after => |include_path| {
break :blk .{ "-idirafter", include_path.getPath2(self, null) };
},
.framework_path => |include_path| {
break :blk .{ "-F", include_path.getPath2(self, null) };
},
.framework_path_system => |include_path| {
break :blk .{ "-iframework", include_path.getPath2(self, null) };
},
.other_step => |other| {
if (other.generated_h) |header| {
break :blk .{ "-isystem", std.fs.path.dirname(header.getPath()).? };
}
if (other.installed_headers_include_tree) |include_tree| {
break :blk .{ "-I", include_tree.generated_directory.getPath() };
}
continue;
},
.config_header_step => |config_header| {
const full_file_path = config_header.output_file.getPath();
const header_dir_path = full_file_path[0 .. full_file_path.len - config_header.include_path.len];
break :blk .{ "-I", header_dir_path };
},
};
try std.json.encodeJsonString(args[0], .{}, writer);
try writer.writeAll(",");
try std.json.encodeJsonString(args[1], .{}, writer);
try writer.writeAll(",");
}

if (entry.module.resolved_target) |*target| {
if (!target.query.isNative()) {
const triple = try target.query.zigTriple(self.allocator);
const cpu = try target.query.serializeCpuAlloc(self.allocator);

try temp.appendSlice("--target=");
try temp.appendSlice(triple);
try std.json.encodeJsonString(temp.items, .{}, writer);
temp.clearRetainingCapacity();

try temp.appendSlice("-mcpu=");
try temp.appendSlice(cpu);
try std.json.encodeJsonString(temp.items, .{}, writer);
}
}

try std.json.encodeJsonString(entry.relative_path, .{}, writer);
try writer.writeAll("],");

try writer.writeAll("\"file\":");
try std.json.encodeJsonString(entry.relative_path, .{}, writer);

try writer.writeAll("}");
if (i != entries.len - 1) {
_ = try writer.write(",");
}
}

try writer.writeAll("]");

try buffered.flush();
}

/// This function is intended to be called by lib/build_runner.zig, not a build.zig file.
pub fn resolveInstallPrefix(b: *Build, install_prefix: ?[]const u8, dir_list: DirList) void {
if (b.dest_dir) |dest_dir| {
Expand Down
22 changes: 22 additions & 0 deletions lib/std/Build/Step/Compile.zig
Original file line number Diff line number Diff line change
Expand Up @@ -1273,6 +1273,16 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
try zig_args.append(lang.internalIdentifier());
}

const path = c_source_file.file.getPath3(mod.owner, step);
if (b.compile_commands_database) |database| {
try database.entries.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) {
Expand Down Expand Up @@ -1300,6 +1310,18 @@ fn getZigArgs(compile: *Compile, fuzz: bool) ![][]const u8 {
}

const root_path = c_source_files.root.getPath2(mod.owner, step);

if (b.compile_commands_database) |database| {
for (c_source_files.files) |file| {
try database.entries.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 }));
}
Expand Down
Loading