aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorjason <jkercher@rlcsystems.com>2022-05-16 13:49:57 -0400
committerjason <jkercher@rlcsystems.com>2022-05-16 13:49:57 -0400
commitfff23e2bbbd1574debce9e0dee894f3cc84a04c4 (patch)
tree4055ea217375d34693861b39fc284e411f7c0366 /src
parent97d1a6787189d7630650612f44c393f7a635019a (diff)
parent33895b6d927c70167f3bfa64c6cc1c15c4e428c5 (diff)
merge from upstream and convert to ^File types
Diffstat (limited to 'src')
-rw-r--r--src/big_int.cpp30
-rw-r--r--src/build_settings.cpp261
-rw-r--r--src/check_builtin.cpp33
-rw-r--r--src/check_decl.cpp20
-rw-r--r--src/check_expr.cpp11
-rw-r--r--src/checker.cpp98
-rw-r--r--src/checker.hpp1
-rw-r--r--src/checker_builtin_procs.hpp4
-rw-r--r--src/common.cpp257
-rw-r--r--src/entity.cpp1
-rw-r--r--src/exact_value.cpp9
-rw-r--r--src/gb/gb.h46
-rw-r--r--src/llvm_abi.cpp88
-rw-r--r--src/llvm_backend.cpp76
-rw-r--r--src/llvm_backend.hpp5
-rw-r--r--src/llvm_backend_general.cpp8
-rw-r--r--src/llvm_backend_proc.cpp193
-rw-r--r--src/main.cpp720
-rw-r--r--src/parser.cpp16
-rw-r--r--src/parser.hpp1
-rw-r--r--src/path.cpp394
-rw-r--r--src/ptr_set.cpp2
-rw-r--r--src/string.cpp37
-rw-r--r--src/string_set.cpp29
24 files changed, 1480 insertions, 860 deletions
diff --git a/src/big_int.cpp b/src/big_int.cpp
index 20f940e8e..5509545ca 100644
--- a/src/big_int.cpp
+++ b/src/big_int.cpp
@@ -40,7 +40,7 @@ typedef mp_int BigInt;
void big_int_from_u64(BigInt *dst, u64 x);
void big_int_from_i64(BigInt *dst, i64 x);
void big_int_init (BigInt *dst, BigInt const *src);
-void big_int_from_string(BigInt *dst, String const &s);
+void big_int_from_string(BigInt *dst, String const &s, bool *success);
void big_int_dealloc(BigInt *dst) {
mp_clear(dst);
@@ -84,7 +84,7 @@ void big_int_quo_eq(BigInt *dst, BigInt const *x);
void big_int_rem_eq(BigInt *dst, BigInt const *x);
bool big_int_is_neg(BigInt const *x);
-
+void big_int_neg(BigInt *dst, BigInt const *x);
void big_int_add_eq(BigInt *dst, BigInt const *x) {
BigInt res = {};
@@ -169,7 +169,11 @@ BigInt big_int_make_i64(i64 x) {
}
-void big_int_from_string(BigInt *dst, String const &s) {
+void big_int_from_string(BigInt *dst, String const &s, bool *success) {
+ *success = true;
+
+ bool is_negative = false;
+
u64 base = 10;
bool has_prefix = false;
if (s.len > 2 && s[0] == '0') {
@@ -197,11 +201,26 @@ void big_int_from_string(BigInt *dst, String const &s) {
isize i = 0;
for (; i < len; i++) {
Rune r = cast(Rune)text[i];
+
+ if (r == '-') {
+ if (is_negative) {
+ // NOTE(Jeroen): Can't have a doubly negative number.
+ *success = false;
+ return;
+ }
+ is_negative = true;
+ continue;
+ }
+
if (r == '_') {
continue;
}
u64 v = u64_digit_value(r);
if (v >= base) {
+ // NOTE(Jeroen): Can still be a valid integer if the next character is an `e` or `E`.
+ if (r != 'e' && r != 'E') {
+ *success = false;
+ }
break;
}
BigInt val = big_int_make_u64(v);
@@ -225,6 +244,7 @@ void big_int_from_string(BigInt *dst, String const &s) {
if (gb_char_is_digit(r)) {
v = u64_digit_value(r);
} else {
+ *success = false;
break;
}
exp *= 10;
@@ -234,6 +254,10 @@ void big_int_from_string(BigInt *dst, String const &s) {
big_int_mul_eq(dst, &b);
}
}
+
+ if (is_negative) {
+ big_int_neg(dst, dst);
+ }
}
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 2f3eb03a5..e596e54e5 100644
--- a/src/build_settings.cpp
+++ b/src/build_settings.cpp
@@ -3,7 +3,6 @@
#include <sys/sysctl.h>
#endif
-
// #if defined(GB_SYSTEM_WINDOWS)
// #define DEFAULT_TO_THREADED_CHECKER
// #endif
@@ -31,6 +30,7 @@ enum TargetArchKind : u16 {
TargetArch_amd64,
TargetArch_i386,
+ TargetArch_arm32,
TargetArch_arm64,
TargetArch_wasm32,
TargetArch_wasm64,
@@ -76,6 +76,7 @@ String target_arch_names[TargetArch_COUNT] = {
str_lit(""),
str_lit("amd64"),
str_lit("i386"),
+ str_lit("arm32"),
str_lit("arm64"),
str_lit("wasm32"),
str_lit("wasm64"),
@@ -99,6 +100,7 @@ TargetEndianKind target_endians[TargetArch_COUNT] = {
TargetEndian_Little,
TargetEndian_Little,
TargetEndian_Little,
+ TargetEndian_Little,
};
#ifndef ODIN_VERSION_RAW
@@ -198,6 +200,22 @@ enum RelocMode : u8 {
RelocMode_DynamicNoPIC,
};
+enum BuildPath : u8 {
+ BuildPath_Main_Package, // Input Path to the package directory (or file) we're building.
+ BuildPath_RC, // Input Path for .rc file, can be set with `-resource:`.
+ BuildPath_RES, // Output Path for .res file, generated from previous.
+ BuildPath_Win_SDK_Root, // windows_sdk_root
+ BuildPath_Win_SDK_UM_Lib, // windows_sdk_um_library_path
+ BuildPath_Win_SDK_UCRT_Lib, // windows_sdk_ucrt_library_path
+ BuildPath_VS_EXE, // vs_exe_path
+ BuildPath_VS_LIB, // vs_library_path
+
+ BuildPath_Output, // Output Path for .exe, .dll, .so, etc. Can be overridden with `-out:`.
+ BuildPath_PDB, // Output Path for .pdb file, can be overridden with `-pdb-name:`.
+
+ BuildPathCOUNT,
+};
+
// This stores the information for the specify architecture of this build
struct BuildContext {
// Constants
@@ -226,9 +244,13 @@ struct BuildContext {
bool show_help;
+ Array<Path> build_paths; // Contains `Path` objects to output filename, pdb, resource and intermediate files.
+ // BuildPath enum contains the indices of paths we know *before* the work starts.
+
String out_filepath;
String resource_filepath;
String pdb_filepath;
+
bool has_resource;
String link_flags;
String extra_linker_flags;
@@ -300,8 +322,6 @@ struct BuildContext {
};
-
-
gb_global BuildContext build_context = {0};
bool global_warnings_as_errors(void) {
@@ -350,7 +370,16 @@ gb_global TargetMetrics target_linux_arm64 = {
8,
16,
str_lit("aarch64-linux-elf"),
- str_lit("e-m:e-i8:8:32-i16:32-i64:64-i128:128-n32:64-S128"),
+ str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"),
+};
+
+gb_global TargetMetrics target_linux_arm32 = {
+ TargetOs_linux,
+ TargetArch_arm32,
+ 4,
+ 8,
+ str_lit("arm-linux-gnu"),
+ str_lit("e-m:o-p:32:32-Fi8-i64:64-v128:64:128-a:0:32-n32-S64"),
};
gb_global TargetMetrics target_darwin_amd64 = {
@@ -466,6 +495,7 @@ gb_global NamedTargetMetrics named_targets[] = {
{ str_lit("linux_i386"), &target_linux_i386 },
{ str_lit("linux_amd64"), &target_linux_amd64 },
{ str_lit("linux_arm64"), &target_linux_arm64 },
+ { str_lit("linux_arm32"), &target_linux_arm32 },
{ str_lit("windows_i386"), &target_windows_i386 },
{ str_lit("windows_amd64"), &target_windows_amd64 },
{ str_lit("freebsd_i386"), &target_freebsd_i386 },
@@ -605,28 +635,6 @@ bool allow_check_foreign_filepath(void) {
// is_abs_path
// has_subdir
-enum TargetFileValidity : u8 {
- TargetFileValidity_Invalid,
-
- TargetFileValidity_Writable_File,
- TargetFileValidity_No_Write_Permission,
- TargetFileValidity_Directory,
-
- TargetTargetFileValidity_COUNT,
-};
-
-TargetFileValidity set_output_filename(void) {
- // Assembles the output filename from build_context information.
- // Returns `true` if it doesn't exist or is a file.
- // Returns `false` if a directory or write-protected file.
-
-
-
-
- return TargetFileValidity_Writable_File;
-}
-
-
String const WIN32_SEPARATOR_STRING = {cast(u8 *)"\\", 1};
String const NIX_SEPARATOR_STRING = {cast(u8 *)"/", 1};
@@ -973,7 +981,6 @@ char *token_pos_to_string(TokenPos const &pos) {
return s;
}
-
void init_build_context(TargetMetrics *cross_target) {
BuildContext *bc = &build_context;
@@ -1120,6 +1127,15 @@ void init_build_context(TargetMetrics *cross_target) {
bc->link_flags = str_lit("-arch x86 ");
break;
}
+ } else if (bc->metrics.arch == TargetArch_arm32) {
+ switch (bc->metrics.os) {
+ case TargetOs_linux:
+ bc->link_flags = str_lit("-arch arm ");
+ break;
+ default:
+ gb_printf_err("Compiler Error: Unsupported architecture\n");
+ gb_exit(1);
+ }
} else if (bc->metrics.arch == TargetArch_arm64) {
switch (bc->metrics.os) {
case TargetOs_darwin:
@@ -1152,8 +1168,195 @@ void init_build_context(TargetMetrics *cross_target) {
bc->optimization_level = gb_clamp(bc->optimization_level, 0, 3);
-
-
#undef LINK_FLAG_X64
#undef LINK_FLAG_386
}
+
+#if defined(GB_SYSTEM_WINDOWS)
+// NOTE(IC): In order to find Visual C++ paths without relying on environment variables.
+// NOTE(Jeroen): No longer needed in `main.cpp -> linker_stage`. We now resolve those paths in `init_build_paths`.
+#include "microsoft_craziness.h"
+#endif
+
+// NOTE(Jeroen): Set/create the output and other paths and report an error as appropriate.
+// We've previously called `parse_build_flags`, so `out_filepath` should be set.
+bool init_build_paths(String init_filename) {
+ gbAllocator ha = heap_allocator();
+ BuildContext *bc = &build_context;
+
+ // NOTE(Jeroen): We're pre-allocating BuildPathCOUNT slots so that certain paths are always at the same enumerated index.
+ array_init(&bc->build_paths, permanent_allocator(), BuildPathCOUNT);
+
+ // [BuildPathMainPackage] Turn given init path into a `Path`, which includes normalizing it into a full path.
+ bc->build_paths[BuildPath_Main_Package] = path_from_string(ha, init_filename);
+
+ bool produces_output_file = false;
+ if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) {
+ produces_output_file = true;
+ } else if (bc->command_kind & Command__does_build) {
+ produces_output_file = true;
+ }
+
+ if (!produces_output_file) {
+ // Command doesn't produce output files. We're done.
+ return true;
+ }
+
+ #if defined(GB_SYSTEM_WINDOWS)
+ if (bc->resource_filepath.len > 0) {
+ bc->build_paths[BuildPath_RC] = path_from_string(ha, bc->resource_filepath);
+ bc->build_paths[BuildPath_RES] = path_from_string(ha, bc->resource_filepath);
+ bc->build_paths[BuildPath_RC].ext = copy_string(ha, STR_LIT("rc"));
+ bc->build_paths[BuildPath_RES].ext = copy_string(ha, STR_LIT("res"));
+ }
+
+ if (bc->pdb_filepath.len > 0) {
+ bc->build_paths[BuildPath_PDB] = path_from_string(ha, bc->pdb_filepath);
+ }
+
+ if ((bc->command_kind & Command__does_build) && (!bc->ignore_microsoft_magic)) {
+ // NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
+ Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8();
+
+ if (find_result.windows_sdk_version == 0) {
+ gb_printf_err("Windows SDK not found.\n");
+ return false;
+ }
+
+ if (find_result.windows_sdk_um_library_path.len > 0) {
+ GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
+
+ if (find_result.windows_sdk_root.len > 0) {
+ bc->build_paths[BuildPath_Win_SDK_Root] = path_from_string(ha, find_result.windows_sdk_root);
+ }
+
+ if (find_result.windows_sdk_um_library_path.len > 0) {
+ bc->build_paths[BuildPath_Win_SDK_UM_Lib] = path_from_string(ha, find_result.windows_sdk_um_library_path);
+ }
+
+ if (find_result.windows_sdk_ucrt_library_path.len > 0) {
+ bc->build_paths[BuildPath_Win_SDK_UCRT_Lib] = path_from_string(ha, find_result.windows_sdk_ucrt_library_path);
+ }
+
+ if (find_result.vs_exe_path.len > 0) {
+ bc->build_paths[BuildPath_VS_EXE] = path_from_string(ha, find_result.vs_exe_path);
+ }
+
+ if (find_result.vs_library_path.len > 0) {
+ bc->build_paths[BuildPath_VS_LIB] = path_from_string(ha, find_result.vs_library_path);
+ }
+ }
+
+ gb_free(ha, find_result.windows_sdk_root.text);
+ gb_free(ha, find_result.windows_sdk_um_library_path.text);
+ gb_free(ha, find_result.windows_sdk_ucrt_library_path.text);
+ gb_free(ha, find_result.vs_exe_path.text);
+ gb_free(ha, find_result.vs_library_path.text);
+
+ }
+ #endif
+
+ // All the build targets and OSes.
+ String output_extension;
+
+ if (bc->command_kind == Command_doc && bc->cmd_doc_flags & CmdDocFlag_DocFormat) {
+ output_extension = STR_LIT("odin-doc");
+ } else if (is_arch_wasm()) {
+ output_extension = STR_LIT("wasm");
+ } else if (build_context.build_mode == BuildMode_Executable) {
+ // By default use a .bin executable extension.
+ output_extension = STR_LIT("bin");
+
+ if (build_context.metrics.os == TargetOs_windows) {
+ output_extension = STR_LIT("exe");
+ } else if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
+ output_extension = make_string(nullptr, 0);
+ }
+ } else if (build_context.build_mode == BuildMode_DynamicLibrary) {
+ // By default use a .so shared library extension.
+ output_extension = STR_LIT("so");
+
+ if (build_context.metrics.os == TargetOs_windows) {
+ output_extension = STR_LIT("dll");
+ } else if (build_context.metrics.os == TargetOs_darwin) {
+ output_extension = STR_LIT("dylib");
+ }
+ } else if (build_context.build_mode == BuildMode_Object) {
+ // By default use a .o object extension.
+ output_extension = STR_LIT("o");
+
+ if (build_context.metrics.os == TargetOs_windows) {
+ output_extension = STR_LIT("obj");
+ }
+ } else if (build_context.build_mode == BuildMode_Assembly) {
+ // By default use a .S asm extension.
+ output_extension = STR_LIT("S");
+ } else if (build_context.build_mode == BuildMode_LLVM_IR) {
+ output_extension = STR_LIT("ll");
+ } else {
+ GB_PANIC("Unhandled build mode/target combination.\n");
+ }
+
+ if (bc->out_filepath.len > 0) {
+ bc->build_paths[BuildPath_Output] = path_from_string(ha, bc->out_filepath);
+ if (build_context.metrics.os == TargetOs_windows) {
+ String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
+ defer (gb_free(ha, output_file.text));
+ if (path_is_directory(bc->build_paths[BuildPath_Output])) {
+ gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
+ return false;
+ } else if (bc->build_paths[BuildPath_Output].ext.len == 0) {
+ gb_printf_err("Output path %.*s must have an appropriate extension.\n", LIT(output_file));
+ return false;
+ }
+ }
+ } else {
+ Path output_path;
+
+ if (str_eq(init_filename, str_lit("."))) {
+ // We must name the output file after the current directory.
+ debugf("Output name will be created from current base name %.*s.\n", LIT(bc->build_paths[BuildPath_Main_Package].basename));
+ String last_element = last_path_element(bc->build_paths[BuildPath_Main_Package].basename);
+
+ if (last_element.len == 0) {
+ gb_printf_err("The output name is created from the last path element. `%.*s` has none. Use `-out:output_name.ext` to set it.\n", LIT(bc->build_paths[BuildPath_Main_Package].basename));
+ return false;
+ }
+ output_path.basename = copy_string(ha, bc->build_paths[BuildPath_Main_Package].basename);
+ output_path.name = copy_string(ha, last_element);
+
+ } else {
+ // Init filename was not 'current path'.
+ // Contruct the output name from the path elements as usual.
+ String output_name = remove_directory_from_path(init_filename);
+ output_name = remove_extension_from_path(output_name);
+ output_name = copy_string(ha, string_trim_whitespace(output_name));
+ output_path = path_from_string(ha, output_name);
+
+ // Replace extension.
+ if (output_path.ext.len > 0) {
+ gb_free(ha, output_path.ext.text);
+ }
+ }
+ output_path.ext = copy_string(ha, output_extension);
+
+ bc->build_paths[BuildPath_Output] = output_path;
+ }
+
+ // Do we have an extension? We might not if the output filename was supplied.
+ if (bc->build_paths[BuildPath_Output].ext.len == 0) {
+ if (build_context.metrics.os == TargetOs_windows || build_context.build_mode != BuildMode_Executable) {
+ bc->build_paths[BuildPath_Output].ext = copy_string(ha, output_extension);
+ }
+ }
+
+ // Check if output path is a directory.
+ if (path_is_directory(bc->build_paths[BuildPath_Output])) {
+ String output_file = path_to_string(ha, bc->build_paths[BuildPath_Output]);
+ defer (gb_free(ha, output_file.text));
+ gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file));
+ return false;
+ }
+
+ return true;
+} \ No newline at end of file
diff --git a/src/check_builtin.cpp b/src/check_builtin.cpp
index e055539c5..9a5d1c554 100644
--- a/src/check_builtin.cpp
+++ b/src/check_builtin.cpp
@@ -29,6 +29,7 @@ BuiltinTypeIsProc *builtin_type_is_procs[BuiltinProc__type_simple_boolean_end -
is_type_named,
is_type_pointer,
+ is_type_multi_pointer,
is_type_array,
is_type_enumerated_array,
is_type_slice,
@@ -3866,6 +3867,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_type_is_valid_matrix_elements:
case BuiltinProc_type_is_named:
case BuiltinProc_type_is_pointer:
+ case BuiltinProc_type_is_multi_pointer:
case BuiltinProc_type_is_array:
case BuiltinProc_type_is_enumerated_array:
case BuiltinProc_type_is_slice:
@@ -3926,6 +3928,37 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
break;
}
break;
+ case BuiltinProc_type_field_type:
+ {
+ Operand op = {};
+ Type *bt = check_type(c, ce->args[0]);
+ Type *type = base_type(bt);
+ if (type == nullptr || type == t_invalid) {
+ error(ce->args[0], "Expected a type for '%.*s'", LIT(builtin_name));
+ return false;
+ }
+ Operand x = {};
+ check_expr(c, &x, ce->args[1]);
+
+ if (!is_type_string(x.type) || x.mode != Addressing_Constant || x.value.kind != ExactValue_String) {
+ error(ce->args[1], "Expected a const string for field argument");
+ return false;
+ }
+
+ String field_name = x.value.value_string;
+
+ Selection sel = lookup_field(type, field_name, false);
+ if (sel.index.count == 0) {
+ gbString t = type_to_string(type);
+ error(ce->args[1], "'%.*s' is not a field of type %s", LIT(field_name), t);
+ gb_string_free(t);
+ return false;
+ }
+ operand->mode = Addressing_Type;
+ operand->type = sel.entity->type;
+ break;
+ }
+ break;
case BuiltinProc_type_is_specialization_of:
{
diff --git a/src/check_decl.cpp b/src/check_decl.cpp
index 5acd56097..82ac6c677 100644
--- a/src/check_decl.cpp
+++ b/src/check_decl.cpp
@@ -1018,18 +1018,16 @@ void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
}
Entity *foreign_library = init_entity_foreign_library(ctx, e);
- if (is_arch_wasm()) {
+ if (is_arch_wasm() && foreign_library != nullptr) {
String module_name = str_lit("env");
- if (foreign_library != nullptr) {
- GB_ASSERT (foreign_library->kind == Entity_LibraryName);
- if (foreign_library->LibraryName.paths.count != 1) {
- error(foreign_library->token, "'foreign import' for '%.*s' architecture may only have one path, got %td",
- LIT(target_arch_names[build_context.metrics.arch]), foreign_library->LibraryName.paths.count);
- }
-
- if (foreign_library->LibraryName.paths.count >= 1) {
- module_name = foreign_library->LibraryName.paths[0];
- }
+ GB_ASSERT (foreign_library->kind == Entity_LibraryName);
+ if (foreign_library->LibraryName.paths.count != 1) {
+ error(foreign_library->token, "'foreign import' for '%.*s' architecture may only have one path, got %td",
+ LIT(target_arch_names[build_context.metrics.arch]), foreign_library->LibraryName.paths.count);
+ }
+
+ if (foreign_library->LibraryName.paths.count >= 1) {
+ module_name = foreign_library->LibraryName.paths[0];
}
name = concatenate3_strings(permanent_allocator(), module_name, WASM_MODULE_NAME_SEPARATOR, name);
}
diff --git a/src/check_expr.cpp b/src/check_expr.cpp
index dcf17af39..f578f8c73 100644
--- a/src/check_expr.cpp
+++ b/src/check_expr.cpp
@@ -7241,7 +7241,11 @@ ExprKind check_ternary_if_expr(CheckerContext *c, Operand *o, Ast *node, Type *t
node->viral_state_flags |= te->x->viral_state_flags;
if (te->y != nullptr) {
- check_expr_or_type(c, &y, te->y, type_hint);
+ Type *th = type_hint;
+ if (type_hint == nullptr && is_type_typed(x.type)) {
+ th = x.type;
+ }
+ check_expr_or_type(c, &y, te->y, th);
node->viral_state_flags |= te->y->viral_state_flags;
} else {
error(node, "A ternary expression must have an else clause");
@@ -8161,7 +8165,10 @@ ExprKind check_compound_literal(CheckerContext *c, Operand *o, Ast *node, Type *
case Type_Basic: {
if (!is_type_any(t)) {
if (cl->elems.count != 0) {
- error(node, "Illegal compound literal");
+ gbString s = type_to_string(t);
+ error(node, "Illegal compound literal, %s cannot be used as a compound literal with fields", s);
+ gb_string_free(s);
+ is_constant = false;
}
break;
}
diff --git a/src/checker.cpp b/src/checker.cpp
index 1bb786ea1..8afc6eb14 100644
--- a/src/checker.cpp
+++ b/src/checker.cpp
@@ -544,6 +544,28 @@ GB_COMPARE_PROC(vetted_entity_variable_pos_cmp) {
return token_pos_cmp(x->token.pos, y->token.pos);
}
+bool check_vet_shadowing_assignment(Checker *c, Entity *shadowed, Ast *expr) {
+ Ast *init = unparen_expr(expr);
+ if (init == nullptr) {
+ return false;
+ }
+ if (init->kind == Ast_Ident) {
+ // TODO(bill): Which logic is better? Same name or same entity
+ // bool ignore = init->Ident.token.string == name;
+ bool ignore = init->Ident.entity == shadowed;
+ if (ignore) {
+ return true;
+ }
+ } else if (init->kind == Ast_TernaryIfExpr) {
+ bool x = check_vet_shadowing_assignment(c, shadowed, init->TernaryIfExpr.x);
+ bool y = check_vet_shadowing_assignment(c, shadowed, init->TernaryIfExpr.y);
+ if (x || y) {
+ return true;
+ }
+ }
+
+ return false;
+}
bool check_vet_shadowing(Checker *c, Entity *e, VettedEntity *ve) {
@@ -594,17 +616,14 @@ bool check_vet_shadowing(Checker *c, Entity *e, VettedEntity *ve) {
}
// NOTE(bill): Ignore intentional redeclaration
- // x := x;
+ // x := x
// Suggested in issue #637 (2020-05-11)
+ // Also allow the following
+ // x := x if cond else y
+ // x := z if cond else x
if ((e->flags & EntityFlag_Using) == 0 && e->kind == Entity_Variable) {
- Ast *init = unparen_expr(e->Variable.init_expr);
- if (init != nullptr && init->kind == Ast_Ident) {
- // TODO(bill): Which logic is better? Same name or same entity
- // bool ignore = init->Ident.token.string == name;
- bool ignore = init->Ident.entity == shadowed;
- if (ignore) {
- return false;
- }
+ if (check_vet_shadowing_assignment(c, shadowed, e->Variable.init_expr)) {
+ return false;
}
}
@@ -944,6 +963,7 @@ void init_universal(void) {
{"Unknown", TargetArch_Invalid},
{"amd64", TargetArch_amd64},
{"i386", TargetArch_i386},
+ {"arm32", TargetArch_arm32},
{"arm64", TargetArch_arm64},
{"wasm32", TargetArch_wasm32},
{"wasm64", TargetArch_wasm64},
@@ -4336,25 +4356,21 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
}
String import_name = path_to_entity_name(id->import_name.string, id->fullpath, false);
+ if (is_blank_ident(import_name)) {
+ force_use = true;
+ }
// NOTE(bill, 2019-05-19): If the directory path is not a valid entity name, force the user to assign a custom one
// if (import_name.len == 0 || import_name == "_") {
// import_name = scope->pkg->name;
// }
- if (import_name.len == 0 || is_blank_ident(import_name)) {
- if (id->is_using) {
- // TODO(bill): Should this be a warning?
- } else {
- if (id->import_name.string == "") {
- String invalid_name = id->fullpath;
- invalid_name = get_invalid_import_name(invalid_name);
+ if (import_name.len == 0) {
+ String invalid_name = id->fullpath;
+ invalid_name = get_invalid_import_name(invalid_name);
- error(id->token, "Import name %.*s, is not a valid identifier. Perhaps you want to reference the package by a different name like this: import <new_name> \"%.*s\" ", LIT(invalid_name), LIT(invalid_name));
- } else {
- error(token, "Import name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string));
- }
- }
+ error(id->token, "Import name %.*s, is not a valid identifier. Perhaps you want to reference the package by a different name like this: import <new_name> \"%.*s\" ", LIT(invalid_name), LIT(invalid_name));
+ error(token, "Import name, %.*s, cannot be use as an import name as it is not a valid identifier", LIT(id->import_name.string));
} else {
GB_ASSERT(id->import_name.pos.line != 0);
id->import_name.string = import_name;
@@ -4363,38 +4379,11 @@ void check_add_import_decl(CheckerContext *ctx, Ast *decl) {
scope);
add_entity(ctx, parent_scope, nullptr, e);
- if (force_use || id->is_using) {
+ if (force_use) {
add_entity_use(ctx, nullptr, e);
}
}
- if (id->is_using) {
- if (parent_scope->flags & ScopeFlag_Global) {
- error(id->import_name, "built-in package imports cannot use using");
- return;
- }
-
- // NOTE(bill): Add imported entities to this file's scope
- for_array(elem_index, scope->elements.entries) {
- String name = scope->elements.entries[elem_index].key.string;
- Entity *e = scope->elements.entries[elem_index].value;
- if (e->scope == parent_scope) continue;
-
- if (is_entity_exported(e, true)) {
- Entity *found = scope_lookup_current(parent_scope, name);
- if (found != nullptr) {
- // NOTE(bill):
- // Date: 2019-03-17
- // The order has to be the other way around as `using` adds the entity into the that
- // file scope otherwise the error would be the wrong way around
- redeclaration_error(name, found, e);
- } else {
- add_entity_with_name(ctx, parent_scope, e->identifier, e, name);
- }
- }
- }
- }
-
scope->flags |= ScopeFlag_HasBeenImported;
}
@@ -4413,6 +4402,14 @@ DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) {
}
ac->require_declaration = true;
return true;
+ } else if (name == "priority_index") {
+ ExactValue ev = check_decl_attribute_value(c, value);
+ if (ev.kind != ExactValue_Integer) {
+ error(elem, "Expected an integer value for '%.*s'", LIT(name));
+ } else {
+ ac->foreign_import_priority_index = exact_value_to_i64(ev);
+ }
+ return true;
}
return false;
}
@@ -4469,6 +4466,9 @@ void check_add_foreign_import_decl(CheckerContext *ctx, Ast *decl) {
mpmc_enqueue(&ctx->info->required_foreign_imports_through_force_queue, e);
add_entity_use(ctx, nullptr, e);
}
+ if (ac.foreign_import_priority_index != 0) {
+ e->LibraryName.priority_index = ac.foreign_import_priority_index;
+ }
if (has_asm_extension(fullpath)) {
if (build_context.metrics.arch != TargetArch_amd64 ||
diff --git a/src/checker.hpp b/src/checker.hpp
index 552e6aca7..1c9ffd8c7 100644
--- a/src/checker.hpp
+++ b/src/checker.hpp
@@ -118,6 +118,7 @@ struct AttributeContext {
bool init : 1;
bool set_cold : 1;
u32 optimization_mode; // ProcedureOptimizationMode
+ i64 foreign_import_priority_index;
String objc_class;
String objc_name;
diff --git a/src/checker_builtin_procs.hpp b/src/checker_builtin_procs.hpp
index fe14ae372..d301cae0c 100644
--- a/src/checker_builtin_procs.hpp
+++ b/src/checker_builtin_procs.hpp
@@ -158,6 +158,7 @@ BuiltinProc__type_simple_boolean_begin,
BuiltinProc_type_is_named,
BuiltinProc_type_is_pointer,
+ BuiltinProc_type_is_multi_pointer,
BuiltinProc_type_is_array,
BuiltinProc_type_is_enumerated_array,
BuiltinProc_type_is_slice,
@@ -179,6 +180,7 @@ BuiltinProc__type_simple_boolean_begin,
BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_has_field,
+ BuiltinProc_type_field_type,
BuiltinProc_type_is_specialization_of,
@@ -375,6 +377,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_is_named"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_pointer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_is_multi_pointer"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_enumerated_array"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_slice"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -395,6 +398,7 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("type_has_field"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+ {STR_LIT("type_field_type"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_specialization_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
diff --git a/src/common.cpp b/src/common.cpp
index aaacda04b..94248fb62 100644
--- a/src/common.cpp
+++ b/src/common.cpp
@@ -675,262 +675,7 @@ wchar_t **command_line_to_wargv(wchar_t *cmd_line, int *_argc) {
#endif
-
-#if defined(GB_SYSTEM_WINDOWS)
- bool path_is_directory(String path) {
- gbAllocator a = heap_allocator();
- String16 wstr = string_to_string16(a, path);
- defer (gb_free(a, wstr.text));
-
- i32 attribs = GetFileAttributesW(wstr.text);
- if (attribs < 0) return false;
-
- return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
- }
-
-#else
- bool path_is_directory(String path) {
- gbAllocator a = heap_allocator();
- char *copy = cast(char *)copy_string(a, path).text;
- defer (gb_free(a, copy));
-
- struct stat s;
- if (stat(copy, &s) == 0) {
- return (s.st_mode & S_IFDIR) != 0;
- }
- return false;
- }
-#endif
-
-
-String path_to_full_path(gbAllocator a, String path) {
- gbAllocator ha = heap_allocator();
- char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
- defer (gb_free(ha, path_c));
-
- char *fullpath = gb_path_get_full_name(a, path_c);
- String res = string_trim_whitespace(make_string_c(fullpath));
-#if defined(GB_SYSTEM_WINDOWS)
- for (isize i = 0; i < res.len; i++) {
- if (res.text[i] == '\\') {
- res.text[i] = '/';
- }
- }
-#endif
- return res;
-}
-
-
-
-struct FileInfo {
- String name;
- String fullpath;
- i64 size;
- bool is_dir;
-};
-
-enum ReadDirectoryError {
- ReadDirectory_None,
-
- ReadDirectory_InvalidPath,
- ReadDirectory_NotExists,
- ReadDirectory_Permission,
- ReadDirectory_NotDir,
- ReadDirectory_Empty,
- ReadDirectory_Unknown,
-
- ReadDirectory_COUNT,
-};
-
-i64 get_file_size(String path) {
- char *c_str = alloc_cstring(heap_allocator(), path);
- defer (gb_free(heap_allocator(), c_str));
-
- gbFile f = {};
- gbFileError err = gb_file_open(&f, c_str);
- defer (gb_file_close(&f));
- if (err != gbFileError_None) {
- return -1;
- }
- return gb_file_size(&f);
-}
-
-
-#if defined(GB_SYSTEM_WINDOWS)
-ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
- GB_ASSERT(fi != nullptr);
-
- gbAllocator a = heap_allocator();
-
- while (path.len > 0) {
- Rune end = path[path.len-1];
- if (end == '/') {
- path.len -= 1;
- } else if (end == '\\') {
- path.len -= 1;
- } else {
- break;
- }
- }
-
- if (path.len == 0) {
- return ReadDirectory_InvalidPath;
- }
- {
- char *c_str = alloc_cstring(a, path);
- defer (gb_free(a, c_str));
-
- gbFile f = {};
- gbFileError file_err = gb_file_open(&f, c_str);
- defer (gb_file_close(&f));
-
- switch (file_err) {
- case gbFileError_Invalid: return ReadDirectory_InvalidPath;
- case gbFileError_NotExists: return ReadDirectory_NotExists;
- // case gbFileError_Permission: return ReadDirectory_Permission;
- }
- }
-
- if (!path_is_directory(path)) {
- return ReadDirectory_NotDir;
- }
-
-
- char *new_path = gb_alloc_array(a, char, path.len+3);
- defer (gb_free(a, new_path));
-
- gb_memmove(new_path, path.text, path.len);
- gb_memmove(new_path+path.len, "/*", 2);
- new_path[path.len+2] = 0;
-
- String np = make_string(cast(u8 *)new_path, path.len+2);
- String16 wstr = string_to_string16(a, np);
- defer (gb_free(a, wstr.text));
-
- WIN32_FIND_DATAW file_data = {};
- HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
- if (find_file == INVALID_HANDLE_VALUE) {
- return ReadDirectory_Unknown;
- }
- defer (FindClose(find_file));
-
- array_init(fi, a, 0, 100);
-
- do {
- wchar_t *filename_w = file_data.cFileName;
- i64 size = cast(i64)file_data.nFileSizeLow;
- size |= (cast(i64)file_data.nFileSizeHigh) << 32;
- String name = string16_to_string(a, make_string16_c(filename_w));
- if (name == "." || name == "..") {
- gb_free(a, name.text);
- continue;
- }
-
- String filepath = {};
- filepath.len = path.len+1+name.len;
- filepath.text = gb_alloc_array(a, u8, filepath.len+1);
- defer (gb_free(a, filepath.text));
- gb_memmove(filepath.text, path.text, path.len);
- gb_memmove(filepath.text+path.len, "/", 1);
- gb_memmove(filepath.text+path.len+1, name.text, name.len);
-
- FileInfo info = {};
- info.name = name;
- info.fullpath = path_to_full_path(a, filepath);
- info.size = size;
- info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
- array_add(fi, info);
- } while (FindNextFileW(find_file, &file_data));
-
- if (fi->count == 0) {
- return ReadDirectory_Empty;
- }
-
- return ReadDirectory_None;
-}
-#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
-
-#include <dirent.h>
-
-ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
- GB_ASSERT(fi != nullptr);
-
- gbAllocator a = heap_allocator();
-
- char *c_path = alloc_cstring(a, path);
- defer (gb_free(a, c_path));
-
- DIR *dir = opendir(c_path);
- if (!dir) {
- switch (errno) {
- case ENOENT:
- return ReadDirectory_NotExists;
- case EACCES:
- return ReadDirectory_Permission;
- case ENOTDIR:
- return ReadDirectory_NotDir;
- default:
- // ENOMEM: out of memory
- // EMFILE: per-process limit on open fds reached
- // ENFILE: system-wide limit on total open files reached
- return ReadDirectory_Unknown;
- }
- GB_PANIC("unreachable");
- }
-
- array_init(fi, a, 0, 100);
-
- for (;;) {
- struct dirent *entry = readdir(dir);
- if (entry == nullptr) {
- break;
- }
-
- String name = make_string_c(entry->d_name);
- if (name == "." || name == "..") {
- continue;
- }
-
- String filepath = {};
- filepath.len = path.len+1+name.len;
- filepath.text = gb_alloc_array(a, u8, filepath.len+1);
- defer (gb_free(a, filepath.text));
- gb_memmove(filepath.text, path.text, path.len);
- gb_memmove(filepath.text+path.len, "/", 1);
- gb_memmove(filepath.text+path.len+1, name.text, name.len);
- filepath.text[filepath.len] = 0;
-
-
- struct stat dir_stat = {};
-
- if (stat((char *)filepath.text, &dir_stat)) {
- continue;
- }
-
- if (S_ISDIR(dir_stat.st_mode)) {
- continue;
- }
-
- i64 size = dir_stat.st_size;
-
- FileInfo info = {};
- info.name = name;
- info.fullpath = path_to_full_path(a, filepath);
- info.size = size;
- array_add(fi, info);
- }
-
- if (fi->count == 0) {
- return ReadDirectory_Empty;
- }
-
- return ReadDirectory_None;
-}
-#else
-#error Implement read_directory
-#endif
-
-
+#include "path.cpp"
struct LoadedFile {
void *handle;
diff --git a/src/entity.cpp b/src/entity.cpp
index 1f87f7af6..904a630fb 100644
--- a/src/entity.cpp
+++ b/src/entity.cpp
@@ -252,6 +252,7 @@ struct Entity {
struct {
Slice<String> paths;
String name;
+ i64 priority_index;
} LibraryName;
i32 Nil;
struct {
diff --git a/src/exact_value.cpp b/src/exact_value.cpp
index f6df48951..175cb61f6 100644
--- a/src/exact_value.cpp
+++ b/src/exact_value.cpp
@@ -177,7 +177,11 @@ ExactValue exact_value_typeid(Type *type) {
ExactValue exact_value_integer_from_string(String const &string) {
ExactValue result = {ExactValue_Integer};
- big_int_from_string(&result.value_integer, string);
+ bool success;
+ big_int_from_string(&result.value_integer, string, &success);
+ if (!success) {
+ result = {ExactValue_Invalid};
+ }
return result;
}
@@ -591,6 +595,7 @@ failure:
i32 exact_value_order(ExactValue const &v) {
switch (v.kind) {
case ExactValue_Invalid:
+ case ExactValue_Compound:
return 0;
case ExactValue_Bool:
case ExactValue_String:
@@ -607,8 +612,6 @@ i32 exact_value_order(ExactValue const &v) {
return 6;
case ExactValue_Procedure:
return 7;
- // case ExactValue_Compound:
- // return 8;
default:
GB_PANIC("How'd you get here? Invalid Value.kind %d", v.kind);
diff --git a/src/gb/gb.h b/src/gb/gb.h
index b72a893f7..3b2d6434c 100644
--- a/src/gb/gb.h
+++ b/src/gb/gb.h
@@ -6273,20 +6273,44 @@ char *gb_path_get_full_name(gbAllocator a, char const *path) {
#else
char *p, *result, *fullpath = NULL;
isize len;
- p = realpath(path, NULL);
- fullpath = p;
- if (p == NULL) {
- // NOTE(bill): File does not exist
- fullpath = cast(char *)path;
- }
+ fullpath = realpath(path, NULL);
+
+ if (fullpath == NULL) {
+ // NOTE(Jeroen): Path doesn't exist.
+ if (gb_strlen(path) > 0 && path[0] == '/') {
+ // But it is an absolute path, so return as-is.
+
+ fullpath = (char *)path;
+ len = gb_strlen(fullpath) + 1;
+ result = gb_alloc_array(a, char, len + 1);
+
+ gb_memmove(result, fullpath, len);
+ result[len] = 0;
+
+ } else {
+ // Appears to be a relative path, so construct an absolute one relative to <cwd>.
+ char cwd[4096];
+ getcwd(&cwd[0], 4096);
+
+ isize path_len = gb_strlen(path);
+ isize cwd_len = gb_strlen(cwd);
+ len = cwd_len + 1 + path_len + 1;
+ result = gb_alloc_array(a, char, len);
- len = gb_strlen(fullpath);
+ gb_memmove(result, (void *)cwd, cwd_len);
+ result[cwd_len] = '/';
- result = gb_alloc_array(a, char, len + 1);
- gb_memmove(result, fullpath, len);
- result[len] = 0;
- free(p);
+ gb_memmove(result + cwd_len + 1, (void *)path, gb_strlen(path));
+ result[len] = 0;
+ }
+ } else {
+ len = gb_strlen(fullpath) + 1;
+ result = gb_alloc_array(a, char, len + 1);
+ gb_memmove(result, fullpath, len);
+ result[len] = 0;
+ free(fullpath);
+ }
return result;
#endif
}
diff --git a/src/llvm_abi.cpp b/src/llvm_abi.cpp
index 07d2dd6e3..c6ff12f95 100644
--- a/src/llvm_abi.cpp
+++ b/src/llvm_abi.cpp
@@ -516,6 +516,10 @@ namespace lbAbiAmd64SysV {
bool is_register(LLVMTypeRef type) {
LLVMTypeKind kind = LLVMGetTypeKind(type);
+ i64 sz = lb_sizeof(type);
+ if (sz == 0) {
+ return false;
+ }
switch (kind) {
case LLVMIntegerTypeKind:
case LLVMHalfTypeKind:
@@ -1164,6 +1168,88 @@ namespace lbAbiWasm32 {
}
}
+namespace lbAbiArm32 {
+ Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention);
+ lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
+
+ LB_ABI_INFO(abi_info) {
+ lbFunctionType *ft = gb_alloc_item(permanent_allocator(), lbFunctionType);
+ ft->ctx = c;
+ ft->args = compute_arg_types(c, arg_types, arg_count, calling_convention);
+ ft->ret = compute_return_type(c, return_type, return_is_defined);
+ ft->calling_convention = calling_convention;
+ return ft;
+ }
+
+ bool is_register(LLVMTypeRef type, bool is_return) {
+ LLVMTypeKind kind = LLVMGetTypeKind(type);
+ switch (kind) {
+ case LLVMHalfTypeKind:
+ case LLVMFloatTypeKind:
+ case LLVMDoubleTypeKind:
+ return true;
+ case LLVMIntegerTypeKind:
+ return lb_sizeof(type) <= 8;
+ case LLVMFunctionTypeKind:
+ return true;
+ case LLVMPointerTypeKind:
+ return true;
+ case LLVMVectorTypeKind:
+ return true;
+ }
+ return false;
+ }
+
+ lbArgType non_struct(LLVMContextRef c, LLVMTypeRef type, bool is_return) {
+ LLVMAttributeRef attr = nullptr;
+ LLVMTypeRef i1 = LLVMInt1TypeInContext(c);
+ if (type == i1) {
+ attr = lb_create_enum_attribute(c, "zeroext");
+ }
+ return lb_arg_type_direct(type, nullptr, nullptr, attr);
+ }
+
+ Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count, ProcCallingConvention calling_convention) {
+ auto args = array_make<lbArgType>(heap_allocator(), arg_count);
+
+ for (unsigned i = 0; i < arg_count; i++) {
+ LLVMTypeRef t = arg_types[i];
+ if (is_register(t, false)) {
+ args[i] = non_struct(c, t, false);
+ } else {
+ i64 sz = lb_sizeof(t);
+ i64 a = lb_alignof(t);
+ if (is_calling_convention_odin(calling_convention) && sz > 8) {
+ // Minor change to improve performance using the Odin calling conventions
+ args[i] = lb_arg_type_indirect(t, nullptr);
+ } else if (a <= 4) {
+ unsigned n = cast(unsigned)((sz + 3) / 4);
+ args[i] = lb_arg_type_direct(LLVMArrayType(LLVMIntTypeInContext(c, 32), n));
+ } else {
+ unsigned n = cast(unsigned)((sz + 7) / 8);
+ args[i] = lb_arg_type_direct(LLVMArrayType(LLVMIntTypeInContext(c, 64), n));
+ }
+ }
+ }
+ return args;
+ }
+
+ lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) {
+ if (!return_is_defined) {
+ return lb_arg_type_direct(LLVMVoidTypeInContext(c));
+ } else if (!is_register(return_type, true)) {
+ switch (lb_sizeof(return_type)) {
+ case 1: return lb_arg_type_direct(LLVMIntTypeInContext(c, 8), return_type, nullptr, nullptr);
+ case 2: return lb_arg_type_direct(LLVMIntTypeInContext(c, 16), return_type, nullptr, nullptr);
+ case 3: case 4: return lb_arg_type_direct(LLVMIntTypeInContext(c, 32), return_type, nullptr, nullptr);
+ }
+ LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
+ return lb_arg_type_indirect(return_type, attr);
+ }
+ return non_struct(c, return_type, true);
+ }
+};
+
LB_ABI_INFO(lb_get_abi_info) {
switch (calling_convention) {
@@ -1203,6 +1289,8 @@ LB_ABI_INFO(lb_get_abi_info) {
}
case TargetArch_i386:
return lbAbi386::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
+ case TargetArch_arm32:
+ return lbAbiArm32::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
case TargetArch_arm64:
return lbAbiArm64::abi_info(c, arg_types, arg_count, return_type, return_is_defined, calling_convention);
case TargetArch_wasm32:
diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp
index f5cb84785..7cf588853 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -29,29 +29,53 @@ void lb_add_foreign_library_path(lbModule *m, Entity *e) {
GB_ASSERT(e->kind == Entity_LibraryName);
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;
+ }
+ GB_ASSERT(x->kind == Entity_LibraryName);
+ GB_ASSERT(y->kind == Entity_LibraryName);
+
+ cmp = i64_cmp(x->LibraryName.priority_index, y->LibraryName.priority_index);
+ if (cmp) {
+ return cmp;
+ }
+
+ 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) {
@@ -967,7 +991,12 @@ lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *startup_runtime)
}
String lb_filepath_ll_for_module(lbModule *m) {
- String path = m->gen->output_base;
+ String path = concatenate3_strings(permanent_allocator(),
+ build_context.build_paths[BuildPath_Output].basename,
+ STR_LIT("/"),
+ build_context.build_paths[BuildPath_Output].name
+ );
+
if (m->pkg) {
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
} else if (USE_SEPARATE_MODULES) {
@@ -978,7 +1007,12 @@ String lb_filepath_ll_for_module(lbModule *m) {
return path;
}
String lb_filepath_obj_for_module(lbModule *m) {
- String path = m->gen->output_base;
+ String path = concatenate3_strings(permanent_allocator(),
+ build_context.build_paths[BuildPath_Output].basename,
+ STR_LIT("/"),
+ build_context.build_paths[BuildPath_Output].name
+ );
+
if (m->pkg) {
path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
}
@@ -1912,4 +1946,6 @@ void lb_generate_code(lbGenerator *gen) {
}
}
}
+
+ gb_sort_array(gen->foreign_libraries.data, gen->foreign_libraries.count, foreign_library_cmp);
}
diff --git a/src/llvm_backend.hpp b/src/llvm_backend.hpp
index f2bcfaff6..a460b1a23 100644
--- a/src/llvm_backend.hpp
+++ b/src/llvm_backend.hpp
@@ -135,7 +135,6 @@ struct lbModule {
u32 nested_type_name_guid;
Array<lbProcedure *> procedures_to_generate;
- Array<String> foreign_library_paths;
lbProcedure *curr_procedure;
@@ -162,6 +161,10 @@ struct lbGenerator {
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_generated_index;
};
diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp
index 330059622..0866e3687 100644
--- a/src/llvm_backend_general.cpp
+++ b/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->hasher_procs, a);
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);
-
map_init(&m->debug_values, a);
array_init(&m->debug_incomplete_types, a, 0, 1024);
@@ -87,7 +85,6 @@ bool lb_init_generator(lbGenerator *gen, Checker *c) {
return false;
}
-
String init_fullpath = c->parser->init_fullpath;
if (build_context.out_filepath.len == 0) {
@@ -127,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->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) {
for_array(i, gen->info->packages.entries) {
AstPackage *pkg = gen->info->packages.entries[i].value;
diff --git a/src/llvm_backend_proc.cpp b/src/llvm_backend_proc.cpp
index 96ff19d10..a0e9a5da5 100644
--- a/src/llvm_backend_proc.cpp
+++ b/src/llvm_backend_proc.cpp
@@ -189,7 +189,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
GB_ASSERT(entity->kind == Entity_Procedure);
String link_name = entity->Procedure.link_name;
if (entity->flags & EntityFlag_CustomLinkName &&
- link_name != "") {
+ link_name != "") {
if (string_starts_with(link_name, str_lit("__"))) {
LLVMSetLinkage(p->value, LLVMExternalLinkage);
} else {
@@ -200,12 +200,12 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
}
}
lb_set_linkage_from_entity_flags(p->module, p->value, entity->flags);
-
-
+
+
if (p->is_foreign) {
lb_set_wasm_import_attributes(p->value, entity, p->name);
}
-
+
// NOTE(bill): offset==0 is the return value
isize offset = 1;
@@ -280,7 +280,7 @@ lbProcedure *lb_create_procedure(lbModule *m, Entity *entity, bool ignore_body)
if (p->body != nullptr) {
// String debug_name = entity->token.string.text;
String debug_name = p->name;
-
+
p->debug_info = LLVMDIBuilderCreateFunction(m->debug_builder, scope,
cast(char const *)debug_name.text, debug_name.len,
cast(char const *)p->name.text, p->name.len,
@@ -823,12 +823,6 @@ lbValue lb_emit_call(lbProcedure *p, lbValue value, Array<lbValue> const &args,
GB_ASSERT(pt->kind == Type_Proc);
Type *results = pt->Proc.results;
- if (p->entity != nullptr) {
- if (p->entity->flags & EntityFlag_Disabled) {
- return {};
- }
- }
-
lbAddr context_ptr = {};
if (pt->Proc.calling_convention == ProcCC_Odin) {
context_ptr = lb_find_or_generate_context_ptr(p);
@@ -1315,22 +1309,22 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case BuiltinProc_clamp:
return lb_emit_clamp(p, type_of_expr(expr),
- lb_build_expr(p, ce->args[0]),
- lb_build_expr(p, ce->args[1]),
- lb_build_expr(p, ce->args[2]));
+ lb_build_expr(p, ce->args[0]),
+ lb_build_expr(p, ce->args[1]),
+ lb_build_expr(p, ce->args[2]));
case BuiltinProc_soa_zip:
return lb_soa_zip(p, ce, tv);
case BuiltinProc_soa_unzip:
return lb_soa_unzip(p, ce, tv);
-
+
case BuiltinProc_transpose:
{
lbValue m = lb_build_expr(p, ce->args[0]);
return lb_emit_matrix_tranpose(p, m, tv.type);
}
-
+
case BuiltinProc_outer_product:
{
lbValue a = lb_build_expr(p, ce->args[0]);
@@ -1347,13 +1341,13 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
GB_ASSERT(is_type_matrix(tv.type));
return lb_emit_arith_matrix(p, Token_Mul, a, b, tv.type, true);
}
-
+
case BuiltinProc_matrix_flatten:
{
lbValue m = lb_build_expr(p, ce->args[0]);
return lb_emit_matrix_flatten(p, m, tv.type);
}
-
+
// "Intrinsics"
case BuiltinProc_alloca:
@@ -1370,7 +1364,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case BuiltinProc_cpu_relax:
if (build_context.metrics.arch == TargetArch_i386 ||
- build_context.metrics.arch == TargetArch_amd64) {
+ build_context.metrics.arch == TargetArch_amd64) {
LLVMTypeRef func_type = LLVMFunctionType(LLVMVoidTypeInContext(p->module->ctx), nullptr, 0, false);
LLVMValueRef the_asm = llvm_get_inline_asm(func_type, str_lit("pause"), {}, true);
GB_ASSERT(the_asm != nullptr);
@@ -1538,7 +1532,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
lbValue dst = lb_build_expr(p, ce->args[0]);
lbValue src = lb_build_expr(p, ce->args[1]);
lbValue len = lb_build_expr(p, ce->args[2]);
-
+
lb_mem_copy_overlapping(p, dst, src, len, false);
return {};
}
@@ -1547,7 +1541,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
lbValue dst = lb_build_expr(p, ce->args[0]);
lbValue src = lb_build_expr(p, ce->args[1]);
lbValue len = lb_build_expr(p, ce->args[2]);
-
+
lb_mem_copy_non_overlapping(p, dst, src, len, false);
return {};
}
@@ -1651,7 +1645,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
res.type = type_deref(dst.type);
return res;
}
-
+
case BuiltinProc_unaligned_store:
{
lbValue dst = lb_build_expr(p, ce->args[0]);
@@ -1661,7 +1655,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
lb_mem_copy_non_overlapping(p, dst, src, lb_const_int(p->module, t_int, type_size_of(t)), false);
return {};
}
-
+
case BuiltinProc_unaligned_load:
{
lbValue src = lb_build_expr(p, ce->args[0]);
@@ -1843,7 +1837,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
res.type = t;
return lb_emit_conv(p, res, t);
}
-
+
case BuiltinProc_prefetch_read_instruction:
case BuiltinProc_prefetch_read_data:
case BuiltinProc_prefetch_write_instruction:
@@ -1871,27 +1865,27 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
cache = 1;
break;
}
-
+
char const *name = "llvm.prefetch";
-
+
LLVMTypeRef types[1] = {lb_type(p->module, t_rawptr)};
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
GB_ASSERT_MSG(id != 0, "Unable to find %s.%s", name, LLVMPrintTypeToString(types[0]));
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, gb_count_of(types));
-
+
LLVMTypeRef llvm_i32 = lb_type(p->module, t_i32);
LLVMValueRef args[4] = {};
args[0] = ptr.value;
args[1] = LLVMConstInt(llvm_i32, rw, false);
args[2] = LLVMConstInt(llvm_i32, locality, false);
args[3] = LLVMConstInt(llvm_i32, cache, false);
-
+
lbValue res = {};
res.value = LLVMBuildCall(p->builder, ip, args, gb_count_of(args), "");
res.type = nullptr;
return res;
}
-
+
case BuiltinProc___entry_point:
if (p->module->info->entry_point) {
lbValue entry_point = lb_find_procedure_value_from_entity(p->module, p->module->info->entry_point);
@@ -1909,22 +1903,22 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
arg = lb_emit_conv(p, arg, t_uintptr);
args[i] = arg.value;
}
-
+
LLVMTypeRef llvm_uintptr = lb_type(p->module, t_uintptr);
LLVMTypeRef *llvm_arg_types = gb_alloc_array(permanent_allocator(), LLVMTypeRef, arg_count);
for (unsigned i = 0; i < arg_count; i++) {
llvm_arg_types[i] = llvm_uintptr;
}
-
+
LLVMTypeRef func_type = LLVMFunctionType(llvm_uintptr, llvm_arg_types, arg_count, false);
-
+
LLVMValueRef inline_asm = nullptr;
-
+
switch (build_context.metrics.arch) {
case TargetArch_amd64:
{
GB_ASSERT(arg_count <= 7);
-
+
char asm_string[] = "syscall";
gbString constraints = gb_string_make(heap_allocator(), "={rax}");
for (unsigned i = 0; i < arg_count; i++) {
@@ -1963,11 +1957,11 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
case TargetArch_i386:
{
GB_ASSERT(arg_count <= 7);
-
+
char asm_string_default[] = "int $0x80";
char *asm_string = asm_string_default;
gbString constraints = gb_string_make(heap_allocator(), "={eax}");
-
+
for (unsigned i = 0; i < gb_min(arg_count, 6); i++) {
constraints = gb_string_appendc(constraints, ",{");
static char const *regs[] = {
@@ -1984,56 +1978,81 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
if (arg_count == 7) {
char asm_string7[] = "push %[arg6]\npush %%ebp\nmov 4(%%esp), %%ebp\nint $0x80\npop %%ebp\nadd $4, %%esp";
asm_string = asm_string7;
-
+
constraints = gb_string_appendc(constraints, ",rm");
}
-
+
inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
}
break;
case TargetArch_arm64:
{
- GB_ASSERT(arg_count <= 7);
-
- if(build_context.metrics.os == TargetOs_darwin) {
- char asm_string[] = "svc #0x80";
- gbString constraints = gb_string_make(heap_allocator(), "={x0}");
- for (unsigned i = 0; i < arg_count; i++) {
- constraints = gb_string_appendc(constraints, ",{");
- static char const *regs[] = {
- "x16",
- "x0",
- "x1",
- "x2",
- "x3",
- "x4",
- "x5",
- };
- constraints = gb_string_appendc(constraints, regs[i]);
- constraints = gb_string_appendc(constraints, "}");
- }
-
- inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
- } else {
- char asm_string[] = "svc #0";
- gbString constraints = gb_string_make(heap_allocator(), "={x0}");
- for (unsigned i = 0; i < arg_count; i++) {
- constraints = gb_string_appendc(constraints, ",{");
- static char const *regs[] = {
- "x8",
- "x0",
- "x1",
- "x2",
- "x3",
- "x4",
- "x5",
- };
- constraints = gb_string_appendc(constraints, regs[i]);
- constraints = gb_string_appendc(constraints, "}");
- }
-
- inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
- }
+ GB_ASSERT(arg_count <= 7);
+
+ if(build_context.metrics.os == TargetOs_darwin) {
+ char asm_string[] = "svc #0x80";
+ gbString constraints = gb_string_make(heap_allocator(), "={x0}");
+ for (unsigned i = 0; i < arg_count; i++) {
+ constraints = gb_string_appendc(constraints, ",{");
+ static char const *regs[] = {
+ "x16",
+ "x0",
+ "x1",
+ "x2",
+ "x3",
+ "x4",
+ "x5",
+ };
+ constraints = gb_string_appendc(constraints, regs[i]);
+ constraints = gb_string_appendc(constraints, "}");
+ }
+
+ inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
+ } else {
+ char asm_string[] = "svc #0";
+ gbString constraints = gb_string_make(heap_allocator(), "={x0}");
+ for (unsigned i = 0; i < arg_count; i++) {
+ constraints = gb_string_appendc(constraints, ",{");
+ static char const *regs[] = {
+ "x8",
+ "x0",
+ "x1",
+ "x2",
+ "x3",
+ "x4",
+ "x5",
+ };
+ constraints = gb_string_appendc(constraints, regs[i]);
+ constraints = gb_string_appendc(constraints, "}");
+ }
+
+ inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
+ }
+ }
+ break;
+ case TargetArch_arm32:
+ {
+ // TODO(bill): Check this is correct
+ GB_ASSERT(arg_count <= 7);
+
+ char asm_string[] = "svc #0";
+ gbString constraints = gb_string_make(heap_allocator(), "={r0}");
+ for (unsigned i = 0; i < arg_count; i++) {
+ constraints = gb_string_appendc(constraints, ",{");
+ static char const *regs[] = {
+ "r8",
+ "r0",
+ "r1",
+ "r2",
+ "r3",
+ "r4",
+ "r5",
+ };
+ constraints = gb_string_appendc(constraints, regs[i]);
+ constraints = gb_string_appendc(constraints, "}");
+ }
+
+ inline_asm = llvm_get_inline_asm(func_type, make_string_c(asm_string), make_string_c(constraints));
}
break;
default:
@@ -2255,6 +2274,15 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
// NOTE(bill): Regular call
lbValue value = {};
Ast *proc_expr = unparen_expr(ce->proc);
+
+ Entity *proc_entity = entity_of_node(proc_expr);
+ if (proc_entity != nullptr) {
+ if (proc_entity->flags & EntityFlag_Disabled) {
+ GB_ASSERT(tv.type == nullptr);
+ return {};
+ }
+ }
+
if (proc_expr->tav.mode == Addressing_Constant) {
ExactValue v = proc_expr->tav.value;
switch (v.kind) {
@@ -2281,13 +2309,6 @@ lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
}
}
- Entity *proc_entity = entity_of_node(proc_expr);
- if (proc_entity != nullptr) {
- if (proc_entity->flags & EntityFlag_Disabled) {
- return {};
- }
- }
-
if (value.value == nullptr) {
value = lb_build_expr(p, proc_expr);
}
diff --git a/src/main.cpp b/src/main.cpp
index fc8792ceb..561fa0fca 100644
--- a/src/main.cpp
+++ b/src/main.cpp
@@ -46,7 +46,6 @@ gb_global Timings global_timings = {0};
#include "checker.cpp"
#include "docs.cpp"
-
#include "llvm_backend.cpp"
#if defined(GB_SYSTEM_OSX)
@@ -57,16 +56,8 @@ gb_global Timings global_timings = {0};
#endif
#include "query_data.cpp"
-
-
-#if defined(GB_SYSTEM_WINDOWS)
-// NOTE(IC): In order to find Visual C++ paths without relying on environment variables.
-#include "microsoft_craziness.h"
-#endif
-
#include "bug_report.cpp"
-
// NOTE(bill): 'name' is used in debugging and profiling modes
i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
isize const cmd_cap = 64<<20; // 64 MiB should be more than enough
@@ -130,34 +121,35 @@ i32 system_exec_command_line_app(char const *name, char const *fmt, ...) {
}
-
-
i32 linker_stage(lbGenerator *gen) {
i32 result = 0;
Timings *timings = &global_timings;
- String output_base = gen->output_base;
+ String output_filename = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
+ debugf("Linking %.*s\n", LIT(output_filename));
+
+ // TOOD(Jeroen): Make a `build_paths[BuildPath_Object] to avoid `%.*s.o`.
if (is_arch_wasm()) {
timings_start_section(timings, str_lit("wasm-ld"));
#if defined(GB_SYSTEM_WINDOWS)
result = system_exec_command_line_app("wasm-ld",
- "\"%.*s\\bin\\wasm-ld\" \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s",
+ "\"%.*s\\bin\\wasm-ld\" \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
LIT(build_context.ODIN_ROOT),
- LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+ LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#else
result = system_exec_command_line_app("wasm-ld",
- "wasm-ld \"%.*s.wasm.o\" -o \"%.*s.wasm\" %.*s %.*s",
- LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+ "wasm-ld \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
+ LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#endif
return result;
}
if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
-#ifdef GB_SYSTEM_UNIX
+#if defined(GB_SYSTEM_UNIX)
result = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
- LIT(output_base), LIT(output_base), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
+ LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#else
gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
LIT(target_os_names[build_context.metrics.os]),
@@ -172,369 +164,360 @@ i32 linker_stage(lbGenerator *gen) {
build_context.keep_object_files = true;
} else {
#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);
-
- gbString lib_str = gb_string_make(heap_allocator(), "");
- defer (gb_string_free(lib_str));
-
- char const *output_ext = "exe";
- gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
- defer (gb_string_free(link_settings));
-
-
- // NOTE(ic): It would be nice to extend this so that we could specify the Visual Studio version that we want instead of defaulting to the latest.
- Find_Result_Utf8 find_result = find_visual_studio_and_windows_sdk_utf8();
+ 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
- if (find_result.windows_sdk_version == 0) {
- gb_printf_err("Windows SDK not found.\n");
- exit(1);
- }
- if (build_context.ignore_microsoft_magic) {
- find_result = {};
- }
+ 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);
- // Add library search paths.
- if (find_result.vs_library_path.len > 0) {
- GB_ASSERT(find_result.windows_sdk_um_library_path.len > 0);
- GB_ASSERT(find_result.windows_sdk_ucrt_library_path.len > 0);
+ gbString lib_str = gb_string_make(heap_allocator(), "");
+ defer (gb_string_free(lib_str));
- 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(find_result.windows_sdk_um_library_path);
- add_path(find_result.windows_sdk_ucrt_library_path);
- add_path(find_result.vs_library_path);
- }
+ 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));
+
+ 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]);
+ // 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 (lib.len == 0) {
+ continue;
+ }
- 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);
+ 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 {
- 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) {
- output_ext = "dll";
- 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 != "") {
- link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(build_context.pdb_filepath));
- }
-
- 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");
- }
+ 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));
+ }
- 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"));
+ 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");
+ }
- 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.ODIN_DEBUG) {
+ link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
+ }
- 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));
}
- 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));
- }
+ 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;
+ }
- char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
- if (!build_context.use_lld) { // msvc
- if (build_context.has_resource) {
- result = system_exec_command_line_app("msvc-link",
- "\"rc.exe\" /nologo /fo \"%.*s.res\" \"%.*s.rc\"",
- LIT(output_base),
- LIT(build_context.resource_filepath)
- );
+ 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) {
return result;
}
- result = system_exec_command_line_app("msvc-link",
- "\"%.*slink.exe\" %s \"%.*s.res\" -OUT:\"%.*s.%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 "
" %.*s "
" %.*s "
" %s "
"",
- LIT(find_result.vs_exe_path), object_files, LIT(output_base), LIT(output_base), output_ext,
- 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\" %s "
- "/nologo /incremental:no /opt:ref /subsystem:%s "
- " %.*s "
- " %.*s "
- " %s "
- "",
- LIT(find_result.vs_exe_path), object_files, LIT(output_base), output_ext,
+ 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];
+ #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 = string_trim_whitespace(e->LibraryName.paths[i]);
+ if (lib.len == 0) {
+ continue;
+ }
+ if (string_set_update(&libs, lib)) {
+ continue;
+ }
- if (result) {
- return result;
+ // 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\" %s "
- "/nologo /incremental:no /opt:ref /subsystem:%s "
- " %.*s "
- " %.*s "
- " %s "
- "",
- LIT(build_context.ODIN_ROOT), object_files, LIT(output_base),output_ext,
- 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);
- // 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));
+ gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
- for_array(i, gen->default_module.foreign_library_paths) {
- String lib = gen->default_module.foreign_library_paths[i];
+ if (build_context.no_crt) {
+ link_settings = gb_string_append_fmt(link_settings, "-nostdlib ");
+ }
- // 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 runtimeto 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));
- }
+ // 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 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));
- }
-
- // Unlike the Win32 linker code, the output_ext includes the dot, because
- // typically executable files on *NIX systems don't have extensions.
- String output_ext = {};
- 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.
- //
- // Shared libraries are .dylib on MacOS and .so on Linux.
+ gbString platform_lib_str = gb_string_make(heap_allocator(), "");
+ defer (gb_string_free(platform_lib_str));
if (build_context.metrics.os == TargetOs_darwin) {
- output_ext = STR_LIT(".dylib");
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib");
} else {
- output_ext = STR_LIT(".so");
+ platform_lib_str = gb_string_appendc(platform_lib_str, "-lc -lm");
}
- 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 ");
- }
- if (build_context.out_filepath.len > 0) {
- //NOTE(thebirk): We have a custom -out arguments, so we should use the extension from that
- isize pos = string_extension_position(build_context.out_filepath);
- if (pos > 0) {
- output_ext = substring(build_context.out_filepath, pos, build_context.out_filepath.len);
- }
- }
-
- 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%.*s\" ", LIT(output_base), LIT(output_ext));
- 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%.*s", LIT(output_base), LIT(output_ext)
- );
+ result = system_exec_command_line_app("ld-link", link_command_line);
if (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;
@@ -666,6 +649,7 @@ enum BuildFlagKind {
BuildFlag_IgnoreWarnings,
BuildFlag_WarningsAsErrors,
BuildFlag_VerboseErrors,
+ BuildFlag_ErrorPosStyle,
// internal use only
BuildFlag_InternalIgnoreLazy,
@@ -829,6 +813,7 @@ bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_IgnoreWarnings, str_lit("ignore-warnings"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_VerboseErrors, str_lit("verbose-errors"), BuildFlagParam_None, Command_all);
+ add_flag(&build_flags, BuildFlag_ErrorPosStyle, str_lit("error-pos-style"), BuildFlagParam_String, Command_all);
add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all);
@@ -859,11 +844,19 @@ bool parse_build_flags(Array<String> args) {
String name = substring(flag, 1, flag.len);
isize end = 0;
+ bool have_equals = false;
for (; end < name.len; end++) {
if (name[end] == ':') break;
- if (name[end] == '=') break; // IMPORTANT TODO(bill): DEPRECATE THIS!!!!
+ if (name[end] == '=') {
+ have_equals = true;
+ break;
+ }
}
name = substring(name, 0, end);
+ if (have_equals && name != "opt") {
+ gb_printf_err("`flag=value` has been deprecated and will be removed next release. Use `%.*s:` instead.\n", LIT(name));
+ }
+
String param = {};
if (end < flag.len-1) param = substring(flag, 2+end, flag.len);
@@ -937,35 +930,35 @@ bool parse_build_flags(Array<String> args) {
switch (bf.param_kind) {
case BuildFlagParam_None:
if (value.kind != ExactValue_Invalid) {
- gb_printf_err("%.*s expected no value, got %.*s", LIT(name), LIT(param));
+ gb_printf_err("%.*s expected no value, got %.*s\n", LIT(name), LIT(param));
bad_flags = true;
ok = false;
}
break;
case BuildFlagParam_Boolean:
if (value.kind != ExactValue_Bool) {
- gb_printf_err("%.*s expected a boolean, got %.*s", LIT(name), LIT(param));
+ gb_printf_err("%.*s expected a boolean, got %.*s\n", LIT(name), LIT(param));
bad_flags = true;
ok = false;
}
break;
case BuildFlagParam_Integer:
if (value.kind != ExactValue_Integer) {
- gb_printf_err("%.*s expected an integer, got %.*s", LIT(name), LIT(param));
+ gb_printf_err("%.*s expected an integer, got %.*s\n", LIT(name), LIT(param));
bad_flags = true;
ok = false;
}
break;
case BuildFlagParam_Float:
if (value.kind != ExactValue_Float) {
- gb_printf_err("%.*s expected a floating pointer number, got %.*s", LIT(name), LIT(param));
+ gb_printf_err("%.*s expected a floating pointer number, got %.*s\n", LIT(name), LIT(param));
bad_flags = true;
ok = false;
}
break;
case BuildFlagParam_String:
if (value.kind != ExactValue_String) {
- gb_printf_err("%.*s expected a string, got %.*s", LIT(name), LIT(param));
+ gb_printf_err("%.*s expected a string, got %.*s\n", LIT(name), LIT(param));
bad_flags = true;
ok = false;
}
@@ -995,7 +988,20 @@ bool parse_build_flags(Array<String> args) {
bad_flags = true;
break;
}
+
build_context.optimization_level = cast(i32)big_int_to_i64(&value.value_integer);
+ if (build_context.optimization_level < 0 || build_context.optimization_level > 3) {
+ gb_printf_err("Invalid optimization level for -o:<integer>, got %d\n", build_context.optimization_level);
+ gb_printf_err("Valid optimization levels:\n");
+ gb_printf_err("\t0\n");
+ gb_printf_err("\t1\n");
+ gb_printf_err("\t2\n");
+ gb_printf_err("\t3\n");
+ bad_flags = true;
+ }
+
+ // Deprecation warning.
+ gb_printf_err("`-opt` has been deprecated and will be removed next release. Use `-o:minimal`, etc.\n");
break;
}
case BuildFlag_OptimizationMode: {
@@ -1508,6 +1514,20 @@ bool parse_build_flags(Array<String> args) {
case BuildFlag_VerboseErrors:
build_context.show_error_line = true;
break;
+
+ case BuildFlag_ErrorPosStyle:
+ GB_ASSERT(value.kind == ExactValue_String);
+
+ if (str_eq_ignore_case(value.value_string, str_lit("odin")) || str_eq_ignore_case(value.value_string, str_lit("default"))) {
+ build_context.ODIN_ERROR_POS_STYLE = ErrorPosStyle_Default;
+ } else if (str_eq_ignore_case(value.value_string, str_lit("unix"))) {
+ build_context.ODIN_ERROR_POS_STYLE = ErrorPosStyle_Unix;
+ } else {
+ gb_printf_err("-error-pos-style options are 'unix', 'odin' and 'default' (odin)\n");
+ bad_flags = true;
+ }
+ break;
+
case BuildFlag_InternalIgnoreLazy:
build_context.ignore_lazy = true;
break;
@@ -1526,6 +1546,10 @@ bool parse_build_flags(Array<String> args) {
gb_printf_err("Invalid -resource path %.*s, missing .rc\n", LIT(path));
bad_flags = true;
break;
+ } else if (!gb_file_exists((const char *)path.text)) {
+ gb_printf_err("Invalid -resource path %.*s, file does not exist.\n", LIT(path));
+ bad_flags = true;
+ break;
}
build_context.resource_filepath = substring(path, 0, string_extension_position(path));
build_context.has_resource = true;
@@ -1540,6 +1564,11 @@ bool parse_build_flags(Array<String> args) {
String path = value.value_string;
path = string_trim_whitespace(path);
if (is_build_flag_path_valid(path)) {
+ if (path_is_directory(path)) {
+ gb_printf_err("Invalid -pdb-name path. %.*s, is a directory.\n", LIT(path));
+ bad_flags = true;
+ break;
+ }
// #if defined(GB_SYSTEM_WINDOWS)
// String ext = path_extension(path);
// if (ext != ".pdb") {
@@ -2666,6 +2695,8 @@ int main(int arg_count, char const **arg_ptr) {
return 1;
}
+ init_filename = copy_string(permanent_allocator(), init_filename);
+
if (init_filename == "-help" ||
init_filename == "--help") {
build_context.show_help = true;
@@ -2688,6 +2719,12 @@ int main(int arg_count, char const **arg_ptr) {
gb_printf_err("Did you mean `%.*s %.*s %.*s -file`?\n", LIT(args[0]), LIT(command), LIT(init_filename));
gb_printf_err("The `-file` flag tells it to treat a file as a self-contained package.\n");
return 1;
+ } else {
+ String const ext = str_lit(".odin");
+ if (!string_ends_with(init_filename, ext)) {
+ gb_printf_err("Expected either a directory or a .odin file, got '%.*s'\n", LIT(init_filename));
+ return 1;
+ }
}
}
}
@@ -2709,13 +2746,24 @@ int main(int arg_count, char const **arg_ptr) {
get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("shared")));
}
-
init_build_context(selected_target_metrics ? selected_target_metrics->metrics : nullptr);
// if (build_context.word_size == 4 && build_context.metrics.os != TargetOs_js) {
// print_usage_line(0, "%.*s 32-bit is not yet supported for this platform", LIT(args[0]));
// return 1;
// }
+ // Set and check build paths...
+ if (!init_build_paths(init_filename)) {
+ return 1;
+ }
+
+ if (build_context.show_debug_messages) {
+ for_array(i, build_context.build_paths) {
+ String build_path = path_to_string(heap_allocator(), build_context.build_paths[i]);
+ debugf("build_paths[%ld]: %.*s\n", i, LIT(build_path));
+ }
+ }
+
init_global_thread_pool();
defer (thread_pool_destroy(&global_thread_pool));
@@ -2732,6 +2780,8 @@ int main(int arg_count, char const **arg_ptr) {
}
defer (destroy_parser(parser));
+ // TODO(jeroen): Remove the `init_filename` param.
+ // Let's put that on `build_context.build_paths[0]` instead.
if (parse_packages(parser, init_filename) != ParseFile_None) {
return 1;
}
@@ -2810,16 +2860,10 @@ int main(int arg_count, char const **arg_ptr) {
}
if (run_output) {
- #if defined(GB_SYSTEM_WINDOWS)
- return system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(gen->output_base), LIT(run_args_string));
- #else
- //NOTE(thebirk): This whole thing is a little leaky
- String output_ext = {};
- String complete_path = concatenate_strings(permanent_allocator(), gen->output_base, output_ext);
- complete_path = path_to_full_path(permanent_allocator(), complete_path);
- return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(complete_path), LIT(run_args_string));
- #endif
- }
+ String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
+ defer (gb_free(heap_allocator(), exe_name.text));
+ return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(exe_name), LIT(run_args_string));
+ }
return 0;
}
diff --git a/src/parser.cpp b/src/parser.cpp
index 767119aa8..1f4093e5f 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -1160,11 +1160,10 @@ Ast *ast_package_decl(AstFile *f, Token token, Token name, CommentGroup *docs, C
return result;
}
-Ast *ast_import_decl(AstFile *f, Token token, bool is_using, Token relpath, Token import_name,
+Ast *ast_import_decl(AstFile *f, Token token, Token relpath, Token import_name,
CommentGroup *docs, CommentGroup *comment) {
Ast *result = alloc_ast_node(f, Ast_ImportDecl);
result->ImportDecl.token = token;
- result->ImportDecl.is_using = is_using;
result->ImportDecl.relpath = relpath;
result->ImportDecl.import_name = import_name;
result->ImportDecl.docs = docs;
@@ -4382,7 +4381,6 @@ Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) {
CommentGroup *docs = f->lead_comment;
Token token = expect_token(f, Token_import);
Token import_name = {};
- bool is_using = kind != ImportDecl_Standard;
switch (f->curr_token.kind) {
case Token_Ident:
@@ -4393,22 +4391,18 @@ Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) {
break;
}
- if (!is_using && is_blank_ident(import_name)) {
- syntax_error(import_name, "Illegal import name: '_'");
- }
-
Token file_path = expect_token_after(f, Token_String, "import");
Ast *s = nullptr;
if (f->curr_proc != nullptr) {
- syntax_error(import_name, "You cannot use 'import' within a procedure. This must be done at the file scope");
+ syntax_error(import_name, "Cannot use 'import' within a procedure. This must be done at the file scope");
s = ast_bad_decl(f, import_name, file_path);
} else {
- s = ast_import_decl(f, token, is_using, file_path, import_name, docs, f->line_comment);
+ s = ast_import_decl(f, token, file_path, import_name, docs, f->line_comment);
array_add(&f->imports, s);
}
- if (is_using) {
+ if (kind != ImportDecl_Standard) {
syntax_error(import_name, "'using import' is not allowed, please use the import name explicitly");
}
@@ -5751,7 +5745,7 @@ ParseFileError parse_packages(Parser *p, String init_filename) {
}
}
}
-
+
{ // Add these packages serially and then process them parallel
mutex_lock(&p->wait_mutex);
diff --git a/src/parser.hpp b/src/parser.hpp
index c7b4fd0d8..698ed7623 100644
--- a/src/parser.hpp
+++ b/src/parser.hpp
@@ -585,7 +585,6 @@ AST_KIND(_DeclBegin, "", bool) \
Token import_name; \
CommentGroup *docs; \
CommentGroup *comment; \
- bool is_using; \
}) \
AST_KIND(ForeignImportDecl, "foreign import declaration", struct { \
Token token; \
diff --git a/src/path.cpp b/src/path.cpp
new file mode 100644
index 000000000..6f83c39ea
--- /dev/null
+++ b/src/path.cpp
@@ -0,0 +1,394 @@
+/*
+ Path handling utilities.
+*/
+String remove_extension_from_path(String const &s) {
+ for (isize i = s.len-1; i >= 0; i--) {
+ if (s[i] == '.') {
+ return substring(s, 0, i);
+ }
+ }
+ return s;
+}
+
+String remove_directory_from_path(String const &s) {
+ isize len = 0;
+ for (isize i = s.len-1; i >= 0; i--) {
+ if (s[i] == '/' ||
+ s[i] == '\\') {
+ break;
+ }
+ len += 1;
+ }
+ return substring(s, s.len-len, s.len);
+}
+
+bool path_is_directory(String path);
+
+String directory_from_path(String const &s) {
+ if (path_is_directory(s)) {
+ return s;
+ }
+
+ isize i = s.len-1;
+ for (; i >= 0; i--) {
+ if (s[i] == '/' ||
+ s[i] == '\\') {
+ break;
+ }
+ }
+ if (i >= 0) {
+ return substring(s, 0, i);
+ }
+ return substring(s, 0, 0);
+}
+
+#if defined(GB_SYSTEM_WINDOWS)
+ bool path_is_directory(String path) {
+ gbAllocator a = heap_allocator();
+ String16 wstr = string_to_string16(a, path);
+ defer (gb_free(a, wstr.text));
+
+ i32 attribs = GetFileAttributesW(wstr.text);
+ if (attribs < 0) return false;
+
+ return (attribs & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ }
+
+#else
+ bool path_is_directory(String path) {
+ gbAllocator a = heap_allocator();
+ char *copy = cast(char *)copy_string(a, path).text;
+ defer (gb_free(a, copy));
+
+ struct stat s;
+ if (stat(copy, &s) == 0) {
+ return (s.st_mode & S_IFDIR) != 0;
+ }
+ return false;
+ }
+#endif
+
+
+String path_to_full_path(gbAllocator a, String path) {
+ gbAllocator ha = heap_allocator();
+ char *path_c = gb_alloc_str_len(ha, cast(char *)path.text, path.len);
+ defer (gb_free(ha, path_c));
+
+ char *fullpath = gb_path_get_full_name(a, path_c);
+ String res = string_trim_whitespace(make_string_c(fullpath));
+#if defined(GB_SYSTEM_WINDOWS)
+ for (isize i = 0; i < res.len; i++) {
+ if (res.text[i] == '\\') {
+ res.text[i] = '/';
+ }
+ }
+#endif
+ return copy_string(a, res);
+}
+
+struct Path {
+ String basename;
+ String name;
+ String ext;
+};
+
+// NOTE(Jeroen): Naively turns a Path into a string.
+String path_to_string(gbAllocator a, Path path) {
+ if (path.basename.len + path.name.len + path.ext.len == 0) {
+ return make_string(nullptr, 0);
+ }
+
+ isize len = path.basename.len + 1 + path.name.len + 1;
+ if (path.ext.len > 0) {
+ len += path.ext.len + 1;
+ }
+
+ u8 *str = gb_alloc_array(a, u8, len);
+
+ isize i = 0;
+ gb_memmove(str+i, path.basename.text, path.basename.len); i += path.basename.len;
+ gb_memmove(str+i, "/", 1); i += 1;
+ gb_memmove(str+i, path.name.text, path.name.len); i += path.name.len;
+ if (path.ext.len > 0) {
+ gb_memmove(str+i, ".", 1); i += 1;
+ gb_memmove(str+i, path.ext.text, path.ext.len); i += path.ext.len;
+ }
+ str[i] = 0;
+
+ String res = make_string(str, i);
+ res = string_trim_whitespace(res);
+ return res;
+}
+
+// NOTE(Jeroen): Naively turns a Path into a string, then normalizes it using `path_to_full_path`.
+String path_to_full_path(gbAllocator a, Path path) {
+ String temp = path_to_string(heap_allocator(), path);
+ defer (gb_free(heap_allocator(), temp.text));
+
+ return path_to_full_path(a, temp);
+}
+
+// NOTE(Jeroen): Takes a path like "odin" or "W:\Odin", turns it into a full path,
+// and then breaks it into its components to make a Path.
+Path path_from_string(gbAllocator a, String const &path) {
+ Path res = {};
+
+ if (path.len == 0) return res;
+
+ String fullpath = path_to_full_path(a, path);
+ defer (gb_free(heap_allocator(), fullpath.text));
+
+ res.basename = directory_from_path(fullpath);
+ res.basename = copy_string(a, res.basename);
+
+ if (path_is_directory(fullpath)) {
+ // It's a directory. We don't need to tinker with the name and extension.
+ // It could have a superfluous trailing `/`. Remove it if so.
+ if (res.basename.len > 0 && res.basename.text[res.basename.len - 1] == '/') {
+ res.basename.len--;
+ }
+ return res;
+ }
+
+ isize name_start = (res.basename.len > 0) ? res.basename.len + 1 : res.basename.len;
+ res.name = substring(fullpath, name_start, fullpath.len);
+ res.name = remove_extension_from_path(res.name);
+ res.name = copy_string(a, res.name);
+
+ res.ext = path_extension(fullpath, false); // false says not to include the dot.
+ res.ext = copy_string(a, res.ext);
+ return res;
+}
+
+// NOTE(Jeroen): Takes a path String and returns the last path element.
+String last_path_element(String const &path) {
+ isize count = 0;
+ u8 * start = (u8 *)(&path.text[path.len - 1]);
+ for (isize length = path.len; length > 0 && path.text[length - 1] != '/'; length--) {
+ count++;
+ start--;
+ }
+ if (count > 0) {
+ start++; // Advance past the `/` and return the substring.
+ String res = make_string(start, count);
+ return res;
+ }
+ // Must be a root path like `/` or `C:/`, return empty String.
+ return STR_LIT("");
+}
+
+bool path_is_directory(Path path) {
+ String path_string = path_to_full_path(heap_allocator(), path);
+ defer (gb_free(heap_allocator(), path_string.text));
+
+ return path_is_directory(path_string);
+}
+
+struct FileInfo {
+ String name;
+ String fullpath;
+ i64 size;
+ bool is_dir;
+};
+
+enum ReadDirectoryError {
+ ReadDirectory_None,
+
+ ReadDirectory_InvalidPath,
+ ReadDirectory_NotExists,
+ ReadDirectory_Permission,
+ ReadDirectory_NotDir,
+ ReadDirectory_Empty,
+ ReadDirectory_Unknown,
+
+ ReadDirectory_COUNT,
+};
+
+i64 get_file_size(String path) {
+ char *c_str = alloc_cstring(heap_allocator(), path);
+ defer (gb_free(heap_allocator(), c_str));
+
+ gbFile f = {};
+ gbFileError err = gb_file_open(&f, c_str);
+ defer (gb_file_close(&f));
+ if (err != gbFileError_None) {
+ return -1;
+ }
+ return gb_file_size(&f);
+}
+
+
+#if defined(GB_SYSTEM_WINDOWS)
+ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
+ GB_ASSERT(fi != nullptr);
+
+ gbAllocator a = heap_allocator();
+
+ while (path.len > 0) {
+ Rune end = path[path.len-1];
+ if (end == '/') {
+ path.len -= 1;
+ } else if (end == '\\') {
+ path.len -= 1;
+ } else {
+ break;
+ }
+ }
+
+ if (path.len == 0) {
+ return ReadDirectory_InvalidPath;
+ }
+ {
+ char *c_str = alloc_cstring(a, path);
+ defer (gb_free(a, c_str));
+
+ gbFile f = {};
+ gbFileError file_err = gb_file_open(&f, c_str);
+ defer (gb_file_close(&f));
+
+ switch (file_err) {
+ case gbFileError_Invalid: return ReadDirectory_InvalidPath;
+ case gbFileError_NotExists: return ReadDirectory_NotExists;
+ // case gbFileError_Permission: return ReadDirectory_Permission;
+ }
+ }
+
+ if (!path_is_directory(path)) {
+ return ReadDirectory_NotDir;
+ }
+
+
+ char *new_path = gb_alloc_array(a, char, path.len+3);
+ defer (gb_free(a, new_path));
+
+ gb_memmove(new_path, path.text, path.len);
+ gb_memmove(new_path+path.len, "/*", 2);
+ new_path[path.len+2] = 0;
+
+ String np = make_string(cast(u8 *)new_path, path.len+2);
+ String16 wstr = string_to_string16(a, np);
+ defer (gb_free(a, wstr.text));
+
+ WIN32_FIND_DATAW file_data = {};
+ HANDLE find_file = FindFirstFileW(wstr.text, &file_data);
+ if (find_file == INVALID_HANDLE_VALUE) {
+ return ReadDirectory_Unknown;
+ }
+ defer (FindClose(find_file));
+
+ array_init(fi, a, 0, 100);
+
+ do {
+ wchar_t *filename_w = file_data.cFileName;
+ i64 size = cast(i64)file_data.nFileSizeLow;
+ size |= (cast(i64)file_data.nFileSizeHigh) << 32;
+ String name = string16_to_string(a, make_string16_c(filename_w));
+ if (name == "." || name == "..") {
+ gb_free(a, name.text);
+ continue;
+ }
+
+ String filepath = {};
+ filepath.len = path.len+1+name.len;
+ filepath.text = gb_alloc_array(a, u8, filepath.len+1);
+ defer (gb_free(a, filepath.text));
+ gb_memmove(filepath.text, path.text, path.len);
+ gb_memmove(filepath.text+path.len, "/", 1);
+ gb_memmove(filepath.text+path.len+1, name.text, name.len);
+
+ FileInfo info = {};
+ info.name = name;
+ info.fullpath = path_to_full_path(a, filepath);
+ info.size = size;
+ info.is_dir = (file_data.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0;
+ array_add(fi, info);
+ } while (FindNextFileW(find_file, &file_data));
+
+ if (fi->count == 0) {
+ return ReadDirectory_Empty;
+ }
+
+ return ReadDirectory_None;
+}
+#elif defined(GB_SYSTEM_LINUX) || defined(GB_SYSTEM_OSX) || defined(GB_SYSTEM_FREEBSD) || defined(GB_SYSTEM_OPENBSD)
+
+#include <dirent.h>
+
+ReadDirectoryError read_directory(String path, Array<FileInfo> *fi) {
+ GB_ASSERT(fi != nullptr);
+
+ gbAllocator a = heap_allocator();
+
+ char *c_path = alloc_cstring(a, path);
+ defer (gb_free(a, c_path));
+
+ DIR *dir = opendir(c_path);
+ if (!dir) {
+ switch (errno) {
+ case ENOENT:
+ return ReadDirectory_NotExists;
+ case EACCES:
+ return ReadDirectory_Permission;
+ case ENOTDIR:
+ return ReadDirectory_NotDir;
+ default:
+ // ENOMEM: out of memory
+ // EMFILE: per-process limit on open fds reached
+ // ENFILE: system-wide limit on total open files reached
+ return ReadDirectory_Unknown;
+ }
+ GB_PANIC("unreachable");
+ }
+
+ array_init(fi, a, 0, 100);
+
+ for (;;) {
+ struct dirent *entry = readdir(dir);
+ if (entry == nullptr) {
+ break;
+ }
+
+ String name = make_string_c(entry->d_name);
+ if (name == "." || name == "..") {
+ continue;
+ }
+
+ String filepath = {};
+ filepath.len = path.len+1+name.len;
+ filepath.text = gb_alloc_array(a, u8, filepath.len+1);
+ defer (gb_free(a, filepath.text));
+ gb_memmove(filepath.text, path.text, path.len);
+ gb_memmove(filepath.text+path.len, "/", 1);
+ gb_memmove(filepath.text+path.len+1, name.text, name.len);
+ filepath.text[filepath.len] = 0;
+
+
+ struct stat dir_stat = {};
+
+ if (stat((char *)filepath.text, &dir_stat)) {
+ continue;
+ }
+
+ if (S_ISDIR(dir_stat.st_mode)) {
+ continue;
+ }
+
+ i64 size = dir_stat.st_size;
+
+ FileInfo info = {};
+ info.name = name;
+ info.fullpath = path_to_full_path(a, filepath);
+ info.size = size;
+ array_add(fi, info);
+ }
+
+ if (fi->count == 0) {
+ return ReadDirectory_Empty;
+ }
+
+ return ReadDirectory_None;
+}
+#else
+#error Implement read_directory
+#endif
+
diff --git a/src/ptr_set.cpp b/src/ptr_set.cpp
index b45997916..ffe48d69a 100644
--- a/src/ptr_set.cpp
+++ b/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_destroy(PtrSet<T> *s);
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> void ptr_set_remove (PtrSet<T> *s, T ptr);
template <typename T> void ptr_set_clear (PtrSet<T> *s);
diff --git a/src/string.cpp b/src/string.cpp
index d3dbc6904..616761265 100644
--- a/src/string.cpp
+++ b/src/string.cpp
@@ -245,15 +245,14 @@ gb_inline isize string_extension_position(String const &str) {
return dot_pos;
}
-String path_extension(String const &str) {
+String path_extension(String const &str, bool include_dot = true) {
isize pos = string_extension_position(str);
if (pos < 0) {
return make_string(nullptr, 0);
}
- return substring(str, pos, str.len);
+ return substring(str, include_dot ? pos : pos + 1, str.len);
}
-
String string_trim_whitespace(String str) {
while (str.len > 0 && rune_is_whitespace(str[str.len-1])) {
str.len--;
@@ -299,38 +298,6 @@ String filename_from_path(String s) {
return make_string(nullptr, 0);
}
-String remove_extension_from_path(String const &s) {
- for (isize i = s.len-1; i >= 0; i--) {
- if (s[i] == '.') {
- return substring(s, 0, i);
- }
- }
- return s;
-}
-
-String remove_directory_from_path(String const &s) {
- isize len = 0;
- for (isize i = s.len-1; i >= 0; i--) {
- if (s[i] == '/' ||
- s[i] == '\\') {
- break;
- }
- len += 1;
- }
- return substring(s, s.len-len, s.len);
-}
-
-String directory_from_path(String const &s) {
- isize i = s.len-1;
- for (; i >= 0; i--) {
- if (s[i] == '/' ||
- s[i] == '\\') {
- break;
- }
- }
- return substring(s, 0, i);
-}
-
String concatenate_strings(gbAllocator a, String const &x, String const &y) {
isize len = x.len+y.len;
u8 *data = gb_alloc_array(a, u8, len+1);
diff --git a/src/string_set.cpp b/src/string_set.cpp
index e27145289..746ad9529 100644
--- a/src/string_set.cpp
+++ b/src/string_set.cpp
@@ -13,6 +13,7 @@ struct StringSet {
void string_set_init (StringSet *s, gbAllocator a, isize capacity = 16);
void string_set_destroy(StringSet *s);
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);
void string_set_remove (StringSet *s, String const &str);
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) {
MapFindResult last;