| ... | ... | @@ -3,10 +3,12 @@ const Allocator = std.mem.Allocator; |
| 3 | 3 | const mem = std.mem; |
| 4 | 4 | const path = std.fs.path; |
| 5 | 5 | const assert = std.debug.assert; |
| 6 | const log = std.log.scoped(.mingw); |
| 6 | 7 | |
| 7 | 8 | const target_util = @import("target.zig"); |
| 8 | 9 | const Compilation = @import("Compilation.zig"); |
| 9 | 10 | const build_options = @import("build_options"); |
| 11 | const Cache = @import("Cache.zig"); |
| 10 | 12 | |
| 11 | 13 | pub const CRTFile = enum { |
| 12 | 14 | crt2_o, |
| ... | ... | @@ -277,6 +279,233 @@ fn add_cc_args( |
| 277 | 279 | }); |
| 278 | 280 | } |
| 279 | 281 | |
| 282 | pub fn buildImportLib(comp: *Compilation, lib_name: []const u8) !void { |
| 283 | var arena_allocator = std.heap.ArenaAllocator.init(comp.gpa); |
| 284 | defer arena_allocator.deinit(); |
| 285 | const arena = &arena_allocator.allocator; |
| 286 | |
| 287 | const def_file_path = findDef(comp, arena, lib_name) catch |err| switch (err) { |
| 288 | error.FileNotFound => { |
| 289 | log.debug("no {s}.def file available to make a DLL import {s}.lib", .{ lib_name, lib_name }); |
| 290 | // In this case we will end up putting foo.lib onto the linker line and letting the linker |
| 291 | // use its library paths to look for libraries and report any problems. |
| 292 | return; |
| 293 | }, |
| 294 | else => |e| return e, |
| 295 | }; |
| 296 | |
| 297 | // We need to invoke `zig clang` to use the preprocessor. |
| 298 | if (!build_options.have_llvm) return error.ZigCompilerNotBuiltWithLLVMExtensions; |
| 299 | const self_exe_path = comp.self_exe_path orelse return error.PreprocessorDisabled; |
| 300 | |
| 301 | const target = comp.getTarget(); |
| 302 | |
| 303 | var cache: Cache = .{ |
| 304 | .gpa = comp.gpa, |
| 305 | .manifest_dir = comp.cache_parent.manifest_dir, |
| 306 | }; |
| 307 | cache.hash.addBytes(build_options.version); |
| 308 | cache.hash.addOptionalBytes(comp.zig_lib_directory.path); |
| 309 | cache.hash.add(target.cpu.arch); |
| 310 | |
| 311 | var man = cache.obtain(); |
| 312 | defer man.deinit(); |
| 313 | |
| 314 | _ = try man.addFile(def_file_path, null); |
| 315 | |
| 316 | const final_lib_basename = try std.fmt.allocPrint(comp.gpa, "{s}.lib", .{lib_name}); |
| 317 | errdefer comp.gpa.free(final_lib_basename); |
| 318 | |
| 319 | if (try man.hit()) { |
| 320 | const digest = man.final(); |
| 321 | |
| 322 | try comp.crt_files.ensureCapacity(comp.gpa, comp.crt_files.count() + 1); |
| 323 | comp.crt_files.putAssumeCapacityNoClobber(final_lib_basename, .{ |
| 324 | .full_object_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{ |
| 325 | "o", &digest, final_lib_basename, |
| 326 | }), |
| 327 | .lock = man.toOwnedLock(), |
| 328 | }); |
| 329 | return; |
| 330 | } |
| 331 | |
| 332 | const digest = man.final(); |
| 333 | const o_sub_path = try std.fs.path.join(arena, &[_][]const u8{ "o", &digest }); |
| 334 | var o_dir = try comp.global_cache_directory.handle.makeOpenPath(o_sub_path, .{}); |
| 335 | defer o_dir.close(); |
| 336 | |
| 337 | const final_def_basename = try std.fmt.allocPrint(arena, "{s}.def", .{lib_name}); |
| 338 | const def_final_path = try comp.global_cache_directory.join(arena, &[_][]const u8{ |
| 339 | "o", &digest, final_def_basename, |
| 340 | }); |
| 341 | |
| 342 | const target_def_arg = switch (target.cpu.arch) { |
| 343 | .i386 => "-DDEF_I386", |
| 344 | .x86_64 => "-DDEF_X64", |
| 345 | .arm, .armeb => switch (target.cpu.arch.ptrBitWidth()) { |
| 346 | 32 => "-DDEF_ARM32", |
| 347 | 64 => "-DDEF_ARM64", |
| 348 | else => unreachable, |
| 349 | }, |
| 350 | else => unreachable, |
| 351 | }; |
| 352 | |
| 353 | const args = [_][]const u8{ |
| 354 | self_exe_path, |
| 355 | "clang", |
| 356 | "-x", |
| 357 | "c", |
| 358 | def_file_path, |
| 359 | "-Wp,-w", |
| 360 | "-undef", |
| 361 | "-P", |
| 362 | "-I", |
| 363 | try comp.zig_lib_directory.join(arena, &[_][]const u8{ "libc", "mingw", "def-include" }), |
| 364 | target_def_arg, |
| 365 | "-E", |
| 366 | "-o", |
| 367 | def_final_path, |
| 368 | }; |
| 369 | |
| 370 | if (comp.verbose_cc) { |
| 371 | Compilation.dump_argv(&args); |
| 372 | } |
| 373 | |
| 374 | const child = try std.ChildProcess.init(&args, arena); |
| 375 | defer child.deinit(); |
| 376 | |
| 377 | child.stdin_behavior = .Ignore; |
| 378 | child.stdout_behavior = .Pipe; |
| 379 | child.stderr_behavior = .Pipe; |
| 380 | |
| 381 | try child.spawn(); |
| 382 | |
| 383 | const stdout_reader = child.stdout.?.reader(); |
| 384 | const stderr_reader = child.stderr.?.reader(); |
| 385 | |
| 386 | // TODO https://github.com/ziglang/zig/issues/6343 |
| 387 | const stdout = try stdout_reader.readAllAlloc(arena, std.math.maxInt(u32)); |
| 388 | const stderr = try stderr_reader.readAllAlloc(arena, 10 * 1024 * 1024); |
| 389 | |
| 390 | const term = child.wait() catch |err| { |
| 391 | // TODO surface a proper error here |
| 392 | log.err("unable to spawn {}: {}", .{ args[0], @errorName(err) }); |
| 393 | return error.ClangPreprocessorFailed; |
| 394 | }; |
| 395 | |
| 396 | switch (term) { |
| 397 | .Exited => |code| { |
| 398 | if (code != 0) { |
| 399 | // TODO surface a proper error here |
| 400 | log.err("clang exited with code {d} and stderr: {s}", .{ code, stderr }); |
| 401 | return error.ClangPreprocessorFailed; |
| 402 | } |
| 403 | }, |
| 404 | else => { |
| 405 | // TODO surface a proper error here |
| 406 | log.err("clang terminated unexpectedly with stderr: {}", .{stderr}); |
| 407 | return error.ClangPreprocessorFailed; |
| 408 | }, |
| 409 | } |
| 410 | |
| 411 | const lib_final_path = try comp.global_cache_directory.join(comp.gpa, &[_][]const u8{ |
| 412 | "o", &digest, final_lib_basename, |
| 413 | }); |
| 414 | errdefer comp.gpa.free(lib_final_path); |
| 415 | |
| 416 | const llvm = @import("llvm.zig"); |
| 417 | const arch_type = @import("target.zig").archToLLVM(target.cpu.arch); |
| 418 | const def_final_path_z = try arena.dupeZ(u8, def_final_path); |
| 419 | const lib_final_path_z = try arena.dupeZ(u8, lib_final_path); |
| 420 | if (llvm.WriteImportLibrary(def_final_path_z.ptr, arch_type, lib_final_path_z.ptr, true)) { |
| 421 | // TODO surface a proper error here |
| 422 | log.err("unable to turn {s}.def into {s}.lib", .{ lib_name, lib_name }); |
| 423 | return error.WritingImportLibFailed; |
| 424 | } |
| 425 | |
| 426 | man.writeManifest() catch |err| { |
| 427 | log.warn("failed to write cache manifest for DLL import {s}.lib: {s}", .{ lib_name, @errorName(err) }); |
| 428 | }; |
| 429 | |
| 430 | try comp.crt_files.putNoClobber(comp.gpa, final_lib_basename, .{ |
| 431 | .full_object_path = lib_final_path, |
| 432 | .lock = man.toOwnedLock(), |
| 433 | }); |
| 434 | } |
| 435 | |
| 436 | /// This function body is verbose but all it does is test 3 different paths and see if a .def file exists. |
| 437 | fn findDef(comp: *Compilation, allocator: *Allocator, lib_name: []const u8) ![]u8 { |
| 438 | const target = comp.getTarget(); |
| 439 | |
| 440 | const lib_path = switch (target.cpu.arch) { |
| 441 | .i386 => "lib32", |
| 442 | .x86_64 => "lib64", |
| 443 | .arm, .armeb => switch (target.cpu.arch.ptrBitWidth()) { |
| 444 | 32 => "libarm32", |
| 445 | 64 => "libarm64", |
| 446 | else => unreachable, |
| 447 | }, |
| 448 | else => unreachable, |
| 449 | }; |
| 450 | |
| 451 | var override_path = std.ArrayList(u8).init(allocator); |
| 452 | defer override_path.deinit(); |
| 453 | |
| 454 | const s = path.sep_str; |
| 455 | |
| 456 | { |
| 457 | // Try the archtecture-specific path first. |
| 458 | const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "{s}" ++ s ++ "{s}.def"; |
| 459 | if (comp.zig_lib_directory.path) |p| { |
| 460 | try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_path, lib_name }); |
| 461 | } else { |
| 462 | try override_path.writer().print(fmt_path, .{ lib_path, lib_name }); |
| 463 | } |
| 464 | if (std.fs.cwd().access(override_path.items, .{})) |_| { |
| 465 | return override_path.toOwnedSlice(); |
| 466 | } else |err| switch (err) { |
| 467 | error.FileNotFound => {}, |
| 468 | else => |e| return e, |
| 469 | } |
| 470 | } |
| 471 | |
| 472 | { |
| 473 | // Try the generic version. |
| 474 | override_path.shrinkRetainingCapacity(0); |
| 475 | const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def"; |
| 476 | if (comp.zig_lib_directory.path) |p| { |
| 477 | try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name }); |
| 478 | } else { |
| 479 | try override_path.writer().print(fmt_path, .{lib_name}); |
| 480 | } |
| 481 | if (std.fs.cwd().access(override_path.items, .{})) |_| { |
| 482 | return override_path.toOwnedSlice(); |
| 483 | } else |err| switch (err) { |
| 484 | error.FileNotFound => {}, |
| 485 | else => |e| return e, |
| 486 | } |
| 487 | } |
| 488 | |
| 489 | { |
| 490 | // Try the generic version and preprocess it. |
| 491 | override_path.shrinkRetainingCapacity(0); |
| 492 | const fmt_path = "libc" ++ s ++ "mingw" ++ s ++ "lib-common" ++ s ++ "{s}.def.in"; |
| 493 | if (comp.zig_lib_directory.path) |p| { |
| 494 | try override_path.writer().print("{s}" ++ s ++ fmt_path, .{ p, lib_name }); |
| 495 | } else { |
| 496 | try override_path.writer().print(fmt_path, .{lib_name}); |
| 497 | } |
| 498 | if (std.fs.cwd().access(override_path.items, .{})) |_| { |
| 499 | return override_path.toOwnedSlice(); |
| 500 | } else |err| switch (err) { |
| 501 | error.FileNotFound => {}, |
| 502 | else => |e| return e, |
| 503 | } |
| 504 | } |
| 505 | |
| 506 | return error.FileNotFound; |
| 507 | } |
| 508 | |
| 280 | 509 | const mingw32_lib_deps = [_][]const u8{ |
| 281 | 510 | "crt0_c.c", |
| 282 | 511 | "dll_argv.c", |