| author | |
| committer | |
| log | 4d59f775289b50a9529e801a6998dcd181945efb |
| tree | a0355897095a7066373e976a05c623fd9dbb252b |
| parent | 2a8fc1a18e7d9017262f5c7ee9669ca7d80ebaa6 |
15 files changed, 1661 insertions(+), 1666 deletions(-)
src-self-hosted/Compilation.zig created+1527| ... | ... | @@ -0,0 +1,1527 @@ |
| 1 | const Compilation = @This(); | |
| 2 | ||
| 3 | const std = @import("std"); | |
| 4 | const mem = std.mem; | |
| 5 | const Allocator = std.mem.Allocator; | |
| 6 | const Value = @import("value.zig").Value; | |
| 7 | const assert = std.debug.assert; | |
| 8 | const log = std.log.scoped(.compilation); | |
| 9 | const Target = std.Target; | |
| 10 | const target_util = @import("target.zig"); | |
| 11 | const Package = @import("Package.zig"); | |
| 12 | const link = @import("link.zig"); | |
| 13 | const trace = @import("tracy.zig").trace; | |
| 14 | const liveness = @import("liveness.zig"); | |
| 15 | const build_options = @import("build_options"); | |
| 16 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; | |
| 17 | const glibc = @import("glibc.zig"); | |
| 18 | const fatal = @import("main.zig").fatal; | |
| 19 | const ZigModule = @import("ZigModule.zig"); | |
| 20 | ||
| 21 | /// General-purpose allocator. Used for both temporary and long-term storage. | |
| 22 | gpa: *Allocator, | |
| 23 | /// Arena-allocated memory used during initialization. Should be untouched until deinit. | |
| 24 | arena_state: std.heap.ArenaAllocator.State, | |
| 25 | bin_file: *link.File, | |
| 26 | c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{}, | |
| 27 | ||
| 28 | link_error_flags: link.File.ErrorFlags = .{}, | |
| 29 | ||
| 30 | work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic), | |
| 31 | ||
| 32 | /// The ErrorMsg memory is owned by the `CObject`, using Compilation's general purpose allocator. | |
| 33 | failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *ErrorMsg) = .{}, | |
| 34 | ||
| 35 | keep_source_files_loaded: bool, | |
| 36 | use_clang: bool, | |
| 37 | sanitize_c: bool, | |
| 38 | /// When this is `true` it means invoking clang as a sub-process is expected to inherit | |
| 39 | /// stdin, stdout, stderr, and if it returns non success, to forward the exit code. | |
| 40 | /// Otherwise we attempt to parse the error messages and expose them via the Compilation API. | |
| 41 | /// This is `true` for `zig cc`, `zig c++`, and `zig translate-c`. | |
| 42 | clang_passthrough_mode: bool, | |
| 43 | /// Whether to print clang argvs to stdout. | |
| 44 | debug_cc: bool, | |
| 45 | disable_c_depfile: bool, | |
| 46 | ||
| 47 | c_source_files: []const CSourceFile, | |
| 48 | clang_argv: []const []const u8, | |
| 49 | cache_parent: *std.cache_hash.Cache, | |
| 50 | /// Path to own executable for invoking `zig clang`. | |
| 51 | self_exe_path: ?[]const u8, | |
| 52 | zig_lib_directory: Directory, | |
| 53 | zig_cache_directory: Directory, | |
| 54 | libc_include_dir_list: []const []const u8, | |
| 55 | rand: *std.rand.Random, | |
| 56 | ||
| 57 | /// Populated when we build libc++.a. A WorkItem to build this is placed in the queue | |
| 58 | /// and resolved before calling linker.flush(). | |
| 59 | libcxx_static_lib: ?[]const u8 = null, | |
| 60 | /// Populated when we build libc++abi.a. A WorkItem to build this is placed in the queue | |
| 61 | /// and resolved before calling linker.flush(). | |
| 62 | libcxxabi_static_lib: ?[]const u8 = null, | |
| 63 | /// Populated when we build libunwind.a. A WorkItem to build this is placed in the queue | |
| 64 | /// and resolved before calling linker.flush(). | |
| 65 | libunwind_static_lib: ?[]const u8 = null, | |
| 66 | /// Populated when we build c.a. A WorkItem to build this is placed in the queue | |
| 67 | /// and resolved before calling linker.flush(). | |
| 68 | libc_static_lib: ?[]const u8 = null, | |
| 69 | ||
| 70 | /// For example `Scrt1.o` and `libc.so.6`. These are populated after building libc from source, | |
| 71 | /// The set of needed CRT (C runtime) files differs depending on the target and compilation settings. | |
| 72 | /// The key is the basename, and the value is the absolute path to the completed build artifact. | |
| 73 | crt_files: std.StringHashMapUnmanaged([]const u8) = .{}, | |
| 74 | ||
| 75 | /// Keeping track of this possibly open resource so we can close it later. | |
| 76 | owned_link_dir: ?std.fs.Dir, | |
| 77 | ||
| 78 | pub const InnerError = ZigModule.InnerError; | |
| 79 | ||
| 80 | /// For passing to a C compiler. | |
| 81 | pub const CSourceFile = struct { | |
| 82 | src_path: []const u8, | |
| 83 | extra_flags: []const []const u8 = &[0][]const u8{}, | |
| 84 | }; | |
| 85 | ||
| 86 | const WorkItem = union(enum) { | |
| 87 | /// Write the machine code for a Decl to the output file. | |
| 88 | codegen_decl: *ZigModule.Decl, | |
| 89 | /// The Decl needs to be analyzed and possibly export itself. | |
| 90 | /// It may have already be analyzed, or it may have been determined | |
| 91 | /// to be outdated; in this case perform semantic analysis again. | |
| 92 | analyze_decl: *ZigModule.Decl, | |
| 93 | /// The source file containing the Decl has been updated, and so the | |
| 94 | /// Decl may need its line number information updated in the debug info. | |
| 95 | update_line_number: *ZigModule.Decl, | |
| 96 | /// Invoke the Clang compiler to create an object file, which gets linked | |
| 97 | /// with the Compilation. | |
| 98 | c_object: *CObject, | |
| 99 | ||
| 100 | /// one of the glibc static objects | |
| 101 | glibc_crt_file: glibc.CRTFile, | |
| 102 | /// one of the glibc shared objects | |
| 103 | glibc_so: *const glibc.Lib, | |
| 104 | }; | |
| 105 | ||
| 106 | pub const CObject = struct { | |
| 107 | /// Relative to cwd. Owned by arena. | |
| 108 | src_path: []const u8, | |
| 109 | /// Owned by arena. | |
| 110 | extra_flags: []const []const u8, | |
| 111 | arena: std.heap.ArenaAllocator.State, | |
| 112 | status: union(enum) { | |
| 113 | new, | |
| 114 | success: struct { | |
| 115 | /// The outputted result. Owned by gpa. | |
| 116 | object_path: []u8, | |
| 117 | /// This is a file system lock on the cache hash manifest representing this | |
| 118 | /// object. It prevents other invocations of the Zig compiler from interfering | |
| 119 | /// with this object until released. | |
| 120 | lock: std.cache_hash.Lock, | |
| 121 | }, | |
| 122 | /// There will be a corresponding ErrorMsg in Compilation.failed_c_objects. | |
| 123 | failure, | |
| 124 | }, | |
| 125 | ||
| 126 | /// Returns if there was failure. | |
| 127 | pub fn clearStatus(self: *CObject, gpa: *Allocator) bool { | |
| 128 | switch (self.status) { | |
| 129 | .new => return false, | |
| 130 | .failure => { | |
| 131 | self.status = .new; | |
| 132 | return true; | |
| 133 | }, | |
| 134 | .success => |*success| { | |
| 135 | gpa.free(success.object_path); | |
| 136 | success.lock.release(); | |
| 137 | self.status = .new; | |
| 138 | return false; | |
| 139 | }, | |
| 140 | } | |
| 141 | } | |
| 142 | ||
| 143 | pub fn destroy(self: *CObject, gpa: *Allocator) void { | |
| 144 | _ = self.clearStatus(gpa); | |
| 145 | self.arena.promote(gpa).deinit(); | |
| 146 | } | |
| 147 | }; | |
| 148 | ||
| 149 | pub const AllErrors = struct { | |
| 150 | arena: std.heap.ArenaAllocator.State, | |
| 151 | list: []const Message, | |
| 152 | ||
| 153 | pub const Message = struct { | |
| 154 | src_path: []const u8, | |
| 155 | line: usize, | |
| 156 | column: usize, | |
| 157 | byte_offset: usize, | |
| 158 | msg: []const u8, | |
| 159 | }; | |
| 160 | ||
| 161 | pub fn deinit(self: *AllErrors, gpa: *Allocator) void { | |
| 162 | self.arena.promote(gpa).deinit(); | |
| 163 | } | |
| 164 | ||
| 165 | fn add( | |
| 166 | arena: *std.heap.ArenaAllocator, | |
| 167 | errors: *std.ArrayList(Message), | |
| 168 | sub_file_path: []const u8, | |
| 169 | source: []const u8, | |
| 170 | simple_err_msg: ErrorMsg, | |
| 171 | ) !void { | |
| 172 | const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset); | |
| 173 | try errors.append(.{ | |
| 174 | .src_path = try arena.allocator.dupe(u8, sub_file_path), | |
| 175 | .msg = try arena.allocator.dupe(u8, simple_err_msg.msg), | |
| 176 | .byte_offset = simple_err_msg.byte_offset, | |
| 177 | .line = loc.line, | |
| 178 | .column = loc.column, | |
| 179 | }); | |
| 180 | } | |
| 181 | }; | |
| 182 | ||
| 183 | pub const Directory = struct { | |
| 184 | /// This field is redundant for operations that can act on the open directory handle | |
| 185 | /// directly, but it is needed when passing the directory to a child process. | |
| 186 | /// `null` means cwd. | |
| 187 | path: ?[]const u8, | |
| 188 | handle: std.fs.Dir, | |
| 189 | }; | |
| 190 | ||
| 191 | pub const EmitLoc = struct { | |
| 192 | /// If this is `null` it means the file will be output to the cache directory. | |
| 193 | /// When provided, both the open file handle and the path name must outlive the `Compilation`. | |
| 194 | directory: ?Compilation.Directory, | |
| 195 | /// This may not have sub-directories in it. | |
| 196 | basename: []const u8, | |
| 197 | }; | |
| 198 | ||
| 199 | pub const InitOptions = struct { | |
| 200 | zig_lib_directory: Directory, | |
| 201 | zig_cache_directory: Directory, | |
| 202 | target: Target, | |
| 203 | root_name: []const u8, | |
| 204 | root_pkg: ?*Package, | |
| 205 | output_mode: std.builtin.OutputMode, | |
| 206 | rand: *std.rand.Random, | |
| 207 | dynamic_linker: ?[]const u8 = null, | |
| 208 | /// `null` means to not emit a binary file. | |
| 209 | emit_bin: ?EmitLoc, | |
| 210 | /// `null` means to not emit a C header file. | |
| 211 | emit_h: ?EmitLoc = null, | |
| 212 | link_mode: ?std.builtin.LinkMode = null, | |
| 213 | object_format: ?std.builtin.ObjectFormat = null, | |
| 214 | optimize_mode: std.builtin.Mode = .Debug, | |
| 215 | keep_source_files_loaded: bool = false, | |
| 216 | clang_argv: []const []const u8 = &[0][]const u8{}, | |
| 217 | lld_argv: []const []const u8 = &[0][]const u8{}, | |
| 218 | lib_dirs: []const []const u8 = &[0][]const u8{}, | |
| 219 | rpath_list: []const []const u8 = &[0][]const u8{}, | |
| 220 | c_source_files: []const CSourceFile = &[0]CSourceFile{}, | |
| 221 | link_objects: []const []const u8 = &[0][]const u8{}, | |
| 222 | framework_dirs: []const []const u8 = &[0][]const u8{}, | |
| 223 | frameworks: []const []const u8 = &[0][]const u8{}, | |
| 224 | system_libs: []const []const u8 = &[0][]const u8{}, | |
| 225 | link_libc: bool = false, | |
| 226 | link_libcpp: bool = false, | |
| 227 | want_pic: ?bool = null, | |
| 228 | want_sanitize_c: ?bool = null, | |
| 229 | want_stack_check: ?bool = null, | |
| 230 | want_valgrind: ?bool = null, | |
| 231 | use_llvm: ?bool = null, | |
| 232 | use_lld: ?bool = null, | |
| 233 | use_clang: ?bool = null, | |
| 234 | rdynamic: bool = false, | |
| 235 | strip: bool = false, | |
| 236 | single_threaded: bool = false, | |
| 237 | is_native_os: bool, | |
| 238 | link_eh_frame_hdr: bool = false, | |
| 239 | linker_script: ?[]const u8 = null, | |
| 240 | version_script: ?[]const u8 = null, | |
| 241 | override_soname: ?[]const u8 = null, | |
| 242 | linker_gc_sections: ?bool = null, | |
| 243 | function_sections: ?bool = null, | |
| 244 | linker_allow_shlib_undefined: ?bool = null, | |
| 245 | linker_bind_global_refs_locally: ?bool = null, | |
| 246 | disable_c_depfile: bool = false, | |
| 247 | linker_z_nodelete: bool = false, | |
| 248 | linker_z_defs: bool = false, | |
| 249 | clang_passthrough_mode: bool = false, | |
| 250 | debug_cc: bool = false, | |
| 251 | debug_link: bool = false, | |
| 252 | stack_size_override: ?u64 = null, | |
| 253 | self_exe_path: ?[]const u8 = null, | |
| 254 | version: ?std.builtin.Version = null, | |
| 255 | libc_installation: ?*const LibCInstallation = null, | |
| 256 | }; | |
| 257 | ||
| 258 | pub fn create(gpa: *Allocator, options: InitOptions) !*Compilation { | |
| 259 | const comp: *Compilation = comp: { | |
| 260 | // For allocations that have the same lifetime as Compilation. This arena is used only during this | |
| 261 | // initialization and then is freed in deinit(). | |
| 262 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); | |
| 263 | errdefer arena_allocator.deinit(); | |
| 264 | const arena = &arena_allocator.allocator; | |
| 265 | ||
| 266 | // We put the `Compilation` itself in the arena. Freeing the arena will free the module. | |
| 267 | // It's initialized later after we prepare the initialization options. | |
| 268 | const comp = try arena.create(Compilation); | |
| 269 | const root_name = try arena.dupe(u8, options.root_name); | |
| 270 | ||
| 271 | const ofmt = options.object_format orelse options.target.getObjectFormat(); | |
| 272 | ||
| 273 | // Make a decision on whether to use LLD or our own linker. | |
| 274 | const use_lld = if (options.use_lld) |explicit| explicit else blk: { | |
| 275 | if (!build_options.have_llvm) | |
| 276 | break :blk false; | |
| 277 | ||
| 278 | if (ofmt == .c) | |
| 279 | break :blk false; | |
| 280 | ||
| 281 | // Our linker can't handle objects or most advanced options yet. | |
| 282 | if (options.link_objects.len != 0 or | |
| 283 | options.c_source_files.len != 0 or | |
| 284 | options.frameworks.len != 0 or | |
| 285 | options.system_libs.len != 0 or | |
| 286 | options.link_libc or options.link_libcpp or | |
| 287 | options.link_eh_frame_hdr or | |
| 288 | options.linker_script != null or options.version_script != null) | |
| 289 | { | |
| 290 | break :blk true; | |
| 291 | } | |
| 292 | break :blk false; | |
| 293 | }; | |
| 294 | ||
| 295 | // Make a decision on whether to use LLVM or our own backend. | |
| 296 | const use_llvm = if (options.use_llvm) |explicit| explicit else blk: { | |
| 297 | // We would want to prefer LLVM for release builds when it is available, however | |
| 298 | // we don't have an LLVM backend yet :) | |
| 299 | // We would also want to prefer LLVM for architectures that we don't have self-hosted support for too. | |
| 300 | break :blk false; | |
| 301 | }; | |
| 302 | ||
| 303 | const must_dynamic_link = dl: { | |
| 304 | if (target_util.cannotDynamicLink(options.target)) | |
| 305 | break :dl false; | |
| 306 | if (target_util.osRequiresLibC(options.target)) | |
| 307 | break :dl true; | |
| 308 | if (options.link_libc and options.target.isGnuLibC()) | |
| 309 | break :dl true; | |
| 310 | if (options.system_libs.len != 0) | |
| 311 | break :dl true; | |
| 312 | ||
| 313 | break :dl false; | |
| 314 | }; | |
| 315 | const default_link_mode: std.builtin.LinkMode = if (must_dynamic_link) .Dynamic else .Static; | |
| 316 | const link_mode: std.builtin.LinkMode = if (options.link_mode) |lm| blk: { | |
| 317 | if (lm == .Static and must_dynamic_link) { | |
| 318 | return error.UnableToStaticLink; | |
| 319 | } | |
| 320 | break :blk lm; | |
| 321 | } else default_link_mode; | |
| 322 | ||
| 323 | const libc_dirs = try detectLibCIncludeDirs( | |
| 324 | arena, | |
| 325 | options.zig_lib_directory.path.?, | |
| 326 | options.target, | |
| 327 | options.is_native_os, | |
| 328 | options.link_libc, | |
| 329 | options.libc_installation, | |
| 330 | ); | |
| 331 | ||
| 332 | const must_pic: bool = b: { | |
| 333 | if (target_util.requiresPIC(options.target, options.link_libc)) | |
| 334 | break :b true; | |
| 335 | break :b link_mode == .Dynamic; | |
| 336 | }; | |
| 337 | const pic = options.want_pic orelse must_pic; | |
| 338 | ||
| 339 | if (options.emit_h != null) fatal("-femit-h not supported yet", .{}); // TODO | |
| 340 | ||
| 341 | const emit_bin = options.emit_bin orelse fatal("-fno-emit-bin not supported yet", .{}); // TODO | |
| 342 | ||
| 343 | // Make a decision on whether to use Clang for translate-c and compiling C files. | |
| 344 | const use_clang = if (options.use_clang) |explicit| explicit else blk: { | |
| 345 | if (build_options.have_llvm) { | |
| 346 | // Can't use it if we don't have it! | |
| 347 | break :blk false; | |
| 348 | } | |
| 349 | // It's not planned to do our own translate-c or C compilation. | |
| 350 | break :blk true; | |
| 351 | }; | |
| 352 | ||
| 353 | const is_safe_mode = switch (options.optimize_mode) { | |
| 354 | .Debug, .ReleaseSafe => true, | |
| 355 | .ReleaseFast, .ReleaseSmall => false, | |
| 356 | }; | |
| 357 | ||
| 358 | const sanitize_c = options.want_sanitize_c orelse is_safe_mode; | |
| 359 | ||
| 360 | const stack_check: bool = b: { | |
| 361 | if (!target_util.supportsStackProbing(options.target)) | |
| 362 | break :b false; | |
| 363 | break :b options.want_stack_check orelse is_safe_mode; | |
| 364 | }; | |
| 365 | ||
| 366 | const valgrind: bool = b: { | |
| 367 | if (!target_util.hasValgrindSupport(options.target)) | |
| 368 | break :b false; | |
| 369 | break :b options.want_valgrind orelse (options.optimize_mode == .Debug); | |
| 370 | }; | |
| 371 | ||
| 372 | const single_threaded = options.single_threaded or target_util.isSingleThreaded(options.target); | |
| 373 | ||
| 374 | // We put everything into the cache hash that *cannot be modified during an incremental update*. | |
| 375 | // For example, one cannot change the target between updates, but one can change source files, | |
| 376 | // so the target goes into the cache hash, but source files do not. This is so that we can | |
| 377 | // find the same binary and incrementally update it even if there are modified source files. | |
| 378 | // We do this even if outputting to the current directory because we need somewhere to store | |
| 379 | // incremental compilation metadata. | |
| 380 | const cache = try arena.create(std.cache_hash.Cache); | |
| 381 | cache.* = .{ | |
| 382 | .gpa = gpa, | |
| 383 | .manifest_dir = try options.zig_cache_directory.handle.makeOpenPath("h", .{}), | |
| 384 | }; | |
| 385 | errdefer cache.manifest_dir.close(); | |
| 386 | ||
| 387 | // This is shared hasher state common to zig source and all C source files. | |
| 388 | cache.hash.addBytes(build_options.version); | |
| 389 | cache.hash.addBytes(options.zig_lib_directory.path orelse "."); | |
| 390 | cache.hash.add(options.optimize_mode); | |
| 391 | cache.hash.add(options.target.cpu.arch); | |
| 392 | cache.hash.addBytes(options.target.cpu.model.name); | |
| 393 | cache.hash.add(options.target.cpu.features.ints); | |
| 394 | cache.hash.add(options.target.os.tag); | |
| 395 | cache.hash.add(options.target.abi); | |
| 396 | cache.hash.add(ofmt); | |
| 397 | cache.hash.add(pic); | |
| 398 | cache.hash.add(stack_check); | |
| 399 | cache.hash.add(link_mode); | |
| 400 | cache.hash.add(options.strip); | |
| 401 | cache.hash.add(options.link_libc); | |
| 402 | cache.hash.add(options.output_mode); | |
| 403 | // TODO audit this and make sure everything is in it | |
| 404 | ||
| 405 | const zig_module: ?*ZigModule = if (options.root_pkg) |root_pkg| blk: { | |
| 406 | // Options that are specific to zig source files, that cannot be | |
| 407 | // modified between incremental updates. | |
| 408 | var hash = cache.hash; | |
| 409 | ||
| 410 | hash.add(valgrind); | |
| 411 | hash.add(single_threaded); | |
| 412 | switch (options.target.os.getVersionRange()) { | |
| 413 | .linux => |linux| { | |
| 414 | hash.add(linux.range.min); | |
| 415 | hash.add(linux.range.max); | |
| 416 | hash.add(linux.glibc); | |
| 417 | }, | |
| 418 | .windows => |windows| { | |
| 419 | hash.add(windows.min); | |
| 420 | hash.add(windows.max); | |
| 421 | }, | |
| 422 | .semver => |semver| { | |
| 423 | hash.add(semver.min); | |
| 424 | hash.add(semver.max); | |
| 425 | }, | |
| 426 | .none => {}, | |
| 427 | } | |
| 428 | ||
| 429 | const digest = hash.final(); | |
| 430 | const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); | |
| 431 | var artifact_dir = try options.zig_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); | |
| 432 | errdefer artifact_dir.close(); | |
| 433 | const zig_cache_artifact_directory: Directory = .{ | |
| 434 | .handle = artifact_dir, | |
| 435 | .path = if (options.zig_cache_directory.path) |p| | |
| 436 | try std.fs.path.join(arena, &[_][]const u8{ p, artifact_sub_dir }) | |
| 437 | else | |
| 438 | artifact_sub_dir, | |
| 439 | }; | |
| 440 | ||
| 441 | // TODO when we implement serialization and deserialization of incremental compilation metadata, | |
| 442 | // this is where we would load it. We have open a handle to the directory where | |
| 443 | // the output either already is, or will be. | |
| 444 | // However we currently do not have serialization of such metadata, so for now | |
| 445 | // we set up an empty ZigModule that does the entire compilation fresh. | |
| 446 | ||
| 447 | const root_scope = rs: { | |
| 448 | if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) { | |
| 449 | const root_scope = try gpa.create(ZigModule.Scope.File); | |
| 450 | root_scope.* = .{ | |
| 451 | .sub_file_path = root_pkg.root_src_path, | |
| 452 | .source = .{ .unloaded = {} }, | |
| 453 | .contents = .{ .not_available = {} }, | |
| 454 | .status = .never_loaded, | |
| 455 | .root_container = .{ | |
| 456 | .file_scope = root_scope, | |
| 457 | .decls = .{}, | |
| 458 | }, | |
| 459 | }; | |
| 460 | break :rs &root_scope.base; | |
| 461 | } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) { | |
| 462 | const root_scope = try gpa.create(ZigModule.Scope.ZIRModule); | |
| 463 | root_scope.* = .{ | |
| 464 | .sub_file_path = root_pkg.root_src_path, | |
| 465 | .source = .{ .unloaded = {} }, | |
| 466 | .contents = .{ .not_available = {} }, | |
| 467 | .status = .never_loaded, | |
| 468 | .decls = .{}, | |
| 469 | }; | |
| 470 | break :rs &root_scope.base; | |
| 471 | } else { | |
| 472 | unreachable; | |
| 473 | } | |
| 474 | }; | |
| 475 | ||
| 476 | const zig_module = try arena.create(ZigModule); | |
| 477 | zig_module.* = .{ | |
| 478 | .gpa = gpa, | |
| 479 | .comp = comp, | |
| 480 | .root_pkg = root_pkg, | |
| 481 | .root_scope = root_scope, | |
| 482 | .zig_cache_artifact_directory = zig_cache_artifact_directory, | |
| 483 | }; | |
| 484 | break :blk zig_module; | |
| 485 | } else null; | |
| 486 | errdefer if (zig_module) |zm| zm.deinit(); | |
| 487 | ||
| 488 | // For resource management purposes. | |
| 489 | var owned_link_dir: ?std.fs.Dir = null; | |
| 490 | errdefer if (owned_link_dir) |*dir| dir.close(); | |
| 491 | ||
| 492 | const bin_directory = emit_bin.directory orelse blk: { | |
| 493 | if (zig_module) |zm| break :blk zm.zig_cache_artifact_directory; | |
| 494 | ||
| 495 | const digest = cache.hash.peek(); | |
| 496 | const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); | |
| 497 | var artifact_dir = try options.zig_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); | |
| 498 | owned_link_dir = artifact_dir; | |
| 499 | const link_artifact_directory: Directory = .{ | |
| 500 | .handle = artifact_dir, | |
| 501 | .path = if (options.zig_cache_directory.path) |p| | |
| 502 | try std.fs.path.join(arena, &[_][]const u8{ p, artifact_sub_dir }) | |
| 503 | else | |
| 504 | artifact_sub_dir, | |
| 505 | }; | |
| 506 | break :blk link_artifact_directory; | |
| 507 | }; | |
| 508 | ||
| 509 | const bin_file = try link.File.openPath(gpa, .{ | |
| 510 | .directory = bin_directory, | |
| 511 | .sub_path = emit_bin.basename, | |
| 512 | .root_name = root_name, | |
| 513 | .zig_module = zig_module, | |
| 514 | .target = options.target, | |
| 515 | .dynamic_linker = options.dynamic_linker, | |
| 516 | .output_mode = options.output_mode, | |
| 517 | .link_mode = link_mode, | |
| 518 | .object_format = ofmt, | |
| 519 | .optimize_mode = options.optimize_mode, | |
| 520 | .use_lld = use_lld, | |
| 521 | .use_llvm = use_llvm, | |
| 522 | .link_libc = options.link_libc, | |
| 523 | .link_libcpp = options.link_libcpp, | |
| 524 | .objects = options.link_objects, | |
| 525 | .frameworks = options.frameworks, | |
| 526 | .framework_dirs = options.framework_dirs, | |
| 527 | .system_libs = options.system_libs, | |
| 528 | .lib_dirs = options.lib_dirs, | |
| 529 | .rpath_list = options.rpath_list, | |
| 530 | .strip = options.strip, | |
| 531 | .is_native_os = options.is_native_os, | |
| 532 | .function_sections = options.function_sections orelse false, | |
| 533 | .allow_shlib_undefined = options.linker_allow_shlib_undefined, | |
| 534 | .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false, | |
| 535 | .z_nodelete = options.linker_z_nodelete, | |
| 536 | .z_defs = options.linker_z_defs, | |
| 537 | .stack_size_override = options.stack_size_override, | |
| 538 | .linker_script = options.linker_script, | |
| 539 | .version_script = options.version_script, | |
| 540 | .gc_sections = options.linker_gc_sections, | |
| 541 | .eh_frame_hdr = options.link_eh_frame_hdr, | |
| 542 | .rdynamic = options.rdynamic, | |
| 543 | .extra_lld_args = options.lld_argv, | |
| 544 | .override_soname = options.override_soname, | |
| 545 | .version = options.version, | |
| 546 | .libc_installation = libc_dirs.libc_installation, | |
| 547 | .pic = pic, | |
| 548 | .valgrind = valgrind, | |
| 549 | .stack_check = stack_check, | |
| 550 | .single_threaded = single_threaded, | |
| 551 | .debug_link = options.debug_link, | |
| 552 | }); | |
| 553 | errdefer bin_file.destroy(); | |
| 554 | ||
| 555 | comp.* = .{ | |
| 556 | .gpa = gpa, | |
| 557 | .arena_state = arena_allocator.state, | |
| 558 | .zig_lib_directory = options.zig_lib_directory, | |
| 559 | .zig_cache_directory = options.zig_cache_directory, | |
| 560 | .bin_file = bin_file, | |
| 561 | .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa), | |
| 562 | .keep_source_files_loaded = options.keep_source_files_loaded, | |
| 563 | .use_clang = use_clang, | |
| 564 | .clang_argv = options.clang_argv, | |
| 565 | .c_source_files = options.c_source_files, | |
| 566 | .cache_parent = cache, | |
| 567 | .self_exe_path = options.self_exe_path, | |
| 568 | .libc_include_dir_list = libc_dirs.libc_include_dir_list, | |
| 569 | .sanitize_c = sanitize_c, | |
| 570 | .rand = options.rand, | |
| 571 | .clang_passthrough_mode = options.clang_passthrough_mode, | |
| 572 | .debug_cc = options.debug_cc, | |
| 573 | .disable_c_depfile = options.disable_c_depfile, | |
| 574 | .owned_link_dir = owned_link_dir, | |
| 575 | }; | |
| 576 | break :comp comp; | |
| 577 | }; | |
| 578 | errdefer comp.destroy(); | |
| 579 | ||
| 580 | // Add a `CObject` for each `c_source_files`. | |
| 581 | try comp.c_object_table.ensureCapacity(gpa, options.c_source_files.len); | |
| 582 | for (options.c_source_files) |c_source_file| { | |
| 583 | var local_arena = std.heap.ArenaAllocator.init(gpa); | |
| 584 | errdefer local_arena.deinit(); | |
| 585 | ||
| 586 | const c_object = try local_arena.allocator.create(CObject); | |
| 587 | ||
| 588 | c_object.* = .{ | |
| 589 | .status = .{ .new = {} }, | |
| 590 | // TODO look into refactoring to turn these 2 fields simply into a CSourceFile | |
| 591 | .src_path = try local_arena.allocator.dupe(u8, c_source_file.src_path), | |
| 592 | .extra_flags = try local_arena.allocator.dupe([]const u8, c_source_file.extra_flags), | |
| 593 | .arena = local_arena.state, | |
| 594 | }; | |
| 595 | comp.c_object_table.putAssumeCapacityNoClobber(c_object, {}); | |
| 596 | } | |
| 597 | ||
| 598 | // If we need to build glibc for the target, add work items for it. | |
| 599 | // We go through the work queue so that building can be done in parallel. | |
| 600 | if (comp.wantBuildGLibCFromSource()) { | |
| 601 | try comp.addBuildingGLibCWorkItems(); | |
| 602 | } | |
| 603 | ||
| 604 | return comp; | |
| 605 | } | |
| 606 | ||
| 607 | pub fn destroy(self: *Compilation) void { | |
| 608 | const optional_zig_module = self.bin_file.options.zig_module; | |
| 609 | self.bin_file.destroy(); | |
| 610 | if (optional_zig_module) |zig_module| zig_module.deinit(); | |
| 611 | ||
| 612 | const gpa = self.gpa; | |
| 613 | self.work_queue.deinit(); | |
| 614 | ||
| 615 | { | |
| 616 | var it = self.crt_files.iterator(); | |
| 617 | while (it.next()) |entry| { | |
| 618 | gpa.free(entry.key); | |
| 619 | gpa.free(entry.value); | |
| 620 | } | |
| 621 | self.crt_files.deinit(gpa); | |
| 622 | } | |
| 623 | ||
| 624 | for (self.c_object_table.items()) |entry| { | |
| 625 | entry.key.destroy(gpa); | |
| 626 | } | |
| 627 | self.c_object_table.deinit(gpa); | |
| 628 | ||
| 629 | for (self.failed_c_objects.items()) |entry| { | |
| 630 | entry.value.destroy(gpa); | |
| 631 | } | |
| 632 | self.failed_c_objects.deinit(gpa); | |
| 633 | ||
| 634 | self.cache_parent.manifest_dir.close(); | |
| 635 | if (self.owned_link_dir) |*dir| dir.close(); | |
| 636 | ||
| 637 | // This destroys `self`. | |
| 638 | self.arena_state.promote(gpa).deinit(); | |
| 639 | } | |
| 640 | ||
| 641 | pub fn getTarget(self: Compilation) Target { | |
| 642 | return self.bin_file.options.target; | |
| 643 | } | |
| 644 | ||
| 645 | /// Detect changes to source files, perform semantic analysis, and update the output files. | |
| 646 | pub fn update(self: *Compilation) !void { | |
| 647 | const tracy = trace(@src()); | |
| 648 | defer tracy.end(); | |
| 649 | ||
| 650 | // For compiling C objects, we rely on the cache hash system to avoid duplicating work. | |
| 651 | // TODO Look into caching this data in memory to improve performance. | |
| 652 | // Add a WorkItem for each C object. | |
| 653 | try self.work_queue.ensureUnusedCapacity(self.c_object_table.items().len); | |
| 654 | for (self.c_object_table.items()) |entry| { | |
| 655 | self.work_queue.writeItemAssumeCapacity(.{ .c_object = entry.key }); | |
| 656 | } | |
| 657 | ||
| 658 | if (self.bin_file.options.zig_module) |zig_module| { | |
| 659 | zig_module.generation += 1; | |
| 660 | ||
| 661 | // TODO Detect which source files changed. | |
| 662 | // Until then we simulate a full cache miss. Source files could have been loaded for any reason; | |
| 663 | // to force a refresh we unload now. | |
| 664 | if (zig_module.root_scope.cast(ZigModule.Scope.File)) |zig_file| { | |
| 665 | zig_file.unload(zig_module.gpa); | |
| 666 | zig_module.analyzeContainer(&zig_file.root_container) catch |err| switch (err) { | |
| 667 | error.AnalysisFail => { | |
| 668 | assert(self.totalErrorCount() != 0); | |
| 669 | }, | |
| 670 | else => |e| return e, | |
| 671 | }; | |
| 672 | } else if (zig_module.root_scope.cast(ZigModule.Scope.ZIRModule)) |zir_module| { | |
| 673 | zir_module.unload(zig_module.gpa); | |
| 674 | zig_module.analyzeRootZIRModule(zir_module) catch |err| switch (err) { | |
| 675 | error.AnalysisFail => { | |
| 676 | assert(self.totalErrorCount() != 0); | |
| 677 | }, | |
| 678 | else => |e| return e, | |
| 679 | }; | |
| 680 | } | |
| 681 | } | |
| 682 | ||
| 683 | try self.performAllTheWork(); | |
| 684 | ||
| 685 | if (self.bin_file.options.zig_module) |zig_module| { | |
| 686 | // Process the deletion set. | |
| 687 | while (zig_module.deletion_set.popOrNull()) |decl| { | |
| 688 | if (decl.dependants.items().len != 0) { | |
| 689 | decl.deletion_flag = false; | |
| 690 | continue; | |
| 691 | } | |
| 692 | try zig_module.deleteDecl(decl); | |
| 693 | } | |
| 694 | } | |
| 695 | ||
| 696 | // This is needed before reading the error flags. | |
| 697 | try self.bin_file.flush(self); | |
| 698 | ||
| 699 | self.link_error_flags = self.bin_file.errorFlags(); | |
| 700 | ||
| 701 | // If there are any errors, we anticipate the source files being loaded | |
| 702 | // to report error messages. Otherwise we unload all source files to save memory. | |
| 703 | if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) { | |
| 704 | if (self.bin_file.options.zig_module) |zig_module| { | |
| 705 | zig_module.root_scope.unload(self.gpa); | |
| 706 | } | |
| 707 | } | |
| 708 | } | |
| 709 | ||
| 710 | /// Having the file open for writing is problematic as far as executing the | |
| 711 | /// binary is concerned. This will remove the write flag, or close the file, | |
| 712 | /// or whatever is needed so that it can be executed. | |
| 713 | /// After this, one must call` makeFileWritable` before calling `update`. | |
| 714 | pub fn makeBinFileExecutable(self: *Compilation) !void { | |
| 715 | return self.bin_file.makeExecutable(); | |
| 716 | } | |
| 717 | ||
| 718 | pub fn makeBinFileWritable(self: *Compilation) !void { | |
| 719 | return self.bin_file.makeWritable(); | |
| 720 | } | |
| 721 | ||
| 722 | pub fn totalErrorCount(self: *Compilation) usize { | |
| 723 | var total: usize = self.failed_c_objects.items().len; | |
| 724 | ||
| 725 | if (self.bin_file.options.zig_module) |zig_module| { | |
| 726 | total += zig_module.failed_decls.items().len + | |
| 727 | zig_module.failed_exports.items().len + | |
| 728 | zig_module.failed_files.items().len; | |
| 729 | } | |
| 730 | ||
| 731 | // The "no entry point found" error only counts if there are no other errors. | |
| 732 | if (total == 0) { | |
| 733 | return @boolToInt(self.link_error_flags.no_entry_point_found); | |
| 734 | } | |
| 735 | ||
| 736 | return total; | |
| 737 | } | |
| 738 | ||
| 739 | pub fn getAllErrorsAlloc(self: *Compilation) !AllErrors { | |
| 740 | var arena = std.heap.ArenaAllocator.init(self.gpa); | |
| 741 | errdefer arena.deinit(); | |
| 742 | ||
| 743 | var errors = std.ArrayList(AllErrors.Message).init(self.gpa); | |
| 744 | defer errors.deinit(); | |
| 745 | ||
| 746 | for (self.failed_c_objects.items()) |entry| { | |
| 747 | const c_object = entry.key; | |
| 748 | const err_msg = entry.value; | |
| 749 | try AllErrors.add(&arena, &errors, c_object.src_path, "", err_msg.*); | |
| 750 | } | |
| 751 | if (self.bin_file.options.zig_module) |zig_module| { | |
| 752 | for (zig_module.failed_files.items()) |entry| { | |
| 753 | const scope = entry.key; | |
| 754 | const err_msg = entry.value; | |
| 755 | const source = try scope.getSource(zig_module); | |
| 756 | try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*); | |
| 757 | } | |
| 758 | for (zig_module.failed_decls.items()) |entry| { | |
| 759 | const decl = entry.key; | |
| 760 | const err_msg = entry.value; | |
| 761 | const source = try decl.scope.getSource(zig_module); | |
| 762 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); | |
| 763 | } | |
| 764 | for (zig_module.failed_exports.items()) |entry| { | |
| 765 | const decl = entry.key.owner_decl; | |
| 766 | const err_msg = entry.value; | |
| 767 | const source = try decl.scope.getSource(zig_module); | |
| 768 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); | |
| 769 | } | |
| 770 | } | |
| 771 | ||
| 772 | if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) { | |
| 773 | const global_err_src_path = blk: { | |
| 774 | if (self.bin_file.options.zig_module) |zig_module| break :blk zig_module.root_pkg.root_src_path; | |
| 775 | if (self.c_source_files.len != 0) break :blk self.c_source_files[0].src_path; | |
| 776 | if (self.bin_file.options.objects.len != 0) break :blk self.bin_file.options.objects[0]; | |
| 777 | break :blk "(no file)"; | |
| 778 | }; | |
| 779 | try errors.append(.{ | |
| 780 | .src_path = global_err_src_path, | |
| 781 | .line = 0, | |
| 782 | .column = 0, | |
| 783 | .byte_offset = 0, | |
| 784 | .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}), | |
| 785 | }); | |
| 786 | } | |
| 787 | ||
| 788 | assert(errors.items.len == self.totalErrorCount()); | |
| 789 | ||
| 790 | return AllErrors{ | |
| 791 | .list = try arena.allocator.dupe(AllErrors.Message, errors.items), | |
| 792 | .arena = arena.state, | |
| 793 | }; | |
| 794 | } | |
| 795 | ||
| 796 | pub fn performAllTheWork(self: *Compilation) error{OutOfMemory}!void { | |
| 797 | while (self.work_queue.readItem()) |work_item| switch (work_item) { | |
| 798 | .codegen_decl => |decl| switch (decl.analysis) { | |
| 799 | .unreferenced => unreachable, | |
| 800 | .in_progress => unreachable, | |
| 801 | .outdated => unreachable, | |
| 802 | ||
| 803 | .sema_failure, | |
| 804 | .codegen_failure, | |
| 805 | .dependency_failure, | |
| 806 | .sema_failure_retryable, | |
| 807 | => continue, | |
| 808 | ||
| 809 | .complete, .codegen_failure_retryable => { | |
| 810 | const zig_module = self.bin_file.options.zig_module.?; | |
| 811 | if (decl.typed_value.most_recent.typed_value.val.cast(Value.Payload.Function)) |payload| { | |
| 812 | switch (payload.func.analysis) { | |
| 813 | .queued => zig_module.analyzeFnBody(decl, payload.func) catch |err| switch (err) { | |
| 814 | error.AnalysisFail => { | |
| 815 | assert(payload.func.analysis != .in_progress); | |
| 816 | continue; | |
| 817 | }, | |
| 818 | error.OutOfMemory => return error.OutOfMemory, | |
| 819 | }, | |
| 820 | .in_progress => unreachable, | |
| 821 | .sema_failure, .dependency_failure => continue, | |
| 822 | .success => {}, | |
| 823 | } | |
| 824 | // Here we tack on additional allocations to the Decl's arena. The allocations are | |
| 825 | // lifetime annotations in the ZIR. | |
| 826 | var decl_arena = decl.typed_value.most_recent.arena.?.promote(zig_module.gpa); | |
| 827 | defer decl.typed_value.most_recent.arena.?.* = decl_arena.state; | |
| 828 | log.debug("analyze liveness of {}\n", .{decl.name}); | |
| 829 | try liveness.analyze(zig_module.gpa, &decl_arena.allocator, payload.func.analysis.success); | |
| 830 | } | |
| 831 | ||
| 832 | assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); | |
| 833 | ||
| 834 | self.bin_file.updateDecl(zig_module, decl) catch |err| switch (err) { | |
| 835 | error.OutOfMemory => return error.OutOfMemory, | |
| 836 | error.AnalysisFail => { | |
| 837 | decl.analysis = .dependency_failure; | |
| 838 | }, | |
| 839 | else => { | |
| 840 | try zig_module.failed_decls.ensureCapacity(zig_module.gpa, zig_module.failed_decls.items().len + 1); | |
| 841 | zig_module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | |
| 842 | zig_module.gpa, | |
| 843 | decl.src(), | |
| 844 | "unable to codegen: {}", | |
| 845 | .{@errorName(err)}, | |
| 846 | )); | |
| 847 | decl.analysis = .codegen_failure_retryable; | |
| 848 | }, | |
| 849 | }; | |
| 850 | }, | |
| 851 | }, | |
| 852 | .analyze_decl => |decl| { | |
| 853 | const zig_module = self.bin_file.options.zig_module.?; | |
| 854 | zig_module.ensureDeclAnalyzed(decl) catch |err| switch (err) { | |
| 855 | error.OutOfMemory => return error.OutOfMemory, | |
| 856 | error.AnalysisFail => continue, | |
| 857 | }; | |
| 858 | }, | |
| 859 | .update_line_number => |decl| { | |
| 860 | const zig_module = self.bin_file.options.zig_module.?; | |
| 861 | self.bin_file.updateDeclLineNumber(zig_module, decl) catch |err| { | |
| 862 | try zig_module.failed_decls.ensureCapacity(zig_module.gpa, zig_module.failed_decls.items().len + 1); | |
| 863 | zig_module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | |
| 864 | zig_module.gpa, | |
| 865 | decl.src(), | |
| 866 | "unable to update line number: {}", | |
| 867 | .{@errorName(err)}, | |
| 868 | )); | |
| 869 | decl.analysis = .codegen_failure_retryable; | |
| 870 | }; | |
| 871 | }, | |
| 872 | .c_object => |c_object| { | |
| 873 | self.updateCObject(c_object) catch |err| switch (err) { | |
| 874 | error.AnalysisFail => continue, | |
| 875 | else => { | |
| 876 | try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1); | |
| 877 | self.failed_c_objects.putAssumeCapacityNoClobber(c_object, try ErrorMsg.create( | |
| 878 | self.gpa, | |
| 879 | 0, | |
| 880 | "unable to build C object: {}", | |
| 881 | .{@errorName(err)}, | |
| 882 | )); | |
| 883 | c_object.status = .{ .failure = {} }; | |
| 884 | }, | |
| 885 | }; | |
| 886 | }, | |
| 887 | .glibc_crt_file => |crt_file| { | |
| 888 | glibc.buildCRTFile(self, crt_file) catch |err| { | |
| 889 | // This is a problem with the Zig installation. It's mostly OK to crash here, | |
| 890 | // but TODO because it would be even better if we could recover gracefully | |
| 891 | // from temporary problems such as out-of-disk-space. | |
| 892 | fatal("unable to build glibc CRT file: {}", .{@errorName(err)}); | |
| 893 | }; | |
| 894 | }, | |
| 895 | .glibc_so => |glibc_lib| { | |
| 896 | fatal("TODO build glibc shared object '{}.so.{}'", .{ glibc_lib.name, glibc_lib.sover }); | |
| 897 | }, | |
| 898 | }; | |
| 899 | } | |
| 900 | ||
| 901 | fn updateCObject(comp: *Compilation, c_object: *CObject) !void { | |
| 902 | const tracy = trace(@src()); | |
| 903 | defer tracy.end(); | |
| 904 | ||
| 905 | if (!build_options.have_llvm) { | |
| 906 | return comp.failCObj(c_object, "clang not available: compiler not built with LLVM extensions enabled", .{}); | |
| 907 | } | |
| 908 | const self_exe_path = comp.self_exe_path orelse | |
| 909 | return comp.failCObj(c_object, "clang compilation disabled", .{}); | |
| 910 | ||
| 911 | if (c_object.clearStatus(comp.gpa)) { | |
| 912 | // There was previous failure. | |
| 913 | comp.failed_c_objects.removeAssertDiscard(c_object); | |
| 914 | } | |
| 915 | ||
| 916 | var ch = comp.cache_parent.obtain(); | |
| 917 | defer ch.deinit(); | |
| 918 | ||
| 919 | ch.hash.add(comp.sanitize_c); | |
| 920 | ch.hash.addListOfBytes(comp.clang_argv); | |
| 921 | ch.hash.add(comp.bin_file.options.link_libcpp); | |
| 922 | ch.hash.addListOfBytes(comp.libc_include_dir_list); | |
| 923 | // TODO | |
| 924 | //cache_int(cache_hash, g->code_model); | |
| 925 | //cache_bool(cache_hash, codegen_have_frame_pointer(g)); | |
| 926 | _ = try ch.addFile(c_object.src_path, null); | |
| 927 | { | |
| 928 | // Hash the extra flags, with special care to call addFile for file parameters. | |
| 929 | // TODO this logic can likely be improved by utilizing clang_options_data.zig. | |
| 930 | const file_args = [_][]const u8{"-include"}; | |
| 931 | var arg_i: usize = 0; | |
| 932 | while (arg_i < c_object.extra_flags.len) : (arg_i += 1) { | |
| 933 | const arg = c_object.extra_flags[arg_i]; | |
| 934 | ch.hash.addBytes(arg); | |
| 935 | for (file_args) |file_arg| { | |
| 936 | if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_object.extra_flags.len) { | |
| 937 | arg_i += 1; | |
| 938 | _ = try ch.addFile(c_object.extra_flags[arg_i], null); | |
| 939 | } | |
| 940 | } | |
| 941 | } | |
| 942 | } | |
| 943 | ||
| 944 | var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); | |
| 945 | defer arena_allocator.deinit(); | |
| 946 | const arena = &arena_allocator.allocator; | |
| 947 | ||
| 948 | const c_source_basename = std.fs.path.basename(c_object.src_path); | |
| 949 | // Special case when doing build-obj for just one C file. When there are more than one object | |
| 950 | // file and building an object we need to link them together, but with just one it should go | |
| 951 | // directly to the output file. | |
| 952 | const direct_o = comp.c_source_files.len == 1 and comp.bin_file.options.zig_module == null and | |
| 953 | comp.bin_file.options.output_mode == .Obj and comp.bin_file.options.objects.len == 0; | |
| 954 | const o_basename_noext = if (direct_o) | |
| 955 | comp.bin_file.options.root_name | |
| 956 | else | |
| 957 | mem.split(c_source_basename, ".").next().?; | |
| 958 | const o_basename = try std.fmt.allocPrint(arena, "{}{}", .{ o_basename_noext, comp.getTarget().oFileExt() }); | |
| 959 | ||
| 960 | const full_object_path = if (!(try ch.hit()) or comp.disable_c_depfile) blk: { | |
| 961 | var argv = std.ArrayList([]const u8).init(comp.gpa); | |
| 962 | defer argv.deinit(); | |
| 963 | ||
| 964 | // We can't know the digest until we do the C compiler invocation, so we need a temporary filename. | |
| 965 | const out_obj_path = try comp.tmpFilePath(arena, o_basename); | |
| 966 | ||
| 967 | try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang", "-c" }); | |
| 968 | ||
| 969 | const ext = classifyFileExt(c_object.src_path); | |
| 970 | // TODO capture the .d file and deal with caching stuff | |
| 971 | try comp.addCCArgs(arena, &argv, ext, false, null); | |
| 972 | ||
| 973 | try argv.append("-o"); | |
| 974 | try argv.append(out_obj_path); | |
| 975 | ||
| 976 | try argv.append(c_object.src_path); | |
| 977 | try argv.appendSlice(c_object.extra_flags); | |
| 978 | ||
| 979 | if (comp.debug_cc) { | |
| 980 | for (argv.items[0 .. argv.items.len - 1]) |arg| { | |
| 981 | std.debug.print("{} ", .{arg}); | |
| 982 | } | |
| 983 | std.debug.print("{}\n", .{argv.items[argv.items.len - 1]}); | |
| 984 | } | |
| 985 | ||
| 986 | const child = try std.ChildProcess.init(argv.items, arena); | |
| 987 | defer child.deinit(); | |
| 988 | ||
| 989 | if (comp.clang_passthrough_mode) { | |
| 990 | child.stdin_behavior = .Inherit; | |
| 991 | child.stdout_behavior = .Inherit; | |
| 992 | child.stderr_behavior = .Inherit; | |
| 993 | ||
| 994 | const term = child.spawnAndWait() catch |err| { | |
| 995 | return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) }); | |
| 996 | }; | |
| 997 | switch (term) { | |
| 998 | .Exited => |code| { | |
| 999 | if (code != 0) { | |
| 1000 | // TODO make std.process.exit and std.ChildProcess exit code have the same type | |
| 1001 | // and forward it here. Currently it is u32 vs u8. | |
| 1002 | std.process.exit(1); | |
| 1003 | } | |
| 1004 | }, | |
| 1005 | else => std.process.exit(1), | |
| 1006 | } | |
| 1007 | } else { | |
| 1008 | child.stdin_behavior = .Ignore; | |
| 1009 | child.stdout_behavior = .Pipe; | |
| 1010 | child.stderr_behavior = .Pipe; | |
| 1011 | ||
| 1012 | try child.spawn(); | |
| 1013 | ||
| 1014 | const stdout_reader = child.stdout.?.reader(); | |
| 1015 | const stderr_reader = child.stderr.?.reader(); | |
| 1016 | ||
| 1017 | // TODO Need to poll to read these streams to prevent a deadlock (or rely on evented I/O). | |
| 1018 | const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32)); | |
| 1019 | const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); | |
| 1020 | ||
| 1021 | const term = child.wait() catch |err| { | |
| 1022 | return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) }); | |
| 1023 | }; | |
| 1024 | ||
| 1025 | switch (term) { | |
| 1026 | .Exited => |code| { | |
| 1027 | if (code != 0) { | |
| 1028 | // TODO parse clang stderr and turn it into an error message | |
| 1029 | // and then call failCObjWithOwnedErrorMsg | |
| 1030 | std.log.err("clang failed with stderr: {}", .{stderr}); | |
| 1031 | return comp.failCObj(c_object, "clang exited with code {}", .{code}); | |
| 1032 | } | |
| 1033 | }, | |
| 1034 | else => { | |
| 1035 | std.log.err("clang terminated with stderr: {}", .{stderr}); | |
| 1036 | return comp.failCObj(c_object, "clang terminated unexpectedly", .{}); | |
| 1037 | }, | |
| 1038 | } | |
| 1039 | } | |
| 1040 | ||
| 1041 | // TODO handle .d files | |
| 1042 | ||
| 1043 | // Rename into place. | |
| 1044 | const digest = ch.final(); | |
| 1045 | const full_object_path = if (comp.zig_cache_directory.path) |p| | |
| 1046 | try std.fs.path.join(arena, &[_][]const u8{ p, "o", &digest, o_basename }) | |
| 1047 | else | |
| 1048 | try std.fs.path.join(arena, &[_][]const u8{ "o", &digest, o_basename }); | |
| 1049 | try std.fs.rename(out_obj_path, full_object_path); | |
| 1050 | ||
| 1051 | ch.writeManifest() catch |err| { | |
| 1052 | std.log.warn("failed to write cache manifest when compiling '{}': {}", .{ c_object.src_path, @errorName(err) }); | |
| 1053 | }; | |
| 1054 | break :blk full_object_path; | |
| 1055 | } else blk: { | |
| 1056 | const digest = ch.final(); | |
| 1057 | const full_object_path = if (comp.zig_cache_directory.path) |p| | |
| 1058 | try std.fs.path.join(arena, &[_][]const u8{ p, "o", &digest, o_basename }) | |
| 1059 | else | |
| 1060 | try std.fs.path.join(arena, &[_][]const u8{ "o", &digest, o_basename }); | |
| 1061 | break :blk full_object_path; | |
| 1062 | }; | |
| 1063 | ||
| 1064 | c_object.status = .{ | |
| 1065 | .success = .{ | |
| 1066 | .object_path = full_object_path, | |
| 1067 | .lock = ch.toOwnedLock(), | |
| 1068 | }, | |
| 1069 | }; | |
| 1070 | } | |
| 1071 | ||
| 1072 | fn tmpFilePath(comp: *Compilation, arena: *Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 { | |
| 1073 | const s = std.fs.path.sep_str; | |
| 1074 | return std.fmt.allocPrint( | |
| 1075 | arena, | |
| 1076 | "{}" ++ s ++ "tmp" ++ s ++ "{x}-{}", | |
| 1077 | .{ comp.zig_cache_directory.path.?, comp.rand.int(u64), suffix }, | |
| 1078 | ); | |
| 1079 | } | |
| 1080 | ||
| 1081 | /// Add common C compiler args between translate-c and C object compilation. | |
| 1082 | fn addCCArgs( | |
| 1083 | comp: *Compilation, | |
| 1084 | arena: *Allocator, | |
| 1085 | argv: *std.ArrayList([]const u8), | |
| 1086 | ext: FileExt, | |
| 1087 | translate_c: bool, | |
| 1088 | out_dep_path: ?[]const u8, | |
| 1089 | ) !void { | |
| 1090 | const target = comp.getTarget(); | |
| 1091 | ||
| 1092 | if (translate_c) { | |
| 1093 | try argv.appendSlice(&[_][]const u8{ "-x", "c" }); | |
| 1094 | } | |
| 1095 | ||
| 1096 | if (ext == .cpp) { | |
| 1097 | try argv.append("-nostdinc++"); | |
| 1098 | } | |
| 1099 | try argv.appendSlice(&[_][]const u8{ | |
| 1100 | "-nostdinc", | |
| 1101 | "-fno-spell-checking", | |
| 1102 | }); | |
| 1103 | ||
| 1104 | // We don't ever put `-fcolor-diagnostics` or `-fno-color-diagnostics` because in passthrough mode | |
| 1105 | // we want Clang to infer it, and in normal mode we always want it off, which will be true since | |
| 1106 | // clang will detect stderr as a pipe rather than a terminal. | |
| 1107 | if (!comp.clang_passthrough_mode) { | |
| 1108 | // Make stderr more easily parseable. | |
| 1109 | try argv.append("-fno-caret-diagnostics"); | |
| 1110 | } | |
| 1111 | ||
| 1112 | if (comp.bin_file.options.function_sections) { | |
| 1113 | try argv.append("-ffunction-sections"); | |
| 1114 | } | |
| 1115 | ||
| 1116 | try argv.ensureCapacity(argv.items.len + comp.bin_file.options.framework_dirs.len * 2); | |
| 1117 | for (comp.bin_file.options.framework_dirs) |framework_dir| { | |
| 1118 | argv.appendAssumeCapacity("-iframework"); | |
| 1119 | argv.appendAssumeCapacity(framework_dir); | |
| 1120 | } | |
| 1121 | ||
| 1122 | if (comp.bin_file.options.link_libcpp) { | |
| 1123 | const libcxx_include_path = try std.fs.path.join(arena, &[_][]const u8{ | |
| 1124 | comp.zig_lib_directory.path.?, "libcxx", "include", | |
| 1125 | }); | |
| 1126 | const libcxxabi_include_path = try std.fs.path.join(arena, &[_][]const u8{ | |
| 1127 | comp.zig_lib_directory.path.?, "libcxxabi", "include", | |
| 1128 | }); | |
| 1129 | ||
| 1130 | try argv.append("-isystem"); | |
| 1131 | try argv.append(libcxx_include_path); | |
| 1132 | ||
| 1133 | try argv.append("-isystem"); | |
| 1134 | try argv.append(libcxxabi_include_path); | |
| 1135 | ||
| 1136 | if (target.abi.isMusl()) { | |
| 1137 | try argv.append("-D_LIBCPP_HAS_MUSL_LIBC"); | |
| 1138 | } | |
| 1139 | try argv.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); | |
| 1140 | try argv.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); | |
| 1141 | } | |
| 1142 | ||
| 1143 | const llvm_triple = try @import("codegen/llvm.zig").targetTriple(arena, target); | |
| 1144 | try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple }); | |
| 1145 | ||
| 1146 | switch (ext) { | |
| 1147 | .c, .cpp, .h => { | |
| 1148 | // According to Rich Felker libc headers are supposed to go before C language headers. | |
| 1149 | // However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics | |
| 1150 | // and other compiler specific items. | |
| 1151 | const c_headers_dir = try std.fs.path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, "include" }); | |
| 1152 | try argv.append("-isystem"); | |
| 1153 | try argv.append(c_headers_dir); | |
| 1154 | ||
| 1155 | for (comp.libc_include_dir_list) |include_dir| { | |
| 1156 | try argv.append("-isystem"); | |
| 1157 | try argv.append(include_dir); | |
| 1158 | } | |
| 1159 | ||
| 1160 | if (target.cpu.model.llvm_name) |llvm_name| { | |
| 1161 | try argv.appendSlice(&[_][]const u8{ | |
| 1162 | "-Xclang", "-target-cpu", "-Xclang", llvm_name, | |
| 1163 | }); | |
| 1164 | } | |
| 1165 | // TODO CLI args for target features | |
| 1166 | //if (g->zig_target->llvm_cpu_features != nullptr) { | |
| 1167 | // // https://github.com/ziglang/zig/issues/5017 | |
| 1168 | // SplitIterator it = memSplit(str(g->zig_target->llvm_cpu_features), str(",")); | |
| 1169 | // Optional<Slice<uint8_t>> flag = SplitIterator_next(&it); | |
| 1170 | // while (flag.is_some) { | |
| 1171 | // try argv.append("-Xclang"); | |
| 1172 | // try argv.append("-target-feature"); | |
| 1173 | // try argv.append("-Xclang"); | |
| 1174 | // try argv.append(buf_ptr(buf_create_from_slice(flag.value))); | |
| 1175 | // flag = SplitIterator_next(&it); | |
| 1176 | // } | |
| 1177 | //} | |
| 1178 | if (translate_c) { | |
| 1179 | // This gives us access to preprocessing entities, presumably at the cost of performance. | |
| 1180 | try argv.append("-Xclang"); | |
| 1181 | try argv.append("-detailed-preprocessing-record"); | |
| 1182 | } | |
| 1183 | if (out_dep_path) |p| { | |
| 1184 | try argv.append("-MD"); | |
| 1185 | try argv.append("-MV"); | |
| 1186 | try argv.append("-MF"); | |
| 1187 | try argv.append(p); | |
| 1188 | } | |
| 1189 | }, | |
| 1190 | .so, .assembly, .ll, .bc, .unknown => {}, | |
| 1191 | } | |
| 1192 | // TODO CLI args for cpu features when compiling assembly | |
| 1193 | //for (size_t i = 0; i < g->zig_target->llvm_cpu_features_asm_len; i += 1) { | |
| 1194 | // try argv.append(g->zig_target->llvm_cpu_features_asm_ptr[i]); | |
| 1195 | //} | |
| 1196 | ||
| 1197 | if (target.os.tag == .freestanding) { | |
| 1198 | try argv.append("-ffreestanding"); | |
| 1199 | } | |
| 1200 | ||
| 1201 | // windows.h has files such as pshpack1.h which do #pragma packing, triggering a clang warning. | |
| 1202 | // So for this target, we disable this warning. | |
| 1203 | if (target.os.tag == .windows and target.abi.isGnu()) { | |
| 1204 | try argv.append("-Wno-pragma-pack"); | |
| 1205 | } | |
| 1206 | ||
| 1207 | if (!comp.bin_file.options.strip) { | |
| 1208 | try argv.append("-g"); | |
| 1209 | } | |
| 1210 | ||
| 1211 | if (comp.haveFramePointer()) { | |
| 1212 | try argv.append("-fno-omit-frame-pointer"); | |
| 1213 | } else { | |
| 1214 | try argv.append("-fomit-frame-pointer"); | |
| 1215 | } | |
| 1216 | ||
| 1217 | if (comp.sanitize_c) { | |
| 1218 | try argv.append("-fsanitize=undefined"); | |
| 1219 | try argv.append("-fsanitize-trap=undefined"); | |
| 1220 | } | |
| 1221 | ||
| 1222 | switch (comp.bin_file.options.optimize_mode) { | |
| 1223 | .Debug => { | |
| 1224 | // windows c runtime requires -D_DEBUG if using debug libraries | |
| 1225 | try argv.append("-D_DEBUG"); | |
| 1226 | try argv.append("-Og"); | |
| 1227 | ||
| 1228 | if (comp.bin_file.options.link_libc) { | |
| 1229 | try argv.append("-fstack-protector-strong"); | |
| 1230 | try argv.append("--param"); | |
| 1231 | try argv.append("ssp-buffer-size=4"); | |
| 1232 | } else { | |
| 1233 | try argv.append("-fno-stack-protector"); | |
| 1234 | } | |
| 1235 | }, | |
| 1236 | .ReleaseSafe => { | |
| 1237 | // See the comment in the BuildModeFastRelease case for why we pass -O2 rather | |
| 1238 | // than -O3 here. | |
| 1239 | try argv.append("-O2"); | |
| 1240 | if (comp.bin_file.options.link_libc) { | |
| 1241 | try argv.append("-D_FORTIFY_SOURCE=2"); | |
| 1242 | try argv.append("-fstack-protector-strong"); | |
| 1243 | try argv.append("--param"); | |
| 1244 | try argv.append("ssp-buffer-size=4"); | |
| 1245 | } else { | |
| 1246 | try argv.append("-fno-stack-protector"); | |
| 1247 | } | |
| 1248 | }, | |
| 1249 | .ReleaseFast => { | |
| 1250 | try argv.append("-DNDEBUG"); | |
| 1251 | // Here we pass -O2 rather than -O3 because, although we do the equivalent of | |
| 1252 | // -O3 in Zig code, the justification for the difference here is that Zig | |
| 1253 | // has better detection and prevention of undefined behavior, so -O3 is safer for | |
| 1254 | // Zig code than it is for C code. Also, C programmers are used to their code | |
| 1255 | // running in -O2 and thus the -O3 path has been tested less. | |
| 1256 | try argv.append("-O2"); | |
| 1257 | try argv.append("-fno-stack-protector"); | |
| 1258 | }, | |
| 1259 | .ReleaseSmall => { | |
| 1260 | try argv.append("-DNDEBUG"); | |
| 1261 | try argv.append("-Os"); | |
| 1262 | try argv.append("-fno-stack-protector"); | |
| 1263 | }, | |
| 1264 | } | |
| 1265 | ||
| 1266 | if (target_util.supports_fpic(target) and comp.bin_file.options.pic) { | |
| 1267 | try argv.append("-fPIC"); | |
| 1268 | } | |
| 1269 | ||
| 1270 | try argv.appendSlice(comp.clang_argv); | |
| 1271 | } | |
| 1272 | ||
| 1273 | fn failCObj(comp: *Compilation, c_object: *CObject, comptime format: []const u8, args: anytype) InnerError { | |
| 1274 | @setCold(true); | |
| 1275 | const err_msg = try ErrorMsg.create(comp.gpa, 0, "unable to build C object: " ++ format, args); | |
| 1276 | return comp.failCObjWithOwnedErrorMsg(c_object, err_msg); | |
| 1277 | } | |
| 1278 | ||
| 1279 | fn failCObjWithOwnedErrorMsg(comp: *Compilation, c_object: *CObject, err_msg: *ErrorMsg) InnerError { | |
| 1280 | { | |
| 1281 | errdefer err_msg.destroy(comp.gpa); | |
| 1282 | try comp.failed_c_objects.ensureCapacity(comp.gpa, comp.failed_c_objects.items().len + 1); | |
| 1283 | } | |
| 1284 | comp.failed_c_objects.putAssumeCapacityNoClobber(c_object, err_msg); | |
| 1285 | c_object.status = .failure; | |
| 1286 | return error.AnalysisFail; | |
| 1287 | } | |
| 1288 | ||
| 1289 | pub const ErrorMsg = struct { | |
| 1290 | byte_offset: usize, | |
| 1291 | msg: []const u8, | |
| 1292 | ||
| 1293 | pub fn create(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !*ErrorMsg { | |
| 1294 | const self = try gpa.create(ErrorMsg); | |
| 1295 | errdefer gpa.destroy(self); | |
| 1296 | self.* = try init(gpa, byte_offset, format, args); | |
| 1297 | return self; | |
| 1298 | } | |
| 1299 | ||
| 1300 | /// Assumes the ErrorMsg struct and msg were both allocated with allocator. | |
| 1301 | pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void { | |
| 1302 | self.deinit(gpa); | |
| 1303 | gpa.destroy(self); | |
| 1304 | } | |
| 1305 | ||
| 1306 | pub fn init(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !ErrorMsg { | |
| 1307 | return ErrorMsg{ | |
| 1308 | .byte_offset = byte_offset, | |
| 1309 | .msg = try std.fmt.allocPrint(gpa, format, args), | |
| 1310 | }; | |
| 1311 | } | |
| 1312 | ||
| 1313 | pub fn deinit(self: *ErrorMsg, gpa: *Allocator) void { | |
| 1314 | gpa.free(self.msg); | |
| 1315 | self.* = undefined; | |
| 1316 | } | |
| 1317 | }; | |
| 1318 | ||
| 1319 | pub const FileExt = enum { | |
| 1320 | c, | |
| 1321 | cpp, | |
| 1322 | h, | |
| 1323 | ll, | |
| 1324 | bc, | |
| 1325 | assembly, | |
| 1326 | so, | |
| 1327 | unknown, | |
| 1328 | }; | |
| 1329 | ||
| 1330 | pub fn hasCExt(filename: []const u8) bool { | |
| 1331 | return mem.endsWith(u8, filename, ".c"); | |
| 1332 | } | |
| 1333 | ||
| 1334 | pub fn hasCppExt(filename: []const u8) bool { | |
| 1335 | return mem.endsWith(u8, filename, ".C") or | |
| 1336 | mem.endsWith(u8, filename, ".cc") or | |
| 1337 | mem.endsWith(u8, filename, ".cpp") or | |
| 1338 | mem.endsWith(u8, filename, ".cxx"); | |
| 1339 | } | |
| 1340 | ||
| 1341 | pub fn hasAsmExt(filename: []const u8) bool { | |
| 1342 | return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S"); | |
| 1343 | } | |
| 1344 | ||
| 1345 | pub fn classifyFileExt(filename: []const u8) FileExt { | |
| 1346 | if (hasCExt(filename)) { | |
| 1347 | return .c; | |
| 1348 | } else if (hasCppExt(filename)) { | |
| 1349 | return .cpp; | |
| 1350 | } else if (mem.endsWith(u8, filename, ".ll")) { | |
| 1351 | return .ll; | |
| 1352 | } else if (mem.endsWith(u8, filename, ".bc")) { | |
| 1353 | return .bc; | |
| 1354 | } else if (hasAsmExt(filename)) { | |
| 1355 | return .assembly; | |
| 1356 | } else if (mem.endsWith(u8, filename, ".h")) { | |
| 1357 | return .h; | |
| 1358 | } else if (mem.endsWith(u8, filename, ".so")) { | |
| 1359 | return .so; | |
| 1360 | } | |
| 1361 | // Look for .so.X, .so.X.Y, .so.X.Y.Z | |
| 1362 | var it = mem.split(filename, "."); | |
| 1363 | _ = it.next().?; | |
| 1364 | var so_txt = it.next() orelse return .unknown; | |
| 1365 | while (!mem.eql(u8, so_txt, "so")) { | |
| 1366 | so_txt = it.next() orelse return .unknown; | |
| 1367 | } | |
| 1368 | const n1 = it.next() orelse return .unknown; | |
| 1369 | const n2 = it.next(); | |
| 1370 | const n3 = it.next(); | |
| 1371 | ||
| 1372 | _ = std.fmt.parseInt(u32, n1, 10) catch return .unknown; | |
| 1373 | if (n2) |x| _ = std.fmt.parseInt(u32, x, 10) catch return .unknown; | |
| 1374 | if (n3) |x| _ = std.fmt.parseInt(u32, x, 10) catch return .unknown; | |
| 1375 | if (it.next() != null) return .unknown; | |
| 1376 | ||
| 1377 | return .so; | |
| 1378 | } | |
| 1379 | ||
| 1380 | test "classifyFileExt" { | |
| 1381 | std.testing.expectEqual(FileExt.cpp, classifyFileExt("foo.cc")); | |
| 1382 | std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.nim")); | |
| 1383 | std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so")); | |
| 1384 | std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so.1")); | |
| 1385 | std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so.1.2")); | |
| 1386 | std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so.1.2.3")); | |
| 1387 | std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.so.1.2.3~")); | |
| 1388 | } | |
| 1389 | ||
| 1390 | fn haveFramePointer(comp: *Compilation) bool { | |
| 1391 | return switch (comp.bin_file.options.optimize_mode) { | |
| 1392 | .Debug, .ReleaseSafe => !comp.bin_file.options.strip, | |
| 1393 | .ReleaseSmall, .ReleaseFast => false, | |
| 1394 | }; | |
| 1395 | } | |
| 1396 | ||
| 1397 | const LibCDirs = struct { | |
| 1398 | libc_include_dir_list: []const []const u8, | |
| 1399 | libc_installation: ?*const LibCInstallation, | |
| 1400 | }; | |
| 1401 | ||
| 1402 | fn detectLibCIncludeDirs( | |
| 1403 | arena: *Allocator, | |
| 1404 | zig_lib_dir: []const u8, | |
| 1405 | target: Target, | |
| 1406 | is_native_os: bool, | |
| 1407 | link_libc: bool, | |
| 1408 | libc_installation: ?*const LibCInstallation, | |
| 1409 | ) !LibCDirs { | |
| 1410 | if (!link_libc) { | |
| 1411 | return LibCDirs{ | |
| 1412 | .libc_include_dir_list = &[0][]u8{}, | |
| 1413 | .libc_installation = null, | |
| 1414 | }; | |
| 1415 | } | |
| 1416 | ||
| 1417 | if (libc_installation) |lci| { | |
| 1418 | return detectLibCFromLibCInstallation(arena, target, lci); | |
| 1419 | } | |
| 1420 | ||
| 1421 | if (target_util.canBuildLibC(target)) { | |
| 1422 | const generic_name = target_util.libCGenericName(target); | |
| 1423 | // Some architectures are handled by the same set of headers. | |
| 1424 | const arch_name = if (target.abi.isMusl()) target_util.archMuslName(target.cpu.arch) else @tagName(target.cpu.arch); | |
| 1425 | const os_name = @tagName(target.os.tag); | |
| 1426 | // Musl's headers are ABI-agnostic and so they all have the "musl" ABI name. | |
| 1427 | const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi); | |
| 1428 | const s = std.fs.path.sep_str; | |
| 1429 | const arch_include_dir = try std.fmt.allocPrint( | |
| 1430 | arena, | |
| 1431 | "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}", | |
| 1432 | .{ zig_lib_dir, arch_name, os_name, abi_name }, | |
| 1433 | ); | |
| 1434 | const generic_include_dir = try std.fmt.allocPrint( | |
| 1435 | arena, | |
| 1436 | "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{}", | |
| 1437 | .{ zig_lib_dir, generic_name }, | |
| 1438 | ); | |
| 1439 | const arch_os_include_dir = try std.fmt.allocPrint( | |
| 1440 | arena, | |
| 1441 | "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-any", | |
| 1442 | .{ zig_lib_dir, @tagName(target.cpu.arch), os_name }, | |
| 1443 | ); | |
| 1444 | const generic_os_include_dir = try std.fmt.allocPrint( | |
| 1445 | arena, | |
| 1446 | "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{}-any", | |
| 1447 | .{ zig_lib_dir, os_name }, | |
| 1448 | ); | |
| 1449 | ||
| 1450 | const list = try arena.alloc([]const u8, 4); | |
| 1451 | list[0] = arch_include_dir; | |
| 1452 | list[1] = generic_include_dir; | |
| 1453 | list[2] = arch_os_include_dir; | |
| 1454 | list[3] = generic_os_include_dir; | |
| 1455 | return LibCDirs{ | |
| 1456 | .libc_include_dir_list = list, | |
| 1457 | .libc_installation = null, | |
| 1458 | }; | |
| 1459 | } | |
| 1460 | ||
| 1461 | if (is_native_os) { | |
| 1462 | const libc = try arena.create(LibCInstallation); | |
| 1463 | libc.* = try LibCInstallation.findNative(.{ .allocator = arena }); | |
| 1464 | return detectLibCFromLibCInstallation(arena, target, libc); | |
| 1465 | } | |
| 1466 | ||
| 1467 | return LibCDirs{ | |
| 1468 | .libc_include_dir_list = &[0][]u8{}, | |
| 1469 | .libc_installation = null, | |
| 1470 | }; | |
| 1471 | } | |
| 1472 | ||
| 1473 | fn detectLibCFromLibCInstallation(arena: *Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs { | |
| 1474 | var list = std.ArrayList([]const u8).init(arena); | |
| 1475 | try list.ensureCapacity(4); | |
| 1476 | ||
| 1477 | list.appendAssumeCapacity(lci.include_dir.?); | |
| 1478 | ||
| 1479 | const is_redundant = mem.eql(u8, lci.sys_include_dir.?, lci.include_dir.?); | |
| 1480 | if (!is_redundant) list.appendAssumeCapacity(lci.sys_include_dir.?); | |
| 1481 | ||
| 1482 | if (target.os.tag == .windows) { | |
| 1483 | if (std.fs.path.dirname(lci.include_dir.?)) |include_dir_parent| { | |
| 1484 | const um_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "um" }); | |
| 1485 | list.appendAssumeCapacity(um_dir); | |
| 1486 | ||
| 1487 | const shared_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "shared" }); | |
| 1488 | list.appendAssumeCapacity(shared_dir); | |
| 1489 | } | |
| 1490 | } | |
| 1491 | return LibCDirs{ | |
| 1492 | .libc_include_dir_list = list.items, | |
| 1493 | .libc_installation = lci, | |
| 1494 | }; | |
| 1495 | } | |
| 1496 | ||
| 1497 | pub fn get_libc_crt_file(comp: *Compilation, arena: *Allocator, basename: []const u8) ![]const u8 { | |
| 1498 | if (comp.wantBuildGLibCFromSource()) { | |
| 1499 | return comp.crt_files.get(basename).?; | |
| 1500 | } | |
| 1501 | const lci = comp.bin_file.options.libc_installation orelse return error.LibCInstallationNotAvailable; | |
| 1502 | const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir; | |
| 1503 | const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename }); | |
| 1504 | return full_path; | |
| 1505 | } | |
| 1506 | ||
| 1507 | fn addBuildingGLibCWorkItems(comp: *Compilation) !void { | |
| 1508 | const static_file_work_items = [_]WorkItem{ | |
| 1509 | .{ .glibc_crt_file = .crti_o }, | |
| 1510 | .{ .glibc_crt_file = .crtn_o }, | |
| 1511 | .{ .glibc_crt_file = .start_os }, | |
| 1512 | .{ .glibc_crt_file = .abi_note_o }, | |
| 1513 | .{ .glibc_crt_file = .scrt1_o }, | |
| 1514 | .{ .glibc_crt_file = .libc_nonshared_a }, | |
| 1515 | }; | |
| 1516 | try comp.work_queue.ensureUnusedCapacity(static_file_work_items.len + glibc.libs.len); | |
| 1517 | comp.work_queue.writeAssumeCapacity(&static_file_work_items); | |
| 1518 | for (glibc.libs) |*glibc_so| { | |
| 1519 | comp.work_queue.writeItemAssumeCapacity(.{ .glibc_so = glibc_so }); | |
| 1520 | } | |
| 1521 | } | |
| 1522 | ||
| 1523 | fn wantBuildGLibCFromSource(comp: *Compilation) bool { | |
| 1524 | return comp.bin_file.options.link_libc and | |
| 1525 | comp.bin_file.options.libc_installation == null and | |
| 1526 | comp.bin_file.options.target.isGnuLibC(); | |
| 1527 | } |
src-self-hosted/Module.zig deleted-1529| ... | ... | @@ -1,1529 +0,0 @@ |
| 1 | //! TODO This is going to get renamed from Module to Compilation. | |
| 2 | const Module = @This(); | |
| 3 | const Compilation = @This(); | |
| 4 | ||
| 5 | const std = @import("std"); | |
| 6 | const mem = std.mem; | |
| 7 | const Allocator = std.mem.Allocator; | |
| 8 | const Value = @import("value.zig").Value; | |
| 9 | const assert = std.debug.assert; | |
| 10 | const log = std.log.scoped(.compilation); | |
| 11 | const Target = std.Target; | |
| 12 | const target_util = @import("target.zig"); | |
| 13 | const Package = @import("Package.zig"); | |
| 14 | const link = @import("link.zig"); | |
| 15 | const trace = @import("tracy.zig").trace; | |
| 16 | const liveness = @import("liveness.zig"); | |
| 17 | const build_options = @import("build_options"); | |
| 18 | const LibCInstallation = @import("libc_installation.zig").LibCInstallation; | |
| 19 | const glibc = @import("glibc.zig"); | |
| 20 | const fatal = @import("main.zig").fatal; | |
| 21 | const ZigModule = @import("ZigModule.zig"); | |
| 22 | ||
| 23 | /// General-purpose allocator. Used for both temporary and long-term storage. | |
| 24 | gpa: *Allocator, | |
| 25 | /// Arena-allocated memory used during initialization. Should be untouched until deinit. | |
| 26 | arena_state: std.heap.ArenaAllocator.State, | |
| 27 | bin_file: *link.File, | |
| 28 | c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{}, | |
| 29 | ||
| 30 | link_error_flags: link.File.ErrorFlags = .{}, | |
| 31 | ||
| 32 | work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic), | |
| 33 | ||
| 34 | /// The ErrorMsg memory is owned by the `CObject`, using Module's general purpose allocator. | |
| 35 | failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *ErrorMsg) = .{}, | |
| 36 | ||
| 37 | keep_source_files_loaded: bool, | |
| 38 | use_clang: bool, | |
| 39 | sanitize_c: bool, | |
| 40 | /// When this is `true` it means invoking clang as a sub-process is expected to inherit | |
| 41 | /// stdin, stdout, stderr, and if it returns non success, to forward the exit code. | |
| 42 | /// Otherwise we attempt to parse the error messages and expose them via the Module API. | |
| 43 | /// This is `true` for `zig cc`, `zig c++`, and `zig translate-c`. | |
| 44 | clang_passthrough_mode: bool, | |
| 45 | /// Whether to print clang argvs to stdout. | |
| 46 | debug_cc: bool, | |
| 47 | disable_c_depfile: bool, | |
| 48 | ||
| 49 | c_source_files: []const CSourceFile, | |
| 50 | clang_argv: []const []const u8, | |
| 51 | cache_parent: *std.cache_hash.Cache, | |
| 52 | /// Path to own executable for invoking `zig clang`. | |
| 53 | self_exe_path: ?[]const u8, | |
| 54 | zig_lib_directory: Directory, | |
| 55 | zig_cache_directory: Directory, | |
| 56 | libc_include_dir_list: []const []const u8, | |
| 57 | rand: *std.rand.Random, | |
| 58 | ||
| 59 | /// Populated when we build libc++.a. A WorkItem to build this is placed in the queue | |
| 60 | /// and resolved before calling linker.flush(). | |
| 61 | libcxx_static_lib: ?[]const u8 = null, | |
| 62 | /// Populated when we build libc++abi.a. A WorkItem to build this is placed in the queue | |
| 63 | /// and resolved before calling linker.flush(). | |
| 64 | libcxxabi_static_lib: ?[]const u8 = null, | |
| 65 | /// Populated when we build libunwind.a. A WorkItem to build this is placed in the queue | |
| 66 | /// and resolved before calling linker.flush(). | |
| 67 | libunwind_static_lib: ?[]const u8 = null, | |
| 68 | /// Populated when we build c.a. A WorkItem to build this is placed in the queue | |
| 69 | /// and resolved before calling linker.flush(). | |
| 70 | libc_static_lib: ?[]const u8 = null, | |
| 71 | ||
| 72 | /// For example `Scrt1.o` and `libc.so.6`. These are populated after building libc from source, | |
| 73 | /// The set of needed CRT (C runtime) files differs depending on the target and compilation settings. | |
| 74 | /// The key is the basename, and the value is the absolute path to the completed build artifact. | |
| 75 | crt_files: std.StringHashMapUnmanaged([]const u8) = .{}, | |
| 76 | ||
| 77 | /// Keeping track of this possibly open resource so we can close it later. | |
| 78 | owned_link_dir: ?std.fs.Dir, | |
| 79 | ||
| 80 | pub const InnerError = ZigModule.InnerError; | |
| 81 | ||
| 82 | /// For passing to a C compiler. | |
| 83 | pub const CSourceFile = struct { | |
| 84 | src_path: []const u8, | |
| 85 | extra_flags: []const []const u8 = &[0][]const u8{}, | |
| 86 | }; | |
| 87 | ||
| 88 | const WorkItem = union(enum) { | |
| 89 | /// Write the machine code for a Decl to the output file. | |
| 90 | codegen_decl: *ZigModule.Decl, | |
| 91 | /// The Decl needs to be analyzed and possibly export itself. | |
| 92 | /// It may have already be analyzed, or it may have been determined | |
| 93 | /// to be outdated; in this case perform semantic analysis again. | |
| 94 | analyze_decl: *ZigModule.Decl, | |
| 95 | /// The source file containing the Decl has been updated, and so the | |
| 96 | /// Decl may need its line number information updated in the debug info. | |
| 97 | update_line_number: *ZigModule.Decl, | |
| 98 | /// Invoke the Clang compiler to create an object file, which gets linked | |
| 99 | /// with the Module. | |
| 100 | c_object: *CObject, | |
| 101 | ||
| 102 | /// one of the glibc static objects | |
| 103 | glibc_crt_file: glibc.CRTFile, | |
| 104 | /// one of the glibc shared objects | |
| 105 | glibc_so: *const glibc.Lib, | |
| 106 | }; | |
| 107 | ||
| 108 | pub const CObject = struct { | |
| 109 | /// Relative to cwd. Owned by arena. | |
| 110 | src_path: []const u8, | |
| 111 | /// Owned by arena. | |
| 112 | extra_flags: []const []const u8, | |
| 113 | arena: std.heap.ArenaAllocator.State, | |
| 114 | status: union(enum) { | |
| 115 | new, | |
| 116 | success: struct { | |
| 117 | /// The outputted result. Owned by gpa. | |
| 118 | object_path: []u8, | |
| 119 | /// This is a file system lock on the cache hash manifest representing this | |
| 120 | /// object. It prevents other invocations of the Zig compiler from interfering | |
| 121 | /// with this object until released. | |
| 122 | lock: std.cache_hash.Lock, | |
| 123 | }, | |
| 124 | /// There will be a corresponding ErrorMsg in Compilation.failed_c_objects. | |
| 125 | failure, | |
| 126 | }, | |
| 127 | ||
| 128 | /// Returns if there was failure. | |
| 129 | pub fn clearStatus(self: *CObject, gpa: *Allocator) bool { | |
| 130 | switch (self.status) { | |
| 131 | .new => return false, | |
| 132 | .failure => { | |
| 133 | self.status = .new; | |
| 134 | return true; | |
| 135 | }, | |
| 136 | .success => |*success| { | |
| 137 | gpa.free(success.object_path); | |
| 138 | success.lock.release(); | |
| 139 | self.status = .new; | |
| 140 | return false; | |
| 141 | }, | |
| 142 | } | |
| 143 | } | |
| 144 | ||
| 145 | pub fn destroy(self: *CObject, gpa: *Allocator) void { | |
| 146 | _ = self.clearStatus(gpa); | |
| 147 | self.arena.promote(gpa).deinit(); | |
| 148 | } | |
| 149 | }; | |
| 150 | ||
| 151 | pub const AllErrors = struct { | |
| 152 | arena: std.heap.ArenaAllocator.State, | |
| 153 | list: []const Message, | |
| 154 | ||
| 155 | pub const Message = struct { | |
| 156 | src_path: []const u8, | |
| 157 | line: usize, | |
| 158 | column: usize, | |
| 159 | byte_offset: usize, | |
| 160 | msg: []const u8, | |
| 161 | }; | |
| 162 | ||
| 163 | pub fn deinit(self: *AllErrors, gpa: *Allocator) void { | |
| 164 | self.arena.promote(gpa).deinit(); | |
| 165 | } | |
| 166 | ||
| 167 | fn add( | |
| 168 | arena: *std.heap.ArenaAllocator, | |
| 169 | errors: *std.ArrayList(Message), | |
| 170 | sub_file_path: []const u8, | |
| 171 | source: []const u8, | |
| 172 | simple_err_msg: ErrorMsg, | |
| 173 | ) !void { | |
| 174 | const loc = std.zig.findLineColumn(source, simple_err_msg.byte_offset); | |
| 175 | try errors.append(.{ | |
| 176 | .src_path = try arena.allocator.dupe(u8, sub_file_path), | |
| 177 | .msg = try arena.allocator.dupe(u8, simple_err_msg.msg), | |
| 178 | .byte_offset = simple_err_msg.byte_offset, | |
| 179 | .line = loc.line, | |
| 180 | .column = loc.column, | |
| 181 | }); | |
| 182 | } | |
| 183 | }; | |
| 184 | ||
| 185 | pub const Directory = struct { | |
| 186 | /// This field is redundant for operations that can act on the open directory handle | |
| 187 | /// directly, but it is needed when passing the directory to a child process. | |
| 188 | /// `null` means cwd. | |
| 189 | path: ?[]const u8, | |
| 190 | handle: std.fs.Dir, | |
| 191 | }; | |
| 192 | ||
| 193 | pub const EmitLoc = struct { | |
| 194 | /// If this is `null` it means the file will be output to the cache directory. | |
| 195 | /// When provided, both the open file handle and the path name must outlive the `Module`. | |
| 196 | directory: ?Module.Directory, | |
| 197 | /// This may not have sub-directories in it. | |
| 198 | basename: []const u8, | |
| 199 | }; | |
| 200 | ||
| 201 | pub const InitOptions = struct { | |
| 202 | zig_lib_directory: Directory, | |
| 203 | zig_cache_directory: Directory, | |
| 204 | target: Target, | |
| 205 | root_name: []const u8, | |
| 206 | root_pkg: ?*Package, | |
| 207 | output_mode: std.builtin.OutputMode, | |
| 208 | rand: *std.rand.Random, | |
| 209 | dynamic_linker: ?[]const u8 = null, | |
| 210 | /// `null` means to not emit a binary file. | |
| 211 | emit_bin: ?EmitLoc, | |
| 212 | /// `null` means to not emit a C header file. | |
| 213 | emit_h: ?EmitLoc = null, | |
| 214 | link_mode: ?std.builtin.LinkMode = null, | |
| 215 | object_format: ?std.builtin.ObjectFormat = null, | |
| 216 | optimize_mode: std.builtin.Mode = .Debug, | |
| 217 | keep_source_files_loaded: bool = false, | |
| 218 | clang_argv: []const []const u8 = &[0][]const u8{}, | |
| 219 | lld_argv: []const []const u8 = &[0][]const u8{}, | |
| 220 | lib_dirs: []const []const u8 = &[0][]const u8{}, | |
| 221 | rpath_list: []const []const u8 = &[0][]const u8{}, | |
| 222 | c_source_files: []const CSourceFile = &[0]CSourceFile{}, | |
| 223 | link_objects: []const []const u8 = &[0][]const u8{}, | |
| 224 | framework_dirs: []const []const u8 = &[0][]const u8{}, | |
| 225 | frameworks: []const []const u8 = &[0][]const u8{}, | |
| 226 | system_libs: []const []const u8 = &[0][]const u8{}, | |
| 227 | link_libc: bool = false, | |
| 228 | link_libcpp: bool = false, | |
| 229 | want_pic: ?bool = null, | |
| 230 | want_sanitize_c: ?bool = null, | |
| 231 | want_stack_check: ?bool = null, | |
| 232 | want_valgrind: ?bool = null, | |
| 233 | use_llvm: ?bool = null, | |
| 234 | use_lld: ?bool = null, | |
| 235 | use_clang: ?bool = null, | |
| 236 | rdynamic: bool = false, | |
| 237 | strip: bool = false, | |
| 238 | single_threaded: bool = false, | |
| 239 | is_native_os: bool, | |
| 240 | link_eh_frame_hdr: bool = false, | |
| 241 | linker_script: ?[]const u8 = null, | |
| 242 | version_script: ?[]const u8 = null, | |
| 243 | override_soname: ?[]const u8 = null, | |
| 244 | linker_gc_sections: ?bool = null, | |
| 245 | function_sections: ?bool = null, | |
| 246 | linker_allow_shlib_undefined: ?bool = null, | |
| 247 | linker_bind_global_refs_locally: ?bool = null, | |
| 248 | disable_c_depfile: bool = false, | |
| 249 | linker_z_nodelete: bool = false, | |
| 250 | linker_z_defs: bool = false, | |
| 251 | clang_passthrough_mode: bool = false, | |
| 252 | debug_cc: bool = false, | |
| 253 | debug_link: bool = false, | |
| 254 | stack_size_override: ?u64 = null, | |
| 255 | self_exe_path: ?[]const u8 = null, | |
| 256 | version: ?std.builtin.Version = null, | |
| 257 | libc_installation: ?*const LibCInstallation = null, | |
| 258 | }; | |
| 259 | ||
| 260 | pub fn create(gpa: *Allocator, options: InitOptions) !*Module { | |
| 261 | const comp: *Module = comp: { | |
| 262 | // For allocations that have the same lifetime as Module. This arena is used only during this | |
| 263 | // initialization and then is freed in deinit(). | |
| 264 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); | |
| 265 | errdefer arena_allocator.deinit(); | |
| 266 | const arena = &arena_allocator.allocator; | |
| 267 | ||
| 268 | // We put the `Module` itself in the arena. Freeing the arena will free the module. | |
| 269 | // It's initialized later after we prepare the initialization options. | |
| 270 | const comp = try arena.create(Module); | |
| 271 | const root_name = try arena.dupe(u8, options.root_name); | |
| 272 | ||
| 273 | const ofmt = options.object_format orelse options.target.getObjectFormat(); | |
| 274 | ||
| 275 | // Make a decision on whether to use LLD or our own linker. | |
| 276 | const use_lld = if (options.use_lld) |explicit| explicit else blk: { | |
| 277 | if (!build_options.have_llvm) | |
| 278 | break :blk false; | |
| 279 | ||
| 280 | if (ofmt == .c) | |
| 281 | break :blk false; | |
| 282 | ||
| 283 | // Our linker can't handle objects or most advanced options yet. | |
| 284 | if (options.link_objects.len != 0 or | |
| 285 | options.c_source_files.len != 0 or | |
| 286 | options.frameworks.len != 0 or | |
| 287 | options.system_libs.len != 0 or | |
| 288 | options.link_libc or options.link_libcpp or | |
| 289 | options.link_eh_frame_hdr or | |
| 290 | options.linker_script != null or options.version_script != null) | |
| 291 | { | |
| 292 | break :blk true; | |
| 293 | } | |
| 294 | break :blk false; | |
| 295 | }; | |
| 296 | ||
| 297 | // Make a decision on whether to use LLVM or our own backend. | |
| 298 | const use_llvm = if (options.use_llvm) |explicit| explicit else blk: { | |
| 299 | // We would want to prefer LLVM for release builds when it is available, however | |
| 300 | // we don't have an LLVM backend yet :) | |
| 301 | // We would also want to prefer LLVM for architectures that we don't have self-hosted support for too. | |
| 302 | break :blk false; | |
| 303 | }; | |
| 304 | ||
| 305 | const must_dynamic_link = dl: { | |
| 306 | if (target_util.cannotDynamicLink(options.target)) | |
| 307 | break :dl false; | |
| 308 | if (target_util.osRequiresLibC(options.target)) | |
| 309 | break :dl true; | |
| 310 | if (options.link_libc and options.target.isGnuLibC()) | |
| 311 | break :dl true; | |
| 312 | if (options.system_libs.len != 0) | |
| 313 | break :dl true; | |
| 314 | ||
| 315 | break :dl false; | |
| 316 | }; | |
| 317 | const default_link_mode: std.builtin.LinkMode = if (must_dynamic_link) .Dynamic else .Static; | |
| 318 | const link_mode: std.builtin.LinkMode = if (options.link_mode) |lm| blk: { | |
| 319 | if (lm == .Static and must_dynamic_link) { | |
| 320 | return error.UnableToStaticLink; | |
| 321 | } | |
| 322 | break :blk lm; | |
| 323 | } else default_link_mode; | |
| 324 | ||
| 325 | const libc_dirs = try detectLibCIncludeDirs( | |
| 326 | arena, | |
| 327 | options.zig_lib_directory.path.?, | |
| 328 | options.target, | |
| 329 | options.is_native_os, | |
| 330 | options.link_libc, | |
| 331 | options.libc_installation, | |
| 332 | ); | |
| 333 | ||
| 334 | const must_pic: bool = b: { | |
| 335 | if (target_util.requiresPIC(options.target, options.link_libc)) | |
| 336 | break :b true; | |
| 337 | break :b link_mode == .Dynamic; | |
| 338 | }; | |
| 339 | const pic = options.want_pic orelse must_pic; | |
| 340 | ||
| 341 | if (options.emit_h != null) fatal("-femit-h not supported yet", .{}); // TODO | |
| 342 | ||
| 343 | const emit_bin = options.emit_bin orelse fatal("-fno-emit-bin not supported yet", .{}); // TODO | |
| 344 | ||
| 345 | // Make a decision on whether to use Clang for translate-c and compiling C files. | |
| 346 | const use_clang = if (options.use_clang) |explicit| explicit else blk: { | |
| 347 | if (build_options.have_llvm) { | |
| 348 | // Can't use it if we don't have it! | |
| 349 | break :blk false; | |
| 350 | } | |
| 351 | // It's not planned to do our own translate-c or C compilation. | |
| 352 | break :blk true; | |
| 353 | }; | |
| 354 | ||
| 355 | const is_safe_mode = switch (options.optimize_mode) { | |
| 356 | .Debug, .ReleaseSafe => true, | |
| 357 | .ReleaseFast, .ReleaseSmall => false, | |
| 358 | }; | |
| 359 | ||
| 360 | const sanitize_c = options.want_sanitize_c orelse is_safe_mode; | |
| 361 | ||
| 362 | const stack_check: bool = b: { | |
| 363 | if (!target_util.supportsStackProbing(options.target)) | |
| 364 | break :b false; | |
| 365 | break :b options.want_stack_check orelse is_safe_mode; | |
| 366 | }; | |
| 367 | ||
| 368 | const valgrind: bool = b: { | |
| 369 | if (!target_util.hasValgrindSupport(options.target)) | |
| 370 | break :b false; | |
| 371 | break :b options.want_valgrind orelse (options.optimize_mode == .Debug); | |
| 372 | }; | |
| 373 | ||
| 374 | const single_threaded = options.single_threaded or target_util.isSingleThreaded(options.target); | |
| 375 | ||
| 376 | // We put everything into the cache hash that *cannot be modified during an incremental update*. | |
| 377 | // For example, one cannot change the target between updates, but one can change source files, | |
| 378 | // so the target goes into the cache hash, but source files do not. This is so that we can | |
| 379 | // find the same binary and incrementally update it even if there are modified source files. | |
| 380 | // We do this even if outputting to the current directory because we need somewhere to store | |
| 381 | // incremental compilation metadata. | |
| 382 | const cache = try arena.create(std.cache_hash.Cache); | |
| 383 | cache.* = .{ | |
| 384 | .gpa = gpa, | |
| 385 | .manifest_dir = try options.zig_cache_directory.handle.makeOpenPath("h", .{}), | |
| 386 | }; | |
| 387 | errdefer cache.manifest_dir.close(); | |
| 388 | ||
| 389 | // This is shared hasher state common to zig source and all C source files. | |
| 390 | cache.hash.addBytes(build_options.version); | |
| 391 | cache.hash.addBytes(options.zig_lib_directory.path orelse "."); | |
| 392 | cache.hash.add(options.optimize_mode); | |
| 393 | cache.hash.add(options.target.cpu.arch); | |
| 394 | cache.hash.addBytes(options.target.cpu.model.name); | |
| 395 | cache.hash.add(options.target.cpu.features.ints); | |
| 396 | cache.hash.add(options.target.os.tag); | |
| 397 | cache.hash.add(options.target.abi); | |
| 398 | cache.hash.add(ofmt); | |
| 399 | cache.hash.add(pic); | |
| 400 | cache.hash.add(stack_check); | |
| 401 | cache.hash.add(link_mode); | |
| 402 | cache.hash.add(options.strip); | |
| 403 | cache.hash.add(options.link_libc); | |
| 404 | cache.hash.add(options.output_mode); | |
| 405 | // TODO audit this and make sure everything is in it | |
| 406 | ||
| 407 | const zig_module: ?*ZigModule = if (options.root_pkg) |root_pkg| blk: { | |
| 408 | // Options that are specific to zig source files, that cannot be | |
| 409 | // modified between incremental updates. | |
| 410 | var hash = cache.hash; | |
| 411 | ||
| 412 | hash.add(valgrind); | |
| 413 | hash.add(single_threaded); | |
| 414 | switch (options.target.os.getVersionRange()) { | |
| 415 | .linux => |linux| { | |
| 416 | hash.add(linux.range.min); | |
| 417 | hash.add(linux.range.max); | |
| 418 | hash.add(linux.glibc); | |
| 419 | }, | |
| 420 | .windows => |windows| { | |
| 421 | hash.add(windows.min); | |
| 422 | hash.add(windows.max); | |
| 423 | }, | |
| 424 | .semver => |semver| { | |
| 425 | hash.add(semver.min); | |
| 426 | hash.add(semver.max); | |
| 427 | }, | |
| 428 | .none => {}, | |
| 429 | } | |
| 430 | ||
| 431 | const digest = hash.final(); | |
| 432 | const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); | |
| 433 | var artifact_dir = try options.zig_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); | |
| 434 | errdefer artifact_dir.close(); | |
| 435 | const zig_cache_artifact_directory: Directory = .{ | |
| 436 | .handle = artifact_dir, | |
| 437 | .path = if (options.zig_cache_directory.path) |p| | |
| 438 | try std.fs.path.join(arena, &[_][]const u8{ p, artifact_sub_dir }) | |
| 439 | else | |
| 440 | artifact_sub_dir, | |
| 441 | }; | |
| 442 | ||
| 443 | // TODO when we implement serialization and deserialization of incremental compilation metadata, | |
| 444 | // this is where we would load it. We have open a handle to the directory where | |
| 445 | // the output either already is, or will be. | |
| 446 | // However we currently do not have serialization of such metadata, so for now | |
| 447 | // we set up an empty ZigModule that does the entire compilation fresh. | |
| 448 | ||
| 449 | const root_scope = rs: { | |
| 450 | if (mem.endsWith(u8, root_pkg.root_src_path, ".zig")) { | |
| 451 | const root_scope = try gpa.create(ZigModule.Scope.File); | |
| 452 | root_scope.* = .{ | |
| 453 | .sub_file_path = root_pkg.root_src_path, | |
| 454 | .source = .{ .unloaded = {} }, | |
| 455 | .contents = .{ .not_available = {} }, | |
| 456 | .status = .never_loaded, | |
| 457 | .root_container = .{ | |
| 458 | .file_scope = root_scope, | |
| 459 | .decls = .{}, | |
| 460 | }, | |
| 461 | }; | |
| 462 | break :rs &root_scope.base; | |
| 463 | } else if (mem.endsWith(u8, root_pkg.root_src_path, ".zir")) { | |
| 464 | const root_scope = try gpa.create(ZigModule.Scope.ZIRModule); | |
| 465 | root_scope.* = .{ | |
| 466 | .sub_file_path = root_pkg.root_src_path, | |
| 467 | .source = .{ .unloaded = {} }, | |
| 468 | .contents = .{ .not_available = {} }, | |
| 469 | .status = .never_loaded, | |
| 470 | .decls = .{}, | |
| 471 | }; | |
| 472 | break :rs &root_scope.base; | |
| 473 | } else { | |
| 474 | unreachable; | |
| 475 | } | |
| 476 | }; | |
| 477 | ||
| 478 | const zig_module = try arena.create(ZigModule); | |
| 479 | zig_module.* = .{ | |
| 480 | .gpa = gpa, | |
| 481 | .comp = comp, | |
| 482 | .root_pkg = root_pkg, | |
| 483 | .root_scope = root_scope, | |
| 484 | .zig_cache_artifact_directory = zig_cache_artifact_directory, | |
| 485 | }; | |
| 486 | break :blk zig_module; | |
| 487 | } else null; | |
| 488 | errdefer if (zig_module) |zm| zm.deinit(); | |
| 489 | ||
| 490 | // For resource management purposes. | |
| 491 | var owned_link_dir: ?std.fs.Dir = null; | |
| 492 | errdefer if (owned_link_dir) |*dir| dir.close(); | |
| 493 | ||
| 494 | const bin_directory = emit_bin.directory orelse blk: { | |
| 495 | if (zig_module) |zm| break :blk zm.zig_cache_artifact_directory; | |
| 496 | ||
| 497 | const digest = cache.hash.peek(); | |
| 498 | const artifact_sub_dir = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); | |
| 499 | var artifact_dir = try options.zig_cache_directory.handle.makeOpenPath(artifact_sub_dir, .{}); | |
| 500 | owned_link_dir = artifact_dir; | |
| 501 | const link_artifact_directory: Directory = .{ | |
| 502 | .handle = artifact_dir, | |
| 503 | .path = if (options.zig_cache_directory.path) |p| | |
| 504 | try std.fs.path.join(arena, &[_][]const u8{ p, artifact_sub_dir }) | |
| 505 | else | |
| 506 | artifact_sub_dir, | |
| 507 | }; | |
| 508 | break :blk link_artifact_directory; | |
| 509 | }; | |
| 510 | ||
| 511 | const bin_file = try link.File.openPath(gpa, .{ | |
| 512 | .directory = bin_directory, | |
| 513 | .sub_path = emit_bin.basename, | |
| 514 | .root_name = root_name, | |
| 515 | .zig_module = zig_module, | |
| 516 | .target = options.target, | |
| 517 | .dynamic_linker = options.dynamic_linker, | |
| 518 | .output_mode = options.output_mode, | |
| 519 | .link_mode = link_mode, | |
| 520 | .object_format = ofmt, | |
| 521 | .optimize_mode = options.optimize_mode, | |
| 522 | .use_lld = use_lld, | |
| 523 | .use_llvm = use_llvm, | |
| 524 | .link_libc = options.link_libc, | |
| 525 | .link_libcpp = options.link_libcpp, | |
| 526 | .objects = options.link_objects, | |
| 527 | .frameworks = options.frameworks, | |
| 528 | .framework_dirs = options.framework_dirs, | |
| 529 | .system_libs = options.system_libs, | |
| 530 | .lib_dirs = options.lib_dirs, | |
| 531 | .rpath_list = options.rpath_list, | |
| 532 | .strip = options.strip, | |
| 533 | .is_native_os = options.is_native_os, | |
| 534 | .function_sections = options.function_sections orelse false, | |
| 535 | .allow_shlib_undefined = options.linker_allow_shlib_undefined, | |
| 536 | .bind_global_refs_locally = options.linker_bind_global_refs_locally orelse false, | |
| 537 | .z_nodelete = options.linker_z_nodelete, | |
| 538 | .z_defs = options.linker_z_defs, | |
| 539 | .stack_size_override = options.stack_size_override, | |
| 540 | .linker_script = options.linker_script, | |
| 541 | .version_script = options.version_script, | |
| 542 | .gc_sections = options.linker_gc_sections, | |
| 543 | .eh_frame_hdr = options.link_eh_frame_hdr, | |
| 544 | .rdynamic = options.rdynamic, | |
| 545 | .extra_lld_args = options.lld_argv, | |
| 546 | .override_soname = options.override_soname, | |
| 547 | .version = options.version, | |
| 548 | .libc_installation = libc_dirs.libc_installation, | |
| 549 | .pic = pic, | |
| 550 | .valgrind = valgrind, | |
| 551 | .stack_check = stack_check, | |
| 552 | .single_threaded = single_threaded, | |
| 553 | .debug_link = options.debug_link, | |
| 554 | }); | |
| 555 | errdefer bin_file.destroy(); | |
| 556 | ||
| 557 | comp.* = .{ | |
| 558 | .gpa = gpa, | |
| 559 | .arena_state = arena_allocator.state, | |
| 560 | .zig_lib_directory = options.zig_lib_directory, | |
| 561 | .zig_cache_directory = options.zig_cache_directory, | |
| 562 | .bin_file = bin_file, | |
| 563 | .work_queue = std.fifo.LinearFifo(WorkItem, .Dynamic).init(gpa), | |
| 564 | .keep_source_files_loaded = options.keep_source_files_loaded, | |
| 565 | .use_clang = use_clang, | |
| 566 | .clang_argv = options.clang_argv, | |
| 567 | .c_source_files = options.c_source_files, | |
| 568 | .cache_parent = cache, | |
| 569 | .self_exe_path = options.self_exe_path, | |
| 570 | .libc_include_dir_list = libc_dirs.libc_include_dir_list, | |
| 571 | .sanitize_c = sanitize_c, | |
| 572 | .rand = options.rand, | |
| 573 | .clang_passthrough_mode = options.clang_passthrough_mode, | |
| 574 | .debug_cc = options.debug_cc, | |
| 575 | .disable_c_depfile = options.disable_c_depfile, | |
| 576 | .owned_link_dir = owned_link_dir, | |
| 577 | }; | |
| 578 | break :comp comp; | |
| 579 | }; | |
| 580 | errdefer comp.destroy(); | |
| 581 | ||
| 582 | // Add a `CObject` for each `c_source_files`. | |
| 583 | try comp.c_object_table.ensureCapacity(gpa, options.c_source_files.len); | |
| 584 | for (options.c_source_files) |c_source_file| { | |
| 585 | var local_arena = std.heap.ArenaAllocator.init(gpa); | |
| 586 | errdefer local_arena.deinit(); | |
| 587 | ||
| 588 | const c_object = try local_arena.allocator.create(CObject); | |
| 589 | ||
| 590 | c_object.* = .{ | |
| 591 | .status = .{ .new = {} }, | |
| 592 | // TODO look into refactoring to turn these 2 fields simply into a CSourceFile | |
| 593 | .src_path = try local_arena.allocator.dupe(u8, c_source_file.src_path), | |
| 594 | .extra_flags = try local_arena.allocator.dupe([]const u8, c_source_file.extra_flags), | |
| 595 | .arena = local_arena.state, | |
| 596 | }; | |
| 597 | comp.c_object_table.putAssumeCapacityNoClobber(c_object, {}); | |
| 598 | } | |
| 599 | ||
| 600 | // If we need to build glibc for the target, add work items for it. | |
| 601 | // We go through the work queue so that building can be done in parallel. | |
| 602 | if (comp.wantBuildGLibCFromSource()) { | |
| 603 | try comp.addBuildingGLibCWorkItems(); | |
| 604 | } | |
| 605 | ||
| 606 | return comp; | |
| 607 | } | |
| 608 | ||
| 609 | pub fn destroy(self: *Module) void { | |
| 610 | const optional_zig_module = self.bin_file.options.zig_module; | |
| 611 | self.bin_file.destroy(); | |
| 612 | if (optional_zig_module) |zig_module| zig_module.deinit(); | |
| 613 | ||
| 614 | const gpa = self.gpa; | |
| 615 | self.work_queue.deinit(); | |
| 616 | ||
| 617 | { | |
| 618 | var it = self.crt_files.iterator(); | |
| 619 | while (it.next()) |entry| { | |
| 620 | gpa.free(entry.key); | |
| 621 | gpa.free(entry.value); | |
| 622 | } | |
| 623 | self.crt_files.deinit(gpa); | |
| 624 | } | |
| 625 | ||
| 626 | for (self.c_object_table.items()) |entry| { | |
| 627 | entry.key.destroy(gpa); | |
| 628 | } | |
| 629 | self.c_object_table.deinit(gpa); | |
| 630 | ||
| 631 | for (self.failed_c_objects.items()) |entry| { | |
| 632 | entry.value.destroy(gpa); | |
| 633 | } | |
| 634 | self.failed_c_objects.deinit(gpa); | |
| 635 | ||
| 636 | self.cache_parent.manifest_dir.close(); | |
| 637 | if (self.owned_link_dir) |*dir| dir.close(); | |
| 638 | ||
| 639 | // This destroys `self`. | |
| 640 | self.arena_state.promote(gpa).deinit(); | |
| 641 | } | |
| 642 | ||
| 643 | pub fn getTarget(self: Module) Target { | |
| 644 | return self.bin_file.options.target; | |
| 645 | } | |
| 646 | ||
| 647 | /// Detect changes to source files, perform semantic analysis, and update the output files. | |
| 648 | pub fn update(self: *Module) !void { | |
| 649 | const tracy = trace(@src()); | |
| 650 | defer tracy.end(); | |
| 651 | ||
| 652 | // For compiling C objects, we rely on the cache hash system to avoid duplicating work. | |
| 653 | // TODO Look into caching this data in memory to improve performance. | |
| 654 | // Add a WorkItem for each C object. | |
| 655 | try self.work_queue.ensureUnusedCapacity(self.c_object_table.items().len); | |
| 656 | for (self.c_object_table.items()) |entry| { | |
| 657 | self.work_queue.writeItemAssumeCapacity(.{ .c_object = entry.key }); | |
| 658 | } | |
| 659 | ||
| 660 | if (self.bin_file.options.zig_module) |zig_module| { | |
| 661 | zig_module.generation += 1; | |
| 662 | ||
| 663 | // TODO Detect which source files changed. | |
| 664 | // Until then we simulate a full cache miss. Source files could have been loaded for any reason; | |
| 665 | // to force a refresh we unload now. | |
| 666 | if (zig_module.root_scope.cast(ZigModule.Scope.File)) |zig_file| { | |
| 667 | zig_file.unload(zig_module.gpa); | |
| 668 | zig_module.analyzeContainer(&zig_file.root_container) catch |err| switch (err) { | |
| 669 | error.AnalysisFail => { | |
| 670 | assert(self.totalErrorCount() != 0); | |
| 671 | }, | |
| 672 | else => |e| return e, | |
| 673 | }; | |
| 674 | } else if (zig_module.root_scope.cast(ZigModule.Scope.ZIRModule)) |zir_module| { | |
| 675 | zir_module.unload(zig_module.gpa); | |
| 676 | zig_module.analyzeRootZIRModule(zir_module) catch |err| switch (err) { | |
| 677 | error.AnalysisFail => { | |
| 678 | assert(self.totalErrorCount() != 0); | |
| 679 | }, | |
| 680 | else => |e| return e, | |
| 681 | }; | |
| 682 | } | |
| 683 | } | |
| 684 | ||
| 685 | try self.performAllTheWork(); | |
| 686 | ||
| 687 | if (self.bin_file.options.zig_module) |zig_module| { | |
| 688 | // Process the deletion set. | |
| 689 | while (zig_module.deletion_set.popOrNull()) |decl| { | |
| 690 | if (decl.dependants.items().len != 0) { | |
| 691 | decl.deletion_flag = false; | |
| 692 | continue; | |
| 693 | } | |
| 694 | try zig_module.deleteDecl(decl); | |
| 695 | } | |
| 696 | } | |
| 697 | ||
| 698 | // This is needed before reading the error flags. | |
| 699 | try self.bin_file.flush(self); | |
| 700 | ||
| 701 | self.link_error_flags = self.bin_file.errorFlags(); | |
| 702 | ||
| 703 | // If there are any errors, we anticipate the source files being loaded | |
| 704 | // to report error messages. Otherwise we unload all source files to save memory. | |
| 705 | if (self.totalErrorCount() == 0 and !self.keep_source_files_loaded) { | |
| 706 | if (self.bin_file.options.zig_module) |zig_module| { | |
| 707 | zig_module.root_scope.unload(self.gpa); | |
| 708 | } | |
| 709 | } | |
| 710 | } | |
| 711 | ||
| 712 | /// Having the file open for writing is problematic as far as executing the | |
| 713 | /// binary is concerned. This will remove the write flag, or close the file, | |
| 714 | /// or whatever is needed so that it can be executed. | |
| 715 | /// After this, one must call` makeFileWritable` before calling `update`. | |
| 716 | pub fn makeBinFileExecutable(self: *Module) !void { | |
| 717 | return self.bin_file.makeExecutable(); | |
| 718 | } | |
| 719 | ||
| 720 | pub fn makeBinFileWritable(self: *Module) !void { | |
| 721 | return self.bin_file.makeWritable(); | |
| 722 | } | |
| 723 | ||
| 724 | pub fn totalErrorCount(self: *Module) usize { | |
| 725 | var total: usize = self.failed_c_objects.items().len; | |
| 726 | ||
| 727 | if (self.bin_file.options.zig_module) |zig_module| { | |
| 728 | total += zig_module.failed_decls.items().len + | |
| 729 | zig_module.failed_exports.items().len + | |
| 730 | zig_module.failed_files.items().len; | |
| 731 | } | |
| 732 | ||
| 733 | // The "no entry point found" error only counts if there are no other errors. | |
| 734 | if (total == 0) { | |
| 735 | return @boolToInt(self.link_error_flags.no_entry_point_found); | |
| 736 | } | |
| 737 | ||
| 738 | return total; | |
| 739 | } | |
| 740 | ||
| 741 | pub fn getAllErrorsAlloc(self: *Module) !AllErrors { | |
| 742 | var arena = std.heap.ArenaAllocator.init(self.gpa); | |
| 743 | errdefer arena.deinit(); | |
| 744 | ||
| 745 | var errors = std.ArrayList(AllErrors.Message).init(self.gpa); | |
| 746 | defer errors.deinit(); | |
| 747 | ||
| 748 | for (self.failed_c_objects.items()) |entry| { | |
| 749 | const c_object = entry.key; | |
| 750 | const err_msg = entry.value; | |
| 751 | try AllErrors.add(&arena, &errors, c_object.src_path, "", err_msg.*); | |
| 752 | } | |
| 753 | if (self.bin_file.options.zig_module) |zig_module| { | |
| 754 | for (zig_module.failed_files.items()) |entry| { | |
| 755 | const scope = entry.key; | |
| 756 | const err_msg = entry.value; | |
| 757 | const source = try scope.getSource(zig_module); | |
| 758 | try AllErrors.add(&arena, &errors, scope.subFilePath(), source, err_msg.*); | |
| 759 | } | |
| 760 | for (zig_module.failed_decls.items()) |entry| { | |
| 761 | const decl = entry.key; | |
| 762 | const err_msg = entry.value; | |
| 763 | const source = try decl.scope.getSource(zig_module); | |
| 764 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); | |
| 765 | } | |
| 766 | for (zig_module.failed_exports.items()) |entry| { | |
| 767 | const decl = entry.key.owner_decl; | |
| 768 | const err_msg = entry.value; | |
| 769 | const source = try decl.scope.getSource(zig_module); | |
| 770 | try AllErrors.add(&arena, &errors, decl.scope.subFilePath(), source, err_msg.*); | |
| 771 | } | |
| 772 | } | |
| 773 | ||
| 774 | if (errors.items.len == 0 and self.link_error_flags.no_entry_point_found) { | |
| 775 | const global_err_src_path = blk: { | |
| 776 | if (self.bin_file.options.zig_module) |zig_module| break :blk zig_module.root_pkg.root_src_path; | |
| 777 | if (self.c_source_files.len != 0) break :blk self.c_source_files[0].src_path; | |
| 778 | if (self.bin_file.options.objects.len != 0) break :blk self.bin_file.options.objects[0]; | |
| 779 | break :blk "(no file)"; | |
| 780 | }; | |
| 781 | try errors.append(.{ | |
| 782 | .src_path = global_err_src_path, | |
| 783 | .line = 0, | |
| 784 | .column = 0, | |
| 785 | .byte_offset = 0, | |
| 786 | .msg = try std.fmt.allocPrint(&arena.allocator, "no entry point found", .{}), | |
| 787 | }); | |
| 788 | } | |
| 789 | ||
| 790 | assert(errors.items.len == self.totalErrorCount()); | |
| 791 | ||
| 792 | return AllErrors{ | |
| 793 | .list = try arena.allocator.dupe(AllErrors.Message, errors.items), | |
| 794 | .arena = arena.state, | |
| 795 | }; | |
| 796 | } | |
| 797 | ||
| 798 | pub fn performAllTheWork(self: *Module) error{OutOfMemory}!void { | |
| 799 | while (self.work_queue.readItem()) |work_item| switch (work_item) { | |
| 800 | .codegen_decl => |decl| switch (decl.analysis) { | |
| 801 | .unreferenced => unreachable, | |
| 802 | .in_progress => unreachable, | |
| 803 | .outdated => unreachable, | |
| 804 | ||
| 805 | .sema_failure, | |
| 806 | .codegen_failure, | |
| 807 | .dependency_failure, | |
| 808 | .sema_failure_retryable, | |
| 809 | => continue, | |
| 810 | ||
| 811 | .complete, .codegen_failure_retryable => { | |
| 812 | const zig_module = self.bin_file.options.zig_module.?; | |
| 813 | if (decl.typed_value.most_recent.typed_value.val.cast(Value.Payload.Function)) |payload| { | |
| 814 | switch (payload.func.analysis) { | |
| 815 | .queued => zig_module.analyzeFnBody(decl, payload.func) catch |err| switch (err) { | |
| 816 | error.AnalysisFail => { | |
| 817 | assert(payload.func.analysis != .in_progress); | |
| 818 | continue; | |
| 819 | }, | |
| 820 | error.OutOfMemory => return error.OutOfMemory, | |
| 821 | }, | |
| 822 | .in_progress => unreachable, | |
| 823 | .sema_failure, .dependency_failure => continue, | |
| 824 | .success => {}, | |
| 825 | } | |
| 826 | // Here we tack on additional allocations to the Decl's arena. The allocations are | |
| 827 | // lifetime annotations in the ZIR. | |
| 828 | var decl_arena = decl.typed_value.most_recent.arena.?.promote(zig_module.gpa); | |
| 829 | defer decl.typed_value.most_recent.arena.?.* = decl_arena.state; | |
| 830 | log.debug("analyze liveness of {}\n", .{decl.name}); | |
| 831 | try liveness.analyze(zig_module.gpa, &decl_arena.allocator, payload.func.analysis.success); | |
| 832 | } | |
| 833 | ||
| 834 | assert(decl.typed_value.most_recent.typed_value.ty.hasCodeGenBits()); | |
| 835 | ||
| 836 | self.bin_file.updateDecl(zig_module, decl) catch |err| switch (err) { | |
| 837 | error.OutOfMemory => return error.OutOfMemory, | |
| 838 | error.AnalysisFail => { | |
| 839 | decl.analysis = .dependency_failure; | |
| 840 | }, | |
| 841 | else => { | |
| 842 | try zig_module.failed_decls.ensureCapacity(zig_module.gpa, zig_module.failed_decls.items().len + 1); | |
| 843 | zig_module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | |
| 844 | zig_module.gpa, | |
| 845 | decl.src(), | |
| 846 | "unable to codegen: {}", | |
| 847 | .{@errorName(err)}, | |
| 848 | )); | |
| 849 | decl.analysis = .codegen_failure_retryable; | |
| 850 | }, | |
| 851 | }; | |
| 852 | }, | |
| 853 | }, | |
| 854 | .analyze_decl => |decl| { | |
| 855 | const zig_module = self.bin_file.options.zig_module.?; | |
| 856 | zig_module.ensureDeclAnalyzed(decl) catch |err| switch (err) { | |
| 857 | error.OutOfMemory => return error.OutOfMemory, | |
| 858 | error.AnalysisFail => continue, | |
| 859 | }; | |
| 860 | }, | |
| 861 | .update_line_number => |decl| { | |
| 862 | const zig_module = self.bin_file.options.zig_module.?; | |
| 863 | self.bin_file.updateDeclLineNumber(zig_module, decl) catch |err| { | |
| 864 | try zig_module.failed_decls.ensureCapacity(zig_module.gpa, zig_module.failed_decls.items().len + 1); | |
| 865 | zig_module.failed_decls.putAssumeCapacityNoClobber(decl, try ErrorMsg.create( | |
| 866 | zig_module.gpa, | |
| 867 | decl.src(), | |
| 868 | "unable to update line number: {}", | |
| 869 | .{@errorName(err)}, | |
| 870 | )); | |
| 871 | decl.analysis = .codegen_failure_retryable; | |
| 872 | }; | |
| 873 | }, | |
| 874 | .c_object => |c_object| { | |
| 875 | self.updateCObject(c_object) catch |err| switch (err) { | |
| 876 | error.AnalysisFail => continue, | |
| 877 | else => { | |
| 878 | try self.failed_c_objects.ensureCapacity(self.gpa, self.failed_c_objects.items().len + 1); | |
| 879 | self.failed_c_objects.putAssumeCapacityNoClobber(c_object, try ErrorMsg.create( | |
| 880 | self.gpa, | |
| 881 | 0, | |
| 882 | "unable to build C object: {}", | |
| 883 | .{@errorName(err)}, | |
| 884 | )); | |
| 885 | c_object.status = .{ .failure = {} }; | |
| 886 | }, | |
| 887 | }; | |
| 888 | }, | |
| 889 | .glibc_crt_file => |crt_file| { | |
| 890 | glibc.buildCRTFile(self, crt_file) catch |err| { | |
| 891 | // This is a problem with the Zig installation. It's mostly OK to crash here, | |
| 892 | // but TODO because it would be even better if we could recover gracefully | |
| 893 | // from temporary problems such as out-of-disk-space. | |
| 894 | fatal("unable to build glibc CRT file: {}", .{@errorName(err)}); | |
| 895 | }; | |
| 896 | }, | |
| 897 | .glibc_so => |glibc_lib| { | |
| 898 | fatal("TODO build glibc shared object '{}.so.{}'", .{ glibc_lib.name, glibc_lib.sover }); | |
| 899 | }, | |
| 900 | }; | |
| 901 | } | |
| 902 | ||
| 903 | fn updateCObject(comp: *Compilation, c_object: *CObject) !void { | |
| 904 | const tracy = trace(@src()); | |
| 905 | defer tracy.end(); | |
| 906 | ||
| 907 | if (!build_options.have_llvm) { | |
| 908 | return comp.failCObj(c_object, "clang not available: compiler not built with LLVM extensions enabled", .{}); | |
| 909 | } | |
| 910 | const self_exe_path = comp.self_exe_path orelse | |
| 911 | return comp.failCObj(c_object, "clang compilation disabled", .{}); | |
| 912 | ||
| 913 | if (c_object.clearStatus(comp.gpa)) { | |
| 914 | // There was previous failure. | |
| 915 | comp.failed_c_objects.removeAssertDiscard(c_object); | |
| 916 | } | |
| 917 | ||
| 918 | var ch = comp.cache_parent.obtain(); | |
| 919 | defer ch.deinit(); | |
| 920 | ||
| 921 | ch.hash.add(comp.sanitize_c); | |
| 922 | ch.hash.addListOfBytes(comp.clang_argv); | |
| 923 | ch.hash.add(comp.bin_file.options.link_libcpp); | |
| 924 | ch.hash.addListOfBytes(comp.libc_include_dir_list); | |
| 925 | // TODO | |
| 926 | //cache_int(cache_hash, g->code_model); | |
| 927 | //cache_bool(cache_hash, codegen_have_frame_pointer(g)); | |
| 928 | _ = try ch.addFile(c_object.src_path, null); | |
| 929 | { | |
| 930 | // Hash the extra flags, with special care to call addFile for file parameters. | |
| 931 | // TODO this logic can likely be improved by utilizing clang_options_data.zig. | |
| 932 | const file_args = [_][]const u8{"-include"}; | |
| 933 | var arg_i: usize = 0; | |
| 934 | while (arg_i < c_object.extra_flags.len) : (arg_i += 1) { | |
| 935 | const arg = c_object.extra_flags[arg_i]; | |
| 936 | ch.hash.addBytes(arg); | |
| 937 | for (file_args) |file_arg| { | |
| 938 | if (mem.eql(u8, file_arg, arg) and arg_i + 1 < c_object.extra_flags.len) { | |
| 939 | arg_i += 1; | |
| 940 | _ = try ch.addFile(c_object.extra_flags[arg_i], null); | |
| 941 | } | |
| 942 | } | |
| 943 | } | |
| 944 | } | |
| 945 | ||
| 946 | var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); | |
| 947 | defer arena_allocator.deinit(); | |
| 948 | const arena = &arena_allocator.allocator; | |
| 949 | ||
| 950 | const c_source_basename = std.fs.path.basename(c_object.src_path); | |
| 951 | // Special case when doing build-obj for just one C file. When there are more than one object | |
| 952 | // file and building an object we need to link them together, but with just one it should go | |
| 953 | // directly to the output file. | |
| 954 | const direct_o = comp.c_source_files.len == 1 and comp.bin_file.options.zig_module == null and | |
| 955 | comp.bin_file.options.output_mode == .Obj and comp.bin_file.options.objects.len == 0; | |
| 956 | const o_basename_noext = if (direct_o) | |
| 957 | comp.bin_file.options.root_name | |
| 958 | else | |
| 959 | mem.split(c_source_basename, ".").next().?; | |
| 960 | const o_basename = try std.fmt.allocPrint(arena, "{}{}", .{ o_basename_noext, comp.getTarget().oFileExt() }); | |
| 961 | ||
| 962 | const full_object_path = if (!(try ch.hit()) or comp.disable_c_depfile) blk: { | |
| 963 | var argv = std.ArrayList([]const u8).init(comp.gpa); | |
| 964 | defer argv.deinit(); | |
| 965 | ||
| 966 | // We can't know the digest until we do the C compiler invocation, so we need a temporary filename. | |
| 967 | const out_obj_path = try comp.tmpFilePath(arena, o_basename); | |
| 968 | ||
| 969 | try argv.appendSlice(&[_][]const u8{ self_exe_path, "clang", "-c" }); | |
| 970 | ||
| 971 | const ext = classifyFileExt(c_object.src_path); | |
| 972 | // TODO capture the .d file and deal with caching stuff | |
| 973 | try comp.addCCArgs(arena, &argv, ext, false, null); | |
| 974 | ||
| 975 | try argv.append("-o"); | |
| 976 | try argv.append(out_obj_path); | |
| 977 | ||
| 978 | try argv.append(c_object.src_path); | |
| 979 | try argv.appendSlice(c_object.extra_flags); | |
| 980 | ||
| 981 | if (comp.debug_cc) { | |
| 982 | for (argv.items[0 .. argv.items.len - 1]) |arg| { | |
| 983 | std.debug.print("{} ", .{arg}); | |
| 984 | } | |
| 985 | std.debug.print("{}\n", .{argv.items[argv.items.len - 1]}); | |
| 986 | } | |
| 987 | ||
| 988 | const child = try std.ChildProcess.init(argv.items, arena); | |
| 989 | defer child.deinit(); | |
| 990 | ||
| 991 | if (comp.clang_passthrough_mode) { | |
| 992 | child.stdin_behavior = .Inherit; | |
| 993 | child.stdout_behavior = .Inherit; | |
| 994 | child.stderr_behavior = .Inherit; | |
| 995 | ||
| 996 | const term = child.spawnAndWait() catch |err| { | |
| 997 | return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) }); | |
| 998 | }; | |
| 999 | switch (term) { | |
| 1000 | .Exited => |code| { | |
| 1001 | if (code != 0) { | |
| 1002 | // TODO make std.process.exit and std.ChildProcess exit code have the same type | |
| 1003 | // and forward it here. Currently it is u32 vs u8. | |
| 1004 | std.process.exit(1); | |
| 1005 | } | |
| 1006 | }, | |
| 1007 | else => std.process.exit(1), | |
| 1008 | } | |
| 1009 | } else { | |
| 1010 | child.stdin_behavior = .Ignore; | |
| 1011 | child.stdout_behavior = .Pipe; | |
| 1012 | child.stderr_behavior = .Pipe; | |
| 1013 | ||
| 1014 | try child.spawn(); | |
| 1015 | ||
| 1016 | const stdout_reader = child.stdout.?.reader(); | |
| 1017 | const stderr_reader = child.stderr.?.reader(); | |
| 1018 | ||
| 1019 | // TODO Need to poll to read these streams to prevent a deadlock (or rely on evented I/O). | |
| 1020 | const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32)); | |
| 1021 | const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); | |
| 1022 | ||
| 1023 | const term = child.wait() catch |err| { | |
| 1024 | return comp.failCObj(c_object, "unable to spawn {}: {}", .{ argv.items[0], @errorName(err) }); | |
| 1025 | }; | |
| 1026 | ||
| 1027 | switch (term) { | |
| 1028 | .Exited => |code| { | |
| 1029 | if (code != 0) { | |
| 1030 | // TODO parse clang stderr and turn it into an error message | |
| 1031 | // and then call failCObjWithOwnedErrorMsg | |
| 1032 | std.log.err("clang failed with stderr: {}", .{stderr}); | |
| 1033 | return comp.failCObj(c_object, "clang exited with code {}", .{code}); | |
| 1034 | } | |
| 1035 | }, | |
| 1036 | else => { | |
| 1037 | std.log.err("clang terminated with stderr: {}", .{stderr}); | |
| 1038 | return comp.failCObj(c_object, "clang terminated unexpectedly", .{}); | |
| 1039 | }, | |
| 1040 | } | |
| 1041 | } | |
| 1042 | ||
| 1043 | // TODO handle .d files | |
| 1044 | ||
| 1045 | // Rename into place. | |
| 1046 | const digest = ch.final(); | |
| 1047 | const full_object_path = if (comp.zig_cache_directory.path) |p| | |
| 1048 | try std.fs.path.join(arena, &[_][]const u8{ p, "o", &digest, o_basename }) | |
| 1049 | else | |
| 1050 | try std.fs.path.join(arena, &[_][]const u8{ "o", &digest, o_basename }); | |
| 1051 | try std.fs.rename(out_obj_path, full_object_path); | |
| 1052 | ||
| 1053 | ch.writeManifest() catch |err| { | |
| 1054 | std.log.warn("failed to write cache manifest when compiling '{}': {}", .{ c_object.src_path, @errorName(err) }); | |
| 1055 | }; | |
| 1056 | break :blk full_object_path; | |
| 1057 | } else blk: { | |
| 1058 | const digest = ch.final(); | |
| 1059 | const full_object_path = if (comp.zig_cache_directory.path) |p| | |
| 1060 | try std.fs.path.join(arena, &[_][]const u8{ p, "o", &digest, o_basename }) | |
| 1061 | else | |
| 1062 | try std.fs.path.join(arena, &[_][]const u8{ "o", &digest, o_basename }); | |
| 1063 | break :blk full_object_path; | |
| 1064 | }; | |
| 1065 | ||
| 1066 | c_object.status = .{ | |
| 1067 | .success = .{ | |
| 1068 | .object_path = full_object_path, | |
| 1069 | .lock = ch.toOwnedLock(), | |
| 1070 | }, | |
| 1071 | }; | |
| 1072 | } | |
| 1073 | ||
| 1074 | fn tmpFilePath(mod: *Module, arena: *Allocator, suffix: []const u8) error{OutOfMemory}![]const u8 { | |
| 1075 | const s = std.fs.path.sep_str; | |
| 1076 | return std.fmt.allocPrint( | |
| 1077 | arena, | |
| 1078 | "{}" ++ s ++ "tmp" ++ s ++ "{x}-{}", | |
| 1079 | .{ mod.zig_cache_directory.path.?, mod.rand.int(u64), suffix }, | |
| 1080 | ); | |
| 1081 | } | |
| 1082 | ||
| 1083 | /// Add common C compiler args between translate-c and C object compilation. | |
| 1084 | fn addCCArgs( | |
| 1085 | mod: *Module, | |
| 1086 | arena: *Allocator, | |
| 1087 | argv: *std.ArrayList([]const u8), | |
| 1088 | ext: FileExt, | |
| 1089 | translate_c: bool, | |
| 1090 | out_dep_path: ?[]const u8, | |
| 1091 | ) !void { | |
| 1092 | const target = mod.getTarget(); | |
| 1093 | ||
| 1094 | if (translate_c) { | |
| 1095 | try argv.appendSlice(&[_][]const u8{ "-x", "c" }); | |
| 1096 | } | |
| 1097 | ||
| 1098 | if (ext == .cpp) { | |
| 1099 | try argv.append("-nostdinc++"); | |
| 1100 | } | |
| 1101 | try argv.appendSlice(&[_][]const u8{ | |
| 1102 | "-nostdinc", | |
| 1103 | "-fno-spell-checking", | |
| 1104 | }); | |
| 1105 | ||
| 1106 | // We don't ever put `-fcolor-diagnostics` or `-fno-color-diagnostics` because in passthrough mode | |
| 1107 | // we want Clang to infer it, and in normal mode we always want it off, which will be true since | |
| 1108 | // clang will detect stderr as a pipe rather than a terminal. | |
| 1109 | if (!mod.clang_passthrough_mode) { | |
| 1110 | // Make stderr more easily parseable. | |
| 1111 | try argv.append("-fno-caret-diagnostics"); | |
| 1112 | } | |
| 1113 | ||
| 1114 | if (mod.bin_file.options.function_sections) { | |
| 1115 | try argv.append("-ffunction-sections"); | |
| 1116 | } | |
| 1117 | ||
| 1118 | try argv.ensureCapacity(argv.items.len + mod.bin_file.options.framework_dirs.len * 2); | |
| 1119 | for (mod.bin_file.options.framework_dirs) |framework_dir| { | |
| 1120 | argv.appendAssumeCapacity("-iframework"); | |
| 1121 | argv.appendAssumeCapacity(framework_dir); | |
| 1122 | } | |
| 1123 | ||
| 1124 | if (mod.bin_file.options.link_libcpp) { | |
| 1125 | const libcxx_include_path = try std.fs.path.join(arena, &[_][]const u8{ | |
| 1126 | mod.zig_lib_directory.path.?, "libcxx", "include", | |
| 1127 | }); | |
| 1128 | const libcxxabi_include_path = try std.fs.path.join(arena, &[_][]const u8{ | |
| 1129 | mod.zig_lib_directory.path.?, "libcxxabi", "include", | |
| 1130 | }); | |
| 1131 | ||
| 1132 | try argv.append("-isystem"); | |
| 1133 | try argv.append(libcxx_include_path); | |
| 1134 | ||
| 1135 | try argv.append("-isystem"); | |
| 1136 | try argv.append(libcxxabi_include_path); | |
| 1137 | ||
| 1138 | if (target.abi.isMusl()) { | |
| 1139 | try argv.append("-D_LIBCPP_HAS_MUSL_LIBC"); | |
| 1140 | } | |
| 1141 | try argv.append("-D_LIBCPP_DISABLE_VISIBILITY_ANNOTATIONS"); | |
| 1142 | try argv.append("-D_LIBCXXABI_DISABLE_VISIBILITY_ANNOTATIONS"); | |
| 1143 | } | |
| 1144 | ||
| 1145 | const llvm_triple = try @import("codegen/llvm.zig").targetTriple(arena, target); | |
| 1146 | try argv.appendSlice(&[_][]const u8{ "-target", llvm_triple }); | |
| 1147 | ||
| 1148 | switch (ext) { | |
| 1149 | .c, .cpp, .h => { | |
| 1150 | // According to Rich Felker libc headers are supposed to go before C language headers. | |
| 1151 | // However as noted by @dimenus, appending libc headers before c_headers breaks intrinsics | |
| 1152 | // and other compiler specific items. | |
| 1153 | const c_headers_dir = try std.fs.path.join(arena, &[_][]const u8{ mod.zig_lib_directory.path.?, "include" }); | |
| 1154 | try argv.append("-isystem"); | |
| 1155 | try argv.append(c_headers_dir); | |
| 1156 | ||
| 1157 | for (mod.libc_include_dir_list) |include_dir| { | |
| 1158 | try argv.append("-isystem"); | |
| 1159 | try argv.append(include_dir); | |
| 1160 | } | |
| 1161 | ||
| 1162 | if (target.cpu.model.llvm_name) |llvm_name| { | |
| 1163 | try argv.appendSlice(&[_][]const u8{ | |
| 1164 | "-Xclang", "-target-cpu", "-Xclang", llvm_name, | |
| 1165 | }); | |
| 1166 | } | |
| 1167 | // TODO CLI args for target features | |
| 1168 | //if (g->zig_target->llvm_cpu_features != nullptr) { | |
| 1169 | // // https://github.com/ziglang/zig/issues/5017 | |
| 1170 | // SplitIterator it = memSplit(str(g->zig_target->llvm_cpu_features), str(",")); | |
| 1171 | // Optional<Slice<uint8_t>> flag = SplitIterator_next(&it); | |
| 1172 | // while (flag.is_some) { | |
| 1173 | // try argv.append("-Xclang"); | |
| 1174 | // try argv.append("-target-feature"); | |
| 1175 | // try argv.append("-Xclang"); | |
| 1176 | // try argv.append(buf_ptr(buf_create_from_slice(flag.value))); | |
| 1177 | // flag = SplitIterator_next(&it); | |
| 1178 | // } | |
| 1179 | //} | |
| 1180 | if (translate_c) { | |
| 1181 | // This gives us access to preprocessing entities, presumably at the cost of performance. | |
| 1182 | try argv.append("-Xclang"); | |
| 1183 | try argv.append("-detailed-preprocessing-record"); | |
| 1184 | } | |
| 1185 | if (out_dep_path) |p| { | |
| 1186 | try argv.append("-MD"); | |
| 1187 | try argv.append("-MV"); | |
| 1188 | try argv.append("-MF"); | |
| 1189 | try argv.append(p); | |
| 1190 | } | |
| 1191 | }, | |
| 1192 | .so, .assembly, .ll, .bc, .unknown => {}, | |
| 1193 | } | |
| 1194 | // TODO CLI args for cpu features when compiling assembly | |
| 1195 | //for (size_t i = 0; i < g->zig_target->llvm_cpu_features_asm_len; i += 1) { | |
| 1196 | // try argv.append(g->zig_target->llvm_cpu_features_asm_ptr[i]); | |
| 1197 | //} | |
| 1198 | ||
| 1199 | if (target.os.tag == .freestanding) { | |
| 1200 | try argv.append("-ffreestanding"); | |
| 1201 | } | |
| 1202 | ||
| 1203 | // windows.h has files such as pshpack1.h which do #pragma packing, triggering a clang warning. | |
| 1204 | // So for this target, we disable this warning. | |
| 1205 | if (target.os.tag == .windows and target.abi.isGnu()) { | |
| 1206 | try argv.append("-Wno-pragma-pack"); | |
| 1207 | } | |
| 1208 | ||
| 1209 | if (!mod.bin_file.options.strip) { | |
| 1210 | try argv.append("-g"); | |
| 1211 | } | |
| 1212 | ||
| 1213 | if (mod.haveFramePointer()) { | |
| 1214 | try argv.append("-fno-omit-frame-pointer"); | |
| 1215 | } else { | |
| 1216 | try argv.append("-fomit-frame-pointer"); | |
| 1217 | } | |
| 1218 | ||
| 1219 | if (mod.sanitize_c) { | |
| 1220 | try argv.append("-fsanitize=undefined"); | |
| 1221 | try argv.append("-fsanitize-trap=undefined"); | |
| 1222 | } | |
| 1223 | ||
| 1224 | switch (mod.bin_file.options.optimize_mode) { | |
| 1225 | .Debug => { | |
| 1226 | // windows c runtime requires -D_DEBUG if using debug libraries | |
| 1227 | try argv.append("-D_DEBUG"); | |
| 1228 | try argv.append("-Og"); | |
| 1229 | ||
| 1230 | if (mod.bin_file.options.link_libc) { | |
| 1231 | try argv.append("-fstack-protector-strong"); | |
| 1232 | try argv.append("--param"); | |
| 1233 | try argv.append("ssp-buffer-size=4"); | |
| 1234 | } else { | |
| 1235 | try argv.append("-fno-stack-protector"); | |
| 1236 | } | |
| 1237 | }, | |
| 1238 | .ReleaseSafe => { | |
| 1239 | // See the comment in the BuildModeFastRelease case for why we pass -O2 rather | |
| 1240 | // than -O3 here. | |
| 1241 | try argv.append("-O2"); | |
| 1242 | if (mod.bin_file.options.link_libc) { | |
| 1243 | try argv.append("-D_FORTIFY_SOURCE=2"); | |
| 1244 | try argv.append("-fstack-protector-strong"); | |
| 1245 | try argv.append("--param"); | |
| 1246 | try argv.append("ssp-buffer-size=4"); | |
| 1247 | } else { | |
| 1248 | try argv.append("-fno-stack-protector"); | |
| 1249 | } | |
| 1250 | }, | |
| 1251 | .ReleaseFast => { | |
| 1252 | try argv.append("-DNDEBUG"); | |
| 1253 | // Here we pass -O2 rather than -O3 because, although we do the equivalent of | |
| 1254 | // -O3 in Zig code, the justification for the difference here is that Zig | |
| 1255 | // has better detection and prevention of undefined behavior, so -O3 is safer for | |
| 1256 | // Zig code than it is for C code. Also, C programmers are used to their code | |
| 1257 | // running in -O2 and thus the -O3 path has been tested less. | |
| 1258 | try argv.append("-O2"); | |
| 1259 | try argv.append("-fno-stack-protector"); | |
| 1260 | }, | |
| 1261 | .ReleaseSmall => { | |
| 1262 | try argv.append("-DNDEBUG"); | |
| 1263 | try argv.append("-Os"); | |
| 1264 | try argv.append("-fno-stack-protector"); | |
| 1265 | }, | |
| 1266 | } | |
| 1267 | ||
| 1268 | if (target_util.supports_fpic(target) and mod.bin_file.options.pic) { | |
| 1269 | try argv.append("-fPIC"); | |
| 1270 | } | |
| 1271 | ||
| 1272 | try argv.appendSlice(mod.clang_argv); | |
| 1273 | } | |
| 1274 | ||
| 1275 | fn failCObj(mod: *Module, c_object: *CObject, comptime format: []const u8, args: anytype) InnerError { | |
| 1276 | @setCold(true); | |
| 1277 | const err_msg = try ErrorMsg.create(mod.gpa, 0, "unable to build C object: " ++ format, args); | |
| 1278 | return mod.failCObjWithOwnedErrorMsg(c_object, err_msg); | |
| 1279 | } | |
| 1280 | ||
| 1281 | fn failCObjWithOwnedErrorMsg(mod: *Module, c_object: *CObject, err_msg: *ErrorMsg) InnerError { | |
| 1282 | { | |
| 1283 | errdefer err_msg.destroy(mod.gpa); | |
| 1284 | try mod.failed_c_objects.ensureCapacity(mod.gpa, mod.failed_c_objects.items().len + 1); | |
| 1285 | } | |
| 1286 | mod.failed_c_objects.putAssumeCapacityNoClobber(c_object, err_msg); | |
| 1287 | c_object.status = .failure; | |
| 1288 | return error.AnalysisFail; | |
| 1289 | } | |
| 1290 | ||
| 1291 | pub const ErrorMsg = struct { | |
| 1292 | byte_offset: usize, | |
| 1293 | msg: []const u8, | |
| 1294 | ||
| 1295 | pub fn create(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !*ErrorMsg { | |
| 1296 | const self = try gpa.create(ErrorMsg); | |
| 1297 | errdefer gpa.destroy(self); | |
| 1298 | self.* = try init(gpa, byte_offset, format, args); | |
| 1299 | return self; | |
| 1300 | } | |
| 1301 | ||
| 1302 | /// Assumes the ErrorMsg struct and msg were both allocated with allocator. | |
| 1303 | pub fn destroy(self: *ErrorMsg, gpa: *Allocator) void { | |
| 1304 | self.deinit(gpa); | |
| 1305 | gpa.destroy(self); | |
| 1306 | } | |
| 1307 | ||
| 1308 | pub fn init(gpa: *Allocator, byte_offset: usize, comptime format: []const u8, args: anytype) !ErrorMsg { | |
| 1309 | return ErrorMsg{ | |
| 1310 | .byte_offset = byte_offset, | |
| 1311 | .msg = try std.fmt.allocPrint(gpa, format, args), | |
| 1312 | }; | |
| 1313 | } | |
| 1314 | ||
| 1315 | pub fn deinit(self: *ErrorMsg, gpa: *Allocator) void { | |
| 1316 | gpa.free(self.msg); | |
| 1317 | self.* = undefined; | |
| 1318 | } | |
| 1319 | }; | |
| 1320 | ||
| 1321 | pub const FileExt = enum { | |
| 1322 | c, | |
| 1323 | cpp, | |
| 1324 | h, | |
| 1325 | ll, | |
| 1326 | bc, | |
| 1327 | assembly, | |
| 1328 | so, | |
| 1329 | unknown, | |
| 1330 | }; | |
| 1331 | ||
| 1332 | pub fn hasCExt(filename: []const u8) bool { | |
| 1333 | return mem.endsWith(u8, filename, ".c"); | |
| 1334 | } | |
| 1335 | ||
| 1336 | pub fn hasCppExt(filename: []const u8) bool { | |
| 1337 | return mem.endsWith(u8, filename, ".C") or | |
| 1338 | mem.endsWith(u8, filename, ".cc") or | |
| 1339 | mem.endsWith(u8, filename, ".cpp") or | |
| 1340 | mem.endsWith(u8, filename, ".cxx"); | |
| 1341 | } | |
| 1342 | ||
| 1343 | pub fn hasAsmExt(filename: []const u8) bool { | |
| 1344 | return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S"); | |
| 1345 | } | |
| 1346 | ||
| 1347 | pub fn classifyFileExt(filename: []const u8) FileExt { | |
| 1348 | if (hasCExt(filename)) { | |
| 1349 | return .c; | |
| 1350 | } else if (hasCppExt(filename)) { | |
| 1351 | return .cpp; | |
| 1352 | } else if (mem.endsWith(u8, filename, ".ll")) { | |
| 1353 | return .ll; | |
| 1354 | } else if (mem.endsWith(u8, filename, ".bc")) { | |
| 1355 | return .bc; | |
| 1356 | } else if (hasAsmExt(filename)) { | |
| 1357 | return .assembly; | |
| 1358 | } else if (mem.endsWith(u8, filename, ".h")) { | |
| 1359 | return .h; | |
| 1360 | } else if (mem.endsWith(u8, filename, ".so")) { | |
| 1361 | return .so; | |
| 1362 | } | |
| 1363 | // Look for .so.X, .so.X.Y, .so.X.Y.Z | |
| 1364 | var it = mem.split(filename, "."); | |
| 1365 | _ = it.next().?; | |
| 1366 | var so_txt = it.next() orelse return .unknown; | |
| 1367 | while (!mem.eql(u8, so_txt, "so")) { | |
| 1368 | so_txt = it.next() orelse return .unknown; | |
| 1369 | } | |
| 1370 | const n1 = it.next() orelse return .unknown; | |
| 1371 | const n2 = it.next(); | |
| 1372 | const n3 = it.next(); | |
| 1373 | ||
| 1374 | _ = std.fmt.parseInt(u32, n1, 10) catch return .unknown; | |
| 1375 | if (n2) |x| _ = std.fmt.parseInt(u32, x, 10) catch return .unknown; | |
| 1376 | if (n3) |x| _ = std.fmt.parseInt(u32, x, 10) catch return .unknown; | |
| 1377 | if (it.next() != null) return .unknown; | |
| 1378 | ||
| 1379 | return .so; | |
| 1380 | } | |
| 1381 | ||
| 1382 | test "classifyFileExt" { | |
| 1383 | std.testing.expectEqual(FileExt.cpp, classifyFileExt("foo.cc")); | |
| 1384 | std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.nim")); | |
| 1385 | std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so")); | |
| 1386 | std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so.1")); | |
| 1387 | std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so.1.2")); | |
| 1388 | std.testing.expectEqual(FileExt.so, classifyFileExt("foo.so.1.2.3")); | |
| 1389 | std.testing.expectEqual(FileExt.unknown, classifyFileExt("foo.so.1.2.3~")); | |
| 1390 | } | |
| 1391 | ||
| 1392 | fn haveFramePointer(mod: *Module) bool { | |
| 1393 | return switch (mod.bin_file.options.optimize_mode) { | |
| 1394 | .Debug, .ReleaseSafe => !mod.bin_file.options.strip, | |
| 1395 | .ReleaseSmall, .ReleaseFast => false, | |
| 1396 | }; | |
| 1397 | } | |
| 1398 | ||
| 1399 | const LibCDirs = struct { | |
| 1400 | libc_include_dir_list: []const []const u8, | |
| 1401 | libc_installation: ?*const LibCInstallation, | |
| 1402 | }; | |
| 1403 | ||
| 1404 | fn detectLibCIncludeDirs( | |
| 1405 | arena: *Allocator, | |
| 1406 | zig_lib_dir: []const u8, | |
| 1407 | target: Target, | |
| 1408 | is_native_os: bool, | |
| 1409 | link_libc: bool, | |
| 1410 | libc_installation: ?*const LibCInstallation, | |
| 1411 | ) !LibCDirs { | |
| 1412 | if (!link_libc) { | |
| 1413 | return LibCDirs{ | |
| 1414 | .libc_include_dir_list = &[0][]u8{}, | |
| 1415 | .libc_installation = null, | |
| 1416 | }; | |
| 1417 | } | |
| 1418 | ||
| 1419 | if (libc_installation) |lci| { | |
| 1420 | return detectLibCFromLibCInstallation(arena, target, lci); | |
| 1421 | } | |
| 1422 | ||
| 1423 | if (target_util.canBuildLibC(target)) { | |
| 1424 | const generic_name = target_util.libCGenericName(target); | |
| 1425 | // Some architectures are handled by the same set of headers. | |
| 1426 | const arch_name = if (target.abi.isMusl()) target_util.archMuslName(target.cpu.arch) else @tagName(target.cpu.arch); | |
| 1427 | const os_name = @tagName(target.os.tag); | |
| 1428 | // Musl's headers are ABI-agnostic and so they all have the "musl" ABI name. | |
| 1429 | const abi_name = if (target.abi.isMusl()) "musl" else @tagName(target.abi); | |
| 1430 | const s = std.fs.path.sep_str; | |
| 1431 | const arch_include_dir = try std.fmt.allocPrint( | |
| 1432 | arena, | |
| 1433 | "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}", | |
| 1434 | .{ zig_lib_dir, arch_name, os_name, abi_name }, | |
| 1435 | ); | |
| 1436 | const generic_include_dir = try std.fmt.allocPrint( | |
| 1437 | arena, | |
| 1438 | "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "generic-{}", | |
| 1439 | .{ zig_lib_dir, generic_name }, | |
| 1440 | ); | |
| 1441 | const arch_os_include_dir = try std.fmt.allocPrint( | |
| 1442 | arena, | |
| 1443 | "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-any", | |
| 1444 | .{ zig_lib_dir, @tagName(target.cpu.arch), os_name }, | |
| 1445 | ); | |
| 1446 | const generic_os_include_dir = try std.fmt.allocPrint( | |
| 1447 | arena, | |
| 1448 | "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "any-{}-any", | |
| 1449 | .{ zig_lib_dir, os_name }, | |
| 1450 | ); | |
| 1451 | ||
| 1452 | const list = try arena.alloc([]const u8, 4); | |
| 1453 | list[0] = arch_include_dir; | |
| 1454 | list[1] = generic_include_dir; | |
| 1455 | list[2] = arch_os_include_dir; | |
| 1456 | list[3] = generic_os_include_dir; | |
| 1457 | return LibCDirs{ | |
| 1458 | .libc_include_dir_list = list, | |
| 1459 | .libc_installation = null, | |
| 1460 | }; | |
| 1461 | } | |
| 1462 | ||
| 1463 | if (is_native_os) { | |
| 1464 | const libc = try arena.create(LibCInstallation); | |
| 1465 | libc.* = try LibCInstallation.findNative(.{ .allocator = arena }); | |
| 1466 | return detectLibCFromLibCInstallation(arena, target, libc); | |
| 1467 | } | |
| 1468 | ||
| 1469 | return LibCDirs{ | |
| 1470 | .libc_include_dir_list = &[0][]u8{}, | |
| 1471 | .libc_installation = null, | |
| 1472 | }; | |
| 1473 | } | |
| 1474 | ||
| 1475 | fn detectLibCFromLibCInstallation(arena: *Allocator, target: Target, lci: *const LibCInstallation) !LibCDirs { | |
| 1476 | var list = std.ArrayList([]const u8).init(arena); | |
| 1477 | try list.ensureCapacity(4); | |
| 1478 | ||
| 1479 | list.appendAssumeCapacity(lci.include_dir.?); | |
| 1480 | ||
| 1481 | const is_redundant = mem.eql(u8, lci.sys_include_dir.?, lci.include_dir.?); | |
| 1482 | if (!is_redundant) list.appendAssumeCapacity(lci.sys_include_dir.?); | |
| 1483 | ||
| 1484 | if (target.os.tag == .windows) { | |
| 1485 | if (std.fs.path.dirname(lci.include_dir.?)) |include_dir_parent| { | |
| 1486 | const um_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "um" }); | |
| 1487 | list.appendAssumeCapacity(um_dir); | |
| 1488 | ||
| 1489 | const shared_dir = try std.fs.path.join(arena, &[_][]const u8{ include_dir_parent, "shared" }); | |
| 1490 | list.appendAssumeCapacity(shared_dir); | |
| 1491 | } | |
| 1492 | } | |
| 1493 | return LibCDirs{ | |
| 1494 | .libc_include_dir_list = list.items, | |
| 1495 | .libc_installation = lci, | |
| 1496 | }; | |
| 1497 | } | |
| 1498 | ||
| 1499 | pub fn get_libc_crt_file(mod: *Module, arena: *Allocator, basename: []const u8) ![]const u8 { | |
| 1500 | if (mod.wantBuildGLibCFromSource()) { | |
| 1501 | return mod.crt_files.get(basename).?; | |
| 1502 | } | |
| 1503 | const lci = mod.bin_file.options.libc_installation orelse return error.LibCInstallationNotAvailable; | |
| 1504 | const crt_dir_path = lci.crt_dir orelse return error.LibCInstallationMissingCRTDir; | |
| 1505 | const full_path = try std.fs.path.join(arena, &[_][]const u8{ crt_dir_path, basename }); | |
| 1506 | return full_path; | |
| 1507 | } | |
| 1508 | ||
| 1509 | fn addBuildingGLibCWorkItems(mod: *Module) !void { | |
| 1510 | const static_file_work_items = [_]WorkItem{ | |
| 1511 | .{ .glibc_crt_file = .crti_o }, | |
| 1512 | .{ .glibc_crt_file = .crtn_o }, | |
| 1513 | .{ .glibc_crt_file = .start_os }, | |
| 1514 | .{ .glibc_crt_file = .abi_note_o }, | |
| 1515 | .{ .glibc_crt_file = .scrt1_o }, | |
| 1516 | .{ .glibc_crt_file = .libc_nonshared_a }, | |
| 1517 | }; | |
| 1518 | try mod.work_queue.ensureUnusedCapacity(static_file_work_items.len + glibc.libs.len); | |
| 1519 | mod.work_queue.writeAssumeCapacity(&static_file_work_items); | |
| 1520 | for (glibc.libs) |*glibc_so| { | |
| 1521 | mod.work_queue.writeItemAssumeCapacity(.{ .glibc_so = glibc_so }); | |
| 1522 | } | |
| 1523 | } | |
| 1524 | ||
| 1525 | fn wantBuildGLibCFromSource(mod: *Module) bool { | |
| 1526 | return mod.bin_file.options.link_libc and | |
| 1527 | mod.bin_file.options.libc_installation == null and | |
| 1528 | mod.bin_file.options.target.isGnuLibC(); | |
| 1529 | } |
src-self-hosted/Package.zig+2-2| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | pub const Table = std.StringHashMapUnmanaged(*Package); |
| 2 | 2 | |
| 3 | root_src_directory: Module.Directory, | |
| 3 | root_src_directory: Compilation.Directory, | |
| 4 | 4 | /// Relative to `root_src_directory`. |
| 5 | 5 | root_src_path: []u8, |
| 6 | 6 | table: Table, |
| ... | ... | @@ -56,4 +56,4 @@ const mem = std.mem; |
| 56 | 56 | const Allocator = std.mem.Allocator; |
| 57 | 57 | const assert = std.debug.assert; |
| 58 | 58 | const Package = @This(); |
| 59 | const Module = @import("Module.zig"); | |
| 59 | const Compilation = @import("Compilation.zig"); |
src-self-hosted/ZigModule.zig+2-4| ... | ... | @@ -1,9 +1,7 @@ |
| 1 | //! TODO This is going to get renamed from ZigModule to Module (but first we have to rename | |
| 2 | //! Module to Compilation). | |
| 1 | //! TODO This is going to get renamed from ZigModule to Module | |
| 3 | 2 | const Module = @This(); |
| 4 | const Compilation = @import("Module.zig"); | |
| 5 | ||
| 6 | 3 | const std = @import("std"); |
| 4 | const Compilation = @import("Compilation.zig"); | |
| 7 | 5 | const mem = std.mem; |
| 8 | 6 | const Allocator = std.mem.Allocator; |
| 9 | 7 | const ArrayListUnmanaged = std.ArrayListUnmanaged; |
src-self-hosted/codegen.zig+1-1| ... | ... | @@ -8,7 +8,7 @@ const Value = @import("value.zig").Value; |
| 8 | 8 | const TypedValue = @import("TypedValue.zig"); |
| 9 | 9 | const link = @import("link.zig"); |
| 10 | 10 | const Module = @import("ZigModule.zig"); |
| 11 | const Compilation = @import("Module.zig"); | |
| 11 | const Compilation = @import("Compilation.zig"); | |
| 12 | 12 | const ErrorMsg = Compilation.ErrorMsg; |
| 13 | 13 | const Target = std.Target; |
| 14 | 14 | const Allocator = mem.Allocator; |
src-self-hosted/glibc.zig+75-75| ... | ... | @@ -2,7 +2,7 @@ const std = @import("std"); |
| 2 | 2 | const Allocator = std.mem.Allocator; |
| 3 | 3 | const target_util = @import("target.zig"); |
| 4 | 4 | const mem = std.mem; |
| 5 | const Module = @import("Module.zig"); | |
| 5 | const Compilation = @import("Compilation.zig"); | |
| 6 | 6 | const path = std.fs.path; |
| 7 | 7 | const build_options = @import("build_options"); |
| 8 | 8 | const trace = @import("tracy.zig").trace; |
| ... | ... | @@ -246,11 +246,11 @@ pub const CRTFile = enum { |
| 246 | 246 | libc_nonshared_a, |
| 247 | 247 | }; |
| 248 | 248 | |
| 249 | pub fn buildCRTFile(mod: *Module, crt_file: CRTFile) !void { | |
| 249 | pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void { | |
| 250 | 250 | if (!build_options.have_llvm) { |
| 251 | 251 | return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 252 | 252 | } |
| 253 | const gpa = mod.gpa; | |
| 253 | const gpa = comp.gpa; | |
| 254 | 254 | var arena_allocator = std.heap.ArenaAllocator.init(gpa); |
| 255 | 255 | errdefer arena_allocator.deinit(); |
| 256 | 256 | const arena = &arena_allocator.allocator; |
| ... | ... | @@ -258,29 +258,29 @@ pub fn buildCRTFile(mod: *Module, crt_file: CRTFile) !void { |
| 258 | 258 | switch (crt_file) { |
| 259 | 259 | .crti_o => { |
| 260 | 260 | var args = std.ArrayList([]const u8).init(arena); |
| 261 | try add_include_dirs(mod, arena, &args); | |
| 261 | try add_include_dirs(comp, arena, &args); | |
| 262 | 262 | try args.appendSlice(&[_][]const u8{ |
| 263 | 263 | "-D_LIBC_REENTRANT", |
| 264 | 264 | "-include", |
| 265 | try lib_path(mod, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"), | |
| 265 | try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"), | |
| 266 | 266 | "-DMODULE_NAME=libc", |
| 267 | 267 | "-Wno-nonportable-include-path", |
| 268 | 268 | "-include", |
| 269 | try lib_path(mod, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"), | |
| 269 | try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"), | |
| 270 | 270 | "-DTOP_NAMESPACE=glibc", |
| 271 | 271 | "-DASSEMBLER", |
| 272 | 272 | "-g", |
| 273 | 273 | "-Wa,--noexecstack", |
| 274 | 274 | }); |
| 275 | const c_source_file: Module.CSourceFile = .{ | |
| 276 | .src_path = try start_asm_path(mod, arena, "crti.S"), | |
| 275 | const c_source_file: Compilation.CSourceFile = .{ | |
| 276 | .src_path = try start_asm_path(comp, arena, "crti.S"), | |
| 277 | 277 | .extra_flags = args.items, |
| 278 | 278 | }; |
| 279 | return build_libc_object(mod, "crti.o", c_source_file); | |
| 279 | return build_libc_object(comp, "crti.o", c_source_file); | |
| 280 | 280 | }, |
| 281 | 281 | .crtn_o => { |
| 282 | 282 | var args = std.ArrayList([]const u8).init(arena); |
| 283 | try add_include_dirs(mod, arena, &args); | |
| 283 | try add_include_dirs(comp, arena, &args); | |
| 284 | 284 | try args.appendSlice(&[_][]const u8{ |
| 285 | 285 | "-D_LIBC_REENTRANT", |
| 286 | 286 | "-DMODULE_NAME=libc", |
| ... | ... | @@ -289,23 +289,23 @@ pub fn buildCRTFile(mod: *Module, crt_file: CRTFile) !void { |
| 289 | 289 | "-g", |
| 290 | 290 | "-Wa,--noexecstack", |
| 291 | 291 | }); |
| 292 | const c_source_file: Module.CSourceFile = .{ | |
| 293 | .src_path = try start_asm_path(mod, arena, "crtn.S"), | |
| 292 | const c_source_file: Compilation.CSourceFile = .{ | |
| 293 | .src_path = try start_asm_path(comp, arena, "crtn.S"), | |
| 294 | 294 | .extra_flags = args.items, |
| 295 | 295 | }; |
| 296 | return build_libc_object(mod, "crtn.o", c_source_file); | |
| 296 | return build_libc_object(comp, "crtn.o", c_source_file); | |
| 297 | 297 | }, |
| 298 | 298 | .start_os => { |
| 299 | 299 | var args = std.ArrayList([]const u8).init(arena); |
| 300 | try add_include_dirs(mod, arena, &args); | |
| 300 | try add_include_dirs(comp, arena, &args); | |
| 301 | 301 | try args.appendSlice(&[_][]const u8{ |
| 302 | 302 | "-D_LIBC_REENTRANT", |
| 303 | 303 | "-include", |
| 304 | try lib_path(mod, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"), | |
| 304 | try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-modules.h"), | |
| 305 | 305 | "-DMODULE_NAME=libc", |
| 306 | 306 | "-Wno-nonportable-include-path", |
| 307 | 307 | "-include", |
| 308 | try lib_path(mod, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"), | |
| 308 | try lib_path(comp, arena, lib_libc_glibc ++ "include" ++ path.sep_str ++ "libc-symbols.h"), | |
| 309 | 309 | "-DPIC", |
| 310 | 310 | "-DSHARED", |
| 311 | 311 | "-DTOP_NAMESPACE=glibc", |
| ... | ... | @@ -313,19 +313,19 @@ pub fn buildCRTFile(mod: *Module, crt_file: CRTFile) !void { |
| 313 | 313 | "-g", |
| 314 | 314 | "-Wa,--noexecstack", |
| 315 | 315 | }); |
| 316 | const c_source_file: Module.CSourceFile = .{ | |
| 317 | .src_path = try start_asm_path(mod, arena, "start.S"), | |
| 316 | const c_source_file: Compilation.CSourceFile = .{ | |
| 317 | .src_path = try start_asm_path(comp, arena, "start.S"), | |
| 318 | 318 | .extra_flags = args.items, |
| 319 | 319 | }; |
| 320 | return build_libc_object(mod, "start.os", c_source_file); | |
| 320 | return build_libc_object(comp, "start.os", c_source_file); | |
| 321 | 321 | }, |
| 322 | 322 | .abi_note_o => { |
| 323 | 323 | var args = std.ArrayList([]const u8).init(arena); |
| 324 | 324 | try args.appendSlice(&[_][]const u8{ |
| 325 | 325 | "-I", |
| 326 | try lib_path(mod, arena, lib_libc_glibc ++ "glibc" ++ path.sep_str ++ "csu"), | |
| 326 | try lib_path(comp, arena, lib_libc_glibc ++ "glibc" ++ path.sep_str ++ "csu"), | |
| 327 | 327 | }); |
| 328 | try add_include_dirs(mod, arena, &args); | |
| 328 | try add_include_dirs(comp, arena, &args); | |
| 329 | 329 | try args.appendSlice(&[_][]const u8{ |
| 330 | 330 | "-D_LIBC_REENTRANT", |
| 331 | 331 | "-DMODULE_NAME=libc", |
| ... | ... | @@ -334,11 +334,11 @@ pub fn buildCRTFile(mod: *Module, crt_file: CRTFile) !void { |
| 334 | 334 | "-g", |
| 335 | 335 | "-Wa,--noexecstack", |
| 336 | 336 | }); |
| 337 | const c_source_file: Module.CSourceFile = .{ | |
| 338 | .src_path = try lib_path(mod, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "abi-note.S"), | |
| 337 | const c_source_file: Compilation.CSourceFile = .{ | |
| 338 | .src_path = try lib_path(comp, arena, lib_libc_glibc ++ "csu" ++ path.sep_str ++ "abi-note.S"), | |
| 339 | 339 | .extra_flags = args.items, |
| 340 | 340 | }; |
| 341 | return build_libc_object(mod, "abi-note.o", c_source_file); | |
| 341 | return build_libc_object(comp, "abi-note.o", c_source_file); | |
| 342 | 342 | }, |
| 343 | 343 | .scrt1_o => { |
| 344 | 344 | return error.Unimplemented; // TODO |
| ... | ... | @@ -349,8 +349,8 @@ pub fn buildCRTFile(mod: *Module, crt_file: CRTFile) !void { |
| 349 | 349 | } |
| 350 | 350 | } |
| 351 | 351 | |
| 352 | fn start_asm_path(mod: *Module, arena: *Allocator, basename: []const u8) ![]const u8 { | |
| 353 | const arch = mod.getTarget().cpu.arch; | |
| 352 | fn start_asm_path(comp: *Compilation, arena: *Allocator, basename: []const u8) ![]const u8 { | |
| 353 | const arch = comp.getTarget().cpu.arch; | |
| 354 | 354 | const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le; |
| 355 | 355 | const is_aarch64 = arch == .aarch64 or arch == .aarch64_be; |
| 356 | 356 | const is_sparc = arch == .sparc or arch == .sparcel or arch == .sparcv9; |
| ... | ... | @@ -359,7 +359,7 @@ fn start_asm_path(mod: *Module, arena: *Allocator, basename: []const u8) ![]cons |
| 359 | 359 | const s = path.sep_str; |
| 360 | 360 | |
| 361 | 361 | var result = std.ArrayList(u8).init(arena); |
| 362 | try result.appendSlice(mod.zig_lib_directory.path.?); | |
| 362 | try result.appendSlice(comp.zig_lib_directory.path.?); | |
| 363 | 363 | try result.appendSlice(s ++ "libc" ++ s ++ "glibc" ++ s ++ "sysdeps" ++ s); |
| 364 | 364 | if (is_sparc) { |
| 365 | 365 | if (is_64) { |
| ... | ... | @@ -392,76 +392,76 @@ fn start_asm_path(mod: *Module, arena: *Allocator, basename: []const u8) ![]cons |
| 392 | 392 | return result.items; |
| 393 | 393 | } |
| 394 | 394 | |
| 395 | fn add_include_dirs(mod: *Module, arena: *Allocator, args: *std.ArrayList([]const u8)) error{OutOfMemory}!void { | |
| 396 | const target = mod.getTarget(); | |
| 395 | fn add_include_dirs(comp: *Compilation, arena: *Allocator, args: *std.ArrayList([]const u8)) error{OutOfMemory}!void { | |
| 396 | const target = comp.getTarget(); | |
| 397 | 397 | const arch = target.cpu.arch; |
| 398 | 398 | const opt_nptl: ?[]const u8 = if (target.os.tag == .linux) "nptl" else "htl"; |
| 399 | const glibc = try lib_path(mod, arena, lib_libc ++ "glibc"); | |
| 399 | const glibc = try lib_path(comp, arena, lib_libc ++ "glibc"); | |
| 400 | 400 | |
| 401 | 401 | const s = path.sep_str; |
| 402 | 402 | |
| 403 | 403 | try args.append("-I"); |
| 404 | try args.append(try lib_path(mod, arena, lib_libc_glibc ++ "include")); | |
| 404 | try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "include")); | |
| 405 | 405 | |
| 406 | 406 | if (target.os.tag == .linux) { |
| 407 | try add_include_dirs_arch(arena, args, arch, null, try lib_path(mod, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv" ++ s ++ "linux")); | |
| 407 | try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv" ++ s ++ "linux")); | |
| 408 | 408 | } |
| 409 | 409 | |
| 410 | 410 | if (opt_nptl) |nptl| { |
| 411 | try add_include_dirs_arch(arena, args, arch, nptl, try lib_path(mod, arena, lib_libc_glibc ++ "sysdeps")); | |
| 411 | try add_include_dirs_arch(arena, args, arch, nptl, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps")); | |
| 412 | 412 | } |
| 413 | 413 | |
| 414 | 414 | if (target.os.tag == .linux) { |
| 415 | 415 | try args.append("-I"); |
| 416 | try args.append(try lib_path(mod, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ | |
| 416 | try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ | |
| 417 | 417 | "unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "generic")); |
| 418 | 418 | |
| 419 | 419 | try args.append("-I"); |
| 420 | try args.append(try lib_path(mod, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ | |
| 420 | try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ | |
| 421 | 421 | "unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "include")); |
| 422 | 422 | try args.append("-I"); |
| 423 | try args.append(try lib_path(mod, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ | |
| 423 | try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ | |
| 424 | 424 | "unix" ++ s ++ "sysv" ++ s ++ "linux")); |
| 425 | 425 | } |
| 426 | 426 | if (opt_nptl) |nptl| { |
| 427 | 427 | try args.append("-I"); |
| 428 | try args.append(try path.join(arena, &[_][]const u8{ mod.zig_lib_directory.path.?, lib_libc_glibc ++ "sysdeps", nptl })); | |
| 428 | try args.append(try path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, lib_libc_glibc ++ "sysdeps", nptl })); | |
| 429 | 429 | } |
| 430 | 430 | |
| 431 | 431 | try args.append("-I"); |
| 432 | try args.append(try lib_path(mod, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "pthread")); | |
| 432 | try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "pthread")); | |
| 433 | 433 | |
| 434 | 434 | try args.append("-I"); |
| 435 | try args.append(try lib_path(mod, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv")); | |
| 435 | try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix" ++ s ++ "sysv")); | |
| 436 | 436 | |
| 437 | try add_include_dirs_arch(arena, args, arch, null, try lib_path(mod, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix")); | |
| 437 | try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix")); | |
| 438 | 438 | |
| 439 | 439 | try args.append("-I"); |
| 440 | try args.append(try lib_path(mod, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix")); | |
| 440 | try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "unix")); | |
| 441 | 441 | |
| 442 | try add_include_dirs_arch(arena, args, arch, null, try lib_path(mod, arena, lib_libc_glibc ++ "sysdeps")); | |
| 442 | try add_include_dirs_arch(arena, args, arch, null, try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps")); | |
| 443 | 443 | |
| 444 | 444 | try args.append("-I"); |
| 445 | try args.append(try lib_path(mod, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "generic")); | |
| 445 | try args.append(try lib_path(comp, arena, lib_libc_glibc ++ "sysdeps" ++ s ++ "generic")); | |
| 446 | 446 | |
| 447 | 447 | try args.append("-I"); |
| 448 | try args.append(try path.join(arena, &[_][]const u8{ mod.zig_lib_directory.path.?, lib_libc ++ "glibc" })); | |
| 448 | try args.append(try path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, lib_libc ++ "glibc" })); | |
| 449 | 449 | |
| 450 | 450 | try args.append("-I"); |
| 451 | 451 | try args.append(try std.fmt.allocPrint(arena, "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-{}-{}", .{ |
| 452 | mod.zig_lib_directory.path.?, @tagName(arch), @tagName(target.os.tag), @tagName(target.abi), | |
| 452 | comp.zig_lib_directory.path.?, @tagName(arch), @tagName(target.os.tag), @tagName(target.abi), | |
| 453 | 453 | })); |
| 454 | 454 | |
| 455 | 455 | try args.append("-I"); |
| 456 | try args.append(try lib_path(mod, arena, lib_libc ++ "include" ++ s ++ "generic-glibc")); | |
| 456 | try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "generic-glibc")); | |
| 457 | 457 | |
| 458 | 458 | try args.append("-I"); |
| 459 | 459 | try args.append(try std.fmt.allocPrint(arena, "{}" ++ s ++ "libc" ++ s ++ "include" ++ s ++ "{}-linux-any", .{ |
| 460 | mod.zig_lib_directory.path.?, @tagName(arch), | |
| 460 | comp.zig_lib_directory.path.?, @tagName(arch), | |
| 461 | 461 | })); |
| 462 | 462 | |
| 463 | 463 | try args.append("-I"); |
| 464 | try args.append(try lib_path(mod, arena, lib_libc ++ "include" ++ s ++ "any-linux-any")); | |
| 464 | try args.append(try lib_path(comp, arena, lib_libc ++ "include" ++ s ++ "any-linux-any")); | |
| 465 | 465 | } |
| 466 | 466 | |
| 467 | 467 | fn add_include_dirs_arch( |
| ... | ... | @@ -576,60 +576,60 @@ fn add_include_dirs_arch( |
| 576 | 576 | } |
| 577 | 577 | } |
| 578 | 578 | |
| 579 | fn path_from_lib(mod: *Module, arena: *Allocator, sub_path: []const u8) ![]const u8 { | |
| 580 | return path.join(arena, &[_][]const u8{ mod.zig_lib_directory.path.?, sub_path }); | |
| 579 | fn path_from_lib(comp: *Compilation, arena: *Allocator, sub_path: []const u8) ![]const u8 { | |
| 580 | return path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, sub_path }); | |
| 581 | 581 | } |
| 582 | 582 | |
| 583 | 583 | const lib_libc = "libc" ++ path.sep_str; |
| 584 | 584 | const lib_libc_glibc = lib_libc ++ "glibc" ++ path.sep_str; |
| 585 | 585 | |
| 586 | fn lib_path(mod: *Module, arena: *Allocator, sub_path: []const u8) ![]const u8 { | |
| 587 | return path.join(arena, &[_][]const u8{ mod.zig_lib_directory.path.?, sub_path }); | |
| 586 | fn lib_path(comp: *Compilation, arena: *Allocator, sub_path: []const u8) ![]const u8 { | |
| 587 | return path.join(arena, &[_][]const u8{ comp.zig_lib_directory.path.?, sub_path }); | |
| 588 | 588 | } |
| 589 | 589 | |
| 590 | fn build_libc_object(mod: *Module, basename: []const u8, c_source_file: Module.CSourceFile) !void { | |
| 590 | fn build_libc_object(comp: *Compilation, basename: []const u8, c_source_file: Compilation.CSourceFile) !void { | |
| 591 | 591 | const tracy = trace(@src()); |
| 592 | 592 | defer tracy.end(); |
| 593 | 593 | |
| 594 | 594 | // TODO: This is extracted into a local variable to work around a stage1 miscompilation. |
| 595 | const emit_bin = Module.EmitLoc{ | |
| 595 | const emit_bin = Compilation.EmitLoc{ | |
| 596 | 596 | .directory = null, // Put it in the cache directory. |
| 597 | 597 | .basename = basename, |
| 598 | 598 | }; |
| 599 | const sub_module = try Module.create(mod.gpa, .{ | |
| 599 | const sub_compilation = try Compilation.create(comp.gpa, .{ | |
| 600 | 600 | // TODO use the global cache directory here |
| 601 | .zig_cache_directory = mod.zig_cache_directory, | |
| 602 | .zig_lib_directory = mod.zig_lib_directory, | |
| 603 | .target = mod.getTarget(), | |
| 601 | .zig_cache_directory = comp.zig_cache_directory, | |
| 602 | .zig_lib_directory = comp.zig_lib_directory, | |
| 603 | .target = comp.getTarget(), | |
| 604 | 604 | .root_name = mem.split(basename, ".").next().?, |
| 605 | 605 | .root_pkg = null, |
| 606 | 606 | .output_mode = .Obj, |
| 607 | .rand = mod.rand, | |
| 608 | .libc_installation = mod.bin_file.options.libc_installation, | |
| 607 | .rand = comp.rand, | |
| 608 | .libc_installation = comp.bin_file.options.libc_installation, | |
| 609 | 609 | .emit_bin = emit_bin, |
| 610 | .optimize_mode = mod.bin_file.options.optimize_mode, | |
| 610 | .optimize_mode = comp.bin_file.options.optimize_mode, | |
| 611 | 611 | .want_sanitize_c = false, |
| 612 | 612 | .want_stack_check = false, |
| 613 | 613 | .want_valgrind = false, |
| 614 | .want_pic = mod.bin_file.options.pic, | |
| 614 | .want_pic = comp.bin_file.options.pic, | |
| 615 | 615 | .emit_h = null, |
| 616 | .strip = mod.bin_file.options.strip, | |
| 617 | .is_native_os = mod.bin_file.options.is_native_os, | |
| 618 | .self_exe_path = mod.self_exe_path, | |
| 619 | .c_source_files = &[1]Module.CSourceFile{c_source_file}, | |
| 620 | .debug_cc = mod.debug_cc, | |
| 621 | .debug_link = mod.bin_file.options.debug_link, | |
| 616 | .strip = comp.bin_file.options.strip, | |
| 617 | .is_native_os = comp.bin_file.options.is_native_os, | |
| 618 | .self_exe_path = comp.self_exe_path, | |
| 619 | .c_source_files = &[1]Compilation.CSourceFile{c_source_file}, | |
| 620 | .debug_cc = comp.debug_cc, | |
| 621 | .debug_link = comp.bin_file.options.debug_link, | |
| 622 | 622 | }); |
| 623 | defer sub_module.destroy(); | |
| 623 | defer sub_compilation.destroy(); | |
| 624 | 624 | |
| 625 | try sub_module.update(); | |
| 625 | try sub_compilation.update(); | |
| 626 | 626 | |
| 627 | try mod.crt_files.ensureCapacity(mod.gpa, mod.crt_files.count() + 1); | |
| 628 | const artifact_path = if (sub_module.bin_file.options.directory.path) |p| | |
| 629 | try std.fs.path.join(mod.gpa, &[_][]const u8{ p, basename }) | |
| 627 | try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1); | |
| 628 | const artifact_path = if (sub_compilation.bin_file.options.directory.path) |p| | |
| 629 | try std.fs.path.join(comp.gpa, &[_][]const u8{ p, basename }) | |
| 630 | 630 | else |
| 631 | try mod.gpa.dupe(u8, basename); | |
| 631 | try comp.gpa.dupe(u8, basename); | |
| 632 | 632 | |
| 633 | 633 | // TODO obtain a lock on the artifact and put that in crt_files as well. |
| 634 | mod.crt_files.putAssumeCapacityNoClobber(basename, artifact_path); | |
| 634 | comp.crt_files.putAssumeCapacityNoClobber(basename, artifact_path); | |
| 635 | 635 | } |
src-self-hosted/introspect.zig+7-7| ... | ... | @@ -2,12 +2,12 @@ const std = @import("std"); |
| 2 | 2 | const mem = std.mem; |
| 3 | 3 | const fs = std.fs; |
| 4 | 4 | const CacheHash = std.cache_hash.CacheHash; |
| 5 | const Module = @import("Module.zig"); | |
| 5 | const Compilation = @import("Compilation.zig"); | |
| 6 | 6 | |
| 7 | 7 | /// Returns the sub_path that worked, or `null` if none did. |
| 8 | 8 | /// The path of the returned Directory is relative to `base`. |
| 9 | 9 | /// The handle of the returned Directory is open. |
| 10 | fn testZigInstallPrefix(base_dir: fs.Dir) ?Module.Directory { | |
| 10 | fn testZigInstallPrefix(base_dir: fs.Dir) ?Compilation.Directory { | |
| 11 | 11 | const test_index_file = "std" ++ fs.path.sep_str ++ "std.zig"; |
| 12 | 12 | |
| 13 | 13 | zig_dir: { |
| ... | ... | @@ -19,7 +19,7 @@ fn testZigInstallPrefix(base_dir: fs.Dir) ?Module.Directory { |
| 19 | 19 | break :zig_dir; |
| 20 | 20 | }; |
| 21 | 21 | file.close(); |
| 22 | return Module.Directory{ .handle = test_zig_dir, .path = lib_zig }; | |
| 22 | return Compilation.Directory{ .handle = test_zig_dir, .path = lib_zig }; | |
| 23 | 23 | } |
| 24 | 24 | |
| 25 | 25 | // Try lib/std/std.zig |
| ... | ... | @@ -29,11 +29,11 @@ fn testZigInstallPrefix(base_dir: fs.Dir) ?Module.Directory { |
| 29 | 29 | return null; |
| 30 | 30 | }; |
| 31 | 31 | file.close(); |
| 32 | return Module.Directory{ .handle = test_zig_dir, .path = "lib" }; | |
| 32 | return Compilation.Directory{ .handle = test_zig_dir, .path = "lib" }; | |
| 33 | 33 | } |
| 34 | 34 | |
| 35 | 35 | /// Both the directory handle and the path are newly allocated resources which the caller now owns. |
| 36 | pub fn findZigLibDir(gpa: *mem.Allocator) !Module.Directory { | |
| 36 | pub fn findZigLibDir(gpa: *mem.Allocator) !Compilation.Directory { | |
| 37 | 37 | const self_exe_path = try fs.selfExePathAlloc(gpa); |
| 38 | 38 | defer gpa.free(self_exe_path); |
| 39 | 39 | |
| ... | ... | @@ -44,7 +44,7 @@ pub fn findZigLibDir(gpa: *mem.Allocator) !Module.Directory { |
| 44 | 44 | pub fn findZigLibDirFromSelfExe( |
| 45 | 45 | allocator: *mem.Allocator, |
| 46 | 46 | self_exe_path: []const u8, |
| 47 | ) error{ OutOfMemory, FileNotFound }!Module.Directory { | |
| 47 | ) error{ OutOfMemory, FileNotFound }!Compilation.Directory { | |
| 48 | 48 | const cwd = fs.cwd(); |
| 49 | 49 | var cur_path: []const u8 = self_exe_path; |
| 50 | 50 | while (fs.path.dirname(cur_path)) |dirname| : (cur_path = dirname) { |
| ... | ... | @@ -52,7 +52,7 @@ pub fn findZigLibDirFromSelfExe( |
| 52 | 52 | defer base_dir.close(); |
| 53 | 53 | |
| 54 | 54 | const sub_directory = testZigInstallPrefix(base_dir) orelse continue; |
| 55 | return Module.Directory{ | |
| 55 | return Compilation.Directory{ | |
| 56 | 56 | .handle = sub_directory.handle, |
| 57 | 57 | .path = try fs.path.join(allocator, &[_][]const u8{ dirname, sub_directory.path.? }), |
| 58 | 58 | }; |
src-self-hosted/link.zig+1-2| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const Allocator = std.mem.Allocator; |
| 3 | const Compilation = @import("Module.zig"); | |
| 3 | const Compilation = @import("Compilation.zig"); | |
| 4 | 4 | const ZigModule = @import("ZigModule.zig"); |
| 5 | 5 | const fs = std.fs; |
| 6 | 6 | const trace = @import("tracy.zig").trace; |
| ... | ... | @@ -23,7 +23,6 @@ pub const Options = struct { |
| 23 | 23 | optimize_mode: std.builtin.Mode, |
| 24 | 24 | root_name: []const u8, |
| 25 | 25 | /// Not every Compilation compiles .zig code! For example you could do `zig build-exe foo.o`. |
| 26 | /// TODO rename Module to Compilation and then (as a separate commit) ZigModule to Module. | |
| 27 | 26 | zig_module: ?*ZigModule, |
| 28 | 27 | dynamic_linker: ?[]const u8 = null, |
| 29 | 28 | /// Used for calculating how much space to reserve for symbols in case the binary file |
src-self-hosted/link/C.zig+1-1| ... | ... | @@ -3,7 +3,7 @@ const mem = std.mem; |
| 3 | 3 | const assert = std.debug.assert; |
| 4 | 4 | const Allocator = std.mem.Allocator; |
| 5 | 5 | const Module = @import("../ZigModule.zig"); |
| 6 | const Compilation = @import("../Module.zig"); | |
| 6 | const Compilation = @import("../Compilation.zig"); | |
| 7 | 7 | const fs = std.fs; |
| 8 | 8 | const codegen = @import("../codegen/c.zig"); |
| 9 | 9 | const link = @import("../link.zig"); |
src-self-hosted/link/Coff.zig+1-1| ... | ... | @@ -8,7 +8,7 @@ const fs = std.fs; |
| 8 | 8 | |
| 9 | 9 | const trace = @import("../tracy.zig").trace; |
| 10 | 10 | const Module = @import("../ZigModule.zig"); |
| 11 | const Compilation = @import("../Module.zig"); | |
| 11 | const Compilation = @import("../Compilation.zig"); | |
| 12 | 12 | const codegen = @import("../codegen.zig"); |
| 13 | 13 | const link = @import("../link.zig"); |
| 14 | 14 |
src-self-hosted/link/Elf.zig+1-1| ... | ... | @@ -4,7 +4,7 @@ const assert = std.debug.assert; |
| 4 | 4 | const Allocator = std.mem.Allocator; |
| 5 | 5 | const ir = @import("../ir.zig"); |
| 6 | 6 | const Module = @import("../ZigModule.zig"); |
| 7 | const Compilation = @import("../Module.zig"); | |
| 7 | const Compilation = @import("../Compilation.zig"); | |
| 8 | 8 | const fs = std.fs; |
| 9 | 9 | const elf = std.elf; |
| 10 | 10 | const codegen = @import("../codegen.zig"); |
src-self-hosted/link/MachO.zig+1-1| ... | ... | @@ -13,7 +13,7 @@ const trace = @import("../tracy.zig").trace; |
| 13 | 13 | const Type = @import("../type.zig").Type; |
| 14 | 14 | |
| 15 | 15 | const Module = @import("../ZigModule.zig"); |
| 16 | const Compilation = @import("../Module.zig"); | |
| 16 | const Compilation = @import("../Compilation.zig"); | |
| 17 | 17 | const link = @import("../link.zig"); |
| 18 | 18 | const File = link.File; |
| 19 | 19 |
src-self-hosted/link/Wasm.zig+1-1| ... | ... | @@ -7,7 +7,7 @@ const fs = std.fs; |
| 7 | 7 | const leb = std.debug.leb; |
| 8 | 8 | |
| 9 | 9 | const Module = @import("../ZigModule.zig"); |
| 10 | const Compilation = @import("../Module.zig"); | |
| 10 | const Compilation = @import("../Compilation.zig"); | |
| 11 | 11 | const codegen = @import("../codegen/wasm.zig"); |
| 12 | 12 | const link = @import("../link.zig"); |
| 13 | 13 |
src-self-hosted/main.zig+25-25| ... | ... | @@ -7,7 +7,7 @@ const process = std.process; |
| 7 | 7 | const Allocator = mem.Allocator; |
| 8 | 8 | const ArrayList = std.ArrayList; |
| 9 | 9 | const ast = std.zig.ast; |
| 10 | const Module = @import("Module.zig"); | |
| 10 | const Compilation = @import("Compilation.zig"); | |
| 11 | 11 | const link = @import("link.zig"); |
| 12 | 12 | const Package = @import("Package.zig"); |
| 13 | 13 | const zir = @import("zir.zig"); |
| ... | ... | @@ -331,7 +331,7 @@ pub fn buildOutputType( |
| 331 | 331 | var rpath_list = std.ArrayList([]const u8).init(gpa); |
| 332 | 332 | defer rpath_list.deinit(); |
| 333 | 333 | |
| 334 | var c_source_files = std.ArrayList(Module.CSourceFile).init(gpa); | |
| 334 | var c_source_files = std.ArrayList(Compilation.CSourceFile).init(gpa); | |
| 335 | 335 | defer c_source_files.deinit(); |
| 336 | 336 | |
| 337 | 337 | var link_objects = std.ArrayList([]const u8).init(gpa); |
| ... | ... | @@ -566,7 +566,7 @@ pub fn buildOutputType( |
| 566 | 566 | mem.endsWith(u8, arg, ".lib")) |
| 567 | 567 | { |
| 568 | 568 | try link_objects.append(arg); |
| 569 | } else if (Module.hasAsmExt(arg) or Module.hasCExt(arg) or Module.hasCppExt(arg)) { | |
| 569 | } else if (Compilation.hasAsmExt(arg) or Compilation.hasCExt(arg) or Compilation.hasCppExt(arg)) { | |
| 570 | 570 | // TODO a way to pass extra flags on the CLI |
| 571 | 571 | try c_source_files.append(.{ .src_path = arg }); |
| 572 | 572 | } else if (mem.endsWith(u8, arg, ".so") or |
| ... | ... | @@ -611,7 +611,7 @@ pub fn buildOutputType( |
| 611 | 611 | try clang_argv.appendSlice(it.other_args); |
| 612 | 612 | }, |
| 613 | 613 | .positional => { |
| 614 | const file_ext = Module.classifyFileExt(mem.spanZ(it.only_arg)); | |
| 614 | const file_ext = Compilation.classifyFileExt(mem.spanZ(it.only_arg)); | |
| 615 | 615 | switch (file_ext) { |
| 616 | 616 | .assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(.{ .src_path = it.only_arg }), |
| 617 | 617 | .unknown, .so => try link_objects.append(it.only_arg), |
| ... | ... | @@ -998,9 +998,9 @@ pub fn buildOutputType( |
| 998 | 998 | var cleanup_emit_bin_dir: ?fs.Dir = null; |
| 999 | 999 | defer if (cleanup_emit_bin_dir) |*dir| dir.close(); |
| 1000 | 1000 | |
| 1001 | const emit_bin_loc: ?Module.EmitLoc = switch (emit_bin) { | |
| 1001 | const emit_bin_loc: ?Compilation.EmitLoc = switch (emit_bin) { | |
| 1002 | 1002 | .no => null, |
| 1003 | .yes_default_path => Module.EmitLoc{ | |
| 1003 | .yes_default_path => Compilation.EmitLoc{ | |
| 1004 | 1004 | .directory = .{ .path = null, .handle = fs.cwd() }, |
| 1005 | 1005 | .basename = try std.zig.binNameAlloc( |
| 1006 | 1006 | arena, |
| ... | ... | @@ -1016,7 +1016,7 @@ pub fn buildOutputType( |
| 1016 | 1016 | if (fs.path.dirname(full_path)) |dirname| { |
| 1017 | 1017 | const handle = try fs.cwd().openDir(dirname, .{}); |
| 1018 | 1018 | cleanup_emit_bin_dir = handle; |
| 1019 | break :b Module.EmitLoc{ | |
| 1019 | break :b Compilation.EmitLoc{ | |
| 1020 | 1020 | .basename = basename, |
| 1021 | 1021 | .directory = .{ |
| 1022 | 1022 | .path = dirname, |
| ... | ... | @@ -1024,7 +1024,7 @@ pub fn buildOutputType( |
| 1024 | 1024 | }, |
| 1025 | 1025 | }; |
| 1026 | 1026 | } else { |
| 1027 | break :b Module.EmitLoc{ | |
| 1027 | break :b Compilation.EmitLoc{ | |
| 1028 | 1028 | .basename = basename, |
| 1029 | 1029 | .directory = .{ .path = null, .handle = fs.cwd() }, |
| 1030 | 1030 | }; |
| ... | ... | @@ -1035,9 +1035,9 @@ pub fn buildOutputType( |
| 1035 | 1035 | var cleanup_emit_h_dir: ?fs.Dir = null; |
| 1036 | 1036 | defer if (cleanup_emit_h_dir) |*dir| dir.close(); |
| 1037 | 1037 | |
| 1038 | const emit_h_loc: ?Module.EmitLoc = switch (emit_h) { | |
| 1038 | const emit_h_loc: ?Compilation.EmitLoc = switch (emit_h) { | |
| 1039 | 1039 | .no => null, |
| 1040 | .yes_default_path => Module.EmitLoc{ | |
| 1040 | .yes_default_path => Compilation.EmitLoc{ | |
| 1041 | 1041 | .directory = .{ .path = null, .handle = fs.cwd() }, |
| 1042 | 1042 | .basename = try std.fmt.allocPrint(arena, "{}.h", .{root_name}), |
| 1043 | 1043 | }, |
| ... | ... | @@ -1046,7 +1046,7 @@ pub fn buildOutputType( |
| 1046 | 1046 | if (fs.path.dirname(full_path)) |dirname| { |
| 1047 | 1047 | const handle = try fs.cwd().openDir(dirname, .{}); |
| 1048 | 1048 | cleanup_emit_h_dir = handle; |
| 1049 | break :b Module.EmitLoc{ | |
| 1049 | break :b Compilation.EmitLoc{ | |
| 1050 | 1050 | .basename = basename, |
| 1051 | 1051 | .directory = .{ |
| 1052 | 1052 | .path = dirname, |
| ... | ... | @@ -1054,7 +1054,7 @@ pub fn buildOutputType( |
| 1054 | 1054 | }, |
| 1055 | 1055 | }; |
| 1056 | 1056 | } else { |
| 1057 | break :b Module.EmitLoc{ | |
| 1057 | break :b Compilation.EmitLoc{ | |
| 1058 | 1058 | .basename = basename, |
| 1059 | 1059 | .directory = .{ .path = null, .handle = fs.cwd() }, |
| 1060 | 1060 | }; |
| ... | ... | @@ -1103,7 +1103,7 @@ pub fn buildOutputType( |
| 1103 | 1103 | const cache_parent_dir = if (root_pkg) |pkg| pkg.root_src_directory.handle else fs.cwd(); |
| 1104 | 1104 | var cache_dir = try cache_parent_dir.makeOpenPath("zig-cache", .{}); |
| 1105 | 1105 | defer cache_dir.close(); |
| 1106 | const zig_cache_directory: Module.Directory = .{ | |
| 1106 | const zig_cache_directory: Compilation.Directory = .{ | |
| 1107 | 1107 | .handle = cache_dir, |
| 1108 | 1108 | .path = blk: { |
| 1109 | 1109 | if (root_pkg) |pkg| { |
| ... | ... | @@ -1115,7 +1115,7 @@ pub fn buildOutputType( |
| 1115 | 1115 | }, |
| 1116 | 1116 | }; |
| 1117 | 1117 | |
| 1118 | const module = Module.create(gpa, .{ | |
| 1118 | const comp = Compilation.create(gpa, .{ | |
| 1119 | 1119 | .zig_lib_directory = zig_lib_directory, |
| 1120 | 1120 | .zig_cache_directory = zig_cache_directory, |
| 1121 | 1121 | .root_name = root_name, |
| ... | ... | @@ -1170,15 +1170,15 @@ pub fn buildOutputType( |
| 1170 | 1170 | .debug_cc = debug_cc, |
| 1171 | 1171 | .debug_link = debug_link, |
| 1172 | 1172 | }) catch |err| { |
| 1173 | fatal("unable to create module: {}", .{@errorName(err)}); | |
| 1173 | fatal("unable to create compilation: {}", .{@errorName(err)}); | |
| 1174 | 1174 | }; |
| 1175 | defer module.destroy(); | |
| 1175 | defer comp.destroy(); | |
| 1176 | 1176 | |
| 1177 | 1177 | const stdin = std.io.getStdIn().inStream(); |
| 1178 | 1178 | const stderr = std.io.getStdErr().outStream(); |
| 1179 | 1179 | var repl_buf: [1024]u8 = undefined; |
| 1180 | 1180 | |
| 1181 | try updateModule(gpa, module, zir_out_path); | |
| 1181 | try updateModule(gpa, comp, zir_out_path); | |
| 1182 | 1182 | |
| 1183 | 1183 | if (build_options.have_llvm and only_pp_or_asm) { |
| 1184 | 1184 | // this may include dumping the output to stdout |
| ... | ... | @@ -1188,7 +1188,7 @@ pub fn buildOutputType( |
| 1188 | 1188 | while (watch) { |
| 1189 | 1189 | try stderr.print("🦎 ", .{}); |
| 1190 | 1190 | if (output_mode == .Exe) { |
| 1191 | try module.makeBinFileExecutable(); | |
| 1191 | try comp.makeBinFileExecutable(); | |
| 1192 | 1192 | } |
| 1193 | 1193 | if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| { |
| 1194 | 1194 | try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)}); |
| ... | ... | @@ -1198,9 +1198,9 @@ pub fn buildOutputType( |
| 1198 | 1198 | |
| 1199 | 1199 | if (mem.eql(u8, actual_line, "update")) { |
| 1200 | 1200 | if (output_mode == .Exe) { |
| 1201 | try module.makeBinFileWritable(); | |
| 1201 | try comp.makeBinFileWritable(); | |
| 1202 | 1202 | } |
| 1203 | try updateModule(gpa, module, zir_out_path); | |
| 1203 | try updateModule(gpa, comp, zir_out_path); | |
| 1204 | 1204 | } else if (mem.eql(u8, actual_line, "exit")) { |
| 1205 | 1205 | break; |
| 1206 | 1206 | } else if (mem.eql(u8, actual_line, "help")) { |
| ... | ... | @@ -1214,11 +1214,11 @@ pub fn buildOutputType( |
| 1214 | 1214 | } |
| 1215 | 1215 | } |
| 1216 | 1216 | |
| 1217 | fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !void { | |
| 1218 | try module.update(); | |
| 1217 | fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8) !void { | |
| 1218 | try comp.update(); | |
| 1219 | 1219 | |
| 1220 | var errors = try module.getAllErrorsAlloc(); | |
| 1221 | defer errors.deinit(module.gpa); | |
| 1220 | var errors = try comp.getAllErrorsAlloc(); | |
| 1221 | defer errors.deinit(comp.gpa); | |
| 1222 | 1222 | |
| 1223 | 1223 | if (errors.list.len != 0) { |
| 1224 | 1224 | for (errors.list) |full_err_msg| { |
| ... | ... | @@ -1232,7 +1232,7 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo |
| 1232 | 1232 | } |
| 1233 | 1233 | |
| 1234 | 1234 | if (zir_out_path) |zop| { |
| 1235 | const zig_module = module.bin_file.options.zig_module orelse | |
| 1235 | const zig_module = comp.bin_file.options.zig_module orelse | |
| 1236 | 1236 | fatal("-femit-zir with no zig source code", .{}); |
| 1237 | 1237 | var new_zir_module = try zir.emit(gpa, zig_module); |
| 1238 | 1238 | defer new_zir_module.deinit(gpa); |
src-self-hosted/test.zig+16-16| ... | ... | @@ -1,6 +1,6 @@ |
| 1 | 1 | const std = @import("std"); |
| 2 | 2 | const link = @import("link.zig"); |
| 3 | const Module = @import("Module.zig"); | |
| 3 | const Compilation = @import("Compilation.zig"); | |
| 4 | 4 | const Allocator = std.mem.Allocator; |
| 5 | 5 | const zir = @import("zir.zig"); |
| 6 | 6 | const Package = @import("Package.zig"); |
| ... | ... | @@ -61,7 +61,7 @@ pub const TestContext = struct { |
| 61 | 61 | ZIR, |
| 62 | 62 | }; |
| 63 | 63 | |
| 64 | /// A Case consists of a set of *updates*. The same Module is used for each | |
| 64 | /// A Case consists of a set of *updates*. The same Compilation is used for each | |
| 65 | 65 | /// update, so each update's source is treated as a single file being |
| 66 | 66 | /// updated by the test harness and incrementally compiled. |
| 67 | 67 | pub const Case = struct { |
| ... | ... | @@ -437,7 +437,7 @@ pub const TestContext = struct { |
| 437 | 437 | allocator: *Allocator, |
| 438 | 438 | root_node: *std.Progress.Node, |
| 439 | 439 | case: Case, |
| 440 | zig_lib_directory: Module.Directory, | |
| 440 | zig_lib_directory: Compilation.Directory, | |
| 441 | 441 | rand: *std.rand.Random, |
| 442 | 442 | ) !void { |
| 443 | 443 | const target_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target); |
| ... | ... | @@ -453,7 +453,7 @@ pub const TestContext = struct { |
| 453 | 453 | var cache_dir = try tmp.dir.makeOpenPath("zig-cache", .{}); |
| 454 | 454 | defer cache_dir.close(); |
| 455 | 455 | const bogus_path = "bogus"; // TODO this will need to be fixed before we can test LLVM extensions |
| 456 | const zig_cache_directory: Module.Directory = .{ | |
| 456 | const zig_cache_directory: Compilation.Directory = .{ | |
| 457 | 457 | .handle = cache_dir, |
| 458 | 458 | .path = try std.fs.path.join(arena, &[_][]const u8{ bogus_path, "zig-cache" }), |
| 459 | 459 | }; |
| ... | ... | @@ -465,15 +465,15 @@ pub const TestContext = struct { |
| 465 | 465 | const ofmt: ?std.builtin.ObjectFormat = if (case.cbe) .c else null; |
| 466 | 466 | const bin_name = try std.zig.binNameAlloc(arena, "test_case", target, case.output_mode, null, ofmt); |
| 467 | 467 | |
| 468 | const emit_directory: Module.Directory = .{ | |
| 468 | const emit_directory: Compilation.Directory = .{ | |
| 469 | 469 | .path = bogus_path, |
| 470 | 470 | .handle = tmp.dir, |
| 471 | 471 | }; |
| 472 | const emit_bin: Module.EmitLoc = .{ | |
| 472 | const emit_bin: Compilation.EmitLoc = .{ | |
| 473 | 473 | .directory = emit_directory, |
| 474 | 474 | .basename = bin_name, |
| 475 | 475 | }; |
| 476 | const module = try Module.create(allocator, .{ | |
| 476 | const comp = try Compilation.create(allocator, .{ | |
| 477 | 477 | .zig_cache_directory = zig_cache_directory, |
| 478 | 478 | .zig_lib_directory = zig_lib_directory, |
| 479 | 479 | .rand = rand, |
| ... | ... | @@ -491,7 +491,7 @@ pub const TestContext = struct { |
| 491 | 491 | .object_format = ofmt, |
| 492 | 492 | .is_native_os = case.target.isNativeOs(), |
| 493 | 493 | }); |
| 494 | defer module.destroy(); | |
| 494 | defer comp.destroy(); | |
| 495 | 495 | |
| 496 | 496 | for (case.updates.items) |update, update_index| { |
| 497 | 497 | var update_node = root_node.start("update", 3); |
| ... | ... | @@ -505,20 +505,20 @@ pub const TestContext = struct { |
| 505 | 505 | |
| 506 | 506 | var module_node = update_node.start("parse/analysis/codegen", null); |
| 507 | 507 | module_node.activate(); |
| 508 | try module.makeBinFileWritable(); | |
| 509 | try module.update(); | |
| 508 | try comp.makeBinFileWritable(); | |
| 509 | try comp.update(); | |
| 510 | 510 | module_node.end(); |
| 511 | 511 | |
| 512 | 512 | if (update.case != .Error) { |
| 513 | var all_errors = try module.getAllErrorsAlloc(); | |
| 513 | var all_errors = try comp.getAllErrorsAlloc(); | |
| 514 | 514 | defer all_errors.deinit(allocator); |
| 515 | 515 | if (all_errors.list.len != 0) { |
| 516 | std.debug.print("\nErrors occurred updating the module:\n================\n", .{}); | |
| 516 | std.debug.print("\nErrors occurred updating the compilation:\n================\n", .{}); | |
| 517 | 517 | for (all_errors.list) |err| { |
| 518 | 518 | std.debug.print(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg }); |
| 519 | 519 | } |
| 520 | 520 | if (case.cbe) { |
| 521 | const C = module.bin_file.cast(link.File.C).?; | |
| 521 | const C = comp.bin_file.cast(link.File.C).?; | |
| 522 | 522 | std.debug.print("Generated C: \n===============\n{}\n\n===========\n\n", .{C.main.items}); |
| 523 | 523 | } |
| 524 | 524 | std.debug.print("Test failed.\n", .{}); |
| ... | ... | @@ -549,7 +549,7 @@ pub const TestContext = struct { |
| 549 | 549 | update_node.estimated_total_items = 5; |
| 550 | 550 | var emit_node = update_node.start("emit", null); |
| 551 | 551 | emit_node.activate(); |
| 552 | var new_zir_module = try zir.emit(allocator, module.bin_file.options.zig_module.?); | |
| 552 | var new_zir_module = try zir.emit(allocator, comp.bin_file.options.zig_module.?); | |
| 553 | 553 | defer new_zir_module.deinit(allocator); |
| 554 | 554 | emit_node.end(); |
| 555 | 555 | |
| ... | ... | @@ -584,7 +584,7 @@ pub const TestContext = struct { |
| 584 | 584 | for (handled_errors) |*h| { |
| 585 | 585 | h.* = false; |
| 586 | 586 | } |
| 587 | var all_errors = try module.getAllErrorsAlloc(); | |
| 587 | var all_errors = try comp.getAllErrorsAlloc(); | |
| 588 | 588 | defer all_errors.deinit(allocator); |
| 589 | 589 | for (all_errors.list) |a| { |
| 590 | 590 | for (e) |ex, i| { |
| ... | ... | @@ -666,7 +666,7 @@ pub const TestContext = struct { |
| 666 | 666 | }, |
| 667 | 667 | } |
| 668 | 668 | |
| 669 | try module.makeBinFileExecutable(); | |
| 669 | try comp.makeBinFileExecutable(); | |
| 670 | 670 | |
| 671 | 671 | break :x try std.ChildProcess.exec(.{ |
| 672 | 672 | .allocator = allocator, |