| author | |
| committer | |
| log | cfcf9771c1bde357ad64d81cda9d61ba72d80b15 |
| tree | 143a7434666e186bc79989c9108fdc3ab1c16741 |
| parent | a0f2e6a29f4d5c084a248d24b25fae9f30707001 |
The `zig build` command now makes `@import("@dependencies")` available
to the build runner package. It contains all the dependencies in a
generated file that looks something like this:
```zig
pub const imports = struct {
pub const foo = @import("foo");
pub const @"bar.baz" = @import("bar.baz");
};
pub const build_root = struct {
pub const foo = "<path>";
pub const @"bar.baz" = "<path>";
};
```
The build runner exports this import so that `std.build.Builder` can
access it. `std.build.Builder` uses it to implement the new `dependency`
function which can be used like so:
```zig
const libz_dep = b.dependency("libz", .{});
const libmp3lame_dep = b.dependency("libmp3lame", .{});
// ...
lib.linkLibrary(libz_dep.artifact("z"));
lib.linkLibrary(libmp3lame_dep.artifact("mp3lame"));
```
The `dependency` function calls the build.zig file of the dependency as
a child Builder, and then can be ransacked for its build steps via the
`artifact` function.
This commit also renames `dependency.id` to `dependency.name` in the
`build.zig.ini` file.4 files changed, 297 insertions(+), 57 deletions(-)
lib/build_runner.zig+4-10| ... | ... | @@ -9,6 +9,8 @@ const process = std.process; |
| 9 | 9 | const ArrayList = std.ArrayList; |
| 10 | 10 | const File = std.fs.File; |
| 11 | 11 | |
| 12 | pub const dependencies = @import("@dependencies"); | |
| 13 | ||
| 12 | 14 | pub fn main() !void { |
| 13 | 15 | // Here we use an ArenaAllocator backed by a DirectAllocator because a build is a short-lived, |
| 14 | 16 | // one shot program. We don't need to waste time freeing memory and finding places to squish |
| ... | ... | @@ -207,7 +209,7 @@ pub fn main() !void { |
| 207 | 209 | |
| 208 | 210 | builder.debug_log_scopes = debug_log_scopes.items; |
| 209 | 211 | builder.resolveInstallPrefix(install_prefix, dir_list); |
| 210 | try runBuild(builder); | |
| 212 | try builder.runBuild(root); | |
| 211 | 213 | |
| 212 | 214 | if (builder.validateUserInputDidItFail()) |
| 213 | 215 | return usageAndErr(builder, true, stderr_stream); |
| ... | ... | @@ -223,19 +225,11 @@ pub fn main() !void { |
| 223 | 225 | }; |
| 224 | 226 | } |
| 225 | 227 | |
| 226 | fn runBuild(builder: *Builder) anyerror!void { | |
| 227 | switch (@typeInfo(@typeInfo(@TypeOf(root.build)).Fn.return_type.?)) { | |
| 228 | .Void => root.build(builder), | |
| 229 | .ErrorUnion => try root.build(builder), | |
| 230 | else => @compileError("expected return type of build to be 'void' or '!void'"), | |
| 231 | } | |
| 232 | } | |
| 233 | ||
| 234 | 228 | fn usage(builder: *Builder, already_ran_build: bool, out_stream: anytype) !void { |
| 235 | 229 | // run the build script to collect the options |
| 236 | 230 | if (!already_ran_build) { |
| 237 | 231 | builder.resolveInstallPrefix(null, .{}); |
| 238 | try runBuild(builder); | |
| 232 | try builder.runBuild(root); | |
| 239 | 233 | } |
| 240 | 234 | |
| 241 | 235 | try out_stream.print( |
lib/std/build.zig+152-2| ... | ... | @@ -69,13 +69,15 @@ pub const Builder = struct { |
| 69 | 69 | search_prefixes: ArrayList([]const u8), |
| 70 | 70 | libc_file: ?[]const u8 = null, |
| 71 | 71 | installed_files: ArrayList(InstalledFile), |
| 72 | /// Path to the directory containing build.zig. | |
| 72 | 73 | build_root: []const u8, |
| 73 | 74 | cache_root: []const u8, |
| 74 | 75 | global_cache_root: []const u8, |
| 75 | 76 | release_mode: ?std.builtin.Mode, |
| 76 | 77 | is_release: bool, |
| 78 | /// zig lib dir | |
| 77 | 79 | override_lib_dir: ?[]const u8, |
| 78 | vcpkg_root: VcpkgRoot, | |
| 80 | vcpkg_root: VcpkgRoot = .unattempted, | |
| 79 | 81 | pkg_config_pkg_list: ?(PkgConfigError![]const PkgConfigPkg) = null, |
| 80 | 82 | args: ?[][]const u8 = null, |
| 81 | 83 | debug_log_scopes: []const []const u8 = &.{}, |
| ... | ... | @@ -100,6 +102,8 @@ pub const Builder = struct { |
| 100 | 102 | /// Information about the native target. Computed before build() is invoked. |
| 101 | 103 | host: NativeTargetInfo, |
| 102 | 104 | |
| 105 | dep_prefix: []const u8 = "", | |
| 106 | ||
| 103 | 107 | pub const ExecError = error{ |
| 104 | 108 | ReadFailure, |
| 105 | 109 | ExitCodeFailure, |
| ... | ... | @@ -223,7 +227,6 @@ pub const Builder = struct { |
| 223 | 227 | .is_release = false, |
| 224 | 228 | .override_lib_dir = null, |
| 225 | 229 | .install_path = undefined, |
| 226 | .vcpkg_root = VcpkgRoot{ .unattempted = {} }, | |
| 227 | 230 | .args = null, |
| 228 | 231 | .host = host, |
| 229 | 232 | }; |
| ... | ... | @@ -233,6 +236,89 @@ pub const Builder = struct { |
| 233 | 236 | return self; |
| 234 | 237 | } |
| 235 | 238 | |
| 239 | fn createChild( | |
| 240 | parent: *Builder, | |
| 241 | dep_name: []const u8, | |
| 242 | build_root: []const u8, | |
| 243 | args: anytype, | |
| 244 | ) !*Builder { | |
| 245 | const child = try createChildOnly(parent, dep_name, build_root); | |
| 246 | try applyArgs(child, args); | |
| 247 | return child; | |
| 248 | } | |
| 249 | ||
| 250 | fn createChildOnly(parent: *Builder, dep_name: []const u8, build_root: []const u8) !*Builder { | |
| 251 | const allocator = parent.allocator; | |
| 252 | const child = try allocator.create(Builder); | |
| 253 | child.* = .{ | |
| 254 | .allocator = allocator, | |
| 255 | .install_tls = .{ | |
| 256 | .step = Step.initNoOp(.top_level, "install", allocator), | |
| 257 | .description = "Copy build artifacts to prefix path", | |
| 258 | }, | |
| 259 | .uninstall_tls = .{ | |
| 260 | .step = Step.init(.top_level, "uninstall", allocator, makeUninstall), | |
| 261 | .description = "Remove build artifacts from prefix path", | |
| 262 | }, | |
| 263 | .user_input_options = UserInputOptionsMap.init(allocator), | |
| 264 | .available_options_map = AvailableOptionsMap.init(allocator), | |
| 265 | .available_options_list = ArrayList(AvailableOption).init(allocator), | |
| 266 | .verbose = parent.verbose, | |
| 267 | .verbose_link = parent.verbose_link, | |
| 268 | .verbose_cc = parent.verbose_cc, | |
| 269 | .verbose_air = parent.verbose_air, | |
| 270 | .verbose_llvm_ir = parent.verbose_llvm_ir, | |
| 271 | .verbose_cimport = parent.verbose_cimport, | |
| 272 | .verbose_llvm_cpu_features = parent.verbose_llvm_cpu_features, | |
| 273 | .prominent_compile_errors = parent.prominent_compile_errors, | |
| 274 | .color = parent.color, | |
| 275 | .reference_trace = parent.reference_trace, | |
| 276 | .invalid_user_input = false, | |
| 277 | .zig_exe = parent.zig_exe, | |
| 278 | .default_step = undefined, | |
| 279 | .env_map = parent.env_map, | |
| 280 | .top_level_steps = ArrayList(*TopLevelStep).init(allocator), | |
| 281 | .install_prefix = undefined, | |
| 282 | .dest_dir = parent.dest_dir, | |
| 283 | .lib_dir = parent.lib_dir, | |
| 284 | .exe_dir = parent.exe_dir, | |
| 285 | .h_dir = parent.h_dir, | |
| 286 | .install_path = parent.install_path, | |
| 287 | .sysroot = parent.sysroot, | |
| 288 | .search_prefixes = ArrayList([]const u8).init(allocator), | |
| 289 | .libc_file = parent.libc_file, | |
| 290 | .installed_files = ArrayList(InstalledFile).init(allocator), | |
| 291 | .build_root = build_root, | |
| 292 | .cache_root = parent.cache_root, | |
| 293 | .global_cache_root = parent.global_cache_root, | |
| 294 | .release_mode = parent.release_mode, | |
| 295 | .is_release = parent.is_release, | |
| 296 | .override_lib_dir = parent.override_lib_dir, | |
| 297 | .debug_log_scopes = parent.debug_log_scopes, | |
| 298 | .debug_compile_errors = parent.debug_compile_errors, | |
| 299 | .enable_darling = parent.enable_darling, | |
| 300 | .enable_qemu = parent.enable_qemu, | |
| 301 | .enable_rosetta = parent.enable_rosetta, | |
| 302 | .enable_wasmtime = parent.enable_wasmtime, | |
| 303 | .enable_wine = parent.enable_wine, | |
| 304 | .glibc_runtimes_dir = parent.glibc_runtimes_dir, | |
| 305 | .host = parent.host, | |
| 306 | .dep_prefix = parent.fmt("{s}{s}.", .{ parent.dep_prefix, dep_name }), | |
| 307 | }; | |
| 308 | try child.top_level_steps.append(&child.install_tls); | |
| 309 | try child.top_level_steps.append(&child.uninstall_tls); | |
| 310 | child.default_step = &child.install_tls.step; | |
| 311 | return child; | |
| 312 | } | |
| 313 | ||
| 314 | pub fn applyArgs(b: *Builder, args: anytype) !void { | |
| 315 | // TODO this function is the way that a build.zig file communicates | |
| 316 | // options to its dependencies. It is the programmatic way to give | |
| 317 | // command line arguments to a build.zig script. | |
| 318 | _ = b; | |
| 319 | _ = args; | |
| 320 | } | |
| 321 | ||
| 236 | 322 | pub fn destroy(self: *Builder) void { |
| 237 | 323 | self.env_map.deinit(); |
| 238 | 324 | self.top_level_steps.deinit(); |
| ... | ... | @@ -1300,6 +1386,70 @@ pub const Builder = struct { |
| 1300 | 1386 | &[_][]const u8{ base_dir, dest_rel_path }, |
| 1301 | 1387 | ) catch unreachable; |
| 1302 | 1388 | } |
| 1389 | ||
| 1390 | pub const Dependency = struct { | |
| 1391 | builder: *Builder, | |
| 1392 | ||
| 1393 | pub fn artifact(d: *Dependency, name: []const u8) *LibExeObjStep { | |
| 1394 | var found: ?*LibExeObjStep = null; | |
| 1395 | for (d.builder.install_tls.step.dependencies.items) |dep_step| { | |
| 1396 | const inst = dep_step.cast(InstallArtifactStep) orelse continue; | |
| 1397 | if (mem.eql(u8, inst.artifact.name, name)) { | |
| 1398 | if (found != null) panic("artifact name '{s}' is ambiguous", .{name}); | |
| 1399 | found = inst.artifact; | |
| 1400 | } | |
| 1401 | } | |
| 1402 | return found orelse { | |
| 1403 | for (d.builder.install_tls.step.dependencies.items) |dep_step| { | |
| 1404 | const inst = dep_step.cast(InstallArtifactStep) orelse continue; | |
| 1405 | log.info("available artifact: '{s}'", .{inst.artifact.name}); | |
| 1406 | } | |
| 1407 | panic("unable to find artifact '{s}'", .{name}); | |
| 1408 | }; | |
| 1409 | } | |
| 1410 | }; | |
| 1411 | ||
| 1412 | pub fn dependency(b: *Builder, name: []const u8, args: anytype) *Dependency { | |
| 1413 | const build_runner = @import("root"); | |
| 1414 | const deps = build_runner.dependencies; | |
| 1415 | ||
| 1416 | inline for (@typeInfo(deps.imports).Struct.decls) |decl| { | |
| 1417 | if (mem.startsWith(u8, decl.name, b.dep_prefix) and | |
| 1418 | mem.endsWith(u8, decl.name, name) and | |
| 1419 | decl.name.len == b.dep_prefix.len + name.len) | |
| 1420 | { | |
| 1421 | const build_zig = @field(deps.imports, decl.name); | |
| 1422 | const build_root = @field(deps.build_root, decl.name); | |
| 1423 | return dependencyInner(b, name, build_root, build_zig, args); | |
| 1424 | } | |
| 1425 | } | |
| 1426 | ||
| 1427 | const full_path = b.pathFromRoot("build.zig.ini"); | |
| 1428 | std.debug.print("no dependency named '{s}' in '{s}'\n", .{ name, full_path }); | |
| 1429 | std.process.exit(1); | |
| 1430 | } | |
| 1431 | ||
| 1432 | fn dependencyInner( | |
| 1433 | b: *Builder, | |
| 1434 | name: []const u8, | |
| 1435 | build_root: []const u8, | |
| 1436 | comptime build_zig: type, | |
| 1437 | args: anytype, | |
| 1438 | ) *Dependency { | |
| 1439 | const sub_builder = b.createChild(name, build_root, args) catch unreachable; | |
| 1440 | sub_builder.runBuild(build_zig) catch unreachable; | |
| 1441 | const dep = b.allocator.create(Dependency) catch unreachable; | |
| 1442 | dep.* = .{ .builder = sub_builder }; | |
| 1443 | return dep; | |
| 1444 | } | |
| 1445 | ||
| 1446 | pub fn runBuild(b: *Builder, build_zig: anytype) anyerror!void { | |
| 1447 | switch (@typeInfo(@typeInfo(@TypeOf(build_zig.build)).Fn.return_type.?)) { | |
| 1448 | .Void => build_zig.build(b), | |
| 1449 | .ErrorUnion => try build_zig.build(b), | |
| 1450 | else => @compileError("expected return type of build to be 'void' or '!void'"), | |
| 1451 | } | |
| 1452 | } | |
| 1303 | 1453 | }; |
| 1304 | 1454 | |
| 1305 | 1455 | test "builder.findProgram compiles" { |
src/Package.zig+99-34| ... | ... | @@ -12,6 +12,8 @@ const Compilation = @import("Compilation.zig"); |
| 12 | 12 | const Module = @import("Module.zig"); |
| 13 | 13 | const ThreadPool = @import("ThreadPool.zig"); |
| 14 | 14 | const WaitGroup = @import("WaitGroup.zig"); |
| 15 | const Cache = @import("Cache.zig"); | |
| 16 | const build_options = @import("build_options"); | |
| 15 | 17 | |
| 16 | 18 | pub const Table = std.StringHashMapUnmanaged(*Package); |
| 17 | 19 | |
| ... | ... | @@ -139,6 +141,9 @@ pub fn fetchAndAddDependencies( |
| 139 | 141 | directory: Compilation.Directory, |
| 140 | 142 | global_cache_directory: Compilation.Directory, |
| 141 | 143 | local_cache_directory: Compilation.Directory, |
| 144 | dependencies_source: *std.ArrayList(u8), | |
| 145 | build_roots_source: *std.ArrayList(u8), | |
| 146 | name_prefix: []const u8, | |
| 142 | 147 | ) !void { |
| 143 | 148 | const max_bytes = 10 * 1024 * 1024; |
| 144 | 149 | const gpa = thread_pool.allocator; |
| ... | ... | @@ -156,15 +161,15 @@ pub fn fetchAndAddDependencies( |
| 156 | 161 | var it = ini.iterateSection("\n[dependency]\n"); |
| 157 | 162 | while (it.next()) |dep| { |
| 158 | 163 | var line_it = mem.split(u8, dep, "\n"); |
| 159 | var opt_id: ?[]const u8 = null; | |
| 164 | var opt_name: ?[]const u8 = null; | |
| 160 | 165 | var opt_url: ?[]const u8 = null; |
| 161 | 166 | var expected_hash: ?[]const u8 = null; |
| 162 | 167 | while (line_it.next()) |kv| { |
| 163 | 168 | const eq_pos = mem.indexOfScalar(u8, kv, '=') orelse continue; |
| 164 | 169 | const key = kv[0..eq_pos]; |
| 165 | 170 | const value = kv[eq_pos + 1 ..]; |
| 166 | if (mem.eql(u8, key, "id")) { | |
| 167 | opt_id = value; | |
| 171 | if (mem.eql(u8, key, "name")) { | |
| 172 | opt_name = value; | |
| 168 | 173 | } else if (mem.eql(u8, key, "url")) { |
| 169 | 174 | opt_url = value; |
| 170 | 175 | } else if (mem.eql(u8, key, "hash")) { |
| ... | ... | @@ -181,9 +186,9 @@ pub fn fetchAndAddDependencies( |
| 181 | 186 | } |
| 182 | 187 | } |
| 183 | 188 | |
| 184 | const id = opt_id orelse { | |
| 189 | const name = opt_name orelse { | |
| 185 | 190 | const loc = std.zig.findLineColumn(ini.bytes, @ptrToInt(dep.ptr) - @ptrToInt(ini.bytes.ptr)); |
| 186 | std.log.err("{s}/{s}:{d}:{d} missing key: 'id'", .{ | |
| 191 | std.log.err("{s}/{s}:{d}:{d} missing key: 'name'", .{ | |
| 187 | 192 | directory.path orelse ".", |
| 188 | 193 | "build.zig.ini", |
| 189 | 194 | loc.line, |
| ... | ... | @@ -195,7 +200,7 @@ pub fn fetchAndAddDependencies( |
| 195 | 200 | |
| 196 | 201 | const url = opt_url orelse { |
| 197 | 202 | const loc = std.zig.findLineColumn(ini.bytes, @ptrToInt(dep.ptr) - @ptrToInt(ini.bytes.ptr)); |
| 198 | std.log.err("{s}/{s}:{d}:{d} missing key: 'id'", .{ | |
| 203 | std.log.err("{s}/{s}:{d}:{d} missing key: 'name'", .{ | |
| 199 | 204 | directory.path orelse ".", |
| 200 | 205 | "build.zig.ini", |
| 201 | 206 | loc.line, |
| ... | ... | @@ -205,6 +210,10 @@ pub fn fetchAndAddDependencies( |
| 205 | 210 | continue; |
| 206 | 211 | }; |
| 207 | 212 | |
| 213 | const sub_prefix = try std.fmt.allocPrint(gpa, "{s}{s}.", .{ name_prefix, name }); | |
| 214 | defer gpa.free(sub_prefix); | |
| 215 | const fqn = sub_prefix[0 .. sub_prefix.len - 1]; | |
| 216 | ||
| 208 | 217 | const sub_pkg = try fetchAndUnpack( |
| 209 | 218 | thread_pool, |
| 210 | 219 | http_client, |
| ... | ... | @@ -213,22 +222,56 @@ pub fn fetchAndAddDependencies( |
| 213 | 222 | expected_hash, |
| 214 | 223 | ini, |
| 215 | 224 | directory, |
| 225 | build_roots_source, | |
| 226 | fqn, | |
| 216 | 227 | ); |
| 217 | 228 | |
| 218 | try sub_pkg.fetchAndAddDependencies( | |
| 229 | try pkg.fetchAndAddDependencies( | |
| 219 | 230 | thread_pool, |
| 220 | 231 | http_client, |
| 221 | 232 | sub_pkg.root_src_directory, |
| 222 | 233 | global_cache_directory, |
| 223 | 234 | local_cache_directory, |
| 235 | dependencies_source, | |
| 236 | build_roots_source, | |
| 237 | sub_prefix, | |
| 224 | 238 | ); |
| 225 | 239 | |
| 226 | try addAndAdopt(pkg, gpa, id, sub_pkg); | |
| 240 | try addAndAdopt(pkg, gpa, fqn, sub_pkg); | |
| 241 | ||
| 242 | try dependencies_source.writer().print(" pub const {s} = @import(\"{}\");\n", .{ | |
| 243 | std.zig.fmtId(fqn), std.zig.fmtEscapes(fqn), | |
| 244 | }); | |
| 227 | 245 | } |
| 228 | 246 | |
| 229 | 247 | if (any_error) return error.InvalidBuildZigIniFile; |
| 230 | 248 | } |
| 231 | 249 | |
| 250 | pub fn createFilePkg( | |
| 251 | gpa: Allocator, | |
| 252 | global_cache_directory: Compilation.Directory, | |
| 253 | basename: []const u8, | |
| 254 | contents: []const u8, | |
| 255 | ) !*Package { | |
| 256 | const rand_int = std.crypto.random.int(u64); | |
| 257 | const tmp_dir_sub_path = "tmp" ++ fs.path.sep_str ++ hex64(rand_int); | |
| 258 | { | |
| 259 | var tmp_dir = try global_cache_directory.handle.makeOpenPath(tmp_dir_sub_path, .{}); | |
| 260 | defer tmp_dir.close(); | |
| 261 | try tmp_dir.writeFile(basename, contents); | |
| 262 | } | |
| 263 | ||
| 264 | var hh: Cache.HashHelper = .{}; | |
| 265 | hh.addBytes(build_options.version); | |
| 266 | hh.addBytes(contents); | |
| 267 | const hex_digest = hh.final(); | |
| 268 | ||
| 269 | const o_dir_sub_path = "o" ++ fs.path.sep_str ++ hex_digest; | |
| 270 | try renameTmpIntoCache(global_cache_directory.handle, tmp_dir_sub_path, o_dir_sub_path); | |
| 271 | ||
| 272 | return createWithDir(gpa, global_cache_directory, o_dir_sub_path, basename); | |
| 273 | } | |
| 274 | ||
| 232 | 275 | fn fetchAndUnpack( |
| 233 | 276 | thread_pool: *ThreadPool, |
| 234 | 277 | http_client: *std.http.Client, |
| ... | ... | @@ -237,6 +280,8 @@ fn fetchAndUnpack( |
| 237 | 280 | expected_hash: ?[]const u8, |
| 238 | 281 | ini: std.Ini, |
| 239 | 282 | comp_directory: Compilation.Directory, |
| 283 | build_roots_source: *std.ArrayList(u8), | |
| 284 | fqn: []const u8, | |
| 240 | 285 | ) !*Package { |
| 241 | 286 | const gpa = http_client.allocator; |
| 242 | 287 | const s = fs.path.sep_str; |
| ... | ... | @@ -267,14 +312,22 @@ fn fetchAndUnpack( |
| 267 | 312 | const owned_src_path = try gpa.dupe(u8, build_zig_basename); |
| 268 | 313 | errdefer gpa.free(owned_src_path); |
| 269 | 314 | |
| 315 | const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path}); | |
| 316 | errdefer gpa.free(build_root); | |
| 317 | ||
| 318 | try build_roots_source.writer().print(" pub const {s} = \"{}\";\n", .{ | |
| 319 | std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root), | |
| 320 | }); | |
| 321 | ||
| 270 | 322 | ptr.* = .{ |
| 271 | 323 | .root_src_directory = .{ |
| 272 | .path = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path}), | |
| 324 | .path = build_root, | |
| 273 | 325 | .handle = pkg_dir, |
| 274 | 326 | }, |
| 275 | 327 | .root_src_directory_owned = true, |
| 276 | 328 | .root_src_path = owned_src_path, |
| 277 | 329 | }; |
| 330 | ||
| 278 | 331 | return ptr; |
| 279 | 332 | } |
| 280 | 333 | |
| ... | ... | @@ -331,31 +384,7 @@ fn fetchAndUnpack( |
| 331 | 384 | }; |
| 332 | 385 | |
| 333 | 386 | const pkg_dir_sub_path = "p" ++ s ++ hexDigest(actual_hash); |
| 334 | ||
| 335 | { | |
| 336 | // Rename the temporary directory into the global package cache. | |
| 337 | var handled_missing_dir = false; | |
| 338 | while (true) { | |
| 339 | global_cache_directory.handle.rename(tmp_dir_sub_path, pkg_dir_sub_path) catch |err| switch (err) { | |
| 340 | error.FileNotFound => { | |
| 341 | if (handled_missing_dir) return err; | |
| 342 | global_cache_directory.handle.makeDir("p") catch |mkd_err| switch (mkd_err) { | |
| 343 | error.PathAlreadyExists => handled_missing_dir = true, | |
| 344 | else => |e| return e, | |
| 345 | }; | |
| 346 | continue; | |
| 347 | }, | |
| 348 | error.PathAlreadyExists => { | |
| 349 | // Package has been already downloaded and may already be in use on the system. | |
| 350 | global_cache_directory.handle.deleteTree(tmp_dir_sub_path) catch |del_err| { | |
| 351 | std.log.warn("unable to delete temp directory: {s}", .{@errorName(del_err)}); | |
| 352 | }; | |
| 353 | }, | |
| 354 | else => |e| return e, | |
| 355 | }; | |
| 356 | break; | |
| 357 | } | |
| 358 | } | |
| 387 | try renameTmpIntoCache(global_cache_directory.handle, tmp_dir_sub_path, pkg_dir_sub_path); | |
| 359 | 388 | |
| 360 | 389 | if (expected_hash) |h| { |
| 361 | 390 | const actual_hex = hexDigest(actual_hash); |
| ... | ... | @@ -378,6 +407,13 @@ fn fetchAndUnpack( |
| 378 | 407 | ); |
| 379 | 408 | } |
| 380 | 409 | |
| 410 | const build_root = try global_cache_directory.join(gpa, &.{pkg_dir_sub_path}); | |
| 411 | defer gpa.free(build_root); | |
| 412 | ||
| 413 | try build_roots_source.writer().print(" pub const {s} = \"{}\";\n", .{ | |
| 414 | std.zig.fmtId(fqn), std.zig.fmtEscapes(build_root), | |
| 415 | }); | |
| 416 | ||
| 381 | 417 | return createWithDir(gpa, global_cache_directory, pkg_dir_sub_path, build_zig_basename); |
| 382 | 418 | } |
| 383 | 419 | |
| ... | ... | @@ -516,3 +552,32 @@ fn hexDigest(digest: [Hash.digest_length]u8) [Hash.digest_length * 2]u8 { |
| 516 | 552 | } |
| 517 | 553 | return result; |
| 518 | 554 | } |
| 555 | ||
| 556 | fn renameTmpIntoCache( | |
| 557 | cache_dir: fs.Dir, | |
| 558 | tmp_dir_sub_path: []const u8, | |
| 559 | dest_dir_sub_path: []const u8, | |
| 560 | ) !void { | |
| 561 | assert(dest_dir_sub_path[1] == '/'); | |
| 562 | var handled_missing_dir = false; | |
| 563 | while (true) { | |
| 564 | cache_dir.rename(tmp_dir_sub_path, dest_dir_sub_path) catch |err| switch (err) { | |
| 565 | error.FileNotFound => { | |
| 566 | if (handled_missing_dir) return err; | |
| 567 | cache_dir.makeDir(dest_dir_sub_path[0..1]) catch |mkd_err| switch (mkd_err) { | |
| 568 | error.PathAlreadyExists => handled_missing_dir = true, | |
| 569 | else => |e| return e, | |
| 570 | }; | |
| 571 | continue; | |
| 572 | }, | |
| 573 | error.PathAlreadyExists => { | |
| 574 | // Package has been already downloaded and may already be in use on the system. | |
| 575 | cache_dir.deleteTree(tmp_dir_sub_path) catch |del_err| { | |
| 576 | std.log.warn("unable to delete temp directory: {s}", .{@errorName(del_err)}); | |
| 577 | }; | |
| 578 | }, | |
| 579 | else => |e| return e, | |
| 580 | }; | |
| 581 | break; | |
| 582 | } | |
| 583 | } |
src/main.zig+42-11| ... | ... | @@ -3983,11 +3983,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 3983 | 3983 | }; |
| 3984 | 3984 | defer zig_lib_directory.handle.close(); |
| 3985 | 3985 | |
| 3986 | var main_pkg: Package = .{ | |
| 3987 | .root_src_directory = zig_lib_directory, | |
| 3988 | .root_src_path = "build_runner.zig", | |
| 3989 | }; | |
| 3990 | ||
| 3991 | 3986 | var cleanup_build_dir: ?fs.Dir = null; |
| 3992 | 3987 | defer if (cleanup_build_dir) |*dir| dir.close(); |
| 3993 | 3988 | |
| ... | ... | @@ -4031,12 +4026,6 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4031 | 4026 | }; |
| 4032 | 4027 | child_argv.items[argv_index_build_file] = build_directory.path orelse cwd_path; |
| 4033 | 4028 | |
| 4034 | var build_pkg: Package = .{ | |
| 4035 | .root_src_directory = build_directory, | |
| 4036 | .root_src_path = build_zig_basename, | |
| 4037 | }; | |
| 4038 | try main_pkg.addAndAdopt(arena, "@build", &build_pkg); | |
| 4039 | ||
| 4040 | 4029 | var global_cache_directory: Compilation.Directory = l: { |
| 4041 | 4030 | const p = override_global_cache_dir orelse try introspect.resolveGlobalCacheDir(arena); |
| 4042 | 4031 | break :l .{ |
| ... | ... | @@ -4083,23 +4072,65 @@ pub fn cmdBuild(gpa: Allocator, arena: Allocator, args: []const []const u8) !voi |
| 4083 | 4072 | try thread_pool.init(gpa); |
| 4084 | 4073 | defer thread_pool.deinit(); |
| 4085 | 4074 | |
| 4075 | var main_pkg: Package = .{ | |
| 4076 | .root_src_directory = zig_lib_directory, | |
| 4077 | .root_src_path = "build_runner.zig", | |
| 4078 | }; | |
| 4079 | ||
| 4086 | 4080 | if (!build_options.omit_pkg_fetching_code) { |
| 4087 | 4081 | var http_client: std.http.Client = .{ .allocator = gpa }; |
| 4088 | 4082 | defer http_client.deinit(); |
| 4089 | 4083 | try http_client.rescanRootCertificates(); |
| 4090 | 4084 | |
| 4085 | // Here we provide an import to the build runner that allows using reflection to find | |
| 4086 | // all of the dependencies. Without this, there would be no way to use `@import` to | |
| 4087 | // access dependencies by name, since `@import` requires string literals. | |
| 4088 | var dependencies_source = std.ArrayList(u8).init(gpa); | |
| 4089 | defer dependencies_source.deinit(); | |
| 4090 | try dependencies_source.appendSlice("pub const imports = struct {\n"); | |
| 4091 | ||
| 4092 | // This will go into the same package. It contains the file system paths | |
| 4093 | // to all the build.zig files. | |
| 4094 | var build_roots_source = std.ArrayList(u8).init(gpa); | |
| 4095 | defer build_roots_source.deinit(); | |
| 4096 | ||
| 4097 | // Here we borrow main package's table and will replace it with a fresh | |
| 4098 | // one after this process completes. | |
| 4091 | 4099 | main_pkg.fetchAndAddDependencies( |
| 4092 | 4100 | &thread_pool, |
| 4093 | 4101 | &http_client, |
| 4094 | 4102 | build_directory, |
| 4095 | 4103 | global_cache_directory, |
| 4096 | 4104 | local_cache_directory, |
| 4105 | &dependencies_source, | |
| 4106 | &build_roots_source, | |
| 4107 | "", | |
| 4097 | 4108 | ) catch |err| switch (err) { |
| 4098 | 4109 | error.PackageFetchFailed => process.exit(1), |
| 4099 | 4110 | else => |e| return e, |
| 4100 | 4111 | }; |
| 4112 | ||
| 4113 | try dependencies_source.appendSlice("};\npub const build_root = struct {\n"); | |
| 4114 | try dependencies_source.appendSlice(build_roots_source.items); | |
| 4115 | try dependencies_source.appendSlice("};\n"); | |
| 4116 | ||
| 4117 | const deps_pkg = try Package.createFilePkg( | |
| 4118 | gpa, | |
| 4119 | global_cache_directory, | |
| 4120 | "dependencies.zig", | |
| 4121 | dependencies_source.items, | |
| 4122 | ); | |
| 4123 | ||
| 4124 | mem.swap(Package.Table, &main_pkg.table, &deps_pkg.table); | |
| 4125 | try main_pkg.addAndAdopt(gpa, "@dependencies", deps_pkg); | |
| 4101 | 4126 | } |
| 4102 | 4127 | |
| 4128 | var build_pkg: Package = .{ | |
| 4129 | .root_src_directory = build_directory, | |
| 4130 | .root_src_path = build_zig_basename, | |
| 4131 | }; | |
| 4132 | try main_pkg.addAndAdopt(gpa, "@build", &build_pkg); | |
| 4133 | ||
| 4103 | 4134 | const comp = Compilation.create(gpa, .{ |
| 4104 | 4135 | .zig_lib_directory = zig_lib_directory, |
| 4105 | 4136 | .local_cache_directory = local_cache_directory, |