From: Hans Wennborg Date: Mon, 12 Aug 2013 21:56:42 +0000 (+0000) Subject: clang-cl: Support the /Fe option X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=c65c72d198b02c0d90c6bcced655e4bb11ed3f37;p=clang clang-cl: Support the /Fe option This is used to name the linked output file. Differential Revision: http://llvm-reviews.chandlerc.com/D1344 git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@188210 91177308-0d34-0410-b5e6-96231b3b80d8 --- diff --git a/include/clang/Basic/DiagnosticDriverKinds.td b/include/clang/Basic/DiagnosticDriverKinds.td index 9222db8c33..f378dafb64 100644 --- a/include/clang/Basic/DiagnosticDriverKinds.td +++ b/include/clang/Basic/DiagnosticDriverKinds.td @@ -135,7 +135,7 @@ def warn_drv_assuming_mfloat_abi_is : Warning< "unknown platform, assuming -mfloat-abi=%0">; def warn_ignoring_ftabstop_value : Warning< "ignoring invalid -ftabstop value '%0', using default value %1">; -def warn_drv_overriding_fo_option : Warning< +def warn_drv_overriding_joined_option : Warning< "overriding '%0%1' option with '%2%3'">, InGroup>; def warn_drv_overriding_t_option : Warning< diff --git a/include/clang/Driver/CLCompatOptions.td b/include/clang/Driver/CLCompatOptions.td index 0fdd6f343a..a4b8b61e2b 100644 --- a/include/clang/Driver/CLCompatOptions.td +++ b/include/clang/Driver/CLCompatOptions.td @@ -90,6 +90,9 @@ def _SLASH_Zs : CLFlag<"Zs">, HelpText<"Syntax-check only">, // Non-aliases: +def _SLASH_Fe : CLJoined<"Fe">, + HelpText<"Set output executable file or directory (ends in / or \\)">, + MetaVarName<"">; def _SLASH_Fo : CLJoined<"Fo">, HelpText<"Set output object file, or directory (ends in / or \\)">, MetaVarName<"">; diff --git a/lib/Driver/Driver.cpp b/lib/Driver/Driver.cpp index d6013f19ea..a6fe4f0ec2 100644 --- a/lib/Driver/Driver.cpp +++ b/lib/Driver/Driver.cpp @@ -267,6 +267,26 @@ DerivedArgList *Driver::TranslateInputArgs(const InputArgList &Args) const { return DAL; } +/// \brief Check whether there are multiple instances of OptionID in Args, and +/// if so, issue a diagnostics about it. +static void DiagnoseOptionOverride(const Driver &D, const DerivedArgList &Args, + unsigned OptionID) { + assert(Args.hasArg(OptionID)); + + arg_iterator it = Args.filtered_begin(OptionID); + arg_iterator ie = Args.filtered_end(); + Arg *Previous = *it; + ++it; + + while (it != ie) { + D.Diag(clang::diag::warn_drv_overriding_joined_option) + << Previous->getSpelling() << Previous->getValue() + << (*it)->getSpelling() << (*it)->getValue(); + Previous = *it; + ++it; + } +} + Compilation *Driver::BuildCompilation(ArrayRef ArgList) { llvm::PrettyStackTraceString CrashInfo("Compilation construction"); @@ -358,18 +378,9 @@ Compilation *Driver::BuildCompilation(ArrayRef ArgList) { BuildInputs(C->getDefaultToolChain(), C->getArgs(), Inputs); if (Arg *A = C->getArgs().getLastArg(options::OPT__SLASH_Fo)) { - // Check for multiple /Fo arguments. - for (arg_iterator it = C->getArgs().filtered_begin(options::OPT__SLASH_Fo), - ie = C->getArgs().filtered_end(); it != ie; ++it) { - if (*it != A) { - Diag(clang::diag::warn_drv_overriding_fo_option) - << (*it)->getSpelling() << (*it)->getValue() - << A->getSpelling() << A->getValue(); - } - } - + DiagnoseOptionOverride(*this, C->getArgs(), options::OPT__SLASH_Fo); StringRef V = A->getValue(); - if (V == "") { + if (V.empty()) { // It has to have a value. Diag(clang::diag::err_drv_missing_argument) << A->getSpelling() << 1; C->getArgs().eraseArg(options::OPT__SLASH_Fo); @@ -381,6 +392,16 @@ Compilation *Driver::BuildCompilation(ArrayRef ArgList) { } } + if (Arg *A = C->getArgs().getLastArg(options::OPT__SLASH_Fe)) { + DiagnoseOptionOverride(*this, C->getArgs(), options::OPT__SLASH_Fe); + + if (A->getValue()[0] == '\0') { + // It has to have a value. + Diag(clang::diag::err_drv_missing_argument) << A->getSpelling() << 1; + C->getArgs().eraseArg(options::OPT__SLASH_Fe); + } + } + // Construct the list of abstract actions to perform for this compilation. On // Darwin target OSes this uses the driver-driver and universal actions. if (TC.getTriple().isOSDarwin()) @@ -1565,6 +1586,27 @@ void Driver::BuildJobsForAction(Compilation &C, } } +/// \brief Create output filename based on ArgValue, which could either be a +/// full filename, filename without extension, or a directory. +static const char *MakeCLOutputFilename(const ArgList &Args, StringRef ArgValue, + StringRef BaseName, types::ID FileType) { + SmallString<128> Filename = ArgValue; + assert(!ArgValue.empty() && "Output filename argument must not be empty."); + + if (llvm::sys::path::is_separator(Filename.back())) { + // If the argument is a directory, output to BaseName in that dir. + llvm::sys::path::append(Filename, BaseName); + } + + if (!llvm::sys::path::has_extension(ArgValue)) { + // If the argument didn't provide an extension, then set it. + const char *Extension = types::getTypeTempSuffix(FileType, true); + llvm::sys::path::replace_extension(Filename, Extension); + } + + return Args.MakeArgString(Filename.c_str()); +} + const char *Driver::GetNamedOutputPath(Compilation &C, const JobAction &JA, const char *BaseInput, @@ -1612,23 +1654,21 @@ const char *Driver::GetNamedOutputPath(Compilation &C, C.getArgs().hasArg(options::OPT__SLASH_Fo)) { // The /Fo flag decides the object filename. StringRef Val = C.getArgs().getLastArg(options::OPT__SLASH_Fo)->getValue(); - SmallString<128> Filename = Val; - - if (llvm::sys::path::is_separator(Val.back())) { - // If /Fo names a dir, output to BaseName in that dir. - llvm::sys::path::append(Filename, BaseName); - } - if (!llvm::sys::path::has_extension(Val)) { - // If /Fo doesn't provide a filename with an extension, we set it. - if (llvm::sys::path::has_extension(Filename.str())) - Filename = Filename.substr(0, Filename.rfind(".")); - Filename.append("."); - Filename.append(types::getTypeTempSuffix(types::TY_Object, IsCLMode())); - } - - NamedOutput = C.getArgs().MakeArgString(Filename.c_str()); + 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 (MultipleArchs && BoundArch) { + if (IsCLMode()) { + // clang-cl uses BaseName for the executable name. + SmallString<128> Filename = BaseName; + llvm::sys::path::replace_extension(Filename, "exe"); + NamedOutput = C.getArgs().MakeArgString(Filename.c_str()); + } else if (MultipleArchs && BoundArch) { SmallString<128> Output(DefaultImageName.c_str()); Output += "-"; Output.append(BoundArch); diff --git a/lib/Driver/Types.cpp b/lib/Driver/Types.cpp index 67ef2a6a83..e569a2bf82 100644 --- a/lib/Driver/Types.cpp +++ b/lib/Driver/Types.cpp @@ -47,6 +47,8 @@ types::ID types::getPreprocessedType(ID Id) { const char *types::getTypeTempSuffix(ID Id, bool CLMode) { if (Id == TY_Object && CLMode) return "obj"; + if (Id == TY_Image && CLMode) + return "exe"; return getInfo(Id).TempSuffix; } diff --git a/test/Driver/cl-Fe.c b/test/Driver/cl-Fe.c new file mode 100644 index 0000000000..b1b80a4717 --- /dev/null +++ b/test/Driver/cl-Fe.c @@ -0,0 +1,31 @@ +// Don't attempt slash switches on msys bash. +// REQUIRES: shell-preserves-root + +// Note: %s must be preceded by --, otherwise it may be interpreted as a +// command-line option, e.g. on Mac where %s is commonly under /Users. + + +// RUN: %clang_cl -### -- %s 2>&1 | FileCheck -check-prefix=DEFAULTEXE %s +// DEFAULTEXE: cl-Fe.exe + +// RUN: %clang_cl /Fefoo -### -- %s 2>&1 | FileCheck -check-prefix=FeNOEXT %s +// FeNOEXT: foo.exe + +// RUN: %clang_cl /Fefoo.ext -### -- %s 2>&1 | FileCheck -check-prefix=FeEXT %s +// FeEXT: foo.ext + +// RUN: %clang_cl /Fefoo.dir/ -### -- %s 2>&1 | FileCheck -check-prefix=FeDIR %s +// FeDIR: foo.dir{{[/\\]+}}cl-Fe.exe + +// RUN: %clang_cl /Fefoo.dir/a -### -- %s 2>&1 | FileCheck -check-prefix=FeDIRNAME %s +// FeDIRNAME: foo.dir{{[/\\]+}}a.exe + +// RUN: %clang_cl /Fefoo.dir/a.ext -### -- %s 2>&1 | FileCheck -check-prefix=FeDIRNAMEEXT %s +// FeDIRNAMEEXT: foo.dir{{[/\\]+}}a.ext + +// RUN: %clang_cl /Fe -### 2>&1 | FileCheck -check-prefix=MISSINGARG %s +// MISSINGARG: error: argument to '/Fe' is missing (expected 1 value) + +// RUN: %clang_cl /Fefoo /Febar -### -- %s 2>&1 | FileCheck -check-prefix=FEOVERRIDE %s +// FEOVERRIDE: warning: overriding '/Fefoo' option with '/Febar' +// FEOVERRIDE: bar.exe diff --git a/test/Driver/cl-Fo.c b/test/Driver/cl-Fo.c index fe3d58d46b..e1139247b9 100644 --- a/test/Driver/cl-Fo.c +++ b/test/Driver/cl-Fo.c @@ -21,7 +21,7 @@ // CHECK-DIRNAMEEXT: "-o" "foo.dir{{[/\\]+}}a.ext" // RUN: %clang_cl /Fo.. -### -- %s 2>&1 | FileCheck -check-prefix=CHECK-CRAZY %s -// CHECK-CRAZY: "-o" "...obj" +// CHECK-CRAZY: "-o" "..obj" // RUN: %clang_cl /Fo -### 2>&1 | FileCheck -check-prefix=CHECK-MISSINGARG %s