aboutsummaryrefslogtreecommitdiff
path: root/lib/std/build/TranslateCStep.zig
blob: 1f9bee463c7497cd93b3ff3ffd04806793c4576c (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
111
112
const std = @import("../std.zig");
const build = std.build;
const Step = build.Step;
const Builder = build.Builder;
const LibExeObjStep = build.LibExeObjStep;
const CheckFileStep = build.CheckFileStep;
const fs = std.fs;
const mem = std.mem;
const CrossTarget = std.zig.CrossTarget;

const TranslateCStep = @This();

pub const base_id = .translate_c;

step: Step,
builder: *Builder,
source: build.FileSource,
include_dirs: std.ArrayList([]const u8),
c_macros: std.ArrayList([]const u8),
output_dir: ?[]const u8,
out_basename: []const u8,
target: CrossTarget = CrossTarget{},
output_file: build.GeneratedFile,

pub fn create(builder: *Builder, source: build.FileSource) *TranslateCStep {
    const self = builder.allocator.create(TranslateCStep) catch unreachable;
    self.* = TranslateCStep{
        .step = Step.init(.translate_c, "translate-c", builder.allocator, make),
        .builder = builder,
        .source = source,
        .include_dirs = std.ArrayList([]const u8).init(builder.allocator),
        .c_macros = std.ArrayList([]const u8).init(builder.allocator),
        .output_dir = null,
        .out_basename = undefined,
        .output_file = build.GeneratedFile{ .step = &self.step },
    };
    source.addStepDependencies(&self.step);
    return self;
}

pub fn setTarget(self: *TranslateCStep, target: CrossTarget) void {
    self.target = target;
}

/// Creates a step to build an executable from the translated source.
pub fn addExecutable(self: *TranslateCStep) *LibExeObjStep {
    return self.builder.addExecutableSource("translated_c", build.FileSource{ .generated = &self.output_file });
}

pub fn addIncludeDir(self: *TranslateCStep, include_dir: []const u8) void {
    self.include_dirs.append(self.builder.dupePath(include_dir)) catch unreachable;
}

pub fn addCheckFile(self: *TranslateCStep, expected_matches: []const []const u8) *CheckFileStep {
    return CheckFileStep.create(self.builder, .{ .generated = &self.output_file }, self.builder.dupeStrings(expected_matches));
}

/// If the value is omitted, it is set to 1.
/// `name` and `value` need not live longer than the function call.
pub fn defineCMacro(self: *TranslateCStep, name: []const u8, value: ?[]const u8) void {
    const macro = build.constructCMacro(self.builder.allocator, name, value);
    self.c_macros.append(macro) catch unreachable;
}

/// name_and_value looks like [name]=[value]. If the value is omitted, it is set to 1.
pub fn defineCMacroRaw(self: *TranslateCStep, name_and_value: []const u8) void {
    self.c_macros.append(self.builder.dupe(name_and_value)) catch unreachable;
}

fn make(step: *Step) !void {
    const self = @fieldParentPtr(TranslateCStep, "step", step);

    var argv_list = std.ArrayList([]const u8).init(self.builder.allocator);
    try argv_list.append(self.builder.zig_exe);
    try argv_list.append("translate-c");
    try argv_list.append("-lc");

    try argv_list.append("--enable-cache");

    if (!self.target.isNative()) {
        try argv_list.append("-target");
        try argv_list.append(try self.target.zigTriple(self.builder.allocator));
    }

    for (self.include_dirs.items) |include_dir| {
        try argv_list.append("-I");
        try argv_list.append(include_dir);
    }

    for (self.c_macros.items) |c_macro| {
        try argv_list.append("-D");
        try argv_list.append(c_macro);
    }

    try argv_list.append(self.source.getPath(self.builder));

    const output_path_nl = try self.builder.execFromStep(argv_list.items, &self.step);
    const output_path = mem.trimRight(u8, output_path_nl, "\r\n");

    self.out_basename = fs.path.basename(output_path);
    if (self.output_dir) |output_dir| {
        const full_dest = try fs.path.join(self.builder.allocator, &[_][]const u8{ output_dir, self.out_basename });
        try self.builder.updateFile(output_path, full_dest);
    } else {
        self.output_dir = fs.path.dirname(output_path).?;
    }

    self.output_file.path = fs.path.join(
        self.builder.allocator,
        &[_][]const u8{ self.output_dir.?, self.out_basename },
    ) catch unreachable;
}