clang-cl: implement /fallback mode

When this flag is enabled, clang-cl falls back to cl.exe if it
cannot compile the code itself for some reason.

The idea is to use this to help build projects that almost compile
with clang-cl, except for some files that can then be built with
the fallback mechanism.

Differential Revision: http://llvm-reviews.chandlerc.com/D1711

llvm-svn: 191034
This commit is contained in:
Hans Wennborg 2013-09-19 20:32:16 +00:00
parent 70fefd515a
commit 87cfa71071
7 changed files with 193 additions and 9 deletions

View File

@ -110,6 +110,8 @@ def _SLASH_Zs : CLFlag<"Zs">, HelpText<"Syntax-check only">,
def _SLASH_M_Group : OptionGroup<"</M group>">, Group<cl_compile_Group>;
def _SLASH_fallback : CLCompileFlag<"fallback">,
HelpText<"Fall back to cl.exe if clang-cl fails to compile">;
def _SLASH_Fe : CLJoined<"Fe">,
HelpText<"Set output executable file or directory (ends in / or \\)">,
MetaVarName<"<file or directory>">;

View File

@ -11,6 +11,7 @@
#define CLANG_DRIVER_JOB_H_
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/OwningPtr.h"
#include "llvm/ADT/SmallVector.h"
#include "llvm/Option/Option.h"
@ -31,6 +32,7 @@ class Job {
public:
enum JobClass {
CommandClass,
FallbackCommandClass,
JobListClass
};
@ -54,8 +56,8 @@ public:
bool Quote, bool CrashReport = false) const = 0;
};
/// Command - An executable path/name and argument vector to
/// execute.
/// Command - An executable path/name and argument vector to
/// execute.
class Command : public Job {
/// Source - The action which caused the creation of this job.
const Action &Source;
@ -77,8 +79,8 @@ public:
virtual void Print(llvm::raw_ostream &OS, const char *Terminator,
bool Quote, bool CrashReport = false) const;
int Execute(const StringRef **Redirects, std::string *ErrMsg,
bool *ExecutionFailed) const;
virtual int Execute(const StringRef **Redirects, std::string *ErrMsg,
bool *ExecutionFailed) const;
/// getSource - Return the Action which caused the creation of this job.
const Action &getSource() const { return Source; }
@ -89,11 +91,34 @@ public:
const llvm::opt::ArgStringList &getArguments() const { return Arguments; }
static bool classof(const Job *J) {
return J->getKind() == CommandClass;
return J->getKind() == CommandClass ||
J->getKind() == FallbackCommandClass;
}
};
/// JobList - A sequence of jobs to perform.
/// Like Command, but with a fallback which is executed in case
/// the primary command crashes.
class FallbackCommand : public Command {
public:
FallbackCommand(const Action &Source_, const Tool &Creator_,
const char *Executable_, const ArgStringList &Arguments_,
Command *Fallback_);
virtual void Print(llvm::raw_ostream &OS, const char *Terminator,
bool Quote, bool CrashReport = false) const;
virtual int Execute(const StringRef **Redirects, std::string *ErrMsg,
bool *ExecutionFailed) const;
static bool classof(const Job *J) {
return J->getKind() == FallbackCommandClass;
}
private:
OwningPtr<Command> Fallback;
};
/// JobList - A sequence of jobs to perform.
class JobList : public Job {
public:
typedef SmallVector<Job*, 4> list_type;

View File

@ -63,7 +63,8 @@ public:
virtual bool hasGoodDiagnostics() const { return false; }
/// ConstructJob - Construct jobs to perform the action \p JA,
/// writing to \p Output and with \p Inputs.
/// writing to \p Output and with \p Inputs, and add the jobs to
/// \p C.
///
/// \param TCArgs - The argument list for this toolchain, with any
/// tool chain specific translations applied.

View File

@ -126,6 +126,43 @@ int Command::Execute(const StringRef **Redirects, std::string *ErrMsg,
/*memoryLimit*/ 0, ErrMsg, ExecutionFailed);
}
FallbackCommand::FallbackCommand(const Action &Source_, const Tool &Creator_,
const char *Executable_,
const ArgStringList &Arguments_,
Command *Fallback_)
: Command(Source_, Creator_, Executable_, Arguments_), Fallback(Fallback_) {
}
void FallbackCommand::Print(raw_ostream &OS, const char *Terminator,
bool Quote, bool CrashReport) const {
Command::Print(OS, "", Quote, CrashReport);
OS << " ||";
Fallback->Print(OS, Terminator, Quote, CrashReport);
}
static bool ShouldFallback(int ExitCode) {
// FIXME: We really just want to fall back for internal errors, such
// as when some symbol cannot be mangled, when we should be able to
// parse something but can't, etc.
return ExitCode != 0;
}
int FallbackCommand::Execute(const StringRef **Redirects, std::string *ErrMsg,
bool *ExecutionFailed) const {
int PrimaryStatus = Command::Execute(Redirects, ErrMsg, ExecutionFailed);
if (!ShouldFallback(PrimaryStatus))
return PrimaryStatus;
// Clear ExecutionFailed and ErrMsg before falling back.
if (ErrMsg)
ErrMsg->clear();
if (ExecutionFailed)
*ExecutionFailed = false;
int SecondaryStatus = Fallback->Execute(Redirects, ErrMsg, ExecutionFailed);
return SecondaryStatus;
}
JobList::JobList() : Job(JobListClass) {}
JobList::~JobList() {

View File

@ -3565,7 +3565,15 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
}
// Finally add the compile command to the compilation.
C.addCommand(new Command(JA, *this, Exec, CmdArgs));
if (Args.hasArg(options::OPT__SLASH_fallback)) {
tools::visualstudio::Compile CL(getToolChain());
Command *CLCommand = CL.GetCommand(C, JA, Output, Inputs, Args,
LinkingOutput);
C.addCommand(new FallbackCommand(JA, *this, Exec, CmdArgs, CLCommand));
} else {
C.addCommand(new Command(JA, *this, Exec, CmdArgs));
}
// Handle the debug info splitting at object creation time if we're
// creating an object.
@ -6638,3 +6646,70 @@ void visualstudio::Link::ConstructJob(Compilation &C, const JobAction &JA,
Args.MakeArgString(getToolChain().GetProgramPath("link.exe"));
C.addCommand(new Command(JA, *this, Exec, CmdArgs));
}
void visualstudio::Compile::ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
const ArgList &Args,
const char *LinkingOutput) const {
C.addCommand(GetCommand(C, JA, Output, Inputs, Args, LinkingOutput));
}
Command *visualstudio::Compile::GetCommand(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
const ArgList &Args,
const char *LinkingOutput) const {
ArgStringList CmdArgs;
CmdArgs.push_back("/c"); // Compile only.
CmdArgs.push_back("/W0"); // No warnings.
// The goal is to be able to invoke this tool correctly based on
// any flag accepted by clang-cl.
// These are spelled the same way in clang and cl.exe,.
Args.AddAllArgs(CmdArgs, options::OPT_D, options::OPT_U);
Args.AddAllArgs(CmdArgs, options::OPT_I);
Args.AddLastArg(CmdArgs, options::OPT_O, options::OPT_O0);
// Flags for which clang-cl have an alias.
// FIXME: How can we ensure this stays in sync with relevant clang-cl options?
if (Arg *A = Args.getLastArg(options::OPT_frtti, options::OPT_fno_rtti))
CmdArgs.push_back(A->getOption().getID() == options::OPT_frtti ? "/GR"
: "/GR-");
if (Args.hasArg(options::OPT_fsyntax_only))
CmdArgs.push_back("/Zs");
// Flags that can simply be passed through.
Args.AddAllArgs(CmdArgs, options::OPT__SLASH_LD);
Args.AddAllArgs(CmdArgs, options::OPT__SLASH_LDd);
// The order of these flags is relevant, so pick the last one.
if (Arg *A = Args.getLastArg(options::OPT__SLASH_MD, options::OPT__SLASH_MDd,
options::OPT__SLASH_MT, options::OPT__SLASH_MTd))
A->render(Args, CmdArgs);
// Input filename.
assert(Inputs.size() == 1);
const InputInfo &II = Inputs[0];
assert(II.getType() == types::TY_C || II.getType() == types::TY_CXX);
CmdArgs.push_back(II.getType() == types::TY_C ? "/Tc" : "/Tp");
if (II.isFilename())
CmdArgs.push_back(II.getFilename());
else
II.getInputArg().renderAsInput(Args, CmdArgs);
// Output filename.
assert(Output.getType() == types::TY_Object);
const char *Fo = Args.MakeArgString(std::string("/Fo") +
Output.getFilename());
CmdArgs.push_back(Fo);
// FIXME: If we've put clang-cl as cl.exe on the path, we have a problem.
const char *Exec =
Args.MakeArgString(getToolChain().GetProgramPath("cl.exe"));
return new Command(JA, *this, Exec, CmdArgs);
}

View File

@ -21,6 +21,7 @@ namespace clang {
class ObjCRuntime;
namespace driver {
class Command;
class Driver;
namespace toolchains {
@ -590,7 +591,7 @@ namespace dragonfly {
/// Visual studio tools.
namespace visualstudio {
class LLVM_LIBRARY_VISIBILITY Link : public Tool {
class LLVM_LIBRARY_VISIBILITY Link : public Tool {
public:
Link(const ToolChain &TC) : Tool("visualstudio::Link", "linker", TC) {}
@ -603,6 +604,27 @@ namespace visualstudio {
const llvm::opt::ArgList &TCArgs,
const char *LinkingOutput) const;
};
class LLVM_LIBRARY_VISIBILITY Compile : public Tool {
public:
Compile(const ToolChain &TC) : Tool("visualstudio::Compile", "compiler", TC) {}
virtual bool hasIntegratedAssembler() const { return true; }
virtual bool hasIntegratedCPP() const { return true; }
virtual bool isLinkJob() const { return false; }
virtual void ConstructJob(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
const llvm::opt::ArgList &TCArgs,
const char *LinkingOutput) const;
Command *GetCommand(Compilation &C, const JobAction &JA,
const InputInfo &Output,
const InputInfoList &Inputs,
const llvm::opt::ArgList &TCArgs,
const char *LinkingOutput) const;
};
} // end namespace visualstudio
} // end namespace toolchains

View File

@ -0,0 +1,22 @@
// 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 /fallback /Dfoo=bar /Ubaz /Ifoo /O0 /Ox /GR /GR- /LD /LDd \
// RUN: /MD /MDd /MTd /MT -### -- %s 2>&1 | FileCheck %s
// CHECK: ||
// CHECK: cl.exe
// CHECK: "/c"
// CHECK: "/W0"
// CHECK: "-D" "foo=bar"
// CHECK: "-U" "baz"
// CHECK: "-I" "foo"
// CHECK: "-O3"
// CHECK: "/GR-"
// CHECK: "/LD"
// CHECK: "/LDd"
// CHECK: "/MT"
// CHECK: "/Tc" "{{.*cl-fallback.c}}"
// CHECK: "/Fo{{.*cl-fallback.*.obj}}"