mirror of
https://github.com/ziglang/zig.git
synced 2025-12-07 14:53:08 +00:00
Merge pull request #18988 from castholm/lazy-build-zig
std.Build: add `lazyImport` (`@import` for lazy dependencies)
This commit is contained in:
commit
e204a6edb8
@ -1,4 +1,3 @@
|
|||||||
const root = @import("@build");
|
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const assert = std.debug.assert;
|
const assert = std.debug.assert;
|
||||||
@ -10,6 +9,7 @@ const ArrayList = std.ArrayList;
|
|||||||
const File = std.fs.File;
|
const File = std.fs.File;
|
||||||
const Step = std.Build.Step;
|
const Step = std.Build.Step;
|
||||||
|
|
||||||
|
pub const root = @import("@build");
|
||||||
pub const dependencies = @import("@dependencies");
|
pub const dependencies = @import("@dependencies");
|
||||||
|
|
||||||
pub fn main() !void {
|
pub fn main() !void {
|
||||||
|
|||||||
@ -93,6 +93,8 @@ named_writefiles: std.StringArrayHashMap(*Step.WriteFile),
|
|||||||
/// A map from build root dirs to the corresponding `*Dependency`. This is shared with all child
|
/// A map from build root dirs to the corresponding `*Dependency`. This is shared with all child
|
||||||
/// `Build`s.
|
/// `Build`s.
|
||||||
initialized_deps: *InitializedDepMap,
|
initialized_deps: *InitializedDepMap,
|
||||||
|
/// The hash of this instance's package. `""` means that this is the root package.
|
||||||
|
pkg_hash: []const u8,
|
||||||
/// A mapping from dependency names to package hashes.
|
/// A mapping from dependency names to package hashes.
|
||||||
available_deps: AvailableDeps,
|
available_deps: AvailableDeps,
|
||||||
|
|
||||||
@ -305,6 +307,7 @@ pub fn create(
|
|||||||
.modules = std.StringArrayHashMap(*Module).init(arena),
|
.modules = std.StringArrayHashMap(*Module).init(arena),
|
||||||
.named_writefiles = std.StringArrayHashMap(*Step.WriteFile).init(arena),
|
.named_writefiles = std.StringArrayHashMap(*Step.WriteFile).init(arena),
|
||||||
.initialized_deps = initialized_deps,
|
.initialized_deps = initialized_deps,
|
||||||
|
.pkg_hash = "",
|
||||||
.available_deps = available_deps,
|
.available_deps = available_deps,
|
||||||
.release_mode = .off,
|
.release_mode = .off,
|
||||||
};
|
};
|
||||||
@ -318,10 +321,11 @@ fn createChild(
|
|||||||
parent: *Build,
|
parent: *Build,
|
||||||
dep_name: []const u8,
|
dep_name: []const u8,
|
||||||
build_root: Cache.Directory,
|
build_root: Cache.Directory,
|
||||||
|
pkg_hash: []const u8,
|
||||||
pkg_deps: AvailableDeps,
|
pkg_deps: AvailableDeps,
|
||||||
user_input_options: UserInputOptionsMap,
|
user_input_options: UserInputOptionsMap,
|
||||||
) !*Build {
|
) !*Build {
|
||||||
const child = try createChildOnly(parent, dep_name, build_root, pkg_deps, user_input_options);
|
const child = try createChildOnly(parent, dep_name, build_root, pkg_hash, pkg_deps, user_input_options);
|
||||||
try determineAndApplyInstallPrefix(child);
|
try determineAndApplyInstallPrefix(child);
|
||||||
return child;
|
return child;
|
||||||
}
|
}
|
||||||
@ -330,6 +334,7 @@ fn createChildOnly(
|
|||||||
parent: *Build,
|
parent: *Build,
|
||||||
dep_name: []const u8,
|
dep_name: []const u8,
|
||||||
build_root: Cache.Directory,
|
build_root: Cache.Directory,
|
||||||
|
pkg_hash: []const u8,
|
||||||
pkg_deps: AvailableDeps,
|
pkg_deps: AvailableDeps,
|
||||||
user_input_options: UserInputOptionsMap,
|
user_input_options: UserInputOptionsMap,
|
||||||
) !*Build {
|
) !*Build {
|
||||||
@ -397,6 +402,7 @@ fn createChildOnly(
|
|||||||
.modules = std.StringArrayHashMap(*Module).init(allocator),
|
.modules = std.StringArrayHashMap(*Module).init(allocator),
|
||||||
.named_writefiles = std.StringArrayHashMap(*Step.WriteFile).init(allocator),
|
.named_writefiles = std.StringArrayHashMap(*Step.WriteFile).init(allocator),
|
||||||
.initialized_deps = parent.initialized_deps,
|
.initialized_deps = parent.initialized_deps,
|
||||||
|
.pkg_hash = pkg_hash,
|
||||||
.available_deps = pkg_deps,
|
.available_deps = pkg_deps,
|
||||||
.release_mode = parent.release_mode,
|
.release_mode = parent.release_mode,
|
||||||
};
|
};
|
||||||
@ -1831,6 +1837,26 @@ fn findPkgHashOrFatal(b: *Build, name: []const u8) []const u8 {
|
|||||||
std.debug.panic("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file", .{ name, full_path });
|
std.debug.panic("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file", .{ name, full_path });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline fn findImportPkgHashOrFatal(b: *Build, comptime asking_build_zig: type, comptime dep_name: []const u8) []const u8 {
|
||||||
|
const build_runner = @import("root");
|
||||||
|
const deps = build_runner.dependencies;
|
||||||
|
|
||||||
|
const b_pkg_hash, const b_pkg_deps = comptime for (@typeInfo(deps.packages).Struct.decls) |decl| {
|
||||||
|
const pkg_hash = decl.name;
|
||||||
|
const pkg = @field(deps.packages, pkg_hash);
|
||||||
|
if (@hasDecl(pkg, "build_zig") and pkg.build_zig == asking_build_zig) break .{ pkg_hash, pkg.deps };
|
||||||
|
} else .{ "", deps.root_deps };
|
||||||
|
if (!std.mem.eql(u8, b_pkg_hash, b.pkg_hash)) {
|
||||||
|
std.debug.panic("'{}' is not the struct that corresponds to '{s}'", .{ asking_build_zig, b.pathFromRoot("build.zig") });
|
||||||
|
}
|
||||||
|
comptime for (b_pkg_deps) |dep| {
|
||||||
|
if (std.mem.eql(u8, dep[0], dep_name)) return dep[1];
|
||||||
|
};
|
||||||
|
|
||||||
|
const full_path = b.pathFromRoot("build.zig.zon");
|
||||||
|
std.debug.panic("no dependency named '{s}' in '{s}'. All packages used in build.zig must be declared in this file", .{ dep_name, full_path });
|
||||||
|
}
|
||||||
|
|
||||||
fn markNeededLazyDep(b: *Build, pkg_hash: []const u8) void {
|
fn markNeededLazyDep(b: *Build, pkg_hash: []const u8) void {
|
||||||
b.graph.needed_lazy_dependencies.put(b.graph.arena, pkg_hash, {}) catch @panic("OOM");
|
b.graph.needed_lazy_dependencies.put(b.graph.arena, pkg_hash, {}) catch @panic("OOM");
|
||||||
}
|
}
|
||||||
@ -1861,7 +1887,7 @@ pub fn lazyDependency(b: *Build, name: []const u8, args: anytype) ?*Dependency {
|
|||||||
markNeededLazyDep(b, pkg_hash);
|
markNeededLazyDep(b, pkg_hash);
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg.deps, args);
|
return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg_hash, pkg.deps, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1879,13 +1905,48 @@ pub fn dependency(b: *Build, name: []const u8, args: anytype) *Dependency {
|
|||||||
if (@hasDecl(pkg, "available")) {
|
if (@hasDecl(pkg, "available")) {
|
||||||
std.debug.panic("dependency '{s}{s}' is marked as lazy in build.zig.zon which means it must use the lazyDependency function instead", .{ b.dep_prefix, name });
|
std.debug.panic("dependency '{s}{s}' is marked as lazy in build.zig.zon which means it must use the lazyDependency function instead", .{ b.dep_prefix, name });
|
||||||
}
|
}
|
||||||
return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg.deps, args);
|
return dependencyInner(b, name, pkg.build_root, if (@hasDecl(pkg, "build_zig")) pkg.build_zig else null, pkg_hash, pkg.deps, args);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
unreachable; // Bad @dependencies source
|
unreachable; // Bad @dependencies source
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// In a build.zig file, this function is to `@import` what `lazyDependency` is to `dependency`.
|
||||||
|
/// If the dependency is lazy and has not yet been fetched, it instructs the parent process to fetch
|
||||||
|
/// that dependency after the build script has finished running, then returns `null`.
|
||||||
|
/// If the dependency is lazy but has already been fetched, or if it is eager, it returns
|
||||||
|
/// the build.zig struct of that dependency, just like a regular `@import`.
|
||||||
|
pub inline fn lazyImport(
|
||||||
|
b: *Build,
|
||||||
|
/// The build.zig struct of the package importing the dependency.
|
||||||
|
/// When calling this function from the `build` function of a build.zig file's, you normally
|
||||||
|
/// pass `@This()`.
|
||||||
|
comptime asking_build_zig: type,
|
||||||
|
comptime dep_name: []const u8,
|
||||||
|
) ?type {
|
||||||
|
const build_runner = @import("root");
|
||||||
|
const deps = build_runner.dependencies;
|
||||||
|
const pkg_hash = findImportPkgHashOrFatal(b, asking_build_zig, dep_name);
|
||||||
|
|
||||||
|
inline for (@typeInfo(deps.packages).Struct.decls) |decl| {
|
||||||
|
if (comptime mem.eql(u8, decl.name, pkg_hash)) {
|
||||||
|
const pkg = @field(deps.packages, decl.name);
|
||||||
|
const available = !@hasDecl(pkg, "available") or pkg.available;
|
||||||
|
if (!available) {
|
||||||
|
markNeededLazyDep(b, pkg_hash);
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return if (@hasDecl(pkg, "build_zig"))
|
||||||
|
pkg.build_zig
|
||||||
|
else
|
||||||
|
@compileError("dependency '" ++ dep_name ++ "' does not have a build.zig");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
comptime unreachable; // Bad @dependencies source
|
||||||
|
}
|
||||||
|
|
||||||
pub fn anonymousDependency(
|
pub fn anonymousDependency(
|
||||||
b: *Build,
|
b: *Build,
|
||||||
/// The path to the directory containing the dependency's build.zig file,
|
/// The path to the directory containing the dependency's build.zig file,
|
||||||
@ -1902,7 +1963,7 @@ pub fn anonymousDependency(
|
|||||||
'/', '\\' => byte.* = '.',
|
'/', '\\' => byte.* = '.',
|
||||||
else => continue,
|
else => continue,
|
||||||
};
|
};
|
||||||
return dependencyInner(b, name, build_root, build_zig, &.{}, args);
|
return dependencyInner(b, name, build_root, build_zig, "anonymous", &.{}, args);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn userValuesAreSame(lhs: UserValue, rhs: UserValue) bool {
|
fn userValuesAreSame(lhs: UserValue, rhs: UserValue) bool {
|
||||||
@ -1957,6 +2018,7 @@ pub fn dependencyInner(
|
|||||||
name: []const u8,
|
name: []const u8,
|
||||||
build_root_string: []const u8,
|
build_root_string: []const u8,
|
||||||
comptime build_zig: ?type,
|
comptime build_zig: ?type,
|
||||||
|
pkg_hash: []const u8,
|
||||||
pkg_deps: AvailableDeps,
|
pkg_deps: AvailableDeps,
|
||||||
args: anytype,
|
args: anytype,
|
||||||
) *Dependency {
|
) *Dependency {
|
||||||
@ -1977,7 +2039,7 @@ pub fn dependencyInner(
|
|||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
const sub_builder = b.createChild(name, build_root, pkg_deps, user_input_options) catch @panic("unhandled error");
|
const sub_builder = b.createChild(name, build_root, pkg_hash, pkg_deps, user_input_options) catch @panic("unhandled error");
|
||||||
if (build_zig) |bz| {
|
if (build_zig) |bz| {
|
||||||
sub_builder.runBuild(bz) catch @panic("unhandled error");
|
sub_builder.runBuild(bz) catch @panic("unhandled error");
|
||||||
|
|
||||||
|
|||||||
@ -144,7 +144,7 @@ test "int" {
|
|||||||
}
|
}
|
||||||
|
|
||||||
test "float" {
|
test "float" {
|
||||||
@setEvalBranchQuota(2000);
|
@setEvalBranchQuota(3000);
|
||||||
|
|
||||||
// normal -> normal
|
// normal -> normal
|
||||||
try expect(nextAfter(f16, 0x1.234p0, 2.0) == 0x1.238p0);
|
try expect(nextAfter(f16, 0x1.234p0, 2.0) == 0x1.238p0);
|
||||||
|
|||||||
@ -7531,10 +7531,12 @@ fn analyzeCall(
|
|||||||
|
|
||||||
var is_generic_call = func_ty_info.is_generic;
|
var is_generic_call = func_ty_info.is_generic;
|
||||||
var is_comptime_call = block.is_comptime or modifier == .compile_time;
|
var is_comptime_call = block.is_comptime or modifier == .compile_time;
|
||||||
|
var is_inline_call = is_comptime_call or modifier == .always_inline or func_ty_info.cc == .Inline;
|
||||||
var comptime_reason: ?*const Block.ComptimeReason = null;
|
var comptime_reason: ?*const Block.ComptimeReason = null;
|
||||||
if (!is_comptime_call) {
|
if (!is_inline_call and !is_comptime_call) {
|
||||||
if (sema.typeRequiresComptime(Type.fromInterned(func_ty_info.return_type))) |ct| {
|
if (sema.typeRequiresComptime(Type.fromInterned(func_ty_info.return_type))) |ct| {
|
||||||
is_comptime_call = ct;
|
is_comptime_call = ct;
|
||||||
|
is_inline_call = ct;
|
||||||
if (ct) {
|
if (ct) {
|
||||||
comptime_reason = &.{ .comptime_ret_ty = .{
|
comptime_reason = &.{ .comptime_ret_ty = .{
|
||||||
.block = block,
|
.block = block,
|
||||||
@ -7548,8 +7550,6 @@ fn analyzeCall(
|
|||||||
else => |e| return e,
|
else => |e| return e,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var is_inline_call = is_comptime_call or modifier == .always_inline or
|
|
||||||
func_ty_info.cc == .Inline;
|
|
||||||
|
|
||||||
if (sema.func_is_naked and !is_inline_call and !is_comptime_call) {
|
if (sema.func_is_naked and !is_inline_call and !is_comptime_call) {
|
||||||
const msg = msg: {
|
const msg = msg: {
|
||||||
|
|||||||
@ -604,3 +604,17 @@ test "comptime parameters don't have to be marked comptime if only called at com
|
|||||||
};
|
};
|
||||||
comptime std.debug.assert(S.foo(5, 6) == 11);
|
comptime std.debug.assert(S.foo(5, 6) == 11);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test "inline function with comptime-known comptime-only return type called at runtime" {
|
||||||
|
const S = struct {
|
||||||
|
inline fn foo(x: *i32, y: *const i32) type {
|
||||||
|
x.* = y.*;
|
||||||
|
return f32;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
var a: i32 = 0;
|
||||||
|
const b: i32 = 111;
|
||||||
|
const T = S.foo(&a, &b);
|
||||||
|
try expectEqual(111, a);
|
||||||
|
try expectEqual(f32, T);
|
||||||
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user