diff --git a/src/DocumentStore.zig b/src/DocumentStore.zig index 18d678c5b..5fc212aec 100644 --- a/src/DocumentStore.zig +++ b/src/DocumentStore.zig @@ -875,7 +875,7 @@ pub fn loadBuildConfiguration(self: *DocumentStore, build_file_uri: Uri) !std.js .allocator = self.allocator, .argv = args, .cwd = std.fs.path.dirname(build_file_path).?, - .max_output_bytes = 1024 * 100, + .max_output_bytes = 1024 * 1024, }); }; defer self.allocator.free(zig_run_result.stdout); @@ -1413,6 +1413,16 @@ pub fn uriFromImportStr(self: *DocumentStore, allocator: std.mem.Allocator, hand return try URI.fromPath(allocator, pkg.path); } } + } else if (isBuildFile(handle.uri)) blk: { + const build_file = self.getBuildFile(handle.uri).?; + const build_config = build_file.tryLockConfig() orelse break :blk; + defer build_file.unlockConfig(); + + for (build_config.deps_build_roots) |dep_build_root| { + if (std.mem.eql(u8, import_str, dep_build_root.name)) { + return try URI.fromPath(allocator, dep_build_root.path); + } + } } return null; } else { diff --git a/src/build_runner/0.11.0.zig b/src/build_runner/0.11.0.zig index 9cbb0c2ce..86d6323df 100644 --- a/src/build_runner/0.11.0.zig +++ b/src/build_runner/0.11.0.zig @@ -1,5 +1,6 @@ const root = @import("@build"); const std = @import("std"); +const builtin = @import("builtin"); const log = std.log; const process = std.process; @@ -12,13 +13,15 @@ const Build = std.Build; ///! This is a modified build runner to extract information out of build.zig ///! Modified version of lib/build_runner.zig pub fn main() !void { + // Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived, + // one shot program. We don't need to waste time freeing memory and finding places to squish + // bytes into. So we free everything all at once at the very end. var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const allocator = arena.allocator(); var args = try process.argsAlloc(allocator); - defer process.argsFree(allocator, args); // skip my own exe name var arg_idx: usize = 1; @@ -64,6 +67,7 @@ pub fn main() !void { cache.addPrefix(build_root_directory); cache.addPrefix(local_cache_directory); cache.addPrefix(global_cache_directory); + cache.hash.addBytes(builtin.zig_version_string); const host = try std.zig.system.NativeTargetInfo.detect(.{}); @@ -106,12 +110,6 @@ pub fn main() !void { builder.resolveInstallPrefix(null, Build.DirList{}); try runBuild(builder); - var packages = Packages{ .allocator = allocator }; - defer packages.deinit(); - - var include_dirs: std.StringArrayHashMapUnmanaged(void) = .{}; - defer include_dirs.deinit(allocator); - // This scans the graph of Steps to find all `OptionsStep`s then reifies them // Doing this before the loop to find packages ensures their `GeneratedFile`s have been given paths for (builder.top_level_steps.values()) |tls| { @@ -120,6 +118,9 @@ pub fn main() !void { } } + var packages = Packages{ .allocator = allocator }; + var include_dirs: std.StringArrayHashMapUnmanaged(void) = .{}; + // TODO: We currently add packages from every LibExeObj step that the install step depends on. // Should we error out or keep one step or something similar? // We also flatten them, we should probably keep the nested structure. @@ -129,12 +130,19 @@ pub fn main() !void { } } - const package_list = try packages.toPackageList(); - defer allocator.free(package_list); + var deps_build_roots: std.ArrayListUnmanaged(BuildConfig.DepsBuildRoots) = .{}; + inline for (@typeInfo(dependencies.build_root).Struct.decls) |decl| { + try deps_build_roots.append(allocator, .{ + .name = decl.name, + // XXX Check if it exists? + .path = try std.fs.path.resolve(allocator, &[_][]const u8{ @field(dependencies.build_root, decl.name), "./build.zig" }), + }); + } try std.json.stringify( BuildConfig{ - .packages = package_list, + .deps_build_roots = try deps_build_roots.toOwnedSlice(allocator), + .packages = try packages.toPackageList(), .include_dirs = include_dirs.keys(), }, .{}, @@ -208,14 +216,14 @@ fn processStep( ) anyerror!void { if (step.cast(Build.Step.InstallArtifact)) |install_exe| { if (install_exe.artifact.root_src) |src| { - _ = try packages.addPackage("root", src.getPath(builder)); + if (copied_from_zig.getPath(src, builder)) |path| _ = try packages.addPackage("root", path); } try processIncludeDirs(builder, include_dirs, install_exe.artifact.include_dirs.items); try processPkgConfig(builder.allocator, include_dirs, install_exe.artifact); try processModules(builder, packages, install_exe.artifact.modules); } else if (step.cast(Build.Step.Compile)) |exe| { if (exe.root_src) |src| { - _ = try packages.addPackage("root", src.getPath(builder)); + if (copied_from_zig.getPath(src, builder)) |path| _ = try packages.addPackage("root", path); } try processIncludeDirs(builder, include_dirs, exe.include_dirs.items); try processPkgConfig(builder.allocator, include_dirs, exe); @@ -233,7 +241,7 @@ fn processModules( modules: std.StringArrayHashMap(*Build.Module), ) !void { for (modules.keys(), modules.values()) |name, mod| { - const path = mod.builder.pathFromRoot(mod.source_file.getPath(mod.builder)); + const path = copied_from_zig.getPath(mod.source_file, mod.builder) orelse continue; const already_added = try packages.addPackage(name, path); // if the package has already been added short circuit here or recursive modules will ruin us @@ -252,8 +260,7 @@ fn processIncludeDirs( for (dirs) |dir| { const candidate: []const u8 = switch (dir) { - .path => |path| path.getPath(builder), - .path_system => |path| path.getPath(builder), + .path, .path_system => |path| copied_from_zig.getPath(path, builder) orelse continue, else => continue, }; @@ -310,6 +317,21 @@ fn getPkgConfigIncludes( // TODO: Having a copy of this is not very nice const copied_from_zig = struct { + /// Copied from `std.Build.LazyPath.getPath2` and massaged a bit. + fn getPath(path: std.Build.LazyPath, builder: *Build) ?[]const u8 { + switch (path) { + .path => |p| return builder.pathFromRoot(p), + .cwd_relative => |p| return pathFromCwd(builder, p), + .generated => |gen| return builder.pathFromRoot(gen.path orelse return null), + } + } + + /// Copied from `std.Build.pathFromCwd` as it is non-pub. + fn pathFromCwd(b: *Build, p: []const u8) []u8 { + const cwd = process.getCwdAlloc(b.allocator) catch @panic("OOM"); + return std.fs.path.resolve(b.allocator, &.{ cwd, p }) catch @panic("OOM"); + } + fn runPkgConfig(self: *Build.Step.Compile, lib_name: []const u8) ![]const []const u8 { const b = self.step.owner; const pkg_name = match: { diff --git a/src/build_runner/BuildConfig.zig b/src/build_runner/BuildConfig.zig index 7bfac5b9c..c2e1e2b07 100644 --- a/src/build_runner/BuildConfig.zig +++ b/src/build_runner/BuildConfig.zig @@ -1,8 +1,10 @@ pub const BuildConfig = @This(); +deps_build_roots: []DepsBuildRoots, packages: []Pkg, include_dirs: []const []const u8, +pub const DepsBuildRoots = Pkg; pub const Pkg = struct { name: []const u8, path: []const u8, diff --git a/src/build_runner/master.zig b/src/build_runner/master.zig index 037e7e74a..03fc72739 100644 --- a/src/build_runner/master.zig +++ b/src/build_runner/master.zig @@ -13,13 +13,15 @@ const Build = std.Build; ///! This is a modified build runner to extract information out of build.zig ///! Modified version of lib/build_runner.zig pub fn main() !void { + // Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived, + // one shot program. We don't need to waste time freeing memory and finding places to squish + // bytes into. So we free everything all at once at the very end. var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); defer arena.deinit(); const allocator = arena.allocator(); var args = try process.argsAlloc(allocator); - defer process.argsFree(allocator, args); // skip my own exe name var arg_idx: usize = 1; @@ -110,10 +112,7 @@ pub fn main() !void { try runBuild(builder); var packages = Packages{ .allocator = allocator }; - defer packages.deinit(); - var include_dirs: std.StringArrayHashMapUnmanaged(void) = .{}; - defer include_dirs.deinit(allocator); // This scans the graph of Steps to find all `OptionsStep`s then reifies them // Doing this before the loop to find packages ensures their `GeneratedFile`s have been given paths @@ -132,12 +131,40 @@ pub fn main() !void { } } - const package_list = try packages.toPackageList(); - defer allocator.free(package_list); + // Sample `@dependencies` structure: + // pub const packages = struct { + // pub const @"1220363c7e27b2d3f39de6ff6e90f9537a0634199860fea237a55ddb1e1717f5d6a5" = struct { + // pub const build_root = "/home/rad/.cache/zig/p/1220363c7e27b2d3f39de6ff6e90f9537a0634199860fea237a55ddb1e1717f5d6a5"; + // pub const build_zig = @import("1220363c7e27b2d3f39de6ff6e90f9537a0634199860fea237a55ddb1e1717f5d6a5"); + // pub const deps: []const struct { []const u8, []const u8 } = &.{}; + // }; + // ... + // }; + // pub const root_deps: []const struct { []const u8, []const u8 } = &.{ + // .{ "known_folders", "1220bb12c9bfe291eed1afe6a2070c7c39918ab1979f24a281bba39dfb23f5bcd544" }, + // .{ "diffz", "122089a8247a693cad53beb161bde6c30f71376cd4298798d45b32740c3581405864" }, + // .{ "binned_allocator", "1220363c7e27b2d3f39de6ff6e90f9537a0634199860fea237a55ddb1e1717f5d6a5" }, + // }; + + var deps_build_roots: std.ArrayListUnmanaged(BuildConfig.DepsBuildRoots) = .{}; + for (dependencies.root_deps) |root_dep| { + inline for (@typeInfo(dependencies.packages).Struct.decls) |package| { + if (std.mem.eql(u8, package.name, root_dep[1])) { + const package_info = @field(dependencies.packages, package.name); + if (!@hasDecl(package_info, "build_root")) continue; + try deps_build_roots.append(allocator, .{ + .name = root_dep[0], + // XXX Check if it exists? + .path = try std.fs.path.resolve(allocator, &[_][]const u8{ package_info.build_root, "./build.zig" }), + }); + } + } + } try std.json.stringify( BuildConfig{ - .packages = package_list, + .deps_build_roots = try deps_build_roots.toOwnedSlice(allocator), + .packages = try packages.toPackageList(), .include_dirs = include_dirs.keys(), }, .{ diff --git a/src/features/completions.zig b/src/features/completions.zig index 9ec933d73..5155f256b 100644 --- a/src/features/completions.zig +++ b/src/features/completions.zig @@ -889,6 +889,19 @@ fn completeFileSystemStringLiteral( .detail = pkg.path, }, {}); } + } else if (DocumentStore.isBuildFile(handle.uri)) blk: { + const build_file = store.getBuildFile(handle.uri).?; + const build_config = build_file.tryLockConfig() orelse break :blk; + defer build_file.unlockConfig(); + + try completions.ensureUnusedCapacity(arena, build_config.deps_build_roots.len); + for (build_config.deps_build_roots) |dbr| { + completions.putAssumeCapacity(.{ + .label = dbr.name, + .kind = .Module, + .detail = dbr.path, + }, {}); + } } try completions.ensureUnusedCapacity(arena, 2);