aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJeroen van Rijn <Kelimion@users.noreply.github.com>2022-04-06 18:26:23 +0200
committerJeroen van Rijn <Kelimion@users.noreply.github.com>2022-04-24 13:37:26 +0200
commit3cab2592c3e5a06882ffd711871a08c893b043f1 (patch)
treea012d5e76f816ea36a8e6489f38cdca9088ad4a5
parent5422a3b17eae821df4adf869960995e922eb0e76 (diff)
Compiler: Add early error for output path being a directory.
- Introduce new `Path` type and an array of build paths on the build context. - Resolve input and output paths/files early (before parsing). - Error early if inputs are missing or outputs are directories. - Plumb new file path generation into linker stage instead of its adhoc method. TODO: - Remove more adhoc file path generation in parser and linker stage. - Make intermediate object file generation use new path system. - Round out and robustify Path helper functions.
-rw-r--r--.gitignore1
-rw-r--r--Makefile4
-rwxr-xr-xbuild_odin.sh4
-rw-r--r--src/build_settings.cpp220
-rw-r--r--src/common.cpp257
-rw-r--r--src/gb/gb.h46
-rw-r--r--src/llvm_backend.cpp14
-rw-r--r--src/llvm_backend_general.cpp1
-rw-r--r--src/main.cpp152
-rw-r--r--src/parser.cpp2
-rw-r--r--src/path.cpp333
-rw-r--r--src/string.cpp10
-rw-r--r--tests/core/build.bat28
-rw-r--r--tests/core/math/big/build.bat2
-rwxr-xr-xtests/issues/run.sh4
15 files changed, 674 insertions, 404 deletions
diff --git a/.gitignore b/.gitignore
index e8b3d3050..d03a86fd7 100644
--- a/.gitignore
+++ b/.gitignore
@@ -269,6 +269,7 @@ bin/
# - Linux/MacOS
odin
odin.dSYM
+*.bin
# shared collection
shared/
diff --git a/Makefile b/Makefile
index 82150c6a2..1a1c93180 100644
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
-all: debug demo
+all: debug
demo:
- ./odin run examples/demo/demo.odin
+ ./odin run examples/demo
report:
./odin report
diff --git a/build_odin.sh b/build_odin.sh
index aef3f2836..4810cafd2 100755
--- a/build_odin.sh
+++ b/build_odin.sh
@@ -102,7 +102,7 @@ build_odin() {
}
run_demo() {
- ./odin run examples/demo/demo.odin -file
+ ./odin run examples/demo
}
case $OS in
@@ -147,4 +147,4 @@ if [[ $# -eq 1 ]]; then
exit 0
else
panic "Too many arguments!"
-fi
+fi \ No newline at end of file
diff --git a/src/build_settings.cpp b/src/build_settings.cpp
index 2f3eb03a5..0b582eac8 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
@@ -198,6 +197,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 +241,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 +319,6 @@ struct BuildContext {
};
-
-
gb_global BuildContext build_context = {0};
bool global_warnings_as_errors(void) {
@@ -605,28 +622,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 +968,6 @@ char *token_pos_to_string(TokenPos const &pos) {
return s;
}
-
void init_build_context(TargetMetrics *cross_target) {
BuildContext *bc = &build_context;
@@ -1152,8 +1146,178 @@ 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;
+ }
+
+ GB_ASSERT(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);
+ } else {
+ 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));
+
+ /*
+ NOTE(Jeroen): This fallback substitution can't be made at this stage.
+ if (gen->output_name.len == 0) {
+ gen->output_name = c->info.init_scope->pkg->name;
+ }
+ */
+ Path output_path = path_from_string(ha, output_name);
+
+ #ifndef GB_SYSTEM_WINDOWS
+ char cwd[4096];
+ getcwd(&cwd[0], 4096);
+
+ const u8 * cwd_str = (const u8 *)&cwd[0];
+ output_path.basename = copy_string(ha, make_string(cwd_str, strlen(cwd)));
+ #endif
+
+ // 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) {
+ 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/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/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_backend.cpp b/src/llvm_backend.cpp
index f5cb84785..7781997f7 100644
--- a/src/llvm_backend.cpp
+++ b/src/llvm_backend.cpp
@@ -967,7 +967,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 +983,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);
}
diff --git a/src/llvm_backend_general.cpp b/src/llvm_backend_general.cpp
index 330059622..1a431a4ac 100644
--- a/src/llvm_backend_general.cpp
+++ b/src/llvm_backend_general.cpp
@@ -87,7 +87,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) {
diff --git a/src/main.cpp b/src/main.cpp
index fc8792ceb..7b0364149 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]),
@@ -181,28 +173,11 @@ i32 linker_stage(lbGenerator *gen) {
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();
-
- 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 = {};
- }
-
// 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);
-
+ if (build_context.build_paths[BuildPath_VS_LIB].basename.len > 0) {
String path = {};
auto add_path = [&](String path) {
if (path[path.len-1] == '\\') {
@@ -210,9 +185,9 @@ i32 linker_stage(lbGenerator *gen) {
}
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);
+ 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);
}
@@ -252,14 +227,14 @@ i32 linker_stage(lbGenerator *gen) {
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));
+ String pdb_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_PDB]);
+ link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(pdb_path));
}
if (build_context.no_crt) {
@@ -300,13 +275,21 @@ i32 linker_stage(lbGenerator *gen) {
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.res\" \"%.*s.rc\"",
- LIT(output_base),
- LIT(build_context.resource_filepath)
+ "\"rc.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
+ LIT(res_path),
+ LIT(rc_path)
);
if (result) {
@@ -314,13 +297,13 @@ i32 linker_stage(lbGenerator *gen) {
}
result = system_exec_command_line_app("msvc-link",
- "\"%.*slink.exe\" %s \"%.*s.res\" -OUT:\"%.*s.%s\" %s "
+ "\"%.*slink.exe\" %s \"%.*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,
+ LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename),
link_settings,
subsystem_str,
LIT(build_context.link_flags),
@@ -329,13 +312,13 @@ i32 linker_stage(lbGenerator *gen) {
);
} else {
result = system_exec_command_line_app("msvc-link",
- "\"%.*slink.exe\" %s -OUT:\"%.*s.%s\" %s "
+ "\"%.*slink.exe\" %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), output_ext,
+ LIT(vs_exe_path), object_files, LIT(output_filename),
link_settings,
subsystem_str,
LIT(build_context.link_flags),
@@ -350,13 +333,13 @@ i32 linker_stage(lbGenerator *gen) {
} else { // lld
result = system_exec_command_line_app("msvc-lld-link",
- "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s.%s\" %s "
+ "\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%s "
" %.*s "
" %.*s "
" %s "
"",
- LIT(build_context.ODIN_ROOT), object_files, LIT(output_base),output_ext,
+ LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
link_settings,
subsystem_str,
LIT(build_context.link_flags),
@@ -415,7 +398,7 @@ i32 linker_stage(lbGenerator *gen) {
} 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
+ // 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
@@ -431,9 +414,6 @@ i32 linker_stage(lbGenerator *gen) {
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) {
@@ -461,26 +441,12 @@ i32 linker_stage(lbGenerator *gen) {
// 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.
- if (build_context.metrics.os == TargetOs_darwin) {
- output_ext = STR_LIT(".dylib");
- } else {
- output_ext = STR_LIT(".so");
- }
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));
@@ -507,7 +473,7 @@ i32 linker_stage(lbGenerator *gen) {
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, " -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));
@@ -524,9 +490,7 @@ i32 linker_stage(lbGenerator *gen) {
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("dsymutil", "dsymutil %.*s", LIT(output_filename));
if (result) {
return result;
@@ -1526,6 +1490,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 +1508,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 +2639,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 +2663,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 +2690,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 +2724,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 +2804,14 @@ int main(int arg_count, char const **arg_ptr) {
}
if (run_output) {
+ String exe_name = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
+ defer (gb_free(heap_allocator(), exe_name.text));
+
#if defined(GB_SYSTEM_WINDOWS)
- return system_exec_command_line_app("odin run", "%.*s.exe %.*s", LIT(gen->output_base), LIT(run_args_string));
+ return system_exec_command_line_app("odin run", "%.*s %.*s", LIT(exe_name), 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));
+ return system_exec_command_line_app("odin run", "\"%.*s\" %.*s", LIT(exe_name), LIT(run_args_string));
#endif
}
-
return 0;
}
diff --git a/src/parser.cpp b/src/parser.cpp
index 767119aa8..df7f908a6 100644
--- a/src/parser.cpp
+++ b/src/parser.cpp
@@ -5751,7 +5751,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/path.cpp b/src/path.cpp
new file mode 100644
index 000000000..8d8e532b8
--- /dev/null
+++ b/src/path.cpp
@@ -0,0 +1,333 @@
+/*
+ Path handling utilities.
+*/
+
+#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 (string_ends_with(fullpath, '/')) {
+ // It's a directory. We don't need to tinker with the name and extension.
+ 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;
+}
+
+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/string.cpp b/src/string.cpp
index d3dbc6904..3515df48e 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--;
@@ -328,7 +327,10 @@ String directory_from_path(String const &s) {
break;
}
}
- return substring(s, 0, i);
+ if (i >= 0) {
+ return substring(s, 0, i);
+ }
+ return substring(s, 0, 0);
}
String concatenate_strings(gbAllocator a, String const &x, String const &y) {
diff --git a/tests/core/build.bat b/tests/core/build.bat
index 2f9ba672e..1973c22aa 100644
--- a/tests/core/build.bat
+++ b/tests/core/build.bat
@@ -5,61 +5,61 @@ python3 download_assets.py
echo ---
echo Running core:image tests
echo ---
-%PATH_TO_ODIN% run image %COMMON%
+%PATH_TO_ODIN% run image %COMMON% -out:test_image
echo ---
echo Running core:compress tests
echo ---
-%PATH_TO_ODIN% run compress %COMMON%
+%PATH_TO_ODIN% run compress %COMMON% -out:test_compress
echo ---
echo Running core:strings tests
echo ---
-%PATH_TO_ODIN% run strings %COMMON%
+%PATH_TO_ODIN% run strings %COMMON% -out:test_strings
echo ---
echo Running core:hash tests
echo ---
-%PATH_TO_ODIN% run hash %COMMON% -o:size
+%PATH_TO_ODIN% run hash %COMMON% -o:size -out:test_hash
echo ---
echo Running core:odin tests
echo ---
-%PATH_TO_ODIN% run odin %COMMON% -o:size
+%PATH_TO_ODIN% run odin %COMMON% -o:size -out:test_odin
echo ---
echo Running core:crypto hash tests
echo ---
-%PATH_TO_ODIN% run crypto %COMMON%
+%PATH_TO_ODIN% run crypto %COMMON% -out:test_crypto
echo ---
echo Running core:encoding tests
echo ---
-%PATH_TO_ODIN% run encoding/hxa %COMMON%
-%PATH_TO_ODIN% run encoding/json %COMMON%
-%PATH_TO_ODIN% run encoding/varint %COMMON%
+%PATH_TO_ODIN% run encoding/hxa %COMMON% -out:test_hxa
+%PATH_TO_ODIN% run encoding/json %COMMON% -out:test_json
+%PATH_TO_ODIN% run encoding/varint %COMMON% -out:test_varint
echo ---
echo Running core:math/noise tests
echo ---
-%PATH_TO_ODIN% run math/noise %COMMON%
+%PATH_TO_ODIN% run math/noise %COMMON% -out:test_noise
echo ---
echo Running core:math tests
echo ---
-%PATH_TO_ODIN% run math %COMMON%
+%PATH_TO_ODIN% run math %COMMON% -out:test_math
echo ---
echo Running core:math/linalg/glsl tests
echo ---
-%PATH_TO_ODIN% run math/linalg/glsl %COMMON%
+%PATH_TO_ODIN% run math/linalg/glsl %COMMON% -out:test_glsl
echo ---
echo Running core:path/filepath tests
echo ---
-%PATH_TO_ODIN% run path/filepath %COMMON%
+%PATH_TO_ODIN% run path/filepath %COMMON% -out:test_filepath
echo ---
echo Running core:reflect tests
echo ---
-%PATH_TO_ODIN% run reflect %COMMON%
+%PATH_TO_ODIN% run reflect %COMMON% -out:test_reflect
diff --git a/tests/core/math/big/build.bat b/tests/core/math/big/build.bat
index 16bdbc8ca..ad199d775 100644
--- a/tests/core/math/big/build.bat
+++ b/tests/core/math/big/build.bat
@@ -4,7 +4,7 @@ set PATH_TO_ODIN==..\..\..\..\odin
set TEST_ARGS=-fast-tests
set TEST_ARGS=-no-random
set TEST_ARGS=
-set OUT_NAME=math_big_test_library
+set OUT_NAME=math_big_test_library.dll
set COMMON=-build-mode:shared -show-timings -no-bounds-check -define:MATH_BIG_EXE=false -vet -strict-style
echo ---
echo Running core:math/big tests
diff --git a/tests/issues/run.sh b/tests/issues/run.sh
index 117a9a5f1..91ec99e05 100755
--- a/tests/issues/run.sh
+++ b/tests/issues/run.sh
@@ -8,10 +8,10 @@ COMMON="-collection:tests=tests -out:tests/issues/build/test_issue"
set -x
./odin build tests/issues/test_issue_829.odin $COMMON -file
-tests/issues/build/test_issue
+tests/issues/build/test_issue.bin
./odin build tests/issues/test_issue_1592.odin $COMMON -file
-tests/issues/build/test_issue
+tests/issues/build/test_issue.bin
set +x