diff --git a/src/DocumentStore.zig b/src/DocumentStore.zig index 6d685ee..6dce261 100644 --- a/src/DocumentStore.zig +++ b/src/DocumentStore.zig @@ -32,7 +32,7 @@ const BuildFile = struct { pub const BuildFileConfig = struct { packages: []Pkg, - include_dirs: []IncludeDir, + include_dirs: []const []const u8, pub fn deinit(self: BuildFileConfig, allocator: std.mem.Allocator) void { for (self.packages) |pkg| { @@ -42,7 +42,7 @@ pub const BuildFileConfig = struct { allocator.free(self.packages); for (self.include_dirs) |dir| { - allocator.free(dir.path); + allocator.free(dir); } allocator.free(self.include_dirs); } @@ -51,8 +51,6 @@ pub const BuildFileConfig = struct { name: []const u8, uri: []const u8, }; - - pub const IncludeDir = BuildConfig.IncludeDir; }; pub const Handle = struct { @@ -223,10 +221,10 @@ fn loadBuildConfiguration(context: LoadBuildConfigContext) !void { packages.deinit(allocator); } - var include_dirs = try std.ArrayListUnmanaged(BuildFileConfig.IncludeDir).initCapacity(allocator, config.include_dirs.len); + var include_dirs = try std.ArrayListUnmanaged([]const u8).initCapacity(allocator, config.include_dirs.len); errdefer { for (include_dirs.items) |dir| { - allocator.free(dir.path); + allocator.free(dir); } include_dirs.deinit(allocator); } @@ -245,10 +243,10 @@ fn loadBuildConfiguration(context: LoadBuildConfigContext) !void { } for (config.include_dirs) |dir| { - const path = try allocator.dupe(u8, dir.path); + const path = try allocator.dupe(u8, dir); errdefer allocator.free(path); - include_dirs.appendAssumeCapacity(.{ .path = path, .system = dir.system }); + include_dirs.appendAssumeCapacity(path); } build_file.config = .{ @@ -655,18 +653,7 @@ fn collectCIncludes(self: *DocumentStore, handle: *Handle) ![]CImportHandle { } fn translate(self: *DocumentStore, handle: *Handle, source: []const u8) error{OutOfMemory}!?translate_c.Result { - const dirs: []BuildConfig.IncludeDir = if (handle.associated_build_file) |build_file| build_file.config.include_dirs else &.{}; - const include_dirs = blk: { - var result = try self.allocator.alloc([]const u8, dirs.len); - errdefer self.allocator.free(result); - - for (dirs) |dir, i| { - result[i] = dir.path; - } - - break :blk result; - }; - defer self.allocator.free(include_dirs); + const include_dirs: []const []const u8 = if (handle.associated_build_file) |build_file| build_file.config.include_dirs else &.{}; const maybe_result = try translate_c.translate( self.allocator, diff --git a/src/special/build_runner.zig b/src/special/build_runner.zig index 98ce81f..fe17799 100644 --- a/src/special/build_runner.zig +++ b/src/special/build_runner.zig @@ -8,17 +8,12 @@ const LibExeObjStep = std.build.LibExeObjStep; pub const BuildConfig = struct { packages: []Pkg, - include_dirs: []IncludeDir, + include_dirs: []const []const u8, pub const Pkg = struct { name: []const u8, path: []const u8, }; - - pub const IncludeDir = struct { - path: []const u8, - system: bool, - }; }; ///! This is a modified build runner to extract information out of build.zig @@ -68,7 +63,7 @@ pub fn main() !void { var packages = std.ArrayListUnmanaged(BuildConfig.Pkg){}; defer packages.deinit(allocator); - var include_dirs = std.ArrayListUnmanaged(BuildConfig.IncludeDir){}; + var include_dirs: std.StringArrayHashMapUnmanaged(void) = .{}; defer include_dirs.deinit(allocator); // TODO: We currently add packages from every LibExeObj step that the install step depends on. @@ -83,7 +78,7 @@ pub fn main() !void { try std.json.stringify( BuildConfig{ .packages = packages.items, - .include_dirs = include_dirs.items, + .include_dirs = include_dirs.keys(), }, .{ .whitespace = .{} }, std.io.getStdOut().writer(), @@ -93,16 +88,18 @@ pub fn main() !void { fn processStep( allocator: std.mem.Allocator, packages: *std.ArrayListUnmanaged(BuildConfig.Pkg), - include_dirs: *std.ArrayListUnmanaged(BuildConfig.IncludeDir), + include_dirs: *std.StringArrayHashMapUnmanaged(void), step: *std.build.Step, ) anyerror!void { if (step.cast(InstallArtifactStep)) |install_exe| { try processIncludeDirs(allocator, include_dirs, install_exe.artifact.include_dirs.items); + try processPkgConfig(allocator, include_dirs, install_exe.artifact); for (install_exe.artifact.packages.items) |pkg| { try processPackage(allocator, packages, pkg); } } else if (step.cast(LibExeObjStep)) |exe| { try processIncludeDirs(allocator, include_dirs, exe.include_dirs.items); + try processPkgConfig(allocator, include_dirs, exe); for (exe.packages.items) |pkg| { try processPackage(allocator, packages, pkg); } @@ -141,26 +138,69 @@ fn processPackage( fn processIncludeDirs( allocator: std.mem.Allocator, - include_dirs: *std.ArrayListUnmanaged(BuildConfig.IncludeDir), + include_dirs: *std.StringArrayHashMapUnmanaged(void), dirs: []std.build.LibExeObjStep.IncludeDir, ) !void { try include_dirs.ensureUnusedCapacity(allocator, dirs.len); - outer: for (dirs) |dir| { - const candidate: BuildConfig.IncludeDir = switch (dir) { - .raw_path => |path| .{ .path = path, .system = false }, - .raw_path_system => |path| .{ .path = path, .system = true }, + for (dirs) |dir| { + const candidate: []const u8 = switch (dir) { + .raw_path => |path| path, + .raw_path_system => |path| path, else => continue, }; - for (include_dirs.items) |include_dir| { - if (std.mem.eql(u8, candidate.path, include_dir.path)) continue :outer; - } - - include_dirs.appendAssumeCapacity(candidate); + include_dirs.putAssumeCapacity(candidate, {}); } } +fn processPkgConfig( + allocator: std.mem.Allocator, + include_dirs: *std.StringArrayHashMapUnmanaged(void), + exe: *std.build.LibExeObjStep, +) !void { + for (exe.link_objects.items) |link_object| { + if (link_object != .system_lib) continue; + const system_lib = link_object.system_lib; + + if (system_lib.use_pkg_config == .no) continue; + + getPkgConfigIncludes(allocator, include_dirs, exe, system_lib.name) catch |err| switch (err) { + error.PkgConfigInvalidOutput, + error.PkgConfigCrashed, + error.PkgConfigFailed, + error.PkgConfigNotInstalled, + error.PackageNotFound, + => switch (system_lib.use_pkg_config) { + .yes => { + // pkg-config failed, so zig will not add any include paths + }, + .force => { + log.warn("pkg-config failed for library {s}", .{system_lib.name}); + }, + .no => unreachable, + }, + else => |e| return e, + }; + } +} + +fn getPkgConfigIncludes( + allocator: std.mem.Allocator, + include_dirs: *std.StringArrayHashMapUnmanaged(void), + exe: *std.build.LibExeObjStep, + name: []const u8, +) !void { + if (exe.runPkgConfig(name)) |args| { + for (args) |arg| { + if (std.mem.startsWith(u8, arg, "-I")) { + const candidate = arg[2..]; + try include_dirs.put(allocator, candidate, {}); + } + } + } else |err| return err; +} + fn runBuild(builder: *Builder) anyerror!void { switch (@typeInfo(@typeInfo(@TypeOf(root.build)).Fn.return_type.?)) { .Void => root.build(builder),