From 03652d74235dc83351160d63c33f5342dee74480 Mon Sep 17 00:00:00 2001 From: Jeroen van Rijn Date: Thu, 2 Jul 2026 14:19:56 +0200 Subject: [PATCH] Improve custom output location for LLVM-IR, OBJ + ASM Fixes #6926. If `build` is a subdirectory in the current directory, then `odin build . -out:build` will place `.S`, `.obj` and `.ll` files there if the corresponding `-build-mode` is given. --- src/build_settings.cpp | 36 ++++++++++++++++++++++++++++++------ src/llvm_backend.cpp | 19 ++++++++++++++----- 2 files changed, 44 insertions(+), 11 deletions(-) diff --git a/src/build_settings.cpp b/src/build_settings.cpp index c0f28288f..23f353a3d 100644 --- a/src/build_settings.cpp +++ b/src/build_settings.cpp @@ -2409,12 +2409,32 @@ gb_internal bool init_build_paths(String init_filename) { GB_PANIC("Unhandled build mode/target combination.\n"); } + bool output_should_be_directory = false; + 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])) { + bool output_is_directory = 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)); + + // NOTE(Jeroen): For LLVM-IR, we want `-out` to specify a directory. + // For other outputs we expect it to be a file path. + if (build_context.build_mode == BuildMode_LLVM_IR) { + if (!output_is_directory) { + gb_printf_err("Output path %.*s should be a directory for LLVM-IR output.\n", LIT(output_file)); + return false; + } + output_should_be_directory = true; + + } else if (build_context.build_mode == BuildMode_Object) { + // Both directory or filename prefix allowed + + } else if (build_context.build_mode == BuildMode_Assembly) { + // Both directory or filename prefix allowed + + } else if (build_context.metrics.os == TargetOs_windows) { + if (output_is_directory) { 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) { @@ -2525,7 +2545,11 @@ gb_internal bool init_build_paths(String init_filename) { // 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 || is_arch_wasm() || build_context.build_mode != BuildMode_Executable) { - bc->build_paths[BuildPath_Output].ext = copy_string(ha, output_extension); + + // NOTE(Jeroen): If build mode is LLVM_IR and a custom output was set + if (!output_should_be_directory) { + bc->build_paths[BuildPath_Output].ext = copy_string(ha, output_extension); + } } } @@ -2533,7 +2557,7 @@ gb_internal bool init_build_paths(String init_filename) { defer (gb_free(ha, output_file.text)); // Check if output path is a directory. - if (path_is_directory(bc->build_paths[BuildPath_Output])) { + if (!output_should_be_directory && path_is_directory(bc->build_paths[BuildPath_Output])) { gb_printf_err("Output path %.*s is a directory.\n", LIT(output_file)); return false; } diff --git a/src/llvm_backend.cpp b/src/llvm_backend.cpp index f9e049621..2cf687dc4 100644 --- a/src/llvm_backend.cpp +++ b/src/llvm_backend.cpp @@ -2713,9 +2713,12 @@ gb_internal String lb_filepath_ll_for_module(lbModule *m) { s.len -= prefix.len; } - path = concatenate_strings(permanent_allocator(), path, s); - path = concatenate_strings(permanent_allocator(), s, STR_LIT(".ll")); - + if (build_context.out_filepath.len > 0) { + path = concatenate_strings(permanent_allocator(), path, s); + path = concatenate_strings(permanent_allocator(), path, STR_LIT(".ll")); + } else { + path = concatenate_strings(permanent_allocator(), s, STR_LIT(".ll")); + } return path; } @@ -2736,6 +2739,8 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) { gbString path = gb_string_make_length(heap_allocator(), basename.text, basename.len); path = gb_string_appendc(path, "/"); + bool output_is_directory = path_is_directory(make_string_c(path)); + if (USE_SEPARATE_MODULES) { GB_ASSERT(m->module_name != nullptr); String s = make_string_c(m->module_name); @@ -2760,10 +2765,14 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) { if (build_context.lto_kind != LTO_None) { ext = STR_LIT("bc"); } else if (build_context.build_mode == BuildMode_Assembly) { - ext = STR_LIT("S"); + // Allow a user override for the asm extension. + // If that's a directory, we force the `.S` extension + ext = output_is_directory ? STR_LIT("S") : build_context.build_paths[BuildPath_Output].ext; } else if (build_context.build_mode == BuildMode_Object) { // Allow a user override for the object extension. - ext = build_context.build_paths[BuildPath_Output].ext; + // If that's a directory, we force the `.obj` extension + ext = output_is_directory ? STR_LIT("obj") : build_context.build_paths[BuildPath_Output].ext; + } else { ext = infer_object_extension_from_build_context(); }