aboutsummaryrefslogtreecommitdiff
path: root/tools/gen_macos_headers_c.zig
blob: 95880fe3424b1be2949ba05de16a83918c8b2ee1 (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
const std = @import("std");
const Io = std.Io;
const Dir = std.Io.Dir;
const assert = std.debug.assert;
const info = std.log.info;
const fatal = std.process.fatal;
const Allocator = std.mem.Allocator;

var general_purpose_allocator = std.heap.GeneralPurposeAllocator(.{}){};
const gpa = general_purpose_allocator.allocator();

const usage =
    \\gen_macos_headers_c [dir]
    \\
    \\General Options:
    \\-h, --help                    Print this help and exit
;

pub fn main() anyerror!void {
    var arena_allocator = std.heap.ArenaAllocator.init(gpa);
    defer arena_allocator.deinit();
    const arena = arena_allocator.allocator();

    var threaded: Io.Threaded = .init(gpa, .{});
    defer threaded.deinit();
    const io = threaded.io();

    const args = try std.process.argsAlloc(arena);
    if (args.len == 1) fatal("no command or option specified", .{});

    var positionals = std.array_list.Managed([]const u8).init(arena);

    for (args[1..]) |arg| {
        if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) {
            return info(usage, .{});
        } else try positionals.append(arg);
    }

    if (positionals.items.len != 1) fatal("expected one positional argument: [dir]", .{});

    var dir = try Io.Dir.cwd().openDir(io, positionals.items[0], .{ .follow_symlinks = false });
    defer dir.close(io);
    var paths = std.array_list.Managed([]const u8).init(arena);
    try findHeaders(arena, io, dir, "", &paths);

    const SortFn = struct {
        pub fn lessThan(ctx: void, lhs: []const u8, rhs: []const u8) bool {
            _ = ctx;
            return std.mem.lessThan(u8, lhs, rhs);
        }
    };

    std.mem.sort([]const u8, paths.items, {}, SortFn.lessThan);

    var buffer: [2000]u8 = undefined;
    var stdout_writer = Io.File.stdout().writerStreaming(io, &buffer);
    const w = &stdout_writer.interface;
    try w.writeAll("#define _XOPEN_SOURCE\n");
    for (paths.items) |path| {
        try w.print("#include <{s}>\n", .{path});
    }
    try w.writeAll(
        \\int main(int argc, char **argv) {
        \\    return 0;
        \\}
    );
    try w.flush();
}

fn findHeaders(
    arena: Allocator,
    io: Io,
    dir: Dir,
    prefix: []const u8,
    paths: *std.array_list.Managed([]const u8),
) anyerror!void {
    var it = dir.iterate();
    while (try it.next(io)) |entry| {
        switch (entry.kind) {
            .directory => {
                const path = try Io.Dir.path.join(arena, &.{ prefix, entry.name });
                var subdir = try dir.openDir(io, entry.name, .{ .follow_symlinks = false });
                defer subdir.close(io);
                try findHeaders(arena, io, subdir, path, paths);
            },
            .file, .sym_link => {
                const ext = Io.Dir.path.extension(entry.name);
                if (!std.mem.eql(u8, ext, ".h")) continue;
                const path = try Io.Dir.path.join(arena, &.{ prefix, entry.name });
                try paths.append(path);
            },
            else => {},
        }
    }
}