aboutsummaryrefslogtreecommitdiff
path: root/lib/std/Build/InstallRawStep.zig
blob: 014c44f28785135f0751adc28626570f69db8e4e (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
//! TODO: Rename this to ObjCopyStep now that it invokes the `zig objcopy`
//! subcommand rather than containing an implementation directly.

const std = @import("std");
const InstallRawStep = @This();

const Allocator = std.mem.Allocator;
const ArenaAllocator = std.heap.ArenaAllocator;
const ArrayListUnmanaged = std.ArrayListUnmanaged;
const File = std.fs.File;
const InstallDir = std.Build.InstallDir;
const CompileStep = std.Build.CompileStep;
const Step = std.Build.Step;
const elf = std.elf;
const fs = std.fs;
const io = std.io;
const sort = std.sort;

pub const base_id = .install_raw;

pub const RawFormat = enum {
    bin,
    hex,
};

step: Step,
builder: *std.Build,
artifact: *CompileStep,
dest_dir: InstallDir,
dest_filename: []const u8,
options: CreateOptions,
output_file: std.Build.GeneratedFile,

pub const CreateOptions = struct {
    format: ?RawFormat = null,
    dest_dir: ?InstallDir = null,
    only_section: ?[]const u8 = null,
    pad_to: ?u64 = null,
};

pub fn create(
    builder: *std.Build,
    artifact: *CompileStep,
    dest_filename: []const u8,
    options: CreateOptions,
) *InstallRawStep {
    const self = builder.allocator.create(InstallRawStep) catch @panic("OOM");
    self.* = InstallRawStep{
        .step = Step.init(.install_raw, builder.fmt("install raw binary {s}", .{artifact.step.name}), builder.allocator, make),
        .builder = builder,
        .artifact = artifact,
        .dest_dir = if (options.dest_dir) |d| d else switch (artifact.kind) {
            .obj => unreachable,
            .@"test" => unreachable,
            .exe, .test_exe => .bin,
            .lib => unreachable,
        },
        .dest_filename = dest_filename,
        .options = options,
        .output_file = std.Build.GeneratedFile{ .step = &self.step },
    };
    self.step.dependOn(&artifact.step);

    builder.pushInstalledFile(self.dest_dir, dest_filename);
    return self;
}

pub fn getOutputSource(self: *const InstallRawStep) std.Build.FileSource {
    return std.Build.FileSource{ .generated = &self.output_file };
}

fn make(step: *Step) !void {
    const self = @fieldParentPtr(InstallRawStep, "step", step);
    const b = self.builder;

    if (self.artifact.target.getObjectFormat() != .elf) {
        std.debug.print("InstallRawStep only works with ELF format.\n", .{});
        return error.InvalidObjectFormat;
    }

    const full_src_path = self.artifact.getOutputSource().getPath(b);
    const full_dest_path = b.getInstallPath(self.dest_dir, self.dest_filename);
    self.output_file.path = full_dest_path;

    try fs.cwd().makePath(b.getInstallPath(self.dest_dir, ""));

    var argv_list = std.ArrayList([]const u8).init(b.allocator);
    try argv_list.appendSlice(&.{ b.zig_exe, "objcopy" });

    if (self.options.only_section) |only_section| {
        try argv_list.appendSlice(&.{ "-j", only_section });
    }
    if (self.options.pad_to) |pad_to| {
        try argv_list.appendSlice(&.{
            "--pad-to",
            b.fmt("{d}", .{pad_to}),
        });
    }
    if (self.options.format) |format| switch (format) {
        .bin => try argv_list.appendSlice(&.{ "-O", "binary" }),
        .hex => try argv_list.appendSlice(&.{ "-O", "hex" }),
    };

    try argv_list.appendSlice(&.{ full_src_path, full_dest_path });
    _ = try self.builder.execFromStep(argv_list.items, &self.step);
}

test {
    std.testing.refAllDecls(InstallRawStep);
}