diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 21f11e8..deae3d8 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -61,7 +61,6 @@ jobs: matrix: os: [ ubuntu-24.04 ] BIRTH_ZIG_BUILD_TYPE: ${{ fromJSON(needs.generate-config.outputs.BIRTH_ZIG_BUILD_TYPES) }} - ENABLE_LLVM: [ true, false ] SYSTEM_LLVM: [ true, false ] runs-on: ${{ matrix.os }} env: @@ -78,6 +77,6 @@ jobs: run: sudo apt-get update && sudo apt-get install -y llvm-dev liblld-dev - name: Build and test run: | - zig build test --summary all -Denable_llvm=${{matrix.ENABLE_LLVM}} -Doptimize=${{matrix.BIRTH_ZIG_BUILD_TYPE}} -Dsystem_llvm=${{matrix.SYSTEM_LLVM}} - zig build install -Denable_llvm=${{matrix.ENABLE_LLVM}} -Doptimize=${{matrix.BIRTH_ZIG_BUILD_TYPE}} -Dsystem_llvm=${{matrix.SYSTEM_LLVM}} + zig build test --summary all -Doptimize=${{matrix.BIRTH_ZIG_BUILD_TYPE}} -Dsystem_llvm=${{matrix.SYSTEM_LLVM}} + zig build install -Doptimize=${{matrix.BIRTH_ZIG_BUILD_TYPE}} -Dsystem_llvm=${{matrix.SYSTEM_LLVM}} ldd zig-out/bin/bloat-buster diff --git a/build.zig b/build.zig index bc6e1a2..82230fa 100644 --- a/build.zig +++ b/build.zig @@ -65,34 +65,52 @@ const LLVM = struct { module: *std.Build.Module, fn setup(b: *std.Build, path: []const u8) !LLVM { - if (enable_llvm) { - var llvm_libs = std.ArrayList([]const u8).init(b.allocator); - var flags = std.ArrayList([]const u8).init(b.allocator); - const llvm_config_path = if (b.option([]const u8, "llvm_prefix", "LLVM prefix")) |llvm_prefix| blk: { - const full_path = try std.mem.concat(b.allocator, u8, &.{ llvm_prefix, "/bin/llvm-config" }); - const f = std.fs.cwd().openFile(full_path, .{}) catch return error.llvm_not_found; - f.close(); - break :blk full_path; - } else if (system_llvm) executable_find_in_path(b.allocator, "llvm-config", path) orelse return error.llvm_not_found else blk: { - const home_env = switch (@import("builtin").os.tag) { - .windows => "USERPROFILE", - else => "HOME", - }; - const home_path = env.get(home_env) orelse unreachable; - const download_dir = try std.mem.concat(b.allocator, u8, &.{ home_path, "/Downloads" }); - std.fs.makeDirAbsolute(download_dir) catch {}; - const llvm_base = try std.mem.concat(b.allocator, u8, &.{ "llvm-", @tagName(target.result.cpu.arch), "-", @tagName(target.result.os.tag), "-", @tagName(CmakeBuildType.from_zig_build_type(optimize)) }); - const base = try std.mem.concat(b.allocator, u8, &.{ download_dir, "/", llvm_base }); - const full_path = try std.mem.concat(b.allocator, u8, &.{ base, "/bin/llvm-config" }); + var llvm_libs = std.ArrayList([]const u8).init(b.allocator); + var flags = std.ArrayList([]const u8).init(b.allocator); + const llvm_config_path = if (b.option([]const u8, "llvm_prefix", "LLVM prefix")) |llvm_prefix| blk: { + const full_path = try std.mem.concat(b.allocator, u8, &.{ llvm_prefix, "/bin/llvm-config" }); + const f = std.fs.cwd().openFile(full_path, .{}) catch return error.llvm_not_found; + f.close(); + break :blk full_path; + } else if (system_llvm) executable_find_in_path(b.allocator, "llvm-config", path) orelse return error.llvm_not_found else blk: { + const home_env = switch (@import("builtin").os.tag) { + .windows => "USERPROFILE", + else => "HOME", + }; + const home_path = env.get(home_env) orelse unreachable; + const download_dir = try std.mem.concat(b.allocator, u8, &.{ home_path, "/Downloads" }); + std.fs.makeDirAbsolute(download_dir) catch {}; + const llvm_base = try std.mem.concat(b.allocator, u8, &.{ "llvm-", @tagName(target.result.cpu.arch), "-", @tagName(target.result.os.tag), "-", @tagName(CmakeBuildType.from_zig_build_type(optimize)) }); + const base = try std.mem.concat(b.allocator, u8, &.{ download_dir, "/", llvm_base }); + const full_path = try std.mem.concat(b.allocator, u8, &.{ base, "/bin/llvm-config" }); - const f = std.fs.cwd().openFile(full_path, .{}) catch { - const url = try std.mem.concat(b.allocator, u8, &.{ "https://github.com/birth-software/llvm/releases/download/v19.1.7/", llvm_base, ".7z" }); - var result = try std.process.Child.run(.{ + const f = std.fs.cwd().openFile(full_path, .{}) catch { + const url = try std.mem.concat(b.allocator, u8, &.{ "https://github.com/birth-software/llvm/releases/download/v19.1.7/", llvm_base, ".7z" }); + var result = try std.process.Child.run(.{ + .allocator = b.allocator, + .argv = &.{ "wget", "-P", download_dir, url }, + .max_output_bytes = std.math.maxInt(usize), + }); + var success = false; + switch (result.term) { + .Exited => |exit_code| { + success = exit_code == 0; + }, + else => {}, + } + + if (!success) { + std.debug.print("{s}\n{s}\n", .{ result.stdout, result.stderr }); + } + + if (success) { + const file_7z = try std.mem.concat(b.allocator, u8, &.{ base, ".7z" }); + result = try std.process.Child.run(.{ .allocator = b.allocator, - .argv = &.{ "wget", "-P", download_dir, url }, + .argv = &.{ "7z", "x", try std.mem.concat(b.allocator, u8, &.{ "-o", download_dir }), file_7z }, .max_output_bytes = std.math.maxInt(usize), }); - var success = false; + success = false; switch (result.term) { .Exited => |exit_code| { success = exit_code == 0; @@ -104,163 +122,141 @@ const LLVM = struct { std.debug.print("{s}\n{s}\n", .{ result.stdout, result.stderr }); } - if (success) { - const file_7z = try std.mem.concat(b.allocator, u8, &.{ base, ".7z" }); - result = try std.process.Child.run(.{ - .allocator = b.allocator, - .argv = &.{ "7z", "x", try std.mem.concat(b.allocator, u8, &.{ "-o", download_dir }), file_7z }, - .max_output_bytes = std.math.maxInt(usize), - }); - success = false; - switch (result.term) { - .Exited => |exit_code| { - success = exit_code == 0; - }, - else => {}, - } - - if (!success) { - std.debug.print("{s}\n{s}\n", .{ result.stdout, result.stderr }); - } - - break :blk full_path; - } - - return error.llvm_not_found; - }; - - f.close(); - break :blk full_path; - }; - const llvm_components_result = try run_process_and_capture_stdout(b, &.{ llvm_config_path, "--components" }); - var it = std.mem.splitScalar(u8, llvm_components_result, ' '); - var args = std.ArrayList([]const u8).init(b.allocator); - try args.append(llvm_config_path); - try args.append("--libs"); - while (it.next()) |component| { - try args.append(std.mem.trimRight(u8, component, "\n")); - } - const llvm_libs_result = try run_process_and_capture_stdout(b, args.items); - it = std.mem.splitScalar(u8, llvm_libs_result, ' '); - - while (it.next()) |lib| { - const llvm_lib = std.mem.trimLeft(u8, std.mem.trimRight(u8, lib, "\n"), "-l"); - try llvm_libs.append(llvm_lib); - } - - const llvm_cxx_flags_result = try run_process_and_capture_stdout(b, &.{ llvm_config_path, "--cxxflags" }); - it = std.mem.splitScalar(u8, llvm_cxx_flags_result, ' '); - while (it.next()) |flag| { - const llvm_cxx_flag = std.mem.trimRight(u8, flag, "\n"); - try flags.append(llvm_cxx_flag); - } - - const llvm_lib_dir = std.mem.trimRight(u8, try run_process_and_capture_stdout(b, &.{ llvm_config_path, "--libdir" }), "\n"); - - if (optimize != .ReleaseSmall) { - try flags.append("-g"); - } - - try flags.append("-fno-rtti"); - - const llvm = b.createModule(.{ - .target = target, - .optimize = optimize, - }); - - llvm.addLibraryPath(.{ .cwd_relative = llvm_lib_dir }); - - const a = std.fs.cwd().openDir("/usr/lib/x86_64-linux-gnu/", .{}); - if (a) |_| { - var dir = a catch unreachable; - dir.close(); - llvm.addLibraryPath(.{ .cwd_relative = "/usr/lib/x86_64-linux-gnu/" }); - } else |err| { - err catch {}; - } - - llvm.addCSourceFiles(.{ - .files = &.{"src/llvm.cpp"}, - .flags = flags.items, - }); - - var dir = try std.fs.cwd().openDir("/usr/include/c++", .{ - .iterate = true, - }); - var iterator = dir.iterate(); - const gcc_version = while (try iterator.next()) |entry| { - if (entry.kind == .directory) { - break entry.name; + break :blk full_path; } - } else return error.include_cpp_dir_not_found; - dir.close(); - const general_cpp_include_dir = try std.mem.concat(b.allocator, u8, &.{ "/usr/include/c++/", gcc_version }); - llvm.addIncludePath(.{ .cwd_relative = general_cpp_include_dir }); - { - const arch_cpp_include_dir = try std.mem.concat(b.allocator, u8, &.{ general_cpp_include_dir, "/x86_64-pc-linux-gnu" }); - const d2 = std.fs.cwd().openDir(arch_cpp_include_dir, .{}); - if (d2) |_| { - var d = d2 catch unreachable; - d.close(); - llvm.addIncludePath(.{ .cwd_relative = arch_cpp_include_dir }); - } else |err| err catch {}; - } - - { - const arch_cpp_include_dir = try std.mem.concat(b.allocator, u8, &.{ "/usr/include/x86_64-linux-gnu/c++/", gcc_version }); - const d2 = std.fs.cwd().openDir(arch_cpp_include_dir, .{}); - if (d2) |_| { - var d = d2 catch unreachable; - d.close(); - llvm.addIncludePath(.{ .cwd_relative = arch_cpp_include_dir }); - } else |err| err catch {}; - } - - var found_libcpp = false; - - if (std.fs.cwd().openFile("/usr/lib/libstdc++.so.6", .{})) |file| { - file.close(); - found_libcpp = true; - llvm.addObjectFile(.{ .cwd_relative = "/usr/lib/libstdc++.so.6" }); - } else |err| { - err catch {}; - } - - if (std.fs.cwd().openFile("/usr/lib/x86_64-linux-gnu/libstdc++.so.6", .{})) |file| { - file.close(); - found_libcpp = true; - llvm.addObjectFile(.{ .cwd_relative = "/usr/lib/x86_64-linux-gnu/libstdc++.so.6" }); - } else |err| { - err catch {}; - } - - if (!found_libcpp) { - return error.libcpp_not_found; - } - - const needed_libraries: []const []const u8 = &.{ "unwind", "z", "zstd" }; - - const lld_libs: []const []const u8 = &.{ "lldCommon", "lldCOFF", "lldELF", "lldMachO", "lldMinGW", "lldWasm" }; - - for (needed_libraries) |lib| { - llvm.linkSystemLibrary(lib, .{}); - } - - for (llvm_libs.items) |lib| { - llvm.linkSystemLibrary(lib, .{}); - } - - for (lld_libs) |lib| { - llvm.linkSystemLibrary(lib, .{}); - } - - return LLVM{ - .module = llvm, + return error.llvm_not_found; }; - } else { - return undefined; + + f.close(); + break :blk full_path; + }; + const llvm_components_result = try run_process_and_capture_stdout(b, &.{ llvm_config_path, "--components" }); + var it = std.mem.splitScalar(u8, llvm_components_result, ' '); + var args = std.ArrayList([]const u8).init(b.allocator); + try args.append(llvm_config_path); + try args.append("--libs"); + while (it.next()) |component| { + try args.append(std.mem.trimRight(u8, component, "\n")); } + const llvm_libs_result = try run_process_and_capture_stdout(b, args.items); + it = std.mem.splitScalar(u8, llvm_libs_result, ' '); + + while (it.next()) |lib| { + const llvm_lib = std.mem.trimLeft(u8, std.mem.trimRight(u8, lib, "\n"), "-l"); + try llvm_libs.append(llvm_lib); + } + + const llvm_cxx_flags_result = try run_process_and_capture_stdout(b, &.{ llvm_config_path, "--cxxflags" }); + it = std.mem.splitScalar(u8, llvm_cxx_flags_result, ' '); + while (it.next()) |flag| { + const llvm_cxx_flag = std.mem.trimRight(u8, flag, "\n"); + try flags.append(llvm_cxx_flag); + } + + const llvm_lib_dir = std.mem.trimRight(u8, try run_process_and_capture_stdout(b, &.{ llvm_config_path, "--libdir" }), "\n"); + + if (optimize != .ReleaseSmall) { + try flags.append("-g"); + } + + try flags.append("-fno-rtti"); + + const llvm = b.createModule(.{ + .target = target, + .optimize = optimize, + }); + + llvm.addLibraryPath(.{ .cwd_relative = llvm_lib_dir }); + + const a = std.fs.cwd().openDir("/usr/lib/x86_64-linux-gnu/", .{}); + if (a) |_| { + var dir = a catch unreachable; + dir.close(); + llvm.addLibraryPath(.{ .cwd_relative = "/usr/lib/x86_64-linux-gnu/" }); + } else |err| { + err catch {}; + } + + llvm.addCSourceFiles(.{ + .files = &.{"src/llvm.cpp"}, + .flags = flags.items, + }); + + var dir = try std.fs.cwd().openDir("/usr/include/c++", .{ + .iterate = true, + }); + var iterator = dir.iterate(); + const gcc_version = while (try iterator.next()) |entry| { + if (entry.kind == .directory) { + break entry.name; + } + } else return error.include_cpp_dir_not_found; + dir.close(); + const general_cpp_include_dir = try std.mem.concat(b.allocator, u8, &.{ "/usr/include/c++/", gcc_version }); + llvm.addIncludePath(.{ .cwd_relative = general_cpp_include_dir }); + + { + const arch_cpp_include_dir = try std.mem.concat(b.allocator, u8, &.{ general_cpp_include_dir, "/x86_64-pc-linux-gnu" }); + const d2 = std.fs.cwd().openDir(arch_cpp_include_dir, .{}); + if (d2) |_| { + var d = d2 catch unreachable; + d.close(); + llvm.addIncludePath(.{ .cwd_relative = arch_cpp_include_dir }); + } else |err| err catch {}; + } + + { + const arch_cpp_include_dir = try std.mem.concat(b.allocator, u8, &.{ "/usr/include/x86_64-linux-gnu/c++/", gcc_version }); + const d2 = std.fs.cwd().openDir(arch_cpp_include_dir, .{}); + if (d2) |_| { + var d = d2 catch unreachable; + d.close(); + llvm.addIncludePath(.{ .cwd_relative = arch_cpp_include_dir }); + } else |err| err catch {}; + } + + var found_libcpp = false; + + if (std.fs.cwd().openFile("/usr/lib/libstdc++.so.6", .{})) |file| { + file.close(); + found_libcpp = true; + llvm.addObjectFile(.{ .cwd_relative = "/usr/lib/libstdc++.so.6" }); + } else |err| { + err catch {}; + } + + if (std.fs.cwd().openFile("/usr/lib/x86_64-linux-gnu/libstdc++.so.6", .{})) |file| { + file.close(); + found_libcpp = true; + llvm.addObjectFile(.{ .cwd_relative = "/usr/lib/x86_64-linux-gnu/libstdc++.so.6" }); + } else |err| { + err catch {}; + } + + if (!found_libcpp) { + return error.libcpp_not_found; + } + + const needed_libraries: []const []const u8 = &.{ "unwind", "z", "zstd" }; + + const lld_libs: []const []const u8 = &.{ "lldCommon", "lldCOFF", "lldELF", "lldMachO", "lldMinGW", "lldWasm" }; + + for (needed_libraries) |lib| { + llvm.linkSystemLibrary(lib, .{}); + } + + for (llvm_libs.items) |lib| { + llvm.linkSystemLibrary(lib, .{}); + } + + for (lld_libs) |lib| { + llvm.linkSystemLibrary(lib, .{}); + } + + return LLVM{ + .module = llvm, + }; } fn link(llvm: LLVM, compile: *std.Build.Step.Compile) void { @@ -283,7 +279,6 @@ fn debug_binary(b: *std.Build, exe: *std.Build.Step.Compile) *std.Build.Step.Run return run_step; } -var enable_llvm: bool = undefined; var system_llvm: bool = undefined; var target: std.Build.ResolvedTarget = undefined; var optimize: std.builtin.OptimizeMode = undefined; @@ -293,12 +288,10 @@ pub fn build(b: *std.Build) !void { env = try std.process.getEnvMap(b.allocator); target = b.standardTargetOptions(.{}); optimize = b.standardOptimizeOption(.{}); - enable_llvm = b.option(bool, "enable_llvm", "Enable LLVM") orelse false; system_llvm = b.option(bool, "system_llvm", "Link against system LLVM libraries") orelse true; const path = env.get("PATH") orelse unreachable; const configuration = b.addOptions(); - configuration.addOption(bool, "enable_llvm", enable_llvm); const exe_mod = b.createModule(.{ .root_source_file = b.path("src/main.zig"), @@ -315,9 +308,7 @@ pub fn build(b: *std.Build) !void { }); exe.linkLibC(); - if (enable_llvm) { - llvm.link(exe); - } + llvm.link(exe); b.installArtifact(exe); @@ -338,9 +329,7 @@ pub fn build(b: *std.Build) !void { }); exe_unit_tests.linkLibC(); - if (enable_llvm) { - llvm.link(exe); - } + llvm.link(exe); const run_exe_unit_tests = b.addRunArtifact(exe_unit_tests); diff --git a/src/LLVM.zig b/src/LLVM.zig index 4dec75e..f3335e3 100644 --- a/src/LLVM.zig +++ b/src/LLVM.zig @@ -760,9 +760,16 @@ const LldArgvBuilder = struct { }; test "experiment" { - if (!configuration.enable_llvm) { - return error.SkipZigTest; - } + const std = @import("std"); + var tmp_dir = std.testing.tmpDir(.{}); + defer tmp_dir.cleanup(); + const allocator = std.testing.allocator; + + const object_file_path = try std.mem.joinZ(allocator, "/", &.{ ".zig-cache", "tmp", &tmp_dir.sub_path, "foo.o" }); + // Zig stuf... sigh + defer allocator.free(object_file_path); + const executable_file_path = try std.mem.joinZ(allocator, "/", &.{ ".zig-cache", "tmp", &tmp_dir.sub_path, "foo" }); + defer allocator.free(executable_file_path); lib.GlobalState.initialize(); initialize_all(); @@ -814,9 +821,8 @@ test "experiment" { module.set_target(target_machine); module.run_optimization_pipeline(target_machine, OptimizationPipelineOptions.default(.{ .optimization_level = .O3, .debug_info = 1 })); - const object_path = "foo.o"; const result = module.run_code_generation_pipeline(target_machine, CodeGenerationPipelineOptions{ - .output_file_path = String.from_slice(object_path), + .output_file_path = String.from_slice(object_file_path), .output_dwarf_file_path = .{}, .flags = .{ .code_generation_file_type = .object_file, @@ -826,7 +832,11 @@ test "experiment" { }); switch (result) { .success => {}, - .failed_to_create_file => return error.failed_to_create_file, + .failed_to_create_file => { + lib.print_string_stderr(object_file_path); + lib.print_string_stderr("\n"); + return error.failed_to_create_file; + }, .failed_to_add_emit_passes => return error.failed_to_add_emit_passes, } @@ -834,8 +844,8 @@ test "experiment" { arg_builder.add("ld.lld"); arg_builder.add("--error-limit=0"); arg_builder.add("-o"); - arg_builder.add("foo"); - const objects: []const [*:0]const u8 = &.{object_path}; + arg_builder.add(executable_file_path); + const objects: []const [*:0]const u8 = &.{object_file_path}; for (objects) |object| { arg_builder.add(object); }