mirror of
https://github.com/ziglang/zig.git
synced 2024-11-15 16:45:27 +00:00
zld: permit system static libs
This commits permits passing in static archives using the system lib flag `-la`. With this commit, `zig ld` will now look firstly for a dynamic library (which always takes precedence), and will fall back on `liba.a` if the dylib is not found. The static archive is searched for in the system lib search dirs like the dylibs.
This commit is contained in:
parent
f4101c1153
commit
2b0d322ea0
@ -698,8 +698,8 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||
try positionals.append(comp.libcxx_static_lib.?.full_object_path);
|
||||
}
|
||||
|
||||
// Shared libraries.
|
||||
var shared_libs = std.ArrayList([]const u8).init(arena);
|
||||
// Shared and static libraries passed via `-l` flag.
|
||||
var libs = std.ArrayList([]const u8).init(arena);
|
||||
var search_lib_names = std.ArrayList([]const u8).init(arena);
|
||||
|
||||
const system_libs = self.base.options.system_libs.items();
|
||||
@ -708,9 +708,8 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||
// By this time, we depend on these libs being dynamically linked libraries and not static libraries
|
||||
// (the check for that needs to be earlier), but they could be full paths to .dylib files, in which
|
||||
// case we want to avoid prepending "-l".
|
||||
// TODO I think they should go as an input file instead of via shared_libs.
|
||||
if (Compilation.classifyFileExt(link_lib) == .shared_library) {
|
||||
try shared_libs.append(link_lib);
|
||||
try positionals.append(link_lib);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -760,24 +759,29 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||
}
|
||||
}
|
||||
|
||||
// TODO text-based API, or .tbd files.
|
||||
const exts = &[_][]const u8{ "dylib", "a" };
|
||||
|
||||
for (search_lib_names.items) |l_name| {
|
||||
// TODO text-based API, or .tbd files.
|
||||
const l_name_ext = try std.fmt.allocPrint(arena, "lib{s}.dylib", .{l_name});
|
||||
|
||||
var found = false;
|
||||
for (search_lib_dirs.items) |lib_dir| {
|
||||
const full_path = try fs.path.join(arena, &[_][]const u8{ lib_dir, l_name_ext });
|
||||
|
||||
// Check if the dylib file exists.
|
||||
const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound => continue,
|
||||
else => |e| return e,
|
||||
};
|
||||
defer tmp.close();
|
||||
for (exts) |ext| ext: {
|
||||
const l_name_ext = try std.fmt.allocPrint(arena, "lib{s}.{s}", .{ l_name, ext });
|
||||
|
||||
try shared_libs.append(full_path);
|
||||
found = true;
|
||||
break;
|
||||
for (search_lib_dirs.items) |lib_dir| {
|
||||
const full_path = try fs.path.join(arena, &[_][]const u8{ lib_dir, l_name_ext });
|
||||
|
||||
// Check if the dylib file exists.
|
||||
const tmp = fs.cwd().openFile(full_path, .{}) catch |err| switch (err) {
|
||||
error.FileNotFound => continue,
|
||||
else => |e| return e,
|
||||
};
|
||||
defer tmp.close();
|
||||
|
||||
try libs.append(full_path);
|
||||
found = true;
|
||||
break :ext;
|
||||
}
|
||||
}
|
||||
|
||||
if (!found) {
|
||||
@ -835,7 +839,7 @@ fn linkWithLLD(self: *MachO, comp: *Compilation) !void {
|
||||
}
|
||||
|
||||
try zld.link(positionals.items, full_out_path, .{
|
||||
.shared_libs = shared_libs.items,
|
||||
.libs = libs.items,
|
||||
.rpaths = rpaths.items,
|
||||
});
|
||||
|
||||
|
@ -27,14 +27,14 @@ toc: std.StringArrayHashMapUnmanaged(std.ArrayListUnmanaged(u32)) = .{},
|
||||
// `struct ar_hdr', and as many bytes of member file data as its `ar_size'
|
||||
// member indicates, for each member file.
|
||||
/// String that begins an archive file.
|
||||
pub const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";
|
||||
const ARMAG: *const [SARMAG:0]u8 = "!<arch>\n";
|
||||
/// Size of that string.
|
||||
pub const SARMAG: u4 = 8;
|
||||
const SARMAG: u4 = 8;
|
||||
|
||||
/// String in ar_fmag at the end of each header.
|
||||
pub const ARFMAG: *const [2:0]u8 = "`\n";
|
||||
const ARFMAG: *const [2:0]u8 = "`\n";
|
||||
|
||||
pub const ar_hdr = extern struct {
|
||||
const ar_hdr = extern struct {
|
||||
/// Member file name, sometimes / terminated.
|
||||
ar_name: [16]u8,
|
||||
|
||||
@ -60,7 +60,7 @@ pub const ar_hdr = extern struct {
|
||||
Name: []const u8,
|
||||
Length: u64,
|
||||
};
|
||||
pub fn nameOrLength(self: ar_hdr) !NameOrLength {
|
||||
fn nameOrLength(self: ar_hdr) !NameOrLength {
|
||||
const value = getValue(&self.ar_name);
|
||||
const slash_index = mem.indexOf(u8, value, "/") orelse return error.MalformedArchive;
|
||||
const len = value.len;
|
||||
@ -75,7 +75,7 @@ pub const ar_hdr = extern struct {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn size(self: ar_hdr) !u64 {
|
||||
fn size(self: ar_hdr) !u64 {
|
||||
const value = getValue(&self.ar_size);
|
||||
return std.fmt.parseInt(u64, value, 10);
|
||||
}
|
||||
@ -231,3 +231,9 @@ pub fn parseObject(self: Archive, offset: u32) !*Object {
|
||||
|
||||
return object;
|
||||
}
|
||||
|
||||
pub fn isArchive(file: fs.File) !bool {
|
||||
const magic = try file.reader().readBytesNoEof(Archive.SARMAG);
|
||||
try file.seekTo(0);
|
||||
return mem.eql(u8, &magic, Archive.ARMAG);
|
||||
}
|
||||
|
@ -183,3 +183,9 @@ pub fn parseSymbols(self: *Dylib) !void {
|
||||
try self.symbols.putNoClobber(self.allocator, name, &proxy.base);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isDylib(file: fs.File) !bool {
|
||||
const header = try file.reader().readStruct(macho.mach_header_64);
|
||||
try file.seekTo(0);
|
||||
return header.filetype == macho.MH_DYLIB;
|
||||
}
|
||||
|
@ -485,3 +485,9 @@ pub fn parseDataInCode(self: *Object) !void {
|
||||
try self.data_in_code_entries.append(self.allocator, dice);
|
||||
}
|
||||
}
|
||||
|
||||
pub fn isObject(file: fs.File) !bool {
|
||||
const header = try file.reader().readStruct(macho.mach_header_64);
|
||||
try file.seekTo(0);
|
||||
return header.filetype == macho.MH_OBJECT;
|
||||
}
|
||||
|
@ -187,7 +187,7 @@ pub fn closeFiles(self: Zld) void {
|
||||
}
|
||||
|
||||
const LinkArgs = struct {
|
||||
shared_libs: []const []const u8,
|
||||
libs: []const []const u8,
|
||||
rpaths: []const []const u8,
|
||||
};
|
||||
|
||||
@ -229,7 +229,7 @@ pub fn link(self: *Zld, files: []const []const u8, out_path: []const u8, args: L
|
||||
try self.populateMetadata();
|
||||
try self.addRpaths(args.rpaths);
|
||||
try self.parseInputFiles(files);
|
||||
try self.parseDylibs(args.shared_libs);
|
||||
try self.parseLibs(args.libs);
|
||||
try self.resolveSymbols();
|
||||
try self.resolveStubsAndGotEntries();
|
||||
try self.updateMetadata();
|
||||
@ -265,13 +265,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
|
||||
};
|
||||
|
||||
try_object: {
|
||||
const header = try file.reader().readStruct(macho.mach_header_64);
|
||||
if (header.filetype != macho.MH_OBJECT) {
|
||||
try file.seekTo(0);
|
||||
break :try_object;
|
||||
}
|
||||
|
||||
try file.seekTo(0);
|
||||
if (!(try Object.isObject(file))) break :try_object;
|
||||
try classified.append(.{
|
||||
.kind = .object,
|
||||
.file = file,
|
||||
@ -281,13 +275,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
|
||||
}
|
||||
|
||||
try_archive: {
|
||||
const magic = try file.reader().readBytesNoEof(Archive.SARMAG);
|
||||
if (!mem.eql(u8, &magic, Archive.ARMAG)) {
|
||||
try file.seekTo(0);
|
||||
break :try_archive;
|
||||
}
|
||||
|
||||
try file.seekTo(0);
|
||||
if (!(try Archive.isArchive(file))) break :try_archive;
|
||||
try classified.append(.{
|
||||
.kind = .archive,
|
||||
.file = file,
|
||||
@ -297,13 +285,7 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
|
||||
}
|
||||
|
||||
try_dylib: {
|
||||
const header = try file.reader().readStruct(macho.mach_header_64);
|
||||
if (header.filetype != macho.MH_DYLIB) {
|
||||
try file.seekTo(0);
|
||||
break :try_dylib;
|
||||
}
|
||||
|
||||
try file.seekTo(0);
|
||||
if (!(try Dylib.isDylib(file))) break :try_dylib;
|
||||
try classified.append(.{
|
||||
.kind = .dylib,
|
||||
.file = file,
|
||||
@ -312,7 +294,8 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
|
||||
continue;
|
||||
}
|
||||
|
||||
log.debug("unexpected input file of unknown type '{s}'", .{file_name});
|
||||
file.close();
|
||||
log.warn("unknown filetype for positional input file: '{s}'", .{file_name});
|
||||
}
|
||||
|
||||
// Based on our classification, proceed with parsing.
|
||||
@ -373,35 +356,52 @@ fn parseInputFiles(self: *Zld, files: []const []const u8) !void {
|
||||
}
|
||||
}
|
||||
|
||||
fn parseDylibs(self: *Zld, shared_libs: []const []const u8) !void {
|
||||
for (shared_libs) |lib| {
|
||||
const dylib = try self.allocator.create(Dylib);
|
||||
errdefer self.allocator.destroy(dylib);
|
||||
fn parseLibs(self: *Zld, libs: []const []const u8) !void {
|
||||
for (libs) |lib| {
|
||||
const file = try fs.cwd().openFile(lib, .{});
|
||||
|
||||
dylib.* = Dylib.init(self.allocator);
|
||||
dylib.arch = self.arch.?;
|
||||
dylib.name = try self.allocator.dupe(u8, lib);
|
||||
dylib.file = try fs.cwd().openFile(lib, .{});
|
||||
if (try Dylib.isDylib(file)) {
|
||||
const dylib = try self.allocator.create(Dylib);
|
||||
errdefer self.allocator.destroy(dylib);
|
||||
|
||||
const ordinal = @intCast(u16, self.dylibs.items.len);
|
||||
dylib.ordinal = ordinal + 2; // TODO +2 since 1 is reserved for libSystem
|
||||
dylib.* = Dylib.init(self.allocator);
|
||||
dylib.arch = self.arch.?;
|
||||
dylib.name = try self.allocator.dupe(u8, lib);
|
||||
dylib.file = file;
|
||||
|
||||
// TODO Defer parsing of the dylibs until they are actually needed
|
||||
try dylib.parse();
|
||||
try self.dylibs.append(self.allocator, dylib);
|
||||
const ordinal = @intCast(u16, self.dylibs.items.len);
|
||||
dylib.ordinal = ordinal + 2; // TODO +2 since 1 is reserved for libSystem
|
||||
|
||||
// Add LC_LOAD_DYLIB command
|
||||
const dylib_id = dylib.id orelse unreachable;
|
||||
var dylib_cmd = try createLoadDylibCommand(
|
||||
self.allocator,
|
||||
dylib_id.name,
|
||||
dylib_id.timestamp,
|
||||
dylib_id.current_version,
|
||||
dylib_id.compatibility_version,
|
||||
);
|
||||
errdefer dylib_cmd.deinit(self.allocator);
|
||||
// TODO Defer parsing of the dylibs until they are actually needed
|
||||
try dylib.parse();
|
||||
try self.dylibs.append(self.allocator, dylib);
|
||||
|
||||
try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
|
||||
// Add LC_LOAD_DYLIB command
|
||||
const dylib_id = dylib.id orelse unreachable;
|
||||
var dylib_cmd = try createLoadDylibCommand(
|
||||
self.allocator,
|
||||
dylib_id.name,
|
||||
dylib_id.timestamp,
|
||||
dylib_id.current_version,
|
||||
dylib_id.compatibility_version,
|
||||
);
|
||||
errdefer dylib_cmd.deinit(self.allocator);
|
||||
|
||||
try self.load_commands.append(self.allocator, .{ .Dylib = dylib_cmd });
|
||||
} else if (try Archive.isArchive(file)) {
|
||||
const archive = try self.allocator.create(Archive);
|
||||
errdefer self.allocator.destroy(archive);
|
||||
|
||||
archive.* = Archive.init(self.allocator);
|
||||
archive.arch = self.arch.?;
|
||||
archive.name = try self.allocator.dupe(u8, lib);
|
||||
archive.file = file;
|
||||
try archive.parse();
|
||||
try self.archives.append(self.allocator, archive);
|
||||
} else {
|
||||
file.close();
|
||||
log.warn("unknown filetype for a library: '{s}'", .{lib});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -14,6 +14,7 @@ pub fn addCases(cases: *tests.StandaloneContext) void {
|
||||
cases.addBuildFile("test/standalone/global_linkage/build.zig");
|
||||
cases.addBuildFile("test/standalone/static_c_lib/build.zig");
|
||||
cases.addBuildFile("test/standalone/link_interdependent_static_c_libs/build.zig");
|
||||
cases.addBuildFile("test/standalone/link_static_lib_as_system_lib/build.zig");
|
||||
cases.addBuildFile("test/standalone/issue_339/build.zig");
|
||||
cases.addBuildFile("test/standalone/issue_794/build.zig");
|
||||
cases.addBuildFile("test/standalone/issue_5825/build.zig");
|
||||
|
4
test/standalone/link_static_lib_as_system_lib/a.c
Normal file
4
test/standalone/link_static_lib_as_system_lib/a.c
Normal file
@ -0,0 +1,4 @@
|
||||
#include "a.h"
|
||||
int32_t add(int32_t a, int32_t b) {
|
||||
return a + b;
|
||||
}
|
2
test/standalone/link_static_lib_as_system_lib/a.h
Normal file
2
test/standalone/link_static_lib_as_system_lib/a.h
Normal file
@ -0,0 +1,2 @@
|
||||
#include <stdint.h>
|
||||
int32_t add(int32_t a, int32_t b);
|
23
test/standalone/link_static_lib_as_system_lib/build.zig
Normal file
23
test/standalone/link_static_lib_as_system_lib/build.zig
Normal file
@ -0,0 +1,23 @@
|
||||
const std = @import("std");
|
||||
const Builder = std.build.Builder;
|
||||
|
||||
pub fn build(b: *Builder) void {
|
||||
const mode = b.standardReleaseOptions();
|
||||
|
||||
const lib_a = b.addStaticLibrary("a", null);
|
||||
lib_a.addCSourceFile("a.c", &[_][]const u8{});
|
||||
lib_a.setBuildMode(mode);
|
||||
lib_a.addIncludeDir(".");
|
||||
lib_a.install();
|
||||
|
||||
const test_exe = b.addTest("main.zig");
|
||||
test_exe.setBuildMode(mode);
|
||||
test_exe.linkSystemLibrary("a"); // force linking liba.a as -la
|
||||
test_exe.addSystemIncludeDir(".");
|
||||
const search_path = std.fs.path.join(b.allocator, &[_][]const u8{ b.install_path, "lib" }) catch unreachable;
|
||||
test_exe.addLibPath(search_path);
|
||||
|
||||
const test_step = b.step("test", "Test it");
|
||||
test_step.dependOn(b.getInstallStep());
|
||||
test_step.dependOn(&test_exe.step);
|
||||
}
|
8
test/standalone/link_static_lib_as_system_lib/main.zig
Normal file
8
test/standalone/link_static_lib_as_system_lib/main.zig
Normal file
@ -0,0 +1,8 @@
|
||||
const std = @import("std");
|
||||
const expect = std.testing.expect;
|
||||
const c = @cImport(@cInclude("a.h"));
|
||||
|
||||
test "import C add" {
|
||||
const result = c.add(2, 1);
|
||||
try expect(result == 3);
|
||||
}
|
Loading…
Reference in New Issue
Block a user