zig/src/stage1.zig
Andrew Kelley 06310e3d4e Revert "Fix C include files not being in whole cache (#11365)"
This reverts commit a430630002bf02162ccbf8d3eb10fd73e490cefd.

Wait a minute, I'm sorry, I need to revert this. The whole premise
of this change is broken because the point of the hash is that it tells
whether the same compilation has been done before. This requires items
to be added to the hash in the same sequence every time. This means that
introducing a lock is fundamentally broken because the order needs to be
the same in future runs of the compiler, and not decided by threads
racing against each other.

The proper solution to this is to, in whole cache mode, append the hash
inputs to some data structure, and then after the compilation is
complete, do some kind of sorting on the hash inputs so that they will
be the same order every time, then apply them in sequence. No lock on
the Cache object is needed for this scheme.
2022-04-22 08:19:51 -07:00

475 lines
13 KiB
Zig

//! This is the main entry point for the Zig/C++ hybrid compiler (stage1).
//! It has the functions exported from Zig, called in C++, and bindings for
//! the functions exported from C++, called from Zig.
const std = @import("std");
const assert = std.debug.assert;
const mem = std.mem;
const CrossTarget = std.zig.CrossTarget;
const Target = std.Target;
const builtin = @import("builtin");
const build_options = @import("build_options");
const stage2 = @import("main.zig");
const fatal = stage2.fatal;
const Compilation = @import("Compilation.zig");
const translate_c = @import("translate_c.zig");
const target_util = @import("target.zig");
comptime {
assert(builtin.link_libc);
assert(build_options.is_stage1);
assert(build_options.have_llvm);
if (!builtin.is_test) {
@export(main, .{ .name = "main" });
}
}
pub const log = stage2.log;
pub const log_level = stage2.log_level;
pub fn main(argc: c_int, argv: [*][*:0]u8) callconv(.C) c_int {
std.os.argv = argv[0..@intCast(usize, argc)];
std.debug.maybeEnableSegfaultHandler();
zig_stage1_os_init();
const gpa = std.heap.c_allocator;
var arena_instance = std.heap.ArenaAllocator.init(gpa);
defer arena_instance.deinit();
const arena = arena_instance.allocator();
const args = arena.alloc([]const u8, @intCast(usize, argc)) catch fatal("{s}", .{"OutOfMemory"});
for (args) |*arg, i| {
arg.* = mem.sliceTo(argv[i], 0);
}
if (builtin.mode == .Debug) {
stage2.mainArgs(gpa, arena, args) catch unreachable;
} else {
stage2.mainArgs(gpa, arena, args) catch |err| fatal("{s}", .{@errorName(err)});
}
return 0;
}
/// Matches stage2.Color;
pub const ErrColor = c_int;
/// Matches std.builtin.CodeModel
pub const CodeModel = c_int;
/// Matches std.Target.Os.Tag
pub const OS = c_int;
/// Matches std.builtin.BuildMode
pub const BuildMode = c_int;
pub const TargetSubsystem = enum(c_int) {
Console,
Windows,
Posix,
Native,
EfiApplication,
EfiBootServiceDriver,
EfiRom,
EfiRuntimeDriver,
Auto,
};
pub const Pkg = extern struct {
name_ptr: [*]const u8,
name_len: usize,
path_ptr: [*]const u8,
path_len: usize,
children_ptr: [*]*Pkg,
children_len: usize,
parent: ?*Pkg,
};
pub const Module = extern struct {
root_name_ptr: [*]const u8,
root_name_len: usize,
emit_o_ptr: [*]const u8,
emit_o_len: usize,
emit_h_ptr: [*]const u8,
emit_h_len: usize,
emit_asm_ptr: [*]const u8,
emit_asm_len: usize,
emit_llvm_ir_ptr: [*]const u8,
emit_llvm_ir_len: usize,
emit_bitcode_ptr: [*]const u8,
emit_bitcode_len: usize,
emit_analysis_json_ptr: [*]const u8,
emit_analysis_json_len: usize,
emit_docs_ptr: [*]const u8,
emit_docs_len: usize,
builtin_zig_path_ptr: [*]const u8,
builtin_zig_path_len: usize,
test_filter_ptr: [*]const u8,
test_filter_len: usize,
test_name_prefix_ptr: [*]const u8,
test_name_prefix_len: usize,
userdata: usize,
main_pkg: *Pkg,
main_progress_node: ?*std.Progress.Node,
code_model: CodeModel,
subsystem: TargetSubsystem,
err_color: ErrColor,
pic: bool,
pie: bool,
lto: bool,
unwind_tables: bool,
link_libc: bool,
link_libcpp: bool,
strip: bool,
is_single_threaded: bool,
dll_export_fns: bool,
link_mode_dynamic: bool,
valgrind_enabled: bool,
tsan_enabled: bool,
function_sections: bool,
include_compiler_rt: bool,
enable_stack_probing: bool,
red_zone: bool,
omit_frame_pointer: bool,
enable_time_report: bool,
enable_stack_report: bool,
test_is_evented: bool,
verbose_ir: bool,
verbose_llvm_ir: bool,
verbose_cimport: bool,
verbose_llvm_cpu_features: bool,
// Set by stage1
have_c_main: bool,
have_winmain: bool,
have_wwinmain: bool,
have_winmain_crt_startup: bool,
have_wwinmain_crt_startup: bool,
have_dllmain_crt_startup: bool,
pub fn build_object(mod: *Module) void {
zig_stage1_build_object(mod);
}
pub fn destroy(mod: *Module) void {
zig_stage1_destroy(mod);
}
};
pub const os_init = zig_stage1_os_init;
extern fn zig_stage1_os_init() void;
pub const create = zig_stage1_create;
extern fn zig_stage1_create(
optimize_mode: BuildMode,
main_pkg_path_ptr: [*]const u8,
main_pkg_path_len: usize,
root_src_path_ptr: [*]const u8,
root_src_path_len: usize,
zig_lib_dir_ptr: [*c]const u8,
zig_lib_dir_len: usize,
target: [*c]const Stage2Target,
is_test_build: bool,
) ?*Module;
extern fn zig_stage1_build_object(*Module) void;
extern fn zig_stage1_destroy(*Module) void;
// ABI warning
export fn stage2_panic(ptr: [*]const u8, len: usize) void {
@panic(ptr[0..len]);
}
// ABI warning
const Error = enum(c_int) {
None,
OutOfMemory,
InvalidFormat,
SemanticAnalyzeFail,
AccessDenied,
Interrupted,
SystemResources,
FileNotFound,
FileSystem,
FileTooBig,
DivByZero,
Overflow,
PathAlreadyExists,
Unexpected,
ExactDivRemainder,
NegativeDenominator,
ShiftedOutOneBits,
CCompileErrors,
EndOfFile,
IsDir,
NotDir,
UnsupportedOperatingSystem,
SharingViolation,
PipeBusy,
PrimitiveTypeNotFound,
CacheUnavailable,
PathTooLong,
CCompilerCannotFindFile,
NoCCompilerInstalled,
ReadingDepFile,
InvalidDepFile,
MissingArchitecture,
MissingOperatingSystem,
UnknownArchitecture,
UnknownOperatingSystem,
UnknownABI,
InvalidFilename,
DiskQuota,
DiskSpace,
UnexpectedWriteFailure,
UnexpectedSeekFailure,
UnexpectedFileTruncationFailure,
Unimplemented,
OperationAborted,
BrokenPipe,
NoSpaceLeft,
NotLazy,
IsAsync,
ImportOutsidePkgPath,
UnknownCpuModel,
UnknownCpuFeature,
InvalidCpuFeatures,
InvalidLlvmCpuFeaturesFormat,
UnknownApplicationBinaryInterface,
ASTUnitFailure,
BadPathName,
SymLinkLoop,
ProcessFdQuotaExceeded,
SystemFdQuotaExceeded,
NoDevice,
DeviceBusy,
UnableToSpawnCCompiler,
CCompilerExitCode,
CCompilerCrashed,
CCompilerCannotFindHeaders,
LibCRuntimeNotFound,
LibCStdLibHeaderNotFound,
LibCKernel32LibNotFound,
UnsupportedArchitecture,
WindowsSdkNotFound,
UnknownDynamicLinkerPath,
TargetHasNoDynamicLinker,
InvalidAbiVersion,
InvalidOperatingSystemVersion,
UnknownClangOption,
NestedResponseFile,
ZigIsTheCCompiler,
FileBusy,
Locked,
InvalidCharacter,
UnicodePointTooLarge,
};
// ABI warning
export fn stage2_version_string() [*:0]const u8 {
return build_options.version;
}
// ABI warning
export fn stage2_version() Stage2SemVer {
return .{
.major = build_options.semver.major,
.minor = build_options.semver.minor,
.patch = build_options.semver.patch,
};
}
// ABI warning
export fn stage2_attach_segfault_handler() void {
if (std.debug.runtime_safety and std.debug.have_segfault_handling_support) {
std.debug.attachSegfaultHandler();
}
}
// ABI warning
export fn stage2_progress_create() *std.Progress {
const ptr = std.heap.c_allocator.create(std.Progress) catch @panic("out of memory");
// If the terminal is dumb, we dont want to show the user all the
// output.
ptr.* = std.Progress{ .dont_print_on_dumb = true };
return ptr;
}
// ABI warning
export fn stage2_progress_destroy(progress: *std.Progress) void {
std.heap.c_allocator.destroy(progress);
}
// ABI warning
export fn stage2_progress_start_root(
progress: *std.Progress,
name_ptr: [*]const u8,
name_len: usize,
estimated_total_items: usize,
) *std.Progress.Node {
return progress.start(name_ptr[0..name_len], estimated_total_items);
}
// ABI warning
export fn stage2_progress_disable_tty(progress: *std.Progress) void {
progress.terminal = null;
}
// ABI warning
export fn stage2_progress_start(
node: *std.Progress.Node,
name_ptr: [*]const u8,
name_len: usize,
estimated_total_items: usize,
) *std.Progress.Node {
const child_node = std.heap.c_allocator.create(std.Progress.Node) catch @panic("out of memory");
child_node.* = node.start(
name_ptr[0..name_len],
estimated_total_items,
);
child_node.activate();
return child_node;
}
// ABI warning
export fn stage2_progress_end(node: *std.Progress.Node) void {
node.end();
if (&node.context.root != node) {
std.heap.c_allocator.destroy(node);
}
}
// ABI warning
export fn stage2_progress_complete_one(node: *std.Progress.Node) void {
node.completeOne();
}
// ABI warning
export fn stage2_progress_update_node(node: *std.Progress.Node, done_count: usize, total_count: usize) void {
node.setCompletedItems(done_count);
node.setEstimatedTotalItems(total_count);
node.activate();
node.context.maybeRefresh();
}
// ABI warning
pub const Stage2Target = extern struct {
arch: c_int,
os: OS,
abi: c_int,
is_native_os: bool,
is_native_cpu: bool,
llvm_cpu_name: ?[*:0]const u8,
llvm_cpu_features: ?[*:0]const u8,
llvm_target_abi: ?[*:0]const u8,
};
// ABI warning
const Stage2SemVer = extern struct {
major: u32,
minor: u32,
patch: u32,
};
// ABI warning
export fn stage2_cimport(
stage1: *Module,
c_src_ptr: [*]const u8,
c_src_len: usize,
out_zig_path_ptr: *[*]const u8,
out_zig_path_len: *usize,
out_errors_ptr: *[*]translate_c.ClangErrMsg,
out_errors_len: *usize,
) Error {
const comp = @intToPtr(*Compilation, stage1.userdata);
const c_src = c_src_ptr[0..c_src_len];
const result = comp.cImport(c_src) catch |err| switch (err) {
error.SystemResources => return .SystemResources,
error.OperationAborted => return .OperationAborted,
error.BrokenPipe => return .BrokenPipe,
error.DiskQuota => return .DiskQuota,
error.FileTooBig => return .FileTooBig,
error.NoSpaceLeft => return .NoSpaceLeft,
error.AccessDenied => return .AccessDenied,
error.OutOfMemory => return .OutOfMemory,
error.Unexpected => return .Unexpected,
error.InputOutput => return .FileSystem,
error.ASTUnitFailure => return .ASTUnitFailure,
error.CacheUnavailable => return .CacheUnavailable,
else => return .Unexpected,
};
out_zig_path_ptr.* = result.out_zig_path.ptr;
out_zig_path_len.* = result.out_zig_path.len;
out_errors_ptr.* = result.errors.ptr;
out_errors_len.* = result.errors.len;
if (result.errors.len != 0) return .CCompileErrors;
return Error.None;
}
export fn stage2_add_link_lib(
stage1: *Module,
lib_name_ptr: [*c]const u8,
lib_name_len: usize,
symbol_name_ptr: [*c]const u8,
symbol_name_len: usize,
) ?[*:0]const u8 {
_ = symbol_name_len;
_ = symbol_name_ptr;
const comp = @intToPtr(*Compilation, stage1.userdata);
const lib_name = std.ascii.allocLowerString(comp.gpa, lib_name_ptr[0..lib_name_len]) catch return "out of memory";
const target = comp.getTarget();
const is_libc = target_util.is_libc_lib_name(target, lib_name);
if (is_libc) {
if (!comp.bin_file.options.link_libc) {
return "dependency on libc must be explicitly specified in the build command";
}
return null;
}
if (target_util.is_libcpp_lib_name(target, lib_name)) {
if (!comp.bin_file.options.link_libcpp) {
return "dependency on libc++ must be explicitly specified in the build command";
}
return null;
}
if (!target.isWasm() and !comp.bin_file.options.pic) {
return std.fmt.allocPrintZ(
comp.gpa,
"dependency on dynamic library '{s}' requires enabling Position Independent Code. Fixed by `-l{s}` or `-fPIC`.",
.{ lib_name, lib_name },
) catch "out of memory";
}
comp.stage1AddLinkLib(lib_name) catch |err| {
return std.fmt.allocPrintZ(comp.gpa, "unable to add link lib '{s}': {s}", .{
lib_name, @errorName(err),
}) catch "out of memory";
};
return null;
}
export fn stage2_fetch_file(
stage1: *Module,
path_ptr: [*]const u8,
path_len: usize,
result_len: *usize,
) ?[*]const u8 {
const comp = @intToPtr(*Compilation, stage1.userdata);
const file_path = path_ptr[0..path_len];
const max_file_size = std.math.maxInt(u32);
const contents = if (comp.whole_cache_manifest) |man|
man.addFilePostFetch(file_path, max_file_size) catch return null
else
std.fs.cwd().readFileAlloc(comp.gpa, file_path, max_file_size) catch return null;
result_len.* = contents.len;
// TODO https://github.com/ziglang/zig/issues/3328#issuecomment-716749475
if (contents.len == 0) return @intToPtr(?[*]const u8, 0x1);
return contents.ptr;
}
export fn stage2_append_symbol(stage1: *Module, name_ptr: [*c]const u8, name_len: usize) Error {
if (name_len == 0) return Error.None;
const comp = @intToPtr(*Compilation, stage1.userdata);
const sym_name = comp.gpa.dupe(u8, name_ptr[0..name_len]) catch return Error.OutOfMemory;
comp.export_symbol_names.append(comp.gpa, sym_name) catch return Error.OutOfMemory;
return Error.None;
}