[driver] Associate a JobAction with each result file. This enables the driver

to delete result files for only those commands that fail.
Part of rdar://12984531

llvm-svn: 173361
This commit is contained in:
Chad Rosier 2013-01-24 19:14:47 +00:00
parent 5f8609b7b4
commit 633dcdc52d
7 changed files with 102 additions and 46 deletions

View File

@ -20,6 +20,7 @@ namespace driver {
class DerivedArgList;
class Driver;
class InputArgList;
class JobAction;
class JobList;
class ToolChain;
@ -54,11 +55,11 @@ class Compilation {
ArgStringList TempFiles;
/// Result files which should be removed on failure.
ArgStringList ResultFiles;
ArgStringMap ResultFiles;
/// Result files which are generated correctly on failure, and which should
/// only be removed if we crash.
ArgStringList FailureResultFiles;
ArgStringMap FailureResultFiles;
/// Redirection for stdout, stderr, etc.
const llvm::sys::Path **Redirects;
@ -88,9 +89,9 @@ public:
const ArgStringList &getTempFiles() const { return TempFiles; }
const ArgStringList &getResultFiles() const { return ResultFiles; }
const ArgStringMap &getResultFiles() const { return ResultFiles; }
const ArgStringList &getFailureResultFiles() const {
const ArgStringMap &getFailureResultFiles() const {
return FailureResultFiles;
}
@ -113,24 +114,40 @@ public:
/// addResultFile - Add a file to remove on failure, and returns its
/// argument.
const char *addResultFile(const char *Name) {
ResultFiles.push_back(Name);
const char *addResultFile(const char *Name, const JobAction *JA) {
ResultFiles[JA] = Name;
return Name;
}
/// addFailureResultFile - Add a file to remove if we crash, and returns its
/// argument.
const char *addFailureResultFile(const char *Name) {
FailureResultFiles.push_back(Name);
const char *addFailureResultFile(const char *Name, const JobAction *JA) {
FailureResultFiles[JA] = Name;
return Name;
}
/// CleanupFile - Delete a given file.
///
/// \param IssueErrors - Report failures as errors.
/// \return Whether the file was removed successfully.
bool CleanupFile(const char *File, bool IssueErrors = false) const;
/// CleanupFileList - Remove the files in the given list.
///
/// \param IssueErrors - Report failures as errors.
/// \return Whether all files were removed successfully.
bool CleanupFileList(const ArgStringList &Files,
bool IssueErrors=false) const;
bool IssueErrors = false) const;
/// CleanupFileMap - Remove the files in the given map.
///
/// \param JA - If specified, only delete the files associated with this
/// JobAction. Otherwise, delete all files in the map.
/// \param IssueErrors - Report failures as errors.
/// \return Whether all files were removed successfully.
bool CleanupFileMap(const ArgStringMap &Files,
const JobAction *JA,
bool IssueErrors = false) const;
/// PrintJob - Print one job in -### format.
///

View File

@ -11,14 +11,19 @@
#define CLANG_DRIVER_UTIL_H_
#include "clang/Basic/LLVM.h"
#include "llvm/ADT/DenseMap.h"
namespace clang {
namespace driver {
class Action;
class JobAction;
/// ArgStringList - Type used for constructing argv lists for subprocesses.
typedef SmallVector<const char*, 16> ArgStringList;
/// ArgStringMap - Type used to map a JobAction to its result file.
typedef llvm::DenseMap<const JobAction*, const char*> ArgStringMap;
/// ActionList - Type used for lists of actions.
typedef SmallVector<Action*, 3> ActionList;

View File

@ -199,39 +199,56 @@ void Compilation::PrintDiagnosticJob(raw_ostream &OS, const Job &J) const {
}
}
bool Compilation::CleanupFile(const char *File, bool IssueErrors) const {
llvm::sys::Path P(File);
std::string Error;
// Don't try to remove files which we don't have write access to (but may be
// able to remove), or non-regular files. Underlying tools may have
// intentionally not overwritten them.
if (!P.canWrite() || !P.isRegularFile())
return true;
if (P.eraseFromDisk(false, &Error)) {
// Failure is only failure if the file exists and is "regular". There is
// a race condition here due to the limited interface of
// llvm::sys::Path, we want to know if the removal gave ENOENT.
// FIXME: Grumble, P.exists() is broken. PR3837.
struct stat buf;
if (::stat(P.c_str(), &buf) == 0 ? (buf.st_mode & S_IFMT) == S_IFREG :
(errno != ENOENT)) {
if (IssueErrors)
getDriver().Diag(clang::diag::err_drv_unable_to_remove_file)
<< Error;
return false;
}
}
return true;
}
bool Compilation::CleanupFileList(const ArgStringList &Files,
bool IssueErrors) const {
bool Success = true;
for (ArgStringList::const_iterator
it = Files.begin(), ie = Files.end(); it != ie; ++it)
Success &= CleanupFile(*it, IssueErrors);
return Success;
}
bool Compilation::CleanupFileMap(const ArgStringMap &Files,
const JobAction *JA,
bool IssueErrors) const {
bool Success = true;
for (ArgStringMap::const_iterator
it = Files.begin(), ie = Files.end(); it != ie; ++it) {
llvm::sys::Path P(*it);
std::string Error;
// Don't try to remove files which we don't have write access to (but may be
// able to remove), or non-regular files. Underlying tools may have
// intentionally not overwritten them.
if (!P.canWrite() || !P.isRegularFile())
// If specified, only delete the files associated with the JobAction.
// Otherwise, delete all files in the map.
if (JA && it->first != JA)
continue;
if (P.eraseFromDisk(false, &Error)) {
// Failure is only failure if the file exists and is "regular". There is
// a race condition here due to the limited interface of
// llvm::sys::Path, we want to know if the removal gave ENOENT.
// FIXME: Grumble, P.exists() is broken. PR3837.
struct stat buf;
if (::stat(P.c_str(), &buf) == 0 ? (buf.st_mode & S_IFMT) == S_IFREG :
(errno != ENOENT)) {
if (IssueErrors)
getDriver().Diag(clang::diag::err_drv_unable_to_remove_file)
<< Error;
Success = false;
}
}
Success &= CleanupFile(it->second, IssueErrors);
}
return Success;
}

View File

@ -485,8 +485,9 @@ void Driver::generateCompilationDiagnostics(Compilation &C,
<< "\n\n********************";
} else {
// Failure, remove preprocessed files.
if (!C.getArgs().hasArg(options::OPT_save_temps))
if (!C.getArgs().hasArg(options::OPT_save_temps)) {
C.CleanupFileList(C.getTempFiles(), true);
}
Diag(clang::diag::note_drv_command_failed_diag_msg)
<< "Error generating preprocessed source(s).";
@ -516,11 +517,12 @@ int Driver::ExecuteCompilation(const Compilation &C,
// Otherwise, remove result files as well.
if (!C.getArgs().hasArg(options::OPT_save_temps)) {
C.CleanupFileList(C.getResultFiles(), true);
const JobAction *JA = cast<JobAction>(&FailingCommand->getSource());
C.CleanupFileMap(C.getResultFiles(), JA, true);
// Failure result files are valid unless we crashed.
if (Res < 0)
C.CleanupFileList(C.getFailureResultFiles(), true);
C.CleanupFileMap(C.getFailureResultFiles(), JA, true);
}
// Print extra information about abnormal failures, if possible.
@ -1417,7 +1419,7 @@ const char *Driver::GetNamedOutputPath(Compilation &C,
if (AtTopLevel && !isa<DsymutilJobAction>(JA) &&
!isa<VerifyJobAction>(JA)) {
if (Arg *FinalOutput = C.getArgs().getLastArg(options::OPT_o))
return C.addResultFile(FinalOutput->getValue());
return C.addResultFile(FinalOutput->getValue(), &JA);
}
// Default to writing to stdout?
@ -1487,9 +1489,9 @@ const char *Driver::GetNamedOutputPath(Compilation &C,
BasePath = NamedOutput;
else
llvm::sys::path::append(BasePath, NamedOutput);
return C.addResultFile(C.getArgs().MakeArgString(BasePath.c_str()));
return C.addResultFile(C.getArgs().MakeArgString(BasePath.c_str()), &JA);
} else {
return C.addResultFile(NamedOutput);
return C.addResultFile(NamedOutput, &JA);
}
}

View File

@ -228,6 +228,7 @@ static bool forwardToGCC(const Option &O) {
}
void Clang::AddPreprocessingOptions(Compilation &C,
const JobAction &JA,
const Driver &D,
const ArgList &Args,
ArgStringList &CmdArgs,
@ -248,7 +249,7 @@ void Clang::AddPreprocessingOptions(Compilation &C,
const char *DepFile;
if (Arg *MF = Args.getLastArg(options::OPT_MF)) {
DepFile = MF->getValue();
C.addFailureResultFile(DepFile);
C.addFailureResultFile(DepFile, &JA);
} else if (Output.getType() == types::TY_Dependencies) {
DepFile = Output.getFilename();
} else if (A->getOption().matches(options::OPT_M) ||
@ -256,7 +257,7 @@ void Clang::AddPreprocessingOptions(Compilation &C,
DepFile = "-";
} else {
DepFile = getDependencyFileName(Args, Inputs);
C.addFailureResultFile(DepFile);
C.addFailureResultFile(DepFile, &JA);
}
CmdArgs.push_back("-dependency-file");
CmdArgs.push_back(DepFile);
@ -2300,7 +2301,7 @@ void Clang::ConstructJob(Compilation &C, const JobAction &JA,
//
// FIXME: Support -fpreprocessed
if (types::getPreprocessedType(InputType) != types::TY_INVALID)
AddPreprocessingOptions(C, D, Args, CmdArgs, Output, Inputs);
AddPreprocessingOptions(C, JA, D, Args, CmdArgs, Output, Inputs);
// Don't warn about "clang -c -DPIC -fPIC test.i" because libtool.m4 assumes
// that "The compiler can only warn and ignore the option if not recognized".

View File

@ -40,6 +40,7 @@ namespace tools {
private:
void AddPreprocessingOptions(Compilation &C,
const JobAction &JA,
const Driver &D,
const ArgList &Args,
ArgStringList &CmdArgs,

View File

@ -1,15 +1,15 @@
// RUN: touch %t.o
// RUN: not %clang -DCRASH -o %t.o -MMD -MF %t.d %s
// RUN: not %clang -c -DCRASH -o %t.o -MMD -MF %t.d %s
// RUN: test ! -f %t.o
// RUN: test ! -f %t.d
// RUN: touch %t.o
// RUN: not %clang -DMISSING -o %t.o -MMD -MF %t.d %s
// RUN: not %clang -c -DMISSING -o %t.o -MMD -MF %t.d %s
// RUN: test ! -f %t.o
// RUN: test ! -f %t.d
// RUN: touch %t.o
// RUN: not %clang -o %t.o -MMD -MF %t.d %s
// RUN: not %clang -c -o %t.o -MMD -MF %t.d %s
// RUN: test ! -f %t.o
// RUN: test -f %t.d
@ -23,3 +23,16 @@
#else
invalid C code
#endif
// RUN: touch %t1.c
// RUN: echo "invalid C code" > %t2.c
// RUN: cd %T && not %clang -c %t1.c %t2.c
// RUN: test -f %t1.o
// RUN: test ! -f %t2.o
// RUN: touch %t1.c
// RUN: touch %t2.c
// RUN: chmod -r %t2.c
// RUN: cd %T && not %clang -c %t1.c %t2.c
// RUN: test -f %t1.o
// RUN: test ! -f %t2.o