From 6706162582094209b1d4a68aad108245b7c4b027 Mon Sep 17 00:00:00 2001 From: Jon Ross-Perkins Date: Fri, 13 Mar 2026 09:07:12 -0700 Subject: [PATCH] Error when passing multiple input files with --output (#6896) Fixes #6895 Note this is just a short-term fix to avoid confusion, as the compile structure needs to change on the whole. Assisted-by: Google Antigravity with Gemini --- bazel/carbon_rules/defs.bzl | 2 +- .../codegen/testdata/objcode/basic.carbon | 2 +- toolchain/diagnostics/kind.def | 1 + toolchain/driver/BUILD | 1 + toolchain/driver/compile_subcommand.cpp | 41 +++++++++++++++---- toolchain/driver/compile_subcommand.h | 1 + .../fail_multi_input_with_output.carbon | 16 ++++++++ toolchain/driver/testdata/verbose.carbon | 2 +- 8 files changed, 55 insertions(+), 11 deletions(-) create mode 100644 toolchain/driver/testdata/fail_multi_input_with_output.carbon diff --git a/bazel/carbon_rules/defs.bzl b/bazel/carbon_rules/defs.bzl index 8c260e3c121c..69a83482613e 100644 --- a/bazel/carbon_rules/defs.bzl +++ b/bazel/carbon_rules/defs.bzl @@ -89,7 +89,7 @@ def _carbon_binary_impl(ctx): inputs = depset(direct = srcs_reordered, transitive = dep_hdrs), executable = toolchain_driver, tools = depset(toolchain_data), - arguments = ["compile", "--output=" + out.path] + + arguments = ["compile", "--output=" + out.path, "--output-last-input-only"] + [s.path for s in srcs_reordered] + extra_flags + ctx.attr.flags, mnemonic = "CarbonCompile", progress_message = "Compiling " + src.short_path, diff --git a/toolchain/codegen/testdata/objcode/basic.carbon b/toolchain/codegen/testdata/objcode/basic.carbon index e4b044be135a..aa5d078a570c 100644 --- a/toolchain/codegen/testdata/objcode/basic.carbon +++ b/toolchain/codegen/testdata/objcode/basic.carbon @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon -// EXTRA-ARGS: --target=x86_64-unknown-linux-gnu --output=%t +// EXTRA-ARGS: --output-last-input-only --target=x86_64-unknown-linux-gnu --output=%t // // TODO: Add a way to write some basic tests for object file outputs. // AUTOUPDATE diff --git a/toolchain/diagnostics/kind.def b/toolchain/diagnostics/kind.def index c0084245b90c..f63749fe5b50 100644 --- a/toolchain/diagnostics/kind.def +++ b/toolchain/diagnostics/kind.def @@ -28,6 +28,7 @@ CARBON_DIAGNOSTIC_KIND(DriverCommandLineParseFailed) CARBON_DIAGNOSTIC_KIND(CompilePhaseFlagConflict) CARBON_DIAGNOSTIC_KIND(CompilePreludeManifestError) CARBON_DIAGNOSTIC_KIND(CompileInputNotRegularFile) +CARBON_DIAGNOSTIC_KIND(CompileMultipleInputsWithOutput) CARBON_DIAGNOSTIC_KIND(CompileOutputFileOpenError) CARBON_DIAGNOSTIC_KIND(CompileTargetInvalid) CARBON_DIAGNOSTIC_KIND(ConfigFailedToReadDigest) diff --git a/toolchain/driver/BUILD b/toolchain/driver/BUILD index 70a1537dd138..73751919abd2 100644 --- a/toolchain/driver/BUILD +++ b/toolchain/driver/BUILD @@ -215,6 +215,7 @@ cc_library( "//toolchain/check", "//toolchain/codegen", "//toolchain/diagnostics:emitter", + "//toolchain/diagnostics:format_providers", "//toolchain/diagnostics:sorting_consumer", "//toolchain/format", "//toolchain/language_server", diff --git a/toolchain/driver/compile_subcommand.cpp b/toolchain/driver/compile_subcommand.cpp index 9467f9e6b41c..b17704177e0f 100644 --- a/toolchain/driver/compile_subcommand.cpp +++ b/toolchain/driver/compile_subcommand.cpp @@ -24,6 +24,7 @@ #include "toolchain/check/check.h" #include "toolchain/codegen/codegen.h" #include "toolchain/diagnostics/emitter.h" +#include "toolchain/diagnostics/format_providers.h" #include "toolchain/diagnostics/sorting_consumer.h" #include "toolchain/lex/lex.h" #include "toolchain/lower/lower.h" @@ -358,6 +359,17 @@ Whether to emit DWARF debug information. arg_b.Default(true); arg_b.Set(&include_debug_info); }); + b.AddFlag( + { + .name = "output-last-input-only", + .help = R"""( +Only write output for the last input file, ignoring all others. + +TODO: This is a temporary workaround and should be removed once separate +compilation is better implemented. +)""", + }, + [&](auto& arg_b) { arg_b.Set(&output_last_input_only); }); b.AddFlag( { .name = "verify-llvm-ir", @@ -1028,11 +1040,8 @@ auto CompilationUnit::RunCodeGenHelper() -> bool { llvm::sys::path::replace_extension(output_filename, options_->asm_output ? ".s" : ".o"); } else { - // TODO: Handle the case where multiple input files were specified - // along with an output file name. That should either be an error or - // should produce a single LLVM IR module containing all inputs. - // Currently each unit overwrites the output from the previous one in - // this case. + CARBON_CHECK(total_ir_count_ == 1 || options_->output_last_input_only, + "Handled by CompileMultipleInputsWithOutput diagnostic"); } CARBON_VLOG("Writing output to: {0}\n", output_filename); @@ -1156,9 +1165,21 @@ auto CompileSubcommand::Run(DriverEnv& driver_env) -> DriverResult { } } + int total_unit_count = prelude.size() + options_.input_filenames.size(); + if (!options_.output_last_input_only && total_unit_count > 1 && + !options_.output_filename.empty() && options_.output_filename != "-") { + CARBON_DIAGNOSTIC( + CompileMultipleInputsWithOutput, Error, + "writing {0} input file{0:s} to the same `--output` file would " + "overwrite the output file; for now, pass `--output-last-input-only` " + "if intended", + Diagnostics::IntAsSelect); + driver_env.emitter.Emit(CompileMultipleInputsWithOutput, total_unit_count); + return {.success = false}; + } + // Prepare CompilationUnits before building scope exit handlers. llvm::SmallVector> units; - int total_unit_count = prelude.size() + options_.input_filenames.size(); int unit_index = -1; auto unit_builder = [&](llvm::StringRef filename) { ++unit_index; @@ -1310,8 +1331,12 @@ auto CompileSubcommand::Run(DriverEnv& driver_env) -> DriverResult { "CodeGen should be the last stage"); // Codegen. - for (auto& unit : units) { - unit->RunCodeGen(); + if (options_.output_last_input_only) { + units.back()->RunCodeGen(); + } else { + for (const auto& unit : units) { + unit->RunCodeGen(); + } } return make_result(); } diff --git a/toolchain/driver/compile_subcommand.h b/toolchain/driver/compile_subcommand.h index 71d52cd54d39..aee3765be4da 100644 --- a/toolchain/driver/compile_subcommand.h +++ b/toolchain/driver/compile_subcommand.h @@ -65,6 +65,7 @@ struct CompileOptions { bool builtin_sem_ir = false; bool prelude_import = false; bool include_debug_info = true; + bool output_last_input_only = false; bool run_llvm_verifier = true; llvm::SmallVector exclude_dump_file_prefixes; diff --git a/toolchain/driver/testdata/fail_multi_input_with_output.carbon b/toolchain/driver/testdata/fail_multi_input_with_output.carbon new file mode 100644 index 000000000000..a97750079aeb --- /dev/null +++ b/toolchain/driver/testdata/fail_multi_input_with_output.carbon @@ -0,0 +1,16 @@ +// Part of the Carbon Language project, under the Apache License v2.0 with LLVM +// Exceptions. See /LICENSE for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +// Exclude the prelude import so that the number of prelude files isn't tested. +// ARGS: --include-diagnostic-kind compile --output=foo.o --no-prelude-import foo.carbon bar.carbon +// +// SET-CAPTURE-CONSOLE-OUTPUT +// clang-format off +// AUTOUPDATE +// TIP: To test this file alone, run: +// TIP: bazel test //toolchain/testing:file_test --test_arg=--file_tests=toolchain/driver/testdata/fail_multi_input_with_output.carbon +// TIP: To dump output, run: +// TIP: bazel run //toolchain/testing:file_test -- --dump_output --file_tests=toolchain/driver/testdata/fail_multi_input_with_output.carbon +// CHECK:STDERR: error: writing 2 input files to the same `--output` file would overwrite the output file; for now, pass `--output-last-input-only` if intended [CompileMultipleInputsWithOutput] +// CHECK:STDERR: diff --git a/toolchain/driver/testdata/verbose.carbon b/toolchain/driver/testdata/verbose.carbon index 4e4917eec00b..fec645e48e4e 100644 --- a/toolchain/driver/testdata/verbose.carbon +++ b/toolchain/driver/testdata/verbose.carbon @@ -3,7 +3,7 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception // // INCLUDE-FILE: toolchain/testing/testdata/min_prelude/none.carbon -// ARGS: -v compile --phase=codegen --target=x86_64-unknown-linux-gnu --output=%t %s +// ARGS: -v compile --phase=codegen --target=x86_64-unknown-linux-gnu --output=%t --output-last-input-only %s // // Verifies that various phases are included in vlog output. //