mirror of
https://github.com/Ed94/Odin.git
synced 2026-08-05 15:18:49 +00:00
Make the link order of foreign imports deterministic
This commit is contained in:
+38
-19
@@ -29,29 +29,46 @@ void lb_add_foreign_library_path(lbModule *m, Entity *e) {
|
|||||||
GB_ASSERT(e->kind == Entity_LibraryName);
|
GB_ASSERT(e->kind == Entity_LibraryName);
|
||||||
GB_ASSERT(e->flags & EntityFlag_Used);
|
GB_ASSERT(e->flags & EntityFlag_Used);
|
||||||
|
|
||||||
for_array(i, e->LibraryName.paths) {
|
mutex_lock(&m->gen->foreign_mutex);
|
||||||
String library_path = e->LibraryName.paths[i];
|
if (!ptr_set_update(&m->gen->foreign_libraries_set, e)) {
|
||||||
if (library_path.len == 0) {
|
array_add(&m->gen->foreign_libraries, e);
|
||||||
continue;
|
}
|
||||||
}
|
mutex_unlock(&m->gen->foreign_mutex);
|
||||||
|
}
|
||||||
|
|
||||||
bool ok = true;
|
GB_COMPARE_PROC(foreign_library_cmp) {
|
||||||
for_array(path_index, m->foreign_library_paths) {
|
int cmp = 0;
|
||||||
String path = m->foreign_library_paths[path_index];
|
Entity *x = *(Entity **)a;
|
||||||
#if defined(GB_SYSTEM_WINDOWS)
|
Entity *y = *(Entity **)b;
|
||||||
if (str_eq_ignore_case(path, library_path)) {
|
if (x == y) {
|
||||||
#else
|
return 0;
|
||||||
if (str_eq(path, library_path)) {
|
}
|
||||||
#endif
|
|
||||||
ok = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ok) {
|
if (x->pkg != y->pkg) {
|
||||||
array_add(&m->foreign_library_paths, library_path);
|
isize order_x = x->pkg ? x->pkg->order : 0;
|
||||||
|
isize order_y = y->pkg ? y->pkg->order : 0;
|
||||||
|
cmp = isize_cmp(order_x, order_y);
|
||||||
|
if (cmp) {
|
||||||
|
return cmp;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (x->file != y->file) {
|
||||||
|
String fullpath_x = x->file ? x->file->fullpath : (String{});
|
||||||
|
String fullpath_y = y->file ? y->file->fullpath : (String{});
|
||||||
|
String file_x = filename_from_path(fullpath_x);
|
||||||
|
String file_y = filename_from_path(fullpath_y);
|
||||||
|
|
||||||
|
cmp = string_compare(file_x, file_y);
|
||||||
|
if (cmp) {
|
||||||
|
return cmp;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
cmp = u64_cmp(x->order_in_src, y->order_in_src);
|
||||||
|
if (cmp) {
|
||||||
|
return cmp;
|
||||||
|
}
|
||||||
|
return i32_cmp(x->token.pos.offset, y->token.pos.offset);
|
||||||
}
|
}
|
||||||
|
|
||||||
void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module, Entity *e, String const &name) {
|
void lb_set_entity_from_other_modules_linkage_correctly(lbModule *other_module, Entity *e, String const &name) {
|
||||||
@@ -1922,4 +1939,6 @@ void lb_generate_code(lbGenerator *gen) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gb_sort_array(gen->foreign_libraries.data, gen->foreign_libraries.count, foreign_library_cmp);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -135,7 +135,6 @@ struct lbModule {
|
|||||||
u32 nested_type_name_guid;
|
u32 nested_type_name_guid;
|
||||||
|
|
||||||
Array<lbProcedure *> procedures_to_generate;
|
Array<lbProcedure *> procedures_to_generate;
|
||||||
Array<String> foreign_library_paths;
|
|
||||||
|
|
||||||
lbProcedure *curr_procedure;
|
lbProcedure *curr_procedure;
|
||||||
|
|
||||||
@@ -162,6 +161,10 @@ struct lbGenerator {
|
|||||||
|
|
||||||
PtrMap<Ast *, lbProcedure *> anonymous_proc_lits;
|
PtrMap<Ast *, lbProcedure *> anonymous_proc_lits;
|
||||||
|
|
||||||
|
BlockingMutex foreign_mutex;
|
||||||
|
PtrSet<Entity *> foreign_libraries_set;
|
||||||
|
Array<Entity *> foreign_libraries;
|
||||||
|
|
||||||
std::atomic<u32> global_array_index;
|
std::atomic<u32> global_array_index;
|
||||||
std::atomic<u32> global_generated_index;
|
std::atomic<u32> global_generated_index;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -67,9 +67,7 @@ void lb_init_module(lbModule *m, Checker *c) {
|
|||||||
map_init(&m->equal_procs, a);
|
map_init(&m->equal_procs, a);
|
||||||
map_init(&m->hasher_procs, a);
|
map_init(&m->hasher_procs, a);
|
||||||
array_init(&m->procedures_to_generate, a, 0, 1024);
|
array_init(&m->procedures_to_generate, a, 0, 1024);
|
||||||
array_init(&m->foreign_library_paths, a, 0, 1024);
|
|
||||||
array_init(&m->missing_procedures_to_check, a, 0, 16);
|
array_init(&m->missing_procedures_to_check, a, 0, 16);
|
||||||
|
|
||||||
map_init(&m->debug_values, a);
|
map_init(&m->debug_values, a);
|
||||||
array_init(&m->debug_incomplete_types, a, 0, 1024);
|
array_init(&m->debug_incomplete_types, a, 0, 1024);
|
||||||
|
|
||||||
@@ -126,6 +124,11 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) {
|
|||||||
map_init(&gen->modules_through_ctx, permanent_allocator(), gen->info->packages.entries.count*2);
|
map_init(&gen->modules_through_ctx, permanent_allocator(), gen->info->packages.entries.count*2);
|
||||||
map_init(&gen->anonymous_proc_lits, heap_allocator(), 1024);
|
map_init(&gen->anonymous_proc_lits, heap_allocator(), 1024);
|
||||||
|
|
||||||
|
|
||||||
|
mutex_init(&gen->foreign_mutex);
|
||||||
|
array_init(&gen->foreign_libraries, heap_allocator(), 0, 1024);
|
||||||
|
ptr_set_init(&gen->foreign_libraries_set, heap_allocator(), 1024);
|
||||||
|
|
||||||
if (USE_SEPARATE_MODULES) {
|
if (USE_SEPARATE_MODULES) {
|
||||||
for_array(i, gen->info->packages.entries) {
|
for_array(i, gen->info->packages.entries) {
|
||||||
AstPackage *pkg = gen->info->packages.entries[i].value;
|
AstPackage *pkg = gen->info->packages.entries[i].value;
|
||||||
|
|||||||
+304
-288
@@ -164,341 +164,357 @@ i32 linker_stage(lbGenerator *gen) {
|
|||||||
build_context.keep_object_files = true;
|
build_context.keep_object_files = true;
|
||||||
} else {
|
} else {
|
||||||
#if defined(GB_SYSTEM_WINDOWS)
|
#if defined(GB_SYSTEM_WINDOWS)
|
||||||
String section_name = str_lit("msvc-link");
|
bool is_windows = true;
|
||||||
if (build_context.use_lld) {
|
#else
|
||||||
section_name = str_lit("lld-link");
|
bool is_windows = false;
|
||||||
}
|
#endif
|
||||||
timings_start_section(timings, section_name);
|
#if defined(GB_SYSTEM_OSX)
|
||||||
|
bool is_osx = true;
|
||||||
gbString lib_str = gb_string_make(heap_allocator(), "");
|
#else
|
||||||
defer (gb_string_free(lib_str));
|
bool is_osx = false;
|
||||||
|
#endif
|
||||||
gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
|
|
||||||
defer (gb_string_free(link_settings));
|
|
||||||
|
|
||||||
// Add library search paths.
|
|
||||||
if (build_context.build_paths[BuildPath_VS_LIB].basename.len > 0) {
|
|
||||||
String path = {};
|
|
||||||
auto add_path = [&](String path) {
|
|
||||||
if (path[path.len-1] == '\\') {
|
|
||||||
path.len -= 1;
|
|
||||||
}
|
|
||||||
link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
|
|
||||||
};
|
|
||||||
add_path(build_context.build_paths[BuildPath_Win_SDK_UM_Lib].basename);
|
|
||||||
add_path(build_context.build_paths[BuildPath_Win_SDK_UCRT_Lib].basename);
|
|
||||||
add_path(build_context.build_paths[BuildPath_VS_LIB].basename);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
StringSet libs = {};
|
if (is_windows) {
|
||||||
string_set_init(&libs, heap_allocator(), 64);
|
String section_name = str_lit("msvc-link");
|
||||||
defer (string_set_destroy(&libs));
|
if (build_context.use_lld) {
|
||||||
|
section_name = str_lit("lld-link");
|
||||||
|
}
|
||||||
|
timings_start_section(timings, section_name);
|
||||||
|
|
||||||
StringSet asm_files = {};
|
gbString lib_str = gb_string_make(heap_allocator(), "");
|
||||||
string_set_init(&asm_files, heap_allocator(), 64);
|
defer (gb_string_free(lib_str));
|
||||||
defer (string_set_destroy(&asm_files));
|
|
||||||
|
|
||||||
for_array(j, gen->modules.entries) {
|
gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
|
||||||
lbModule *m = gen->modules.entries[j].value;
|
defer (gb_string_free(link_settings));
|
||||||
for_array(i, m->foreign_library_paths) {
|
|
||||||
String lib = m->foreign_library_paths[i];
|
// Add library search paths.
|
||||||
if (has_asm_extension(lib)) {
|
if (build_context.build_paths[BuildPath_VS_LIB].basename.len > 0) {
|
||||||
string_set_add(&asm_files, lib);
|
String path = {};
|
||||||
} else {
|
auto add_path = [&](String path) {
|
||||||
string_set_add(&libs, lib);
|
if (path[path.len-1] == '\\') {
|
||||||
|
path.len -= 1;
|
||||||
|
}
|
||||||
|
link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
|
||||||
|
};
|
||||||
|
add_path(build_context.build_paths[BuildPath_Win_SDK_UM_Lib].basename);
|
||||||
|
add_path(build_context.build_paths[BuildPath_Win_SDK_UCRT_Lib].basename);
|
||||||
|
add_path(build_context.build_paths[BuildPath_VS_LIB].basename);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
StringSet libs = {};
|
||||||
|
string_set_init(&libs, heap_allocator(), 64);
|
||||||
|
defer (string_set_destroy(&libs));
|
||||||
|
|
||||||
|
StringSet asm_files = {};
|
||||||
|
string_set_init(&asm_files, heap_allocator(), 64);
|
||||||
|
defer (string_set_destroy(&asm_files));
|
||||||
|
|
||||||
|
for_array(j, gen->foreign_libraries) {
|
||||||
|
Entity *e = gen->foreign_libraries[j];
|
||||||
|
GB_ASSERT(e->kind == Entity_LibraryName);
|
||||||
|
for_array(i, e->LibraryName.paths) {
|
||||||
|
String lib = string_trim_whitespace(e->LibraryName.paths[i]);
|
||||||
|
if (lib.len == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// IMPORTANT NOTE(bill): calling `string_to_lower` here is not an issue because
|
||||||
|
// we will never uses these strings afterwards
|
||||||
|
string_to_lower(&lib);
|
||||||
|
if (has_asm_extension(lib)) {
|
||||||
|
if (!string_set_update(&asm_files, lib)) {
|
||||||
|
String asm_file = asm_files.entries[i].value;
|
||||||
|
String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj"));
|
||||||
|
|
||||||
|
result = system_exec_command_line_app("nasm",
|
||||||
|
"\"%.*s\\bin\\nasm\\windows\\nasm.exe\" \"%.*s\" "
|
||||||
|
"-f win64 "
|
||||||
|
"-o \"%.*s\" "
|
||||||
|
"%.*s "
|
||||||
|
"",
|
||||||
|
LIT(build_context.ODIN_ROOT), LIT(asm_file),
|
||||||
|
LIT(obj_file),
|
||||||
|
LIT(build_context.extra_assembler_flags)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
array_add(&gen->output_object_paths, obj_file);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!string_set_update(&libs, lib)) {
|
||||||
|
lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib));
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for_array(i, gen->default_module.foreign_library_paths) {
|
if (build_context.build_mode == BuildMode_DynamicLibrary) {
|
||||||
String lib = gen->default_module.foreign_library_paths[i];
|
link_settings = gb_string_append_fmt(link_settings, " /DLL");
|
||||||
if (has_asm_extension(lib)) {
|
|
||||||
string_set_add(&asm_files, lib);
|
|
||||||
} else {
|
} else {
|
||||||
string_set_add(&libs, lib);
|
link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
for_array(i, libs.entries) {
|
if (build_context.pdb_filepath != "") {
|
||||||
String lib = libs.entries[i].value;
|
String pdb_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_PDB]);
|
||||||
lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib));
|
link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(pdb_path));
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (build_context.build_mode == BuildMode_DynamicLibrary) {
|
|
||||||
link_settings = gb_string_append_fmt(link_settings, " /DLL");
|
|
||||||
} else {
|
|
||||||
link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (build_context.pdb_filepath != "") {
|
|
||||||
String pdb_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_PDB]);
|
|
||||||
link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(pdb_path));
|
|
||||||
}
|
|
||||||
|
|
||||||
if (build_context.no_crt) {
|
|
||||||
link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
|
|
||||||
} else {
|
|
||||||
link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (build_context.ODIN_DEBUG) {
|
|
||||||
link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
|
|
||||||
}
|
|
||||||
|
|
||||||
for_array(i, asm_files.entries) {
|
|
||||||
String asm_file = asm_files.entries[i].value;
|
|
||||||
String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj"));
|
|
||||||
|
|
||||||
result = system_exec_command_line_app("nasm",
|
|
||||||
"\"%.*s\\bin\\nasm\\windows\\nasm.exe\" \"%.*s\" "
|
|
||||||
"-f win64 "
|
|
||||||
"-o \"%.*s\" "
|
|
||||||
"%.*s "
|
|
||||||
"",
|
|
||||||
LIT(build_context.ODIN_ROOT), LIT(asm_file),
|
|
||||||
LIT(obj_file),
|
|
||||||
LIT(build_context.extra_assembler_flags)
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
array_add(&gen->output_object_paths, obj_file);
|
|
||||||
}
|
|
||||||
|
|
||||||
gbString object_files = gb_string_make(heap_allocator(), "");
|
if (build_context.no_crt) {
|
||||||
defer (gb_string_free(object_files));
|
link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
|
||||||
for_array(i, gen->output_object_paths) {
|
} else {
|
||||||
String object_path = gen->output_object_paths[i];
|
link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
|
||||||
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
|
}
|
||||||
}
|
|
||||||
|
|
||||||
String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]);
|
if (build_context.ODIN_DEBUG) {
|
||||||
defer (gb_free(heap_allocator(), vs_exe_path.text));
|
link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
|
||||||
|
}
|
||||||
|
|
||||||
char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
|
gbString object_files = gb_string_make(heap_allocator(), "");
|
||||||
if (!build_context.use_lld) { // msvc
|
defer (gb_string_free(object_files));
|
||||||
if (build_context.has_resource) {
|
for_array(i, gen->output_object_paths) {
|
||||||
String rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
|
String object_path = gen->output_object_paths[i];
|
||||||
String res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]);
|
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
|
||||||
defer (gb_free(heap_allocator(), rc_path.text));
|
}
|
||||||
defer (gb_free(heap_allocator(), res_path.text));
|
|
||||||
|
|
||||||
result = system_exec_command_line_app("msvc-link",
|
String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]);
|
||||||
"\"rc.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
|
defer (gb_free(heap_allocator(), vs_exe_path.text));
|
||||||
LIT(res_path),
|
|
||||||
LIT(rc_path)
|
char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
|
||||||
);
|
if (!build_context.use_lld) { // msvc
|
||||||
|
if (build_context.has_resource) {
|
||||||
|
String rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
|
||||||
|
String res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]);
|
||||||
|
defer (gb_free(heap_allocator(), rc_path.text));
|
||||||
|
defer (gb_free(heap_allocator(), res_path.text));
|
||||||
|
|
||||||
|
result = system_exec_command_line_app("msvc-link",
|
||||||
|
"\"rc.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
|
||||||
|
LIT(res_path),
|
||||||
|
LIT(rc_path)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
result = system_exec_command_line_app("msvc-link",
|
||||||
|
"\"%.*slink.exe\" %s \"%.*s\" -OUT:\"%.*s\" %s "
|
||||||
|
"/nologo /incremental:no /opt:ref /subsystem:%s "
|
||||||
|
" %.*s "
|
||||||
|
" %.*s "
|
||||||
|
" %s "
|
||||||
|
"",
|
||||||
|
LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename),
|
||||||
|
link_settings,
|
||||||
|
subsystem_str,
|
||||||
|
LIT(build_context.link_flags),
|
||||||
|
LIT(build_context.extra_linker_flags),
|
||||||
|
lib_str
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
result = system_exec_command_line_app("msvc-link",
|
||||||
|
"\"%.*slink.exe\" %s -OUT:\"%.*s\" %s "
|
||||||
|
"/nologo /incremental:no /opt:ref /subsystem:%s "
|
||||||
|
" %.*s "
|
||||||
|
" %.*s "
|
||||||
|
" %s "
|
||||||
|
"",
|
||||||
|
LIT(vs_exe_path), object_files, LIT(output_filename),
|
||||||
|
link_settings,
|
||||||
|
subsystem_str,
|
||||||
|
LIT(build_context.link_flags),
|
||||||
|
LIT(build_context.extra_linker_flags),
|
||||||
|
lib_str
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
result = system_exec_command_line_app("msvc-link",
|
} else { // lld
|
||||||
"\"%.*slink.exe\" %s \"%.*s\" -OUT:\"%.*s\" %s "
|
result = system_exec_command_line_app("msvc-lld-link",
|
||||||
|
"\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
|
||||||
"/nologo /incremental:no /opt:ref /subsystem:%s "
|
"/nologo /incremental:no /opt:ref /subsystem:%s "
|
||||||
" %.*s "
|
" %.*s "
|
||||||
" %.*s "
|
" %.*s "
|
||||||
" %s "
|
" %s "
|
||||||
"",
|
"",
|
||||||
LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename),
|
LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
|
||||||
link_settings,
|
|
||||||
subsystem_str,
|
|
||||||
LIT(build_context.link_flags),
|
|
||||||
LIT(build_context.extra_linker_flags),
|
|
||||||
lib_str
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
result = system_exec_command_line_app("msvc-link",
|
|
||||||
"\"%.*slink.exe\" %s -OUT:\"%.*s\" %s "
|
|
||||||
"/nologo /incremental:no /opt:ref /subsystem:%s "
|
|
||||||
" %.*s "
|
|
||||||
" %.*s "
|
|
||||||
" %s "
|
|
||||||
"",
|
|
||||||
LIT(vs_exe_path), object_files, LIT(output_filename),
|
|
||||||
link_settings,
|
link_settings,
|
||||||
subsystem_str,
|
subsystem_str,
|
||||||
LIT(build_context.link_flags),
|
LIT(build_context.link_flags),
|
||||||
LIT(build_context.extra_linker_flags),
|
LIT(build_context.extra_linker_flags),
|
||||||
lib_str
|
lib_str
|
||||||
);
|
);
|
||||||
}
|
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
return result;
|
return result;
|
||||||
}
|
|
||||||
|
|
||||||
} else { // lld
|
|
||||||
result = system_exec_command_line_app("msvc-lld-link",
|
|
||||||
"\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
|
|
||||||
"/nologo /incremental:no /opt:ref /subsystem:%s "
|
|
||||||
" %.*s "
|
|
||||||
" %.*s "
|
|
||||||
" %s "
|
|
||||||
"",
|
|
||||||
LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
|
|
||||||
link_settings,
|
|
||||||
subsystem_str,
|
|
||||||
LIT(build_context.link_flags),
|
|
||||||
LIT(build_context.extra_linker_flags),
|
|
||||||
lib_str
|
|
||||||
);
|
|
||||||
|
|
||||||
if (result) {
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
#else
|
|
||||||
timings_start_section(timings, str_lit("ld-link"));
|
|
||||||
|
|
||||||
// NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
|
|
||||||
char cwd[256];
|
|
||||||
getcwd(&cwd[0], 256);
|
|
||||||
//printf("%s\n", cwd);
|
|
||||||
|
|
||||||
// NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
|
|
||||||
// files can be passed with -l:
|
|
||||||
gbString lib_str = gb_string_make(heap_allocator(), "-L/");
|
|
||||||
defer (gb_string_free(lib_str));
|
|
||||||
|
|
||||||
for_array(i, gen->default_module.foreign_library_paths) {
|
|
||||||
String lib = gen->default_module.foreign_library_paths[i];
|
|
||||||
|
|
||||||
// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
|
|
||||||
// This allows you to specify '-f' in a #foreign_system_library,
|
|
||||||
// without having to implement any new syntax specifically for MacOS.
|
|
||||||
if (build_context.metrics.os == TargetOs_darwin) {
|
|
||||||
if (string_ends_with(lib, str_lit(".framework"))) {
|
|
||||||
// framework thingie
|
|
||||||
String lib_name = lib;
|
|
||||||
lib_name = remove_extension_from_path(lib_name);
|
|
||||||
lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
|
|
||||||
} else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) {
|
|
||||||
// For:
|
|
||||||
// object
|
|
||||||
// dynamic lib
|
|
||||||
// static libs, absolute full path relative to the file in which the lib was imported from
|
|
||||||
lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
|
|
||||||
} else {
|
|
||||||
// dynamic or static system lib, just link regularly searching system library paths
|
|
||||||
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
|
|
||||||
// since those are statically linked to at link time. shared libraries (.so) has to be
|
|
||||||
// available at runtime wherever the executable is run, so we make require those to be
|
|
||||||
// local to the executable (unless the system collection is used, in which case we search
|
|
||||||
// the system library paths for the library file).
|
|
||||||
if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o"))) {
|
|
||||||
// static libs and object files, absolute full path relative to the file in which the lib was imported from
|
|
||||||
lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
|
|
||||||
} else if (string_ends_with(lib, str_lit(".so"))) {
|
|
||||||
// dynamic lib, relative path to executable
|
|
||||||
// NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
|
|
||||||
// at runtime to the executable
|
|
||||||
lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
|
|
||||||
} else {
|
|
||||||
// dynamic or static system lib, just link regularly searching system library paths
|
|
||||||
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
gbString object_files = gb_string_make(heap_allocator(), "");
|
|
||||||
defer (gb_string_free(object_files));
|
|
||||||
for_array(i, gen->output_object_paths) {
|
|
||||||
String object_path = gen->output_object_paths[i];
|
|
||||||
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
|
|
||||||
}
|
|
||||||
|
|
||||||
gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
|
|
||||||
|
|
||||||
if (build_context.no_crt) {
|
|
||||||
link_settings = gb_string_append_fmt(link_settings, "-nostdlib ");
|
|
||||||
}
|
|
||||||
|
|
||||||
// NOTE(dweiler): We use clang as a frontend for the linker as there are
|
|
||||||
// other runtime and compiler support libraries that need to be linked in
|
|
||||||
// very specific orders such as libgcc_s, ld-linux-so, unwind, etc.
|
|
||||||
// These are not always typically inside /lib, /lib64, or /usr versions
|
|
||||||
// of that, e.g libgcc.a is in /usr/lib/gcc/{version}, and can vary on
|
|
||||||
// the distribution of Linux even. The gcc or clang specs is the only
|
|
||||||
// reliable way to query this information to call ld directly.
|
|
||||||
if (build_context.build_mode == BuildMode_DynamicLibrary) {
|
|
||||||
// NOTE(dweiler): Let the frontend know we're building a shared library
|
|
||||||
// so it doesn't generate symbols which cannot be relocated.
|
|
||||||
link_settings = gb_string_appendc(link_settings, "-shared ");
|
|
||||||
|
|
||||||
// NOTE(dweiler): _odin_entry_point must be called at initialization
|
|
||||||
// time of the shared object, similarly, _odin_exit_point must be called
|
|
||||||
// at deinitialization. We can pass both -init and -fini to the linker by
|
|
||||||
// using a comma separated list of arguments to -Wl.
|
|
||||||
//
|
|
||||||
// This previously used ld but ld cannot actually build a shared library
|
|
||||||
// correctly this way since all the other dependencies provided implicitly
|
|
||||||
// by the compiler frontend are still needed and most of the command
|
|
||||||
// line arguments prepared previously are incompatible with ld.
|
|
||||||
link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
|
|
||||||
link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
|
|
||||||
} else if (build_context.metrics.os != TargetOs_openbsd) {
|
|
||||||
// OpenBSD defaults to PIE executable. do not pass -no-pie for it.
|
|
||||||
link_settings = gb_string_appendc(link_settings, "-no-pie ");
|
|
||||||
}
|
|
||||||
|
|
||||||
gbString platform_lib_str = gb_string_make(heap_allocator(), "");
|
|
||||||
defer (gb_string_free(platform_lib_str));
|
|
||||||
if (build_context.metrics.os == TargetOs_darwin) {
|
|
||||||
platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib");
|
|
||||||
} else {
|
} else {
|
||||||
platform_lib_str = gb_string_appendc(platform_lib_str, "-lc -lm");
|
timings_start_section(timings, str_lit("ld-link"));
|
||||||
}
|
|
||||||
|
|
||||||
if (build_context.metrics.os == TargetOs_darwin) {
|
// NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
|
||||||
// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
|
char cwd[256];
|
||||||
// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
|
#if !defined(GB_SYSTEM_WINDOWS)
|
||||||
// make sure to also change the 'mtriple' param passed to 'opt'
|
getcwd(&cwd[0], 256);
|
||||||
if (build_context.metrics.arch == TargetArch_arm64) {
|
#endif
|
||||||
link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=12.0.0 ");
|
//printf("%s\n", cwd);
|
||||||
} else {
|
|
||||||
link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=10.8.0 ");
|
// NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
|
||||||
|
// files can be passed with -l:
|
||||||
|
gbString lib_str = gb_string_make(heap_allocator(), "-L/");
|
||||||
|
defer (gb_string_free(lib_str));
|
||||||
|
|
||||||
|
StringSet libs = {};
|
||||||
|
string_set_init(&libs, heap_allocator(), 64);
|
||||||
|
defer (string_set_destroy(&libs));
|
||||||
|
|
||||||
|
for_array(j, gen->foreign_libraries) {
|
||||||
|
Entity *e = gen->foreign_libraries[j];
|
||||||
|
GB_ASSERT(e->kind == Entity_LibraryName);
|
||||||
|
for_array(i, e->LibraryName.paths) {
|
||||||
|
String lib = e->LibraryName.paths[i];
|
||||||
|
if (string_set_update(&libs, lib)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib));
|
||||||
|
|
||||||
|
// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
|
||||||
|
// This allows you to specify '-f' in a #foreign_system_library,
|
||||||
|
// without having to implement any new syntax specifically for MacOS.
|
||||||
|
if (build_context.metrics.os == TargetOs_darwin) {
|
||||||
|
if (string_ends_with(lib, str_lit(".framework"))) {
|
||||||
|
// framework thingie
|
||||||
|
String lib_name = lib;
|
||||||
|
lib_name = remove_extension_from_path(lib_name);
|
||||||
|
lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
|
||||||
|
} else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) {
|
||||||
|
// For:
|
||||||
|
// object
|
||||||
|
// dynamic lib
|
||||||
|
// static libs, absolute full path relative to the file in which the lib was imported from
|
||||||
|
lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
|
||||||
|
} else {
|
||||||
|
// dynamic or static system lib, just link regularly searching system library paths
|
||||||
|
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
|
||||||
|
// since those are statically linked to at link time. shared libraries (.so) has to be
|
||||||
|
// available at runtime wherever the executable is run, so we make require those to be
|
||||||
|
// local to the executable (unless the system collection is used, in which case we search
|
||||||
|
// the system library paths for the library file).
|
||||||
|
if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o"))) {
|
||||||
|
// static libs and object files, absolute full path relative to the file in which the lib was imported from
|
||||||
|
lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
|
||||||
|
} else if (string_ends_with(lib, str_lit(".so"))) {
|
||||||
|
// dynamic lib, relative path to executable
|
||||||
|
// NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
|
||||||
|
// at runtime to the executable
|
||||||
|
lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
|
||||||
|
} else {
|
||||||
|
// dynamic or static system lib, just link regularly searching system library paths
|
||||||
|
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
// This points the linker to where the entry point is
|
|
||||||
link_settings = gb_string_appendc(link_settings, " -e _main ");
|
|
||||||
}
|
|
||||||
|
|
||||||
gbString link_command_line = gb_string_make(heap_allocator(), "clang -Wno-unused-command-line-argument ");
|
|
||||||
defer (gb_string_free(link_command_line));
|
|
||||||
|
|
||||||
link_command_line = gb_string_appendc(link_command_line, object_files);
|
gbString object_files = gb_string_make(heap_allocator(), "");
|
||||||
link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s\" ", LIT(output_filename));
|
defer (gb_string_free(object_files));
|
||||||
link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str);
|
for_array(i, gen->output_object_paths) {
|
||||||
link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str);
|
String object_path = gen->output_object_paths[i];
|
||||||
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags));
|
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
|
||||||
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags));
|
}
|
||||||
link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings);
|
|
||||||
|
|
||||||
result = system_exec_command_line_app("ld-link", link_command_line);
|
gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
|
||||||
|
|
||||||
if (result) {
|
if (build_context.no_crt) {
|
||||||
return result;
|
link_settings = gb_string_append_fmt(link_settings, "-nostdlib ");
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(GB_SYSTEM_OSX)
|
// NOTE(dweiler): We use clang as a frontend for the linker as there are
|
||||||
if (build_context.ODIN_DEBUG) {
|
// other runtime and compiler support libraries that need to be linked in
|
||||||
// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
|
// very specific orders such as libgcc_s, ld-linux-so, unwind, etc.
|
||||||
// to the symbols in the object file
|
// These are not always typically inside /lib, /lib64, or /usr versions
|
||||||
result = system_exec_command_line_app("dsymutil", "dsymutil %.*s", LIT(output_filename));
|
// of that, e.g libgcc.a is in /usr/lib/gcc/{version}, and can vary on
|
||||||
|
// the distribution of Linux even. The gcc or clang specs is the only
|
||||||
|
// reliable way to query this information to call ld directly.
|
||||||
|
if (build_context.build_mode == BuildMode_DynamicLibrary) {
|
||||||
|
// NOTE(dweiler): Let the frontend know we're building a shared library
|
||||||
|
// so it doesn't generate symbols which cannot be relocated.
|
||||||
|
link_settings = gb_string_appendc(link_settings, "-shared ");
|
||||||
|
|
||||||
|
// NOTE(dweiler): _odin_entry_point must be called at initialization
|
||||||
|
// time of the shared object, similarly, _odin_exit_point must be called
|
||||||
|
// at deinitialization. We can pass both -init and -fini to the linker by
|
||||||
|
// using a comma separated list of arguments to -Wl.
|
||||||
|
//
|
||||||
|
// This previously used ld but ld cannot actually build a shared library
|
||||||
|
// correctly this way since all the other dependencies provided implicitly
|
||||||
|
// by the compiler frontend are still needed and most of the command
|
||||||
|
// line arguments prepared previously are incompatible with ld.
|
||||||
|
link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
|
||||||
|
link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
|
||||||
|
} else if (build_context.metrics.os != TargetOs_openbsd) {
|
||||||
|
// OpenBSD defaults to PIE executable. do not pass -no-pie for it.
|
||||||
|
link_settings = gb_string_appendc(link_settings, "-no-pie ");
|
||||||
|
}
|
||||||
|
|
||||||
|
gbString platform_lib_str = gb_string_make(heap_allocator(), "");
|
||||||
|
defer (gb_string_free(platform_lib_str));
|
||||||
|
if (build_context.metrics.os == TargetOs_darwin) {
|
||||||
|
platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib");
|
||||||
|
} else {
|
||||||
|
platform_lib_str = gb_string_appendc(platform_lib_str, "-lc -lm");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (build_context.metrics.os == TargetOs_darwin) {
|
||||||
|
// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
|
||||||
|
// NOTE: If you change this (although this minimum is as low as you can go with Odin working)
|
||||||
|
// make sure to also change the 'mtriple' param passed to 'opt'
|
||||||
|
if (build_context.metrics.arch == TargetArch_arm64) {
|
||||||
|
link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=12.0.0 ");
|
||||||
|
} else {
|
||||||
|
link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=10.8.0 ");
|
||||||
|
}
|
||||||
|
// This points the linker to where the entry point is
|
||||||
|
link_settings = gb_string_appendc(link_settings, " -e _main ");
|
||||||
|
}
|
||||||
|
|
||||||
|
gbString link_command_line = gb_string_make(heap_allocator(), "clang -Wno-unused-command-line-argument ");
|
||||||
|
defer (gb_string_free(link_command_line));
|
||||||
|
|
||||||
|
link_command_line = gb_string_appendc(link_command_line, object_files);
|
||||||
|
link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s\" ", LIT(output_filename));
|
||||||
|
link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str);
|
||||||
|
link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str);
|
||||||
|
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags));
|
||||||
|
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags));
|
||||||
|
link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings);
|
||||||
|
|
||||||
|
result = system_exec_command_line_app("ld-link", link_command_line);
|
||||||
|
|
||||||
if (result) {
|
if (result) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#endif
|
if (is_osx && build_context.ODIN_DEBUG) {
|
||||||
|
// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
|
||||||
|
// to the symbols in the object file
|
||||||
|
result = system_exec_command_line_app("dsymutil", "dsymutil %.*s", LIT(output_filename));
|
||||||
|
|
||||||
|
if (result) {
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
+1
-1
@@ -13,7 +13,7 @@ struct PtrSet {
|
|||||||
template <typename T> void ptr_set_init (PtrSet<T> *s, gbAllocator a, isize capacity = 16);
|
template <typename T> void ptr_set_init (PtrSet<T> *s, gbAllocator a, isize capacity = 16);
|
||||||
template <typename T> void ptr_set_destroy(PtrSet<T> *s);
|
template <typename T> void ptr_set_destroy(PtrSet<T> *s);
|
||||||
template <typename T> T ptr_set_add (PtrSet<T> *s, T ptr);
|
template <typename T> T ptr_set_add (PtrSet<T> *s, T ptr);
|
||||||
template <typename T> bool ptr_set_update (PtrSet<T> *s, T ptr); // returns true if it previously existsed
|
template <typename T> bool ptr_set_update (PtrSet<T> *s, T ptr); // returns true if it previously existed
|
||||||
template <typename T> bool ptr_set_exists (PtrSet<T> *s, T ptr);
|
template <typename T> bool ptr_set_exists (PtrSet<T> *s, T ptr);
|
||||||
template <typename T> void ptr_set_remove (PtrSet<T> *s, T ptr);
|
template <typename T> void ptr_set_remove (PtrSet<T> *s, T ptr);
|
||||||
template <typename T> void ptr_set_clear (PtrSet<T> *s);
|
template <typename T> void ptr_set_clear (PtrSet<T> *s);
|
||||||
|
|||||||
@@ -13,6 +13,7 @@ struct StringSet {
|
|||||||
void string_set_init (StringSet *s, gbAllocator a, isize capacity = 16);
|
void string_set_init (StringSet *s, gbAllocator a, isize capacity = 16);
|
||||||
void string_set_destroy(StringSet *s);
|
void string_set_destroy(StringSet *s);
|
||||||
void string_set_add (StringSet *s, String const &str);
|
void string_set_add (StringSet *s, String const &str);
|
||||||
|
bool string_set_update (StringSet *s, String const &str); // returns true if it previously existed
|
||||||
bool string_set_exists (StringSet *s, String const &str);
|
bool string_set_exists (StringSet *s, String const &str);
|
||||||
void string_set_remove (StringSet *s, String const &str);
|
void string_set_remove (StringSet *s, String const &str);
|
||||||
void string_set_clear (StringSet *s);
|
void string_set_clear (StringSet *s);
|
||||||
@@ -149,6 +150,34 @@ void string_set_add(StringSet *s, String const &str) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool string_set_update(StringSet *s, String const &str) {
|
||||||
|
bool exists = false;
|
||||||
|
MapIndex index;
|
||||||
|
MapFindResult fr;
|
||||||
|
StringHashKey key = string_hash_string(str);
|
||||||
|
if (s->hashes.count == 0) {
|
||||||
|
string_set_grow(s);
|
||||||
|
}
|
||||||
|
fr = string_set__find(s, key);
|
||||||
|
if (fr.entry_index != MAP_SENTINEL) {
|
||||||
|
index = fr.entry_index;
|
||||||
|
exists = true;
|
||||||
|
} else {
|
||||||
|
index = string_set__add_entry(s, key);
|
||||||
|
if (fr.entry_prev != MAP_SENTINEL) {
|
||||||
|
s->entries[fr.entry_prev].next = index;
|
||||||
|
} else {
|
||||||
|
s->hashes[fr.hash_index] = index;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
s->entries[index].value = str;
|
||||||
|
|
||||||
|
if (string_set__full(s)) {
|
||||||
|
string_set_grow(s);
|
||||||
|
}
|
||||||
|
return exists;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
void string_set__erase(StringSet *s, MapFindResult fr) {
|
void string_set__erase(StringSet *s, MapFindResult fr) {
|
||||||
MapFindResult last;
|
MapFindResult last;
|
||||||
|
|||||||
Reference in New Issue
Block a user