Index: include/clang/Driver/Options.td =================================================================== --- include/clang/Driver/Options.td +++ include/clang/Driver/Options.td @@ -1237,7 +1237,8 @@ HelpText<"Disable standard #include directories for the C++ standard library">; def nostdlib : Flag<["-"], "nostdlib">; def object : Flag<["-"], "object">; -def o : JoinedOrSeparate<["-"], "o">, Flags<[DriverOption, RenderAsInput, CC1Option]>, +def o : JoinedOrSeparate<["-"], "o">, + Flags<[DriverOption, RenderAsInput, CC1Option, CoreOption]>, HelpText<"Write output to ">, MetaVarName<"">; def pagezero__size : JoinedOrSeparate<["-"], "pagezero_size">; def pass_exit_codes : Flag<["-", "--"], "pass-exit-codes">, Flags<[Unsupported]>; Index: lib/Driver/Driver.cpp =================================================================== --- lib/Driver/Driver.cpp +++ lib/Driver/Driver.cpp @@ -1606,17 +1606,41 @@ bool AtTopLevel, bool MultipleArchs) const { llvm::PrettyStackTraceString CrashInfo("Computing output path"); - // Output to a user requested destination? - if (AtTopLevel && !isa(JA) && - !isa(JA)) { - if (Arg *FinalOutput = C.getArgs().getLastArg(options::OPT_o)) - return C.addResultFile(FinalOutput->getValue(), &JA); + StringRef BaseName = llvm::sys::path::filename(BaseInput); + + if (!isa(JA) && !isa(JA)) { + // Only apply -o to the output of the top level action. + options::ID MaybeDashO = AtTopLevel ? options::OPT_o : options::OPT_INVALID; + // Find the last clang-cl output argument that is relevant to the current job. + options::ID MaybeCLOpt; + switch (JA.getType()) { + case types::TY_PP_Asm: MaybeCLOpt = options::OPT__SLASH_Fa; break; + case types::TY_Object: MaybeCLOpt = options::OPT__SLASH_Fo; break; + case types::TY_Image: MaybeCLOpt = options::OPT__SLASH_Fe; break; + default: MaybeCLOpt = options::OPT_INVALID; break; + } + Arg *OutputArg = C.getArgs().getLastArg(MaybeDashO, MaybeCLOpt); + + // If we had an explicit output argument, honor it. + if (OutputArg) { + Option Op = OutputArg->getOption(); + const char *NamedOutput; + if (Op.matches(options::OPT__SLASH_Fa) || + Op.matches(options::OPT__SLASH_Fo) || + Op.matches(options::OPT__SLASH_Fe)) { + NamedOutput = MakeCLOutputFilename(C.getArgs(), OutputArg->getValue(), + BaseName, JA.getType()); + } else { + assert(Op.matches(options::OPT_o)); + NamedOutput = OutputArg->getValue(); + } + return C.addResultFile(NamedOutput, &JA); + } } // For /P, preprocess to file named after BaseInput. if (C.getArgs().hasArg(options::OPT__SLASH_P)) { assert(AtTopLevel && isa(JA)); - StringRef BaseName = llvm::sys::path::filename(BaseInput); return C.addResultFile(MakeCLOutputFilename(C.getArgs(), "", BaseName, types::TY_PP_C), &JA); } @@ -1627,19 +1651,13 @@ return "-"; // Is this the assembly listing for /FA? - if (JA.getType() == types::TY_PP_Asm && - (C.getArgs().hasArg(options::OPT__SLASH_FA) || - C.getArgs().hasArg(options::OPT__SLASH_Fa))) { - // Use /Fa and the input filename to determine the asm file name. - StringRef BaseName = llvm::sys::path::filename(BaseInput); - StringRef FaValue = C.getArgs().getLastArgValue(options::OPT__SLASH_Fa); - return C.addResultFile(MakeCLOutputFilename(C.getArgs(), FaValue, BaseName, - JA.getType()), &JA); + if (C.getArgs().hasArg(options::OPT__SLASH_FA)) { + return C.addResultFile( + MakeCLOutputFilename(C.getArgs(), "", BaseName, JA.getType()), &JA); } // Output to a temporary file? - if ((!AtTopLevel && !C.getArgs().hasArg(options::OPT_save_temps) && - !C.getArgs().hasArg(options::OPT__SLASH_Fo)) || + if ((!AtTopLevel && !C.getArgs().hasArg(options::OPT_save_temps)) || CCGenDiagnostics) { StringRef Name = llvm::sys::path::filename(BaseInput); std::pair Split = Name.split('.'); @@ -1649,31 +1667,14 @@ return C.addTempFile(C.getArgs().MakeArgString(TmpName.c_str())); } - SmallString<128> BasePath(BaseInput); - StringRef BaseName; - // Dsymutil actions should use the full path. if (isa(JA) || isa(JA)) - BaseName = BasePath; - else - BaseName = llvm::sys::path::filename(BasePath); + BaseName = BaseInput; // Determine what the derived output name should be. const char *NamedOutput; - if (JA.getType() == types::TY_Object && - C.getArgs().hasArg(options::OPT__SLASH_Fo)) { - // The /Fo flag decides the object filename. - StringRef Val = C.getArgs().getLastArg(options::OPT__SLASH_Fo)->getValue(); - NamedOutput = MakeCLOutputFilename(C.getArgs(), Val, BaseName, - types::TY_Object); - } else if (JA.getType() == types::TY_Image && - C.getArgs().hasArg(options::OPT__SLASH_Fe)) { - // The /Fe flag names the linked file. - StringRef Val = C.getArgs().getLastArg(options::OPT__SLASH_Fe)->getValue(); - NamedOutput = MakeCLOutputFilename(C.getArgs(), Val, BaseName, - types::TY_Image); - } else if (JA.getType() == types::TY_Image) { + if (JA.getType() == types::TY_Image) { if (IsCLMode()) { // clang-cl uses BaseName for the executable name. NamedOutput = MakeCLOutputFilename(C.getArgs(), "", BaseName, @@ -1725,6 +1726,7 @@ // As an annoying special case, PCH generation doesn't strip the pathname. if (JA.getType() == types::TY_PCH) { + SmallString<128> BasePath(BaseInput); llvm::sys::path::remove_filename(BasePath); if (BasePath.empty()) BasePath = NamedOutput; Index: test/Driver/cl-outputs.c =================================================================== --- test/Driver/cl-outputs.c +++ test/Driver/cl-outputs.c @@ -109,3 +109,31 @@ // RUN: %clang_cl /P -### -- %s 2>&1 | FileCheck -check-prefix=P %s // P: "-E" // P: "-o" "cl-outputs.i" + +// Last arg wins between -o and -Fo for simple compilation. +// RUN: %clang_cl -c -o t1.obj -Fot2.obj -### -- %s 2>&1 | FileCheck %s -check-prefix o_Fo +// o_Fo: "-cc1" +// o_Fo: "-o" "t2.obj" +// RUN: %clang_cl -c -Fot1.obj -o t2.obj -### -- %s 2>&1 | FileCheck %s -check-prefix Fo_o +// Fo_o: "-cc1" +// Fo_o: "-o" "t2.obj" + +// Last arg wins between -o and -Fe for linked images. +// RUN: %clang_cl -o t1.exe -Fet2.exe -### -- %s 2>&1 | FileCheck %s -check-prefix o_Fe +// o_Fe: "-cc1" +// o_Fe: link.exe +// o_Fe: "-out:t2.exe" +// RUN: %clang_cl -Fet1.exe -o t2.exe -### -- %s 2>&1 | FileCheck %s -check-prefix Fe_o +// Fe_o: "-cc1" +// Fe_o: link.exe +// Fe_o: "-out:t2.exe" + +// -Fo and -Fa work for intermediate outputs. +// RUN: %clang_cl -Fat.s -Fot.obj -o t.exe -### -- %s 2>&1 | FileCheck %s -check-prefix Fa_Fo_o +// Fa_Fo_o: "-cc1" +// Fa_Fo_o: "-S" +// Fa_Fo_o: "-o" "t.s" +// Fa_Fo_o: "-cc1as" +// Fa_Fo_o: "-o" "t.obj" +// Fa_Fo_o: link.exe +// Fa_Fo_o: "-out:t.exe"