zig/src/link/tapi.zig

199 lines
5.9 KiB
Zig

const std = @import("std");
const fs = std.fs;
const mem = std.mem;
const log = std.log.scoped(.tapi);
const yaml = @import("tapi/yaml.zig");
const Allocator = mem.Allocator;
const Yaml = yaml.Yaml;
const VersionField = union(enum) {
string: []const u8,
float: f64,
int: u64,
};
pub const TbdV3 = struct {
archs: []const []const u8,
uuids: []const []const u8,
platform: []const u8,
install_name: []const u8,
current_version: ?VersionField,
compatibility_version: ?VersionField,
objc_constraint: ?[]const u8,
parent_umbrella: ?[]const u8,
exports: ?[]const struct {
archs: []const []const u8,
allowable_clients: ?[]const []const u8,
re_exports: ?[]const []const u8,
symbols: ?[]const []const u8,
weak_symbols: ?[]const []const u8,
objc_classes: ?[]const []const u8,
objc_ivars: ?[]const []const u8,
objc_eh_types: ?[]const []const u8,
},
};
pub const TbdV4 = struct {
tbd_version: u3,
targets: []const []const u8,
uuids: ?[]const struct {
target: []const u8,
value: []const u8,
},
install_name: []const u8,
current_version: ?VersionField,
compatibility_version: ?VersionField,
reexported_libraries: ?[]const struct {
targets: []const []const u8,
libraries: []const []const u8,
},
parent_umbrella: ?[]const struct {
targets: []const []const u8,
umbrella: []const u8,
},
exports: ?[]const struct {
targets: []const []const u8,
symbols: ?[]const []const u8,
weak_symbols: ?[]const []const u8,
objc_classes: ?[]const []const u8,
objc_ivars: ?[]const []const u8,
objc_eh_types: ?[]const []const u8,
},
reexports: ?[]const struct {
targets: []const []const u8,
symbols: ?[]const []const u8,
weak_symbols: ?[]const []const u8,
objc_classes: ?[]const []const u8,
objc_ivars: ?[]const []const u8,
objc_eh_types: ?[]const []const u8,
},
allowable_clients: ?[]const struct {
targets: []const []const u8,
clients: []const []const u8,
},
objc_classes: ?[]const []const u8,
objc_ivars: ?[]const []const u8,
objc_eh_types: ?[]const []const u8,
};
pub const Tbd = union(enum) {
v3: TbdV3,
v4: TbdV4,
/// Caller owns memory.
pub fn targets(self: Tbd, gpa: Allocator) error{OutOfMemory}![]const []const u8 {
var out = std.ArrayList([]const u8).init(gpa);
defer out.deinit();
switch (self) {
.v3 => |v3| {
try out.ensureTotalCapacityPrecise(v3.archs.len);
for (v3.archs) |arch| {
const target = try std.fmt.allocPrint(gpa, "{s}-{s}", .{ arch, v3.platform });
out.appendAssumeCapacity(target);
}
},
.v4 => |v4| {
try out.ensureTotalCapacityPrecise(v4.targets.len);
for (v4.targets) |t| {
out.appendAssumeCapacity(try gpa.dupe(u8, t));
}
},
}
return out.toOwnedSlice();
}
pub fn currentVersion(self: Tbd) ?VersionField {
return switch (self) {
.v3 => |v3| v3.current_version,
.v4 => |v4| v4.current_version,
};
}
pub fn compatibilityVersion(self: Tbd) ?VersionField {
return switch (self) {
.v3 => |v3| v3.compatibility_version,
.v4 => |v4| v4.compatibility_version,
};
}
pub fn installName(self: Tbd) []const u8 {
return switch (self) {
.v3 => |v3| v3.install_name,
.v4 => |v4| v4.install_name,
};
}
};
pub const TapiError = error{
NotLibStub,
FileTooBig,
} || yaml.YamlError || std.fs.File.ReadError;
pub const LibStub = struct {
/// Underlying memory for stub's contents.
yaml: Yaml,
/// Typed contents of the tbd file.
inner: []Tbd,
pub fn loadFromFile(allocator: Allocator, file: fs.File) TapiError!LibStub {
const source = try file.readToEndAlloc(allocator, std.math.maxInt(u32));
defer allocator.free(source);
var lib_stub = LibStub{
.yaml = try Yaml.load(allocator, source),
.inner = undefined,
};
// TODO revisit this logic in the hope of simplifying it.
lib_stub.inner = blk: {
err: {
log.debug("trying to parse as []TbdV4", .{});
const inner = lib_stub.yaml.parse([]TbdV4) catch break :err;
var out = try lib_stub.yaml.arena.allocator().alloc(Tbd, inner.len);
for (inner, 0..) |doc, i| {
out[i] = .{ .v4 = doc };
}
break :blk out;
}
err: {
log.debug("trying to parse as TbdV4", .{});
const inner = lib_stub.yaml.parse(TbdV4) catch break :err;
var out = try lib_stub.yaml.arena.allocator().alloc(Tbd, 1);
out[0] = .{ .v4 = inner };
break :blk out;
}
err: {
log.debug("trying to parse as []TbdV3", .{});
const inner = lib_stub.yaml.parse([]TbdV3) catch break :err;
var out = try lib_stub.yaml.arena.allocator().alloc(Tbd, inner.len);
for (inner, 0..) |doc, i| {
out[i] = .{ .v3 = doc };
}
break :blk out;
}
err: {
log.debug("trying to parse as TbdV3", .{});
const inner = lib_stub.yaml.parse(TbdV3) catch break :err;
var out = try lib_stub.yaml.arena.allocator().alloc(Tbd, 1);
out[0] = .{ .v3 = inner };
break :blk out;
}
return error.NotLibStub;
};
return lib_stub;
}
pub fn deinit(self: *LibStub) void {
self.yaml.deinit();
}
};