forked from OSchip/llvm-project
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:
parent
70fefd515a
commit
87cfa71071
|
@ -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>">;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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() {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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}}"
|
Loading…
Reference in New Issue