Merge pull request #1647 from ziglang/static-libs

Support building static libraries
This commit is contained in:
Andrew Kelley 2018-10-09 13:20:27 -04:00 committed by GitHub
commit 9e38a81230
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 149 additions and 54 deletions

View File

@ -1763,6 +1763,7 @@ struct CodeGen {
bool linker_rdynamic;
bool no_rosegment_workaround;
bool each_lib_rpath;
bool disable_pic;
Buf *mmacosx_version_min;
Buf *mios_version_min;

View File

@ -7228,7 +7228,10 @@ static void init(CodeGen *g) {
bool is_optimized = g->build_mode != BuildModeDebug;
LLVMCodeGenOptLevel opt_level = is_optimized ? LLVMCodeGenLevelAggressive : LLVMCodeGenLevelNone;
LLVMRelocMode reloc_mode = g->is_static ? LLVMRelocStatic : LLVMRelocPIC;
if (g->out_type == OutTypeExe && g->is_static) {
g->disable_pic = true;
}
LLVMRelocMode reloc_mode = g->disable_pic ? LLVMRelocStatic : LLVMRelocPIC;
const char *target_specific_cpu_args;
const char *target_specific_features;
@ -7487,7 +7490,9 @@ static void gen_root_source(CodeGen *g) {
{
g->bootstrap_import = add_special_code(g, create_bootstrap_pkg(g, g->root_package), "bootstrap.zig");
}
if (g->zig_target.os == OsWindows && !g->have_dllmain_crt_startup && g->out_type == OutTypeLib) {
if (g->zig_target.os == OsWindows && !g->have_dllmain_crt_startup &&
g->out_type == OutTypeLib && !g->is_static)
{
g->bootstrap_import = add_special_code(g, create_bootstrap_pkg(g, g->root_package), "bootstrap_lib.zig");
}
@ -8045,6 +8050,7 @@ static Error check_cache(CodeGen *g, Buf *manifest_dir, Buf *digest) {
cache_bool(ch, g->linker_rdynamic);
cache_bool(ch, g->no_rosegment_workaround);
cache_bool(ch, g->each_lib_rpath);
cache_bool(ch, g->disable_pic);
cache_buf_opt(ch, g->mmacosx_version_min);
cache_buf_opt(ch, g->mios_version_min);
cache_usize(ch, g->version_major);

View File

@ -29,10 +29,20 @@ static const char *get_libc_static_file(CodeGen *g, const char *file) {
return buf_ptr(out_buf);
}
static Buf *build_o_raw(CodeGen *parent_gen, const char *oname, Buf *full_path) {
static Buf *build_a_raw(CodeGen *parent_gen, const char *aname, Buf *full_path) {
ZigTarget *child_target = parent_gen->is_native_target ? nullptr : &parent_gen->zig_target;
CodeGen *child_gen = codegen_create(full_path, child_target, OutTypeObj, parent_gen->build_mode,
parent_gen->zig_lib_dir);
// The Mach-O LLD code is not well maintained, and trips an assertion
// when we link compiler_rt and builtin as libraries rather than objects.
// Here we workaround this by having compiler_rt and builtin be objects.
// TODO write our own linker. https://github.com/ziglang/zig/issues/1535
OutType child_out_type = OutTypeLib;
if (parent_gen->zig_target.os == OsMacOSX) {
child_out_type = OutTypeObj;
}
CodeGen *child_gen = codegen_create(full_path, child_target, child_out_type,
parent_gen->build_mode, parent_gen->zig_lib_dir);
child_gen->out_h_path = nullptr;
child_gen->verbose_tokenize = parent_gen->verbose_tokenize;
@ -43,19 +53,22 @@ static Buf *build_o_raw(CodeGen *parent_gen, const char *oname, Buf *full_path)
child_gen->verbose_cimport = parent_gen->verbose_cimport;
codegen_set_strip(child_gen, parent_gen->strip_debug_symbols);
codegen_set_is_static(child_gen, parent_gen->is_static);
codegen_set_is_static(child_gen, true);
child_gen->disable_pic = parent_gen->disable_pic;
codegen_set_out_name(child_gen, buf_create_from_str(oname));
codegen_set_out_name(child_gen, buf_create_from_str(aname));
codegen_set_errmsg_color(child_gen, parent_gen->err_color);
codegen_set_mmacosx_version_min(child_gen, parent_gen->mmacosx_version_min);
codegen_set_mios_version_min(child_gen, parent_gen->mios_version_min);
for (size_t i = 0; i < parent_gen->link_libs_list.length; i += 1) {
LinkLib *link_lib = parent_gen->link_libs_list.at(i);
LinkLib *new_link_lib = codegen_add_link_lib(child_gen, link_lib->name);
new_link_lib->provided_explicitly = link_lib->provided_explicitly;
// This is so that compiler_rt and builtin libraries know whether they
// will eventually be linked with libc. They make different decisions
// about what to export depending on whether libc is linked.
if (parent_gen->libc_link_lib != nullptr) {
LinkLib *new_link_lib = codegen_add_link_lib(child_gen, parent_gen->libc_link_lib->name);
new_link_lib->provided_explicitly = parent_gen->libc_link_lib->provided_explicitly;
}
child_gen->enable_cache = true;
@ -63,12 +76,12 @@ static Buf *build_o_raw(CodeGen *parent_gen, const char *oname, Buf *full_path)
return &child_gen->output_file_path;
}
static Buf *build_o(CodeGen *parent_gen, const char *oname) {
Buf *source_basename = buf_sprintf("%s.zig", oname);
static Buf *build_a(CodeGen *parent_gen, const char *aname) {
Buf *source_basename = buf_sprintf("%s.zig", aname);
Buf *full_path = buf_alloc();
os_path_join(parent_gen->zig_std_special_dir, source_basename, full_path);
return build_o_raw(parent_gen, oname, full_path);
return build_a_raw(parent_gen, aname, full_path);
}
static Buf *build_compiler_rt(CodeGen *parent_gen) {
@ -77,7 +90,7 @@ static Buf *build_compiler_rt(CodeGen *parent_gen) {
Buf *full_path = buf_alloc();
os_path_join(dir_path, buf_create_from_str("index.zig"), full_path);
return build_o_raw(parent_gen, "compiler_rt", full_path);
return build_a_raw(parent_gen, "compiler_rt", full_path);
}
static const char *get_darwin_arch_string(const ZigTarget *t) {
@ -197,6 +210,8 @@ static Buf *get_dynamic_linker_path(CodeGen *g) {
static void construct_linker_job_elf(LinkJob *lj) {
CodeGen *g = lj->codegen;
lj->args.append("-error-limit=0");
if (g->libc_link_lib != nullptr) {
find_libc_lib_path(g);
}
@ -215,10 +230,9 @@ static void construct_linker_job_elf(LinkJob *lj) {
lj->args.append(getLDMOption(&g->zig_target));
bool is_lib = g->out_type == OutTypeLib;
bool is_static = g->is_static || (!is_lib && g->link_libs_list.length == 0);
bool shared = !is_static && is_lib;
bool shared = !g->is_static && is_lib;
Buf *soname = nullptr;
if (is_static) {
if (g->is_static) {
if (g->zig_target.arch.arch == ZigLLVM_arm || g->zig_target.arch.arch == ZigLLVM_armeb ||
g->zig_target.arch.arch == ZigLLVM_thumb || g->zig_target.arch.arch == ZigLLVM_thumbeb)
{
@ -242,7 +256,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
if (lj->link_in_crt) {
const char *crt1o;
const char *crtbegino;
if (is_static) {
if (g->is_static) {
crt1o = "crt1.o";
crtbegino = "crtbeginT.o";
} else {
@ -293,7 +307,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
lj->args.append(buf_ptr(g->libc_static_lib_dir));
}
if (!is_static) {
if (!g->is_static) {
if (g->dynamic_linker != nullptr) {
assert(buf_len(g->dynamic_linker) != 0);
lj->args.append("-dynamic-linker");
@ -315,10 +329,10 @@ static void construct_linker_job_elf(LinkJob *lj) {
lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
}
if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {
if (g->out_type == OutTypeExe || (g->out_type == OutTypeLib && !g->is_static)) {
if (g->libc_link_lib == nullptr) {
Buf *builtin_o_path = build_o(g, "builtin");
lj->args.append(buf_ptr(builtin_o_path));
Buf *builtin_a_path = build_a(g, "builtin");
lj->args.append(buf_ptr(builtin_a_path));
}
// sometimes libgcc is missing stuff, so we still build compiler_rt and rely on weak linkage
@ -345,7 +359,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
// libc dep
if (g->libc_link_lib != nullptr) {
if (is_static) {
if (g->is_static) {
lj->args.append("--start-group");
lj->args.append("-lgcc");
lj->args.append("-lgcc_eh");
@ -387,6 +401,7 @@ static void construct_linker_job_elf(LinkJob *lj) {
static void construct_linker_job_wasm(LinkJob *lj) {
CodeGen *g = lj->codegen;
lj->args.append("-error-limit=0");
lj->args.append("--no-entry"); // So lld doesn't look for _start.
lj->args.append("-o");
lj->args.append(buf_ptr(&g->output_file_path));
@ -425,6 +440,8 @@ static bool zig_lld_link(ZigLLVM_ObjectFormatType oformat, const char **args, si
static void construct_linker_job_coff(LinkJob *lj) {
CodeGen *g = lj->codegen;
lj->args.append("/ERRORLIMIT:0");
if (g->libc_link_lib != nullptr) {
find_libc_lib_path(g);
}
@ -546,10 +563,10 @@ static void construct_linker_job_coff(LinkJob *lj) {
lj->args.append((const char *)buf_ptr(g->link_objects.at(i)));
}
if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {
if (g->out_type == OutTypeExe || (g->out_type == OutTypeLib && !g->is_static)) {
if (g->libc_link_lib == nullptr) {
Buf *builtin_o_path = build_o(g, "builtin");
lj->args.append(buf_ptr(builtin_o_path));
Buf *builtin_a_path = build_a(g, "builtin");
lj->args.append(buf_ptr(builtin_a_path));
}
// msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage
@ -761,6 +778,7 @@ static bool darwin_version_lt(DarwinPlatform *platform, int major, int minor) {
static void construct_linker_job_macho(LinkJob *lj) {
CodeGen *g = lj->codegen;
lj->args.append("-error-limit=0");
lj->args.append("-demangle");
if (g->linker_rdynamic) {
@ -878,7 +896,7 @@ static void construct_linker_job_macho(LinkJob *lj) {
}
// compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce
if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {
if (g->out_type == OutTypeExe || (g->out_type == OutTypeLib && !g->is_static)) {
Buf *compiler_rt_o_path = build_compiler_rt(g);
lj->args.append(buf_ptr(compiler_rt_o_path));
}
@ -960,8 +978,17 @@ void codegen_link(CodeGen *g) {
}
if (g->out_type == OutTypeLib && g->is_static) {
fprintf(stderr, "Zig does not yet support creating static libraries\nSee https://github.com/ziglang/zig/issues/1493\n");
exit(1);
ZigList<const char *> file_names = {};
for (size_t i = 0; i < g->link_objects.length; i += 1) {
file_names.append((const char *)buf_ptr(g->link_objects.at(i)));
}
ZigLLVM_OSType os_type = get_llvm_os_type(g->zig_target.os);
codegen_add_time_event(g, "LLVM Link");
if (ZigLLVMWriteArchive(buf_ptr(&g->output_file_path), file_names.items, file_names.length, os_type)) {
fprintf(stderr, "Unable to write archive '%s'\n", buf_ptr(&g->output_file_path));
exit(1);
}
return;
}
lj.link_in_crt = (g->libc_link_lib != nullptr && g->out_type == OutTypeExe);

View File

@ -47,6 +47,7 @@ static int print_full_usage(const char *arg0) {
" --cache-dir [path] override the cache directory\n"
" --cache [auto|off|on] build in global cache, print out paths to stdout\n"
" --color [auto|off|on] enable or disable colored error messages\n"
" --disable-pic disable Position Independent Code for libraries\n"
" --emit [asm|bin|llvm-ir] emit a specific file format as compilation output\n"
" -ftime-report print timing diagnostics\n"
" --libc-include-dir [path] directory where libc stdlib.h resides\n"
@ -386,6 +387,7 @@ int main(int argc, char **argv) {
size_t ver_minor = 0;
size_t ver_patch = 0;
bool timing_info = false;
bool disable_pic = false;
const char *cache_dir = nullptr;
CliPkg *cur_pkg = allocate<CliPkg>(1);
BuildMode build_mode = BuildModeDebug;
@ -556,6 +558,8 @@ int main(int argc, char **argv) {
each_lib_rpath = true;
} else if (strcmp(arg, "-ftime-report") == 0) {
timing_info = true;
} else if (strcmp(arg, "--disable-pic") == 0) {
disable_pic = true;
} else if (strcmp(arg, "--test-cmd-bin") == 0) {
test_exec_args.append(nullptr);
} else if (arg[1] == 'L' && arg[2] != 0) {
@ -849,6 +853,14 @@ int main(int argc, char **argv) {
buf_out_name = buf_create_from_str("run");
}
CodeGen *g = codegen_create(zig_root_source_file, target, out_type, build_mode, get_zig_lib_dir());
if (disable_pic) {
if (out_type != OutTypeLib || !is_static) {
fprintf(stderr, "--disable-pic only applies to static libraries");
return EXIT_FAILURE;
}
g->disable_pic = true;
}
g->enable_time_report = timing_info;
buf_init_from_str(&g->cache_dir, cache_dir ? cache_dir : default_zig_cache_name);
codegen_set_out_name(g, buf_out_name);

View File

@ -46,6 +46,7 @@ typedef SSIZE_T ssize_t;
#include <sys/wait.h>
#include <fcntl.h>
#include <limits.h>
#include <spawn.h>
#endif
@ -70,6 +71,12 @@ static clock_serv_t cclock;
#include <errno.h>
#include <time.h>
// Apple doesn't provide the environ global variable
#if defined(__APPLE__) && !defined(environ)
#include <crt_externs.h>
#define environ (*_NSGetEnviron())
#endif
#if defined(ZIG_OS_POSIX)
static void populate_termination(Termination *term, int status) {
if (WIFEXITED(status)) {
@ -88,25 +95,22 @@ static void populate_termination(Termination *term, int status) {
}
static void os_spawn_process_posix(const char *exe, ZigList<const char *> &args, Termination *term) {
pid_t pid = fork();
if (pid == -1)
zig_panic("fork failed: %s", strerror(errno));
if (pid == 0) {
// child
const char **argv = allocate<const char *>(args.length + 2);
argv[0] = exe;
argv[args.length + 1] = nullptr;
for (size_t i = 0; i < args.length; i += 1) {
argv[i + 1] = args.at(i);
}
execvp(exe, const_cast<char * const *>(argv));
zig_panic("execvp failed: %s", strerror(errno));
} else {
// parent
int status;
waitpid(pid, &status, 0);
populate_termination(term, status);
const char **argv = allocate<const char *>(args.length + 2);
argv[0] = exe;
argv[args.length + 1] = nullptr;
for (size_t i = 0; i < args.length; i += 1) {
argv[i + 1] = args.at(i);
}
pid_t pid;
int rc = posix_spawn(&pid, exe, nullptr, nullptr, const_cast<char *const*>(argv), environ);
if (rc != 0) {
zig_panic("posix_spawn failed: %s", strerror(rc));
}
int status;
waitpid(pid, &status, 0);
populate_termination(term, status);
}
#endif

View File

@ -250,7 +250,7 @@ Os get_target_os(size_t index) {
return os_list[index];
}
static ZigLLVM_OSType get_llvm_os_type(Os os_type) {
ZigLLVM_OSType get_llvm_os_type(Os os_type) {
switch (os_type) {
case OsFreestanding:
case OsZen:

View File

@ -119,6 +119,6 @@ const char *target_lib_file_ext(ZigTarget *target, bool is_static, size_t versio
Buf *target_dynamic_linker(ZigTarget *target);
bool target_can_exec(const ZigTarget *host_target, const ZigTarget *guest_target);
ZigLLVM_OSType get_llvm_os_type(Os os_type);
#endif

View File

@ -32,6 +32,8 @@
#include <llvm/IR/Verifier.h>
#include <llvm/InitializePasses.h>
#include <llvm/MC/SubtargetFeature.h>
#include <llvm/Object/Archive.h>
#include <llvm/Object/ArchiveWriter.h>
#include <llvm/PassRegistry.h>
#include <llvm/Support/FileSystem.h>
#include <llvm/Support/TargetParser.h>
@ -40,8 +42,8 @@
#include <llvm/Target/TargetMachine.h>
#include <llvm/Transforms/Coroutines.h>
#include <llvm/Transforms/IPO.h>
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
#include <llvm/Transforms/IPO/AlwaysInliner.h>
#include <llvm/Transforms/IPO/PassManagerBuilder.h>
#include <llvm/Transforms/Scalar.h>
#include <llvm/Transforms/Utils.h>
@ -854,6 +856,42 @@ class MyOStream: public raw_ostream {
size_t pos;
};
bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count,
ZigLLVM_OSType os_type)
{
object::Archive::Kind kind;
switch (os_type) {
case ZigLLVM_Win32:
// For some reason llvm-lib passes K_GNU on windows.
// See lib/ToolDrivers/llvm-lib/LibDriver.cpp:168 in libDriverMain
kind = object::Archive::K_GNU;
break;
case ZigLLVM_Linux:
kind = object::Archive::K_GNU;
break;
case ZigLLVM_Darwin:
case ZigLLVM_IOS:
kind = object::Archive::K_DARWIN;
break;
case ZigLLVM_OpenBSD:
case ZigLLVM_FreeBSD:
kind = object::Archive::K_BSD;
break;
default:
kind = object::Archive::K_GNU;
}
SmallVector<NewArchiveMember, 4> new_members;
for (size_t i = 0; i < file_name_count; i += 1) {
Expected<NewArchiveMember> new_member = NewArchiveMember::getFile(file_names[i], true);
Error err = new_member.takeError();
if (err) return true;
new_members.push_back(std::move(*new_member));
}
Error err = writeArchive(archive_name, new_members, true, kind, true, false, nullptr);
if (err) return true;
return false;
}
bool ZigLLDLink(ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count,
void (*append_diagnostic)(void *, const char *, size_t), void *context)

View File

@ -406,6 +406,9 @@ ZIG_EXTERN_C const char *ZigLLVMGetEnvironmentTypeName(enum ZigLLVM_EnvironmentT
ZIG_EXTERN_C bool ZigLLDLink(enum ZigLLVM_ObjectFormatType oformat, const char **args, size_t arg_count,
void (*append_diagnostic)(void *, const char *, size_t), void *context);
ZIG_EXTERN_C bool ZigLLVMWriteArchive(const char *archive_name, const char **file_names, size_t file_name_count,
enum ZigLLVM_OSType os_type);
ZIG_EXTERN_C void ZigLLVMGetNativeTarget(enum ZigLLVM_ArchType *arch_type, enum ZigLLVM_SubArchType *sub_arch_type,
enum ZigLLVM_VendorType *vendor_type, enum ZigLLVM_OSType *os_type, enum ZigLLVM_EnvironmentType *environ_type,
enum ZigLLVM_ObjectFormatType *oformat);

View File

@ -66,7 +66,9 @@ fn posixCallMainAndExit() noreturn {
std.os.posix.exit(callMainWithArgs(argc, argv, envp));
}
fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 {
// This is marked inline because for some reason LLVM in release mode fails to inline it,
// and we want fewer call frames in stack traces.
inline fn callMainWithArgs(argc: usize, argv: [*][*]u8, envp: [][*]u8) u8 {
std.os.ArgIteratorPosix.raw = argv[0..argc];
std.os.posix_environ_raw = envp;
return callMain();
@ -79,7 +81,9 @@ extern fn main(c_argc: i32, c_argv: [*][*]u8, c_envp: [*]?[*]u8) i32 {
return callMainWithArgs(@intCast(usize, c_argc), c_argv, envp);
}
fn callMain() u8 {
// This is marked inline because for some reason LLVM in release mode fails to inline it,
// and we want fewer call frames in stack traces.
inline fn callMain() u8 {
switch (@typeId(@typeOf(root.main).ReturnType)) {
builtin.TypeId.NoReturn => {
root.main();

View File

@ -1,4 +1,4 @@
// This file is included in the compilation unit when exporting a library on windows.
// This file is included in the compilation unit when exporting a DLL on windows.
const std = @import("std");
const builtin = @import("builtin");