Browse Source

Make the link order of foreign imports deterministic

gingerBill 3 years ago
parent
commit
cec049b7d3
6 changed files with 370 additions and 300 deletions
  1. 37 18
      src/llvm_backend.cpp
  2. 4 1
      src/llvm_backend.hpp
  3. 5 2
      src/llvm_backend_general.cpp
  4. 294 278
      src/main.cpp
  5. 1 1
      src/ptr_set.cpp
  6. 29 0
      src/string_set.cpp

+ 37 - 18
src/llvm_backend.cpp

@@ -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) {
-		String library_path = e->LibraryName.paths[i];
-		if (library_path.len == 0) {
-			continue;
-		}
+	mutex_lock(&m->gen->foreign_mutex);
+	if (!ptr_set_update(&m->gen->foreign_libraries_set, e)) {
+		array_add(&m->gen->foreign_libraries, e);
+	}
+	mutex_unlock(&m->gen->foreign_mutex);
+}
 
 
-		bool ok = true;
-		for_array(path_index, m->foreign_library_paths) {
-			String path = m->foreign_library_paths[path_index];
-	#if defined(GB_SYSTEM_WINDOWS)
-			if (str_eq_ignore_case(path, library_path)) {
-	#else
-			if (str_eq(path, library_path)) {
-	#endif
-				ok = false;
-				break;
-			}
+GB_COMPARE_PROC(foreign_library_cmp) {
+	int cmp = 0;
+	Entity *x = *(Entity **)a;
+	Entity *y = *(Entity **)b;
+	if (x == y) {
+		return 0;
+	}
+
+	if (x->pkg != y->pkg) {
+		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);
 
 
-		if (ok) {
-			array_add(&m->foreign_library_paths, library_path);
+		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);
 }
 }

+ 4 - 1
src/llvm_backend.hpp

@@ -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;
 };
 };

+ 5 - 2
src/llvm_backend_general.cpp

@@ -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;

+ 294 - 278
src/main.cpp

@@ -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");
-		if (build_context.use_lld) {
-			section_name = str_lit("lld-link");
-		}
-		timings_start_section(timings, section_name);
+		bool is_windows = true;
+	#else
+		bool is_windows = false;
+	#endif
+	#if defined(GB_SYSTEM_OSX)
+		bool is_osx = true;
+	#else
+		bool is_osx = false;
+	#endif
+
 
 
-		gbString lib_str = gb_string_make(heap_allocator(), "");
-		defer (gb_string_free(lib_str));
+		if (is_windows) {
+			String section_name = str_lit("msvc-link");
+			if (build_context.use_lld) {
+				section_name = str_lit("lld-link");
+			}
+			timings_start_section(timings, section_name);
 
 
-		gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
-		defer (gb_string_free(link_settings));
+			gbString lib_str = gb_string_make(heap_allocator(), "");
+			defer (gb_string_free(lib_str));
 
 
-		// 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);
-		}
+			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 = {};
-		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));
+			StringSet libs = {};
+			string_set_init(&libs, heap_allocator(), 64);
+			defer (string_set_destroy(&libs));
 
 
-		for_array(j, gen->modules.entries) {
-			lbModule *m = gen->modules.entries[j].value;
-			for_array(i, m->foreign_library_paths) {
-				String lib = m->foreign_library_paths[i];
-				if (has_asm_extension(lib)) {
-					string_set_add(&asm_files, lib);
-				} else {
-					string_set_add(&libs, lib);
+			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) {
-			String lib = gen->default_module.foreign_library_paths[i];
-			if (has_asm_extension(lib)) {
-				string_set_add(&asm_files, lib);
+			if (build_context.build_mode == BuildMode_DynamicLibrary) {
+				link_settings = gb_string_append_fmt(link_settings, " /DLL");
 			} else {
 			} else {
-				string_set_add(&libs, lib);
+				link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
 			}
 			}
-		}
-
-		for_array(i, libs.entries) {
-			String lib = libs.entries[i].value;
-			lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib));
-		}
-
-
-		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 (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 (result) {
-				return result;
+			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");
 			}
 			}
-			array_add(&gen->output_object_paths, obj_file);
-		}
 
 
-		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));
-		}
+			if (build_context.ODIN_DEBUG) {
+				link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
+			}
 
 
-		String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]);
-		defer (gb_free(heap_allocator(), vs_exe_path.text));
+			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));
+			}
 
 
-		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));
+			String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]);
+			defer (gb_free(heap_allocator(), vs_exe_path.text));
+
+			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",
-					"\"rc.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
-					LIT(res_path),
-					LIT(rc_path)
-				);
+					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",
-					"\"%.*slink.exe\" %s \"%.*s\" -OUT:\"%.*s\" %s "
+			} 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 "
 					"/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),
-					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),
+					LIT(build_context.ODIN_ROOT), 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) {
-				return result;
+				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];
+			#if !defined(GB_SYSTEM_WINDOWS)
+			getcwd(&cwd[0], 256);
+			#endif
+			//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));
+
+			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));
+						}
+					}
+				}
 			}
 			}
 
 
-		} 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;
+			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));
 			}
 			}
-		}
-	#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);
+			gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
 
 
-		// 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));
+			if (build_context.no_crt) {
+				link_settings = gb_string_append_fmt(link_settings, "-nostdlib ");
+			}
 
 
-		for_array(i, gen->default_module.foreign_library_paths) {
-			String lib = gen->default_module.foreign_library_paths[i];
+			// 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 ");
+			}
 
 
-			// 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.
+			gbString platform_lib_str = gb_string_make(heap_allocator(), "");
+			defer (gb_string_free(platform_lib_str));
 			if (build_context.metrics.os == TargetOs_darwin) {
 			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));
-				}
+				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 {
-				// 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));
-				}
+				platform_lib_str = gb_string_appendc(platform_lib_str, "-lc -lm");
 			}
 			}
-		}
-
-		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 {
-			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 ");
+			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 ");
 			}
 			}
-			// 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));
+			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);
+			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) {
-			return result;
-		}
-
-	#if defined(GB_SYSTEM_OSX)
-		if (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));
+			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
src/ptr_set.cpp

@@ -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);

+ 29 - 0
src/string_set.cpp

@@ -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;