authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-13 19:49:52-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2020-09-13 19:49:52-07:00
log4d59f775289b50a9529e801a6998dcd181945efb
treea0355897095a7066373e976a05c623fd9dbb252b
parent2a8fc1a18e7d9017262f5c7ee9669ca7d80ebaa6

stage2: rename Module to Compilation


15 files changed, 1661 insertions(+), 1666 deletions(-)

src-self-hosted/Compilation.zig created+1527
......@@ -0,0 +1,1527 @@
1const Compilation = @This();
2
3const std = @import("std");
4const mem = std.mem;
5const Allocator = std.mem.Allocator;
6const Value = @import("value.zig").Value;
7const assert = std.debug.assert;
8const log = std.log.scoped(.compilation);
9const Target = std.Target;
10const target_util = @import("target.zig");
11const Package = @import("Package.zig");
12const link = @import("link.zig");
13const trace = @import("tracy.zig").trace;
14const liveness = @import("liveness.zig");
15const build_options = @import("build_options");
16const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
17const glibc = @import("glibc.zig");
18const fatal = @import("main.zig").fatal;
19const ZigModule = @import("ZigModule.zig");
20
21/// General-purpose allocator. Used for both temporary and long-term storage.
22gpa: *Allocator,
23/// Arena-allocated memory used during initialization. Should be untouched until deinit.
24arena_state: std.heap.ArenaAllocator.State,
25bin_file: *link.File,
26c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{},
27
28link_error_flags: link.File.ErrorFlags = .{},
29
30work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
31
32/// The ErrorMsg memory is owned by the `CObject`, using Compilation's general purpose allocator.
33failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *ErrorMsg) = .{},
34
35keep_source_files_loaded: bool,
36use_clang: bool,
37sanitize_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`.
42clang_passthrough_mode: bool,
43/// Whether to print clang argvs to stdout.
44debug_cc: bool,
45disable_c_depfile: bool,
46
47c_source_files: []const CSourceFile,
48clang_argv: []const []const u8,
49cache_parent: *std.cache_hash.Cache,
50/// Path to own executable for invoking `zig clang`.
51self_exe_path: ?[]const u8,
52zig_lib_directory: Directory,
53zig_cache_directory: Directory,
54libc_include_dir_list: []const []const u8,
55rand: *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().
59libcxx_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().
62libcxxabi_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().
65libunwind_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().
68libc_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.
73crt_files: std.StringHashMapUnmanaged([]const u8) = .{},
74
75/// Keeping track of this possibly open resource so we can close it later.
76owned_link_dir: ?std.fs.Dir,
77
78pub const InnerError = ZigModule.InnerError;
79
80/// For passing to a C compiler.
81pub const CSourceFile = struct {
82 src_path: []const u8,
83 extra_flags: []const []const u8 = &[0][]const u8{},
84};
85
86const 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
106pub 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
149pub 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
183pub 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
191pub 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
199pub 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
258pub 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
607pub 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
641pub 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.
646pub 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`.
714pub fn makeBinFileExecutable(self: *Compilation) !void {
715 return self.bin_file.makeExecutable();
716}
717
718pub fn makeBinFileWritable(self: *Compilation) !void {
719 return self.bin_file.makeWritable();
720}
721
722pub 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
739pub 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
796pub 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
901fn 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
1072fn 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.
1082fn 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
1273fn 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
1279fn 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
1289pub 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
1319pub const FileExt = enum {
1320 c,
1321 cpp,
1322 h,
1323 ll,
1324 bc,
1325 assembly,
1326 so,
1327 unknown,
1328};
1329
1330pub fn hasCExt(filename: []const u8) bool {
1331 return mem.endsWith(u8, filename, ".c");
1332}
1333
1334pub 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
1341pub fn hasAsmExt(filename: []const u8) bool {
1342 return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S");
1343}
1344
1345pub 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
1380test "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
1390fn 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
1397const LibCDirs = struct {
1398 libc_include_dir_list: []const []const u8,
1399 libc_installation: ?*const LibCInstallation,
1400};
1401
1402fn 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
1473fn 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
1497pub 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
1507fn 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
1523fn 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.
2const Module = @This();
3const Compilation = @This();
4
5const std = @import("std");
6const mem = std.mem;
7const Allocator = std.mem.Allocator;
8const Value = @import("value.zig").Value;
9const assert = std.debug.assert;
10const log = std.log.scoped(.compilation);
11const Target = std.Target;
12const target_util = @import("target.zig");
13const Package = @import("Package.zig");
14const link = @import("link.zig");
15const trace = @import("tracy.zig").trace;
16const liveness = @import("liveness.zig");
17const build_options = @import("build_options");
18const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
19const glibc = @import("glibc.zig");
20const fatal = @import("main.zig").fatal;
21const ZigModule = @import("ZigModule.zig");
22
23/// General-purpose allocator. Used for both temporary and long-term storage.
24gpa: *Allocator,
25/// Arena-allocated memory used during initialization. Should be untouched until deinit.
26arena_state: std.heap.ArenaAllocator.State,
27bin_file: *link.File,
28c_object_table: std.AutoArrayHashMapUnmanaged(*CObject, void) = .{},
29
30link_error_flags: link.File.ErrorFlags = .{},
31
32work_queue: std.fifo.LinearFifo(WorkItem, .Dynamic),
33
34/// The ErrorMsg memory is owned by the `CObject`, using Module's general purpose allocator.
35failed_c_objects: std.AutoArrayHashMapUnmanaged(*CObject, *ErrorMsg) = .{},
36
37keep_source_files_loaded: bool,
38use_clang: bool,
39sanitize_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`.
44clang_passthrough_mode: bool,
45/// Whether to print clang argvs to stdout.
46debug_cc: bool,
47disable_c_depfile: bool,
48
49c_source_files: []const CSourceFile,
50clang_argv: []const []const u8,
51cache_parent: *std.cache_hash.Cache,
52/// Path to own executable for invoking `zig clang`.
53self_exe_path: ?[]const u8,
54zig_lib_directory: Directory,
55zig_cache_directory: Directory,
56libc_include_dir_list: []const []const u8,
57rand: *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().
61libcxx_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().
64libcxxabi_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().
67libunwind_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().
70libc_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.
75crt_files: std.StringHashMapUnmanaged([]const u8) = .{},
76
77/// Keeping track of this possibly open resource so we can close it later.
78owned_link_dir: ?std.fs.Dir,
79
80pub const InnerError = ZigModule.InnerError;
81
82/// For passing to a C compiler.
83pub const CSourceFile = struct {
84 src_path: []const u8,
85 extra_flags: []const []const u8 = &[0][]const u8{},
86};
87
88const 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
108pub 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
151pub 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
185pub 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
193pub 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
201pub 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
260pub 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
609pub 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
643pub 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.
648pub 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`.
716pub fn makeBinFileExecutable(self: *Module) !void {
717 return self.bin_file.makeExecutable();
718}
719
720pub fn makeBinFileWritable(self: *Module) !void {
721 return self.bin_file.makeWritable();
722}
723
724pub 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
741pub 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
798pub 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
903fn 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
1074fn 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.
1084fn 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
1275fn 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
1281fn 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
1291pub 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
1321pub const FileExt = enum {
1322 c,
1323 cpp,
1324 h,
1325 ll,
1326 bc,
1327 assembly,
1328 so,
1329 unknown,
1330};
1331
1332pub fn hasCExt(filename: []const u8) bool {
1333 return mem.endsWith(u8, filename, ".c");
1334}
1335
1336pub 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
1343pub fn hasAsmExt(filename: []const u8) bool {
1344 return mem.endsWith(u8, filename, ".s") or mem.endsWith(u8, filename, ".S");
1345}
1346
1347pub 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
1382test "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
1392fn 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
1399const LibCDirs = struct {
1400 libc_include_dir_list: []const []const u8,
1401 libc_installation: ?*const LibCInstallation,
1402};
1403
1404fn 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
1475fn 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
1499pub 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
1509fn 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
1525fn 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 @@
11pub const Table = std.StringHashMapUnmanaged(*Package);
22
3root_src_directory: Module.Directory,
3root_src_directory: Compilation.Directory,
44/// Relative to `root_src_directory`.
55root_src_path: []u8,
66table: Table,
......@@ -56,4 +56,4 @@ const mem = std.mem;
5656const Allocator = std.mem.Allocator;
5757const assert = std.debug.assert;
5858const Package = @This();
59const Module = @import("Module.zig");
59const 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
32const Module = @This();
4const Compilation = @import("Module.zig");
5
63const std = @import("std");
4const Compilation = @import("Compilation.zig");
75const mem = std.mem;
86const Allocator = std.mem.Allocator;
97const ArrayListUnmanaged = std.ArrayListUnmanaged;
src-self-hosted/codegen.zig+1-1
......@@ -8,7 +8,7 @@ const Value = @import("value.zig").Value;
88const TypedValue = @import("TypedValue.zig");
99const link = @import("link.zig");
1010const Module = @import("ZigModule.zig");
11const Compilation = @import("Module.zig");
11const Compilation = @import("Compilation.zig");
1212const ErrorMsg = Compilation.ErrorMsg;
1313const Target = std.Target;
1414const Allocator = mem.Allocator;
src-self-hosted/glibc.zig+75-75
......@@ -2,7 +2,7 @@ const std = @import("std");
22const Allocator = std.mem.Allocator;
33const target_util = @import("target.zig");
44const mem = std.mem;
5const Module = @import("Module.zig");
5const Compilation = @import("Compilation.zig");
66const path = std.fs.path;
77const build_options = @import("build_options");
88const trace = @import("tracy.zig").trace;
......@@ -246,11 +246,11 @@ pub const CRTFile = enum {
246246 libc_nonshared_a,
247247};
248248
249pub fn buildCRTFile(mod: *Module, crt_file: CRTFile) !void {
249pub fn buildCRTFile(comp: *Compilation, crt_file: CRTFile) !void {
250250 if (!build_options.have_llvm) {
251251 return error.ZigCompilerNotBuiltWithLLVMExtensions;
252252 }
253 const gpa = mod.gpa;
253 const gpa = comp.gpa;
254254 var arena_allocator = std.heap.ArenaAllocator.init(gpa);
255255 errdefer arena_allocator.deinit();
256256 const arena = &arena_allocator.allocator;
......@@ -258,29 +258,29 @@ pub fn buildCRTFile(mod: *Module, crt_file: CRTFile) !void {
258258 switch (crt_file) {
259259 .crti_o => {
260260 var args = std.ArrayList([]const u8).init(arena);
261 try add_include_dirs(mod, arena, &args);
261 try add_include_dirs(comp, arena, &args);
262262 try args.appendSlice(&[_][]const u8{
263263 "-D_LIBC_REENTRANT",
264264 "-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"),
266266 "-DMODULE_NAME=libc",
267267 "-Wno-nonportable-include-path",
268268 "-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"),
270270 "-DTOP_NAMESPACE=glibc",
271271 "-DASSEMBLER",
272272 "-g",
273273 "-Wa,--noexecstack",
274274 });
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"),
277277 .extra_flags = args.items,
278278 };
279 return build_libc_object(mod, "crti.o", c_source_file);
279 return build_libc_object(comp, "crti.o", c_source_file);
280280 },
281281 .crtn_o => {
282282 var args = std.ArrayList([]const u8).init(arena);
283 try add_include_dirs(mod, arena, &args);
283 try add_include_dirs(comp, arena, &args);
284284 try args.appendSlice(&[_][]const u8{
285285 "-D_LIBC_REENTRANT",
286286 "-DMODULE_NAME=libc",
......@@ -289,23 +289,23 @@ pub fn buildCRTFile(mod: *Module, crt_file: CRTFile) !void {
289289 "-g",
290290 "-Wa,--noexecstack",
291291 });
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"),
294294 .extra_flags = args.items,
295295 };
296 return build_libc_object(mod, "crtn.o", c_source_file);
296 return build_libc_object(comp, "crtn.o", c_source_file);
297297 },
298298 .start_os => {
299299 var args = std.ArrayList([]const u8).init(arena);
300 try add_include_dirs(mod, arena, &args);
300 try add_include_dirs(comp, arena, &args);
301301 try args.appendSlice(&[_][]const u8{
302302 "-D_LIBC_REENTRANT",
303303 "-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"),
305305 "-DMODULE_NAME=libc",
306306 "-Wno-nonportable-include-path",
307307 "-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"),
309309 "-DPIC",
310310 "-DSHARED",
311311 "-DTOP_NAMESPACE=glibc",
......@@ -313,19 +313,19 @@ pub fn buildCRTFile(mod: *Module, crt_file: CRTFile) !void {
313313 "-g",
314314 "-Wa,--noexecstack",
315315 });
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"),
318318 .extra_flags = args.items,
319319 };
320 return build_libc_object(mod, "start.os", c_source_file);
320 return build_libc_object(comp, "start.os", c_source_file);
321321 },
322322 .abi_note_o => {
323323 var args = std.ArrayList([]const u8).init(arena);
324324 try args.appendSlice(&[_][]const u8{
325325 "-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"),
327327 });
328 try add_include_dirs(mod, arena, &args);
328 try add_include_dirs(comp, arena, &args);
329329 try args.appendSlice(&[_][]const u8{
330330 "-D_LIBC_REENTRANT",
331331 "-DMODULE_NAME=libc",
......@@ -334,11 +334,11 @@ pub fn buildCRTFile(mod: *Module, crt_file: CRTFile) !void {
334334 "-g",
335335 "-Wa,--noexecstack",
336336 });
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"),
339339 .extra_flags = args.items,
340340 };
341 return build_libc_object(mod, "abi-note.o", c_source_file);
341 return build_libc_object(comp, "abi-note.o", c_source_file);
342342 },
343343 .scrt1_o => {
344344 return error.Unimplemented; // TODO
......@@ -349,8 +349,8 @@ pub fn buildCRTFile(mod: *Module, crt_file: CRTFile) !void {
349349 }
350350}
351351
352fn start_asm_path(mod: *Module, arena: *Allocator, basename: []const u8) ![]const u8 {
353 const arch = mod.getTarget().cpu.arch;
352fn start_asm_path(comp: *Compilation, arena: *Allocator, basename: []const u8) ![]const u8 {
353 const arch = comp.getTarget().cpu.arch;
354354 const is_ppc = arch == .powerpc or arch == .powerpc64 or arch == .powerpc64le;
355355 const is_aarch64 = arch == .aarch64 or arch == .aarch64_be;
356356 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
359359 const s = path.sep_str;
360360
361361 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.?);
363363 try result.appendSlice(s ++ "libc" ++ s ++ "glibc" ++ s ++ "sysdeps" ++ s);
364364 if (is_sparc) {
365365 if (is_64) {
......@@ -392,76 +392,76 @@ fn start_asm_path(mod: *Module, arena: *Allocator, basename: []const u8) ![]cons
392392 return result.items;
393393}
394394
395fn add_include_dirs(mod: *Module, arena: *Allocator, args: *std.ArrayList([]const u8)) error{OutOfMemory}!void {
396 const target = mod.getTarget();
395fn add_include_dirs(comp: *Compilation, arena: *Allocator, args: *std.ArrayList([]const u8)) error{OutOfMemory}!void {
396 const target = comp.getTarget();
397397 const arch = target.cpu.arch;
398398 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");
400400
401401 const s = path.sep_str;
402402
403403 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"));
405405
406406 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"));
408408 }
409409
410410 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"));
412412 }
413413
414414 if (target.os.tag == .linux) {
415415 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 ++
417417 "unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "generic"));
418418
419419 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 ++
421421 "unix" ++ s ++ "sysv" ++ s ++ "linux" ++ s ++ "include"));
422422 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 ++
424424 "unix" ++ s ++ "sysv" ++ s ++ "linux"));
425425 }
426426 if (opt_nptl) |nptl| {
427427 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 }));
429429 }
430430
431431 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"));
433433
434434 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"));
436436
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"));
438438
439439 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"));
441441
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"));
443443
444444 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"));
446446
447447 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" }));
449449
450450 try args.append("-I");
451451 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),
453453 }));
454454
455455 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"));
457457
458458 try args.append("-I");
459459 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),
461461 }));
462462
463463 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"));
465465}
466466
467467fn add_include_dirs_arch(
......@@ -576,60 +576,60 @@ fn add_include_dirs_arch(
576576 }
577577}
578578
579fn 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 });
579fn 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 });
581581}
582582
583583const lib_libc = "libc" ++ path.sep_str;
584584const lib_libc_glibc = lib_libc ++ "glibc" ++ path.sep_str;
585585
586fn 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 });
586fn 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 });
588588}
589589
590fn build_libc_object(mod: *Module, basename: []const u8, c_source_file: Module.CSourceFile) !void {
590fn build_libc_object(comp: *Compilation, basename: []const u8, c_source_file: Compilation.CSourceFile) !void {
591591 const tracy = trace(@src());
592592 defer tracy.end();
593593
594594 // 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{
596596 .directory = null, // Put it in the cache directory.
597597 .basename = basename,
598598 };
599 const sub_module = try Module.create(mod.gpa, .{
599 const sub_compilation = try Compilation.create(comp.gpa, .{
600600 // 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(),
604604 .root_name = mem.split(basename, ".").next().?,
605605 .root_pkg = null,
606606 .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,
609609 .emit_bin = emit_bin,
610 .optimize_mode = mod.bin_file.options.optimize_mode,
610 .optimize_mode = comp.bin_file.options.optimize_mode,
611611 .want_sanitize_c = false,
612612 .want_stack_check = false,
613613 .want_valgrind = false,
614 .want_pic = mod.bin_file.options.pic,
614 .want_pic = comp.bin_file.options.pic,
615615 .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,
622622 });
623 defer sub_module.destroy();
623 defer sub_compilation.destroy();
624624
625 try sub_module.update();
625 try sub_compilation.update();
626626
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 })
630630 else
631 try mod.gpa.dupe(u8, basename);
631 try comp.gpa.dupe(u8, basename);
632632
633633 // 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);
635635}
src-self-hosted/introspect.zig+7-7
......@@ -2,12 +2,12 @@ const std = @import("std");
22const mem = std.mem;
33const fs = std.fs;
44const CacheHash = std.cache_hash.CacheHash;
5const Module = @import("Module.zig");
5const Compilation = @import("Compilation.zig");
66
77/// Returns the sub_path that worked, or `null` if none did.
88/// The path of the returned Directory is relative to `base`.
99/// The handle of the returned Directory is open.
10fn testZigInstallPrefix(base_dir: fs.Dir) ?Module.Directory {
10fn testZigInstallPrefix(base_dir: fs.Dir) ?Compilation.Directory {
1111 const test_index_file = "std" ++ fs.path.sep_str ++ "std.zig";
1212
1313 zig_dir: {
......@@ -19,7 +19,7 @@ fn testZigInstallPrefix(base_dir: fs.Dir) ?Module.Directory {
1919 break :zig_dir;
2020 };
2121 file.close();
22 return Module.Directory{ .handle = test_zig_dir, .path = lib_zig };
22 return Compilation.Directory{ .handle = test_zig_dir, .path = lib_zig };
2323 }
2424
2525 // Try lib/std/std.zig
......@@ -29,11 +29,11 @@ fn testZigInstallPrefix(base_dir: fs.Dir) ?Module.Directory {
2929 return null;
3030 };
3131 file.close();
32 return Module.Directory{ .handle = test_zig_dir, .path = "lib" };
32 return Compilation.Directory{ .handle = test_zig_dir, .path = "lib" };
3333}
3434
3535/// Both the directory handle and the path are newly allocated resources which the caller now owns.
36pub fn findZigLibDir(gpa: *mem.Allocator) !Module.Directory {
36pub fn findZigLibDir(gpa: *mem.Allocator) !Compilation.Directory {
3737 const self_exe_path = try fs.selfExePathAlloc(gpa);
3838 defer gpa.free(self_exe_path);
3939
......@@ -44,7 +44,7 @@ pub fn findZigLibDir(gpa: *mem.Allocator) !Module.Directory {
4444pub fn findZigLibDirFromSelfExe(
4545 allocator: *mem.Allocator,
4646 self_exe_path: []const u8,
47) error{ OutOfMemory, FileNotFound }!Module.Directory {
47) error{ OutOfMemory, FileNotFound }!Compilation.Directory {
4848 const cwd = fs.cwd();
4949 var cur_path: []const u8 = self_exe_path;
5050 while (fs.path.dirname(cur_path)) |dirname| : (cur_path = dirname) {
......@@ -52,7 +52,7 @@ pub fn findZigLibDirFromSelfExe(
5252 defer base_dir.close();
5353
5454 const sub_directory = testZigInstallPrefix(base_dir) orelse continue;
55 return Module.Directory{
55 return Compilation.Directory{
5656 .handle = sub_directory.handle,
5757 .path = try fs.path.join(allocator, &[_][]const u8{ dirname, sub_directory.path.? }),
5858 };
src-self-hosted/link.zig+1-2
......@@ -1,6 +1,6 @@
11const std = @import("std");
22const Allocator = std.mem.Allocator;
3const Compilation = @import("Module.zig");
3const Compilation = @import("Compilation.zig");
44const ZigModule = @import("ZigModule.zig");
55const fs = std.fs;
66const trace = @import("tracy.zig").trace;
......@@ -23,7 +23,6 @@ pub const Options = struct {
2323 optimize_mode: std.builtin.Mode,
2424 root_name: []const u8,
2525 /// 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.
2726 zig_module: ?*ZigModule,
2827 dynamic_linker: ?[]const u8 = null,
2928 /// 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;
33const assert = std.debug.assert;
44const Allocator = std.mem.Allocator;
55const Module = @import("../ZigModule.zig");
6const Compilation = @import("../Module.zig");
6const Compilation = @import("../Compilation.zig");
77const fs = std.fs;
88const codegen = @import("../codegen/c.zig");
99const link = @import("../link.zig");
src-self-hosted/link/Coff.zig+1-1
......@@ -8,7 +8,7 @@ const fs = std.fs;
88
99const trace = @import("../tracy.zig").trace;
1010const Module = @import("../ZigModule.zig");
11const Compilation = @import("../Module.zig");
11const Compilation = @import("../Compilation.zig");
1212const codegen = @import("../codegen.zig");
1313const link = @import("../link.zig");
1414
src-self-hosted/link/Elf.zig+1-1
......@@ -4,7 +4,7 @@ const assert = std.debug.assert;
44const Allocator = std.mem.Allocator;
55const ir = @import("../ir.zig");
66const Module = @import("../ZigModule.zig");
7const Compilation = @import("../Module.zig");
7const Compilation = @import("../Compilation.zig");
88const fs = std.fs;
99const elf = std.elf;
1010const codegen = @import("../codegen.zig");
src-self-hosted/link/MachO.zig+1-1
......@@ -13,7 +13,7 @@ const trace = @import("../tracy.zig").trace;
1313const Type = @import("../type.zig").Type;
1414
1515const Module = @import("../ZigModule.zig");
16const Compilation = @import("../Module.zig");
16const Compilation = @import("../Compilation.zig");
1717const link = @import("../link.zig");
1818const File = link.File;
1919
src-self-hosted/link/Wasm.zig+1-1
......@@ -7,7 +7,7 @@ const fs = std.fs;
77const leb = std.debug.leb;
88
99const Module = @import("../ZigModule.zig");
10const Compilation = @import("../Module.zig");
10const Compilation = @import("../Compilation.zig");
1111const codegen = @import("../codegen/wasm.zig");
1212const link = @import("../link.zig");
1313
src-self-hosted/main.zig+25-25
......@@ -7,7 +7,7 @@ const process = std.process;
77const Allocator = mem.Allocator;
88const ArrayList = std.ArrayList;
99const ast = std.zig.ast;
10const Module = @import("Module.zig");
10const Compilation = @import("Compilation.zig");
1111const link = @import("link.zig");
1212const Package = @import("Package.zig");
1313const zir = @import("zir.zig");
......@@ -331,7 +331,7 @@ pub fn buildOutputType(
331331 var rpath_list = std.ArrayList([]const u8).init(gpa);
332332 defer rpath_list.deinit();
333333
334 var c_source_files = std.ArrayList(Module.CSourceFile).init(gpa);
334 var c_source_files = std.ArrayList(Compilation.CSourceFile).init(gpa);
335335 defer c_source_files.deinit();
336336
337337 var link_objects = std.ArrayList([]const u8).init(gpa);
......@@ -566,7 +566,7 @@ pub fn buildOutputType(
566566 mem.endsWith(u8, arg, ".lib"))
567567 {
568568 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)) {
570570 // TODO a way to pass extra flags on the CLI
571571 try c_source_files.append(.{ .src_path = arg });
572572 } else if (mem.endsWith(u8, arg, ".so") or
......@@ -611,7 +611,7 @@ pub fn buildOutputType(
611611 try clang_argv.appendSlice(it.other_args);
612612 },
613613 .positional => {
614 const file_ext = Module.classifyFileExt(mem.spanZ(it.only_arg));
614 const file_ext = Compilation.classifyFileExt(mem.spanZ(it.only_arg));
615615 switch (file_ext) {
616616 .assembly, .c, .cpp, .ll, .bc, .h => try c_source_files.append(.{ .src_path = it.only_arg }),
617617 .unknown, .so => try link_objects.append(it.only_arg),
......@@ -998,9 +998,9 @@ pub fn buildOutputType(
998998 var cleanup_emit_bin_dir: ?fs.Dir = null;
999999 defer if (cleanup_emit_bin_dir) |*dir| dir.close();
10001000
1001 const emit_bin_loc: ?Module.EmitLoc = switch (emit_bin) {
1001 const emit_bin_loc: ?Compilation.EmitLoc = switch (emit_bin) {
10021002 .no => null,
1003 .yes_default_path => Module.EmitLoc{
1003 .yes_default_path => Compilation.EmitLoc{
10041004 .directory = .{ .path = null, .handle = fs.cwd() },
10051005 .basename = try std.zig.binNameAlloc(
10061006 arena,
......@@ -1016,7 +1016,7 @@ pub fn buildOutputType(
10161016 if (fs.path.dirname(full_path)) |dirname| {
10171017 const handle = try fs.cwd().openDir(dirname, .{});
10181018 cleanup_emit_bin_dir = handle;
1019 break :b Module.EmitLoc{
1019 break :b Compilation.EmitLoc{
10201020 .basename = basename,
10211021 .directory = .{
10221022 .path = dirname,
......@@ -1024,7 +1024,7 @@ pub fn buildOutputType(
10241024 },
10251025 };
10261026 } else {
1027 break :b Module.EmitLoc{
1027 break :b Compilation.EmitLoc{
10281028 .basename = basename,
10291029 .directory = .{ .path = null, .handle = fs.cwd() },
10301030 };
......@@ -1035,9 +1035,9 @@ pub fn buildOutputType(
10351035 var cleanup_emit_h_dir: ?fs.Dir = null;
10361036 defer if (cleanup_emit_h_dir) |*dir| dir.close();
10371037
1038 const emit_h_loc: ?Module.EmitLoc = switch (emit_h) {
1038 const emit_h_loc: ?Compilation.EmitLoc = switch (emit_h) {
10391039 .no => null,
1040 .yes_default_path => Module.EmitLoc{
1040 .yes_default_path => Compilation.EmitLoc{
10411041 .directory = .{ .path = null, .handle = fs.cwd() },
10421042 .basename = try std.fmt.allocPrint(arena, "{}.h", .{root_name}),
10431043 },
......@@ -1046,7 +1046,7 @@ pub fn buildOutputType(
10461046 if (fs.path.dirname(full_path)) |dirname| {
10471047 const handle = try fs.cwd().openDir(dirname, .{});
10481048 cleanup_emit_h_dir = handle;
1049 break :b Module.EmitLoc{
1049 break :b Compilation.EmitLoc{
10501050 .basename = basename,
10511051 .directory = .{
10521052 .path = dirname,
......@@ -1054,7 +1054,7 @@ pub fn buildOutputType(
10541054 },
10551055 };
10561056 } else {
1057 break :b Module.EmitLoc{
1057 break :b Compilation.EmitLoc{
10581058 .basename = basename,
10591059 .directory = .{ .path = null, .handle = fs.cwd() },
10601060 };
......@@ -1103,7 +1103,7 @@ pub fn buildOutputType(
11031103 const cache_parent_dir = if (root_pkg) |pkg| pkg.root_src_directory.handle else fs.cwd();
11041104 var cache_dir = try cache_parent_dir.makeOpenPath("zig-cache", .{});
11051105 defer cache_dir.close();
1106 const zig_cache_directory: Module.Directory = .{
1106 const zig_cache_directory: Compilation.Directory = .{
11071107 .handle = cache_dir,
11081108 .path = blk: {
11091109 if (root_pkg) |pkg| {
......@@ -1115,7 +1115,7 @@ pub fn buildOutputType(
11151115 },
11161116 };
11171117
1118 const module = Module.create(gpa, .{
1118 const comp = Compilation.create(gpa, .{
11191119 .zig_lib_directory = zig_lib_directory,
11201120 .zig_cache_directory = zig_cache_directory,
11211121 .root_name = root_name,
......@@ -1170,15 +1170,15 @@ pub fn buildOutputType(
11701170 .debug_cc = debug_cc,
11711171 .debug_link = debug_link,
11721172 }) catch |err| {
1173 fatal("unable to create module: {}", .{@errorName(err)});
1173 fatal("unable to create compilation: {}", .{@errorName(err)});
11741174 };
1175 defer module.destroy();
1175 defer comp.destroy();
11761176
11771177 const stdin = std.io.getStdIn().inStream();
11781178 const stderr = std.io.getStdErr().outStream();
11791179 var repl_buf: [1024]u8 = undefined;
11801180
1181 try updateModule(gpa, module, zir_out_path);
1181 try updateModule(gpa, comp, zir_out_path);
11821182
11831183 if (build_options.have_llvm and only_pp_or_asm) {
11841184 // this may include dumping the output to stdout
......@@ -1188,7 +1188,7 @@ pub fn buildOutputType(
11881188 while (watch) {
11891189 try stderr.print("🦎 ", .{});
11901190 if (output_mode == .Exe) {
1191 try module.makeBinFileExecutable();
1191 try comp.makeBinFileExecutable();
11921192 }
11931193 if (stdin.readUntilDelimiterOrEof(&repl_buf, '\n') catch |err| {
11941194 try stderr.print("\nUnable to parse command: {}\n", .{@errorName(err)});
......@@ -1198,9 +1198,9 @@ pub fn buildOutputType(
11981198
11991199 if (mem.eql(u8, actual_line, "update")) {
12001200 if (output_mode == .Exe) {
1201 try module.makeBinFileWritable();
1201 try comp.makeBinFileWritable();
12021202 }
1203 try updateModule(gpa, module, zir_out_path);
1203 try updateModule(gpa, comp, zir_out_path);
12041204 } else if (mem.eql(u8, actual_line, "exit")) {
12051205 break;
12061206 } else if (mem.eql(u8, actual_line, "help")) {
......@@ -1214,11 +1214,11 @@ pub fn buildOutputType(
12141214 }
12151215}
12161216
1217fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !void {
1218 try module.update();
1217fn updateModule(gpa: *Allocator, comp: *Compilation, zir_out_path: ?[]const u8) !void {
1218 try comp.update();
12191219
1220 var errors = try module.getAllErrorsAlloc();
1221 defer errors.deinit(module.gpa);
1220 var errors = try comp.getAllErrorsAlloc();
1221 defer errors.deinit(comp.gpa);
12221222
12231223 if (errors.list.len != 0) {
12241224 for (errors.list) |full_err_msg| {
......@@ -1232,7 +1232,7 @@ fn updateModule(gpa: *Allocator, module: *Module, zir_out_path: ?[]const u8) !vo
12321232 }
12331233
12341234 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
12361236 fatal("-femit-zir with no zig source code", .{});
12371237 var new_zir_module = try zir.emit(gpa, zig_module);
12381238 defer new_zir_module.deinit(gpa);
src-self-hosted/test.zig+16-16
......@@ -1,6 +1,6 @@
11const std = @import("std");
22const link = @import("link.zig");
3const Module = @import("Module.zig");
3const Compilation = @import("Compilation.zig");
44const Allocator = std.mem.Allocator;
55const zir = @import("zir.zig");
66const Package = @import("Package.zig");
......@@ -61,7 +61,7 @@ pub const TestContext = struct {
6161 ZIR,
6262 };
6363
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
6565 /// update, so each update's source is treated as a single file being
6666 /// updated by the test harness and incrementally compiled.
6767 pub const Case = struct {
......@@ -437,7 +437,7 @@ pub const TestContext = struct {
437437 allocator: *Allocator,
438438 root_node: *std.Progress.Node,
439439 case: Case,
440 zig_lib_directory: Module.Directory,
440 zig_lib_directory: Compilation.Directory,
441441 rand: *std.rand.Random,
442442 ) !void {
443443 const target_info = try std.zig.system.NativeTargetInfo.detect(allocator, case.target);
......@@ -453,7 +453,7 @@ pub const TestContext = struct {
453453 var cache_dir = try tmp.dir.makeOpenPath("zig-cache", .{});
454454 defer cache_dir.close();
455455 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 = .{
457457 .handle = cache_dir,
458458 .path = try std.fs.path.join(arena, &[_][]const u8{ bogus_path, "zig-cache" }),
459459 };
......@@ -465,15 +465,15 @@ pub const TestContext = struct {
465465 const ofmt: ?std.builtin.ObjectFormat = if (case.cbe) .c else null;
466466 const bin_name = try std.zig.binNameAlloc(arena, "test_case", target, case.output_mode, null, ofmt);
467467
468 const emit_directory: Module.Directory = .{
468 const emit_directory: Compilation.Directory = .{
469469 .path = bogus_path,
470470 .handle = tmp.dir,
471471 };
472 const emit_bin: Module.EmitLoc = .{
472 const emit_bin: Compilation.EmitLoc = .{
473473 .directory = emit_directory,
474474 .basename = bin_name,
475475 };
476 const module = try Module.create(allocator, .{
476 const comp = try Compilation.create(allocator, .{
477477 .zig_cache_directory = zig_cache_directory,
478478 .zig_lib_directory = zig_lib_directory,
479479 .rand = rand,
......@@ -491,7 +491,7 @@ pub const TestContext = struct {
491491 .object_format = ofmt,
492492 .is_native_os = case.target.isNativeOs(),
493493 });
494 defer module.destroy();
494 defer comp.destroy();
495495
496496 for (case.updates.items) |update, update_index| {
497497 var update_node = root_node.start("update", 3);
......@@ -505,20 +505,20 @@ pub const TestContext = struct {
505505
506506 var module_node = update_node.start("parse/analysis/codegen", null);
507507 module_node.activate();
508 try module.makeBinFileWritable();
509 try module.update();
508 try comp.makeBinFileWritable();
509 try comp.update();
510510 module_node.end();
511511
512512 if (update.case != .Error) {
513 var all_errors = try module.getAllErrorsAlloc();
513 var all_errors = try comp.getAllErrorsAlloc();
514514 defer all_errors.deinit(allocator);
515515 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", .{});
517517 for (all_errors.list) |err| {
518518 std.debug.print(":{}:{}: error: {}\n================\n", .{ err.line + 1, err.column + 1, err.msg });
519519 }
520520 if (case.cbe) {
521 const C = module.bin_file.cast(link.File.C).?;
521 const C = comp.bin_file.cast(link.File.C).?;
522522 std.debug.print("Generated C: \n===============\n{}\n\n===========\n\n", .{C.main.items});
523523 }
524524 std.debug.print("Test failed.\n", .{});
......@@ -549,7 +549,7 @@ pub const TestContext = struct {
549549 update_node.estimated_total_items = 5;
550550 var emit_node = update_node.start("emit", null);
551551 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.?);
553553 defer new_zir_module.deinit(allocator);
554554 emit_node.end();
555555
......@@ -584,7 +584,7 @@ pub const TestContext = struct {
584584 for (handled_errors) |*h| {
585585 h.* = false;
586586 }
587 var all_errors = try module.getAllErrorsAlloc();
587 var all_errors = try comp.getAllErrorsAlloc();
588588 defer all_errors.deinit(allocator);
589589 for (all_errors.list) |a| {
590590 for (e) |ex, i| {
......@@ -666,7 +666,7 @@ pub const TestContext = struct {
666666 },
667667 }
668668
669 try module.makeBinFileExecutable();
669 try comp.makeBinFileExecutable();
670670
671671 break :x try std.ChildProcess.exec(.{
672672 .allocator = allocator,