Add initial dependency file generation support. Patch by Kovarththanan
Rajaratnam, with some updates and formatting changes.
git-svn-id: https://llvm.org/svn/llvm-project/cfe/trunk@58122 91177308-0d34-0410-b5e6-96231b3b80d8
diff --git a/Driver/DependencyFile.cpp b/Driver/DependencyFile.cpp
new file mode 100644
index 0000000..cd26079
--- /dev/null
+++ b/Driver/DependencyFile.cpp
@@ -0,0 +1,220 @@
+//===--- 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/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 <fstream>
+#include <string>
+
+using namespace clang;
+
+namespace {
+class VISIBILITY_HIDDEN DependencyFileCallback : public PPCallbacks {
+ llvm::StringSet<> Files;
+ const Preprocessor *PP;
+ std::ofstream OS;
+ const std::string &InputFile;
+ std::string Target;
+
+private:
+ bool FileMatchesDepCriteria(const char *Filename,
+ SrcMgr::Characteristic_t FileType);
+ void OutputDependencyFile();
+
+public:
+ DependencyFileCallback(const Preprocessor *PP,
+ const std::string &InputFile,
+ const std::string &DepFile,
+ const std::string &Target,
+ const char *&ErrStr);
+ ~DependencyFileCallback();
+ virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason,
+ SrcMgr::Characteristic_t 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::opt<std::string>
+DependencyTarget("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() || !DependencyTarget.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);
+ }
+
+ // Determine name of target
+ std::string Target;
+ if (!DependencyTarget.empty())
+ Target = DependencyTarget;
+ else if (!OutputFile.empty()) {
+ llvm::sys::Path TargetPath(OutputFile);
+ TargetPath.eraseSuffix();
+ TargetPath.appendSuffix(ObjectFileExt);
+ Target = TargetPath.toString();
+ }
+ else {
+ llvm::sys::Path TargetPath(InputFile);
+ TargetPath.eraseSuffix();
+ TargetPath.appendSuffix(ObjectFileExt);
+ Target = TargetPath.toString();
+ }
+
+ DependencyFileCallback *PPDep =
+ new DependencyFileCallback(PP, InputFile, DepFile.toString(),
+ Target, 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::Characteristic_t FileType) {
+ if (strcmp(InputFile.c_str(), Filename) != 0 &&
+ strcmp("<predefines>", Filename) != 0) {
+ if (GenerateDependencyFileNoSysHeaders)
+ return FileType == SrcMgr::C_User;
+ else
+ return true;
+ }
+
+ return false;
+}
+
+void DependencyFileCallback::FileChanged(SourceLocation Loc,
+ FileChangeReason Reason,
+ SrcMgr::Characteristic_t FileType) {
+ if (Reason != PPCallbacks::EnterFile)
+ return;
+
+ const char *Filename = PP->getSourceManager().getSourceName(Loc);
+ if (!FileMatchesDepCriteria(Filename, FileType))
+ return;
+
+ // Remove leading "./"
+ if(Filename[0] == '.' && Filename[1] == '/')
+ Filename = &Filename[2];
+
+ Files.insert(Filename);
+}
+
+void DependencyFileCallback::OutputDependencyFile() {
+ std::string Output;
+ // Add "target: mainfile"
+ Output += Target;
+ Output += ": ";
+ Output += InputFile;
+
+ // Now add each dependency
+ for (llvm::StringSet<>::iterator I = Files.begin(),
+ E = Files.end(); I != E; ++I) {
+ // FIXME: Wrap lines
+ Output += " ";
+ Output += I->getKeyData();
+ }
+
+ OS << Output << "\n";
+}
+
+DependencyFileCallback::DependencyFileCallback(const Preprocessor *PP,
+ const std::string &InputFile,
+ const std::string &DepFile,
+ const std::string &Target,
+ const char *&ErrStr)
+ : PP(PP), InputFile(InputFile), Target(Target) {
+
+ OS.open(DepFile.c_str());
+ if (OS.fail())
+ ErrStr = "Could not open dependency output file\n";
+ else
+ ErrStr = NULL;
+}
+
+DependencyFileCallback::~DependencyFileCallback() {
+ if ((!GenerateDependencyFile && !GenerateDependencyFileNoSysHeaders) ||
+ OS.fail())
+ return;
+
+ OutputDependencyFile();
+ OS.close();
+}
+