llvm-project/clang/tools/clang-cc/DependencyFile.cpp

273 lines
8.5 KiB
C++

//===--- DependencyFile.cpp - Generate dependency file --------------------===//
//
// The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This code generates dependency files.
//
//===----------------------------------------------------------------------===//
#include "clang.h"
#include "clang/Basic/SourceManager.h"
#include "clang/Basic/FileManager.h"
#include "clang/Lex/Preprocessor.h"
#include "clang/Lex/PPCallbacks.h"
#include "clang/Lex/DirectoryLookup.h"
#include "clang/Basic/SourceLocation.h"
#include "llvm/ADT/StringSet.h"
#include "llvm/System/Path.h"
#include "llvm/Support/CommandLine.h"
#include "llvm/Support/Compiler.h"
#include "llvm/Support/raw_ostream.h"
#include <fstream>
#include <string>
using namespace clang;
namespace {
class VISIBILITY_HIDDEN DependencyFileCallback : public PPCallbacks {
std::vector<std::string> Files;
llvm::StringSet<> FilesSet;
const Preprocessor *PP;
std::ofstream OS;
const std::string &InputFile;
std::vector<std::string> Targets;
private:
bool FileMatchesDepCriteria(const char *Filename,
SrcMgr::CharacteristicKind FileType);
void OutputDependencyFile();
public:
DependencyFileCallback(const Preprocessor *PP,
const std::string &InputFile,
const std::string &DepFile,
const std::vector<std::string> &Targets,
const char *&ErrStr);
~DependencyFileCallback();
virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason,
SrcMgr::CharacteristicKind FileType);
};
}
static const char *DependencyFileExt = "d";
static const char *ObjectFileExt = "o";
//===----------------------------------------------------------------------===//
// Dependency file options
//===----------------------------------------------------------------------===//
static llvm::cl::opt<bool>
GenerateDependencyFile("MD",
llvm::cl::desc("Generate dependency for main source file "
"(system headers included)"));
static llvm::cl::opt<bool>
GenerateDependencyFileNoSysHeaders("MMD",
llvm::cl::desc("Generate dependency for main source file "
"(no system headers)"));
static llvm::cl::opt<std::string>
DependencyOutputFile("MF",
llvm::cl::desc("Specify dependency output file"));
static llvm::cl::list<std::string>
DependencyTargets("MT",
llvm::cl::desc("Specify target for dependency"));
// FIXME: Implement feature
static llvm::cl::opt<bool>
PhonyDependencyTarget("MP",
llvm::cl::desc("Create phony target for each dependency "
"(other than main file)"));
bool clang::CreateDependencyFileGen(Preprocessor *PP,
std::string &OutputFile,
const std::string &InputFile,
const char *&ErrStr) {
assert(!InputFile.empty() && "No file given");
ErrStr = NULL;
if (!GenerateDependencyFile && !GenerateDependencyFileNoSysHeaders) {
if (!DependencyOutputFile.empty() || !DependencyTargets.empty() ||
PhonyDependencyTarget)
ErrStr = "Error: to generate dependencies you must specify -MD or -MMD\n";
return false;
}
// Handle conflicting options
if (GenerateDependencyFileNoSysHeaders)
GenerateDependencyFile = false;
// Determine name of dependency output filename
llvm::sys::Path DepFile;
if (!DependencyOutputFile.empty())
DepFile = DependencyOutputFile;
else if (!OutputFile.empty()) {
DepFile = OutputFile;
DepFile.eraseSuffix();
DepFile.appendSuffix(DependencyFileExt);
}
else {
DepFile = InputFile;
DepFile.eraseSuffix();
DepFile.appendSuffix(DependencyFileExt);
}
std::vector<std::string> Targets(DependencyTargets);
// Infer target name if unspecified
if (Targets.empty()) {
if (!OutputFile.empty()) {
llvm::sys::Path TargetPath(OutputFile);
TargetPath.eraseSuffix();
TargetPath.appendSuffix(ObjectFileExt);
Targets.push_back(TargetPath.toString());
} else {
llvm::sys::Path TargetPath(InputFile);
TargetPath.eraseSuffix();
TargetPath.appendSuffix(ObjectFileExt);
Targets.push_back(TargetPath.toString());
}
}
DependencyFileCallback *PPDep =
new DependencyFileCallback(PP, InputFile, DepFile.toString(),
Targets, ErrStr);
if (ErrStr){
delete PPDep;
return false;
}
else {
PP->setPPCallbacks(PPDep);
return true;
}
}
/// FileMatchesDepCriteria - Determine whether the given Filename should be
/// considered as a dependency.
bool DependencyFileCallback::FileMatchesDepCriteria(const char *Filename,
SrcMgr::CharacteristicKind FileType) {
if (strcmp(InputFile.c_str(), Filename) != 0 &&
strcmp("<built-in>", Filename) != 0) {
if (GenerateDependencyFileNoSysHeaders)
return FileType == SrcMgr::C_User;
else
return true;
}
return false;
}
void DependencyFileCallback::FileChanged(SourceLocation Loc,
FileChangeReason Reason,
SrcMgr::CharacteristicKind FileType) {
if (Reason != PPCallbacks::EnterFile)
return;
// Depedency generation really does want to go all the way to the file entry
// for a source location to find out what is depended on. We do not want
// #line markers to affect dependency generation!
SourceManager &SM = PP->getSourceManager();
const FileEntry *FE =
SM.getFileEntryForID(SM.getFileID(SM.getInstantiationLoc(Loc)));
if (FE == 0) return;
const char *Filename = FE->getName();
if (!FileMatchesDepCriteria(Filename, FileType))
return;
// Remove leading "./"
if (Filename[0] == '.' && Filename[1] == '/')
Filename = &Filename[2];
if (FilesSet.insert(Filename))
Files.push_back(Filename);
}
void DependencyFileCallback::OutputDependencyFile() {
// Write out the dependency targets, trying to avoid overly long
// lines when possible. We try our best to emit exactly the same
// dependency file as GCC (4.2), assuming the included files are the
// same.
const unsigned MaxColumns = 75;
unsigned Columns = 0;
for (std::vector<std::string>::iterator
I = Targets.begin(), E = Targets.end(); I != E; ++I) {
unsigned N = I->length();
if (Columns == 0) {
Columns += N;
OS << *I;
} else if (Columns + N + 2 > MaxColumns) {
Columns = N + 2;
OS << " \\\n " << *I;
} else {
Columns += N + 1;
OS << " " << *I;
}
}
OS << ":";
Columns += 1;
// Now add each dependency in the order it was seen, but avoiding
// duplicates.
for (std::vector<std::string>::iterator I = Files.begin(),
E = Files.end(); I != E; ++I) {
// Start a new line if this would exceed the column limit. Make
// sure to leave space for a trailing " \" in case we need to
// break the line on the next iteration.
unsigned N = I->length();
if (Columns + (N + 1) + 2 > MaxColumns) {
OS << " \\\n ";
Columns = 2;
}
OS << " " << *I;
Columns += N + 1;
}
OS << "\n";
// Create phony targets if requested.
if (PhonyDependencyTarget) {
// Skip the first entry, this is always the input file itself.
for (std::vector<std::string>::iterator I = Files.begin() + 1,
E = Files.end(); I != E; ++I) {
OS << "\n";
OS << *I << ":\n";
}
}
}
DependencyFileCallback::DependencyFileCallback(const Preprocessor *PP,
const std::string &InputFile,
const std::string &DepFile,
const std::vector<std::string>
&Targets,
const char *&ErrStr)
: PP(PP), InputFile(InputFile), Targets(Targets) {
OS.open(DepFile.c_str());
if (OS.fail())
ErrStr = "Could not open dependency output file\n";
else
ErrStr = NULL;
Files.push_back(InputFile);
}
DependencyFileCallback::~DependencyFileCallback() {
if ((!GenerateDependencyFile && !GenerateDependencyFileNoSysHeaders) ||
OS.fail())
return;
OutputDependencyFile();
OS.close();
}