From: Daniel Dunbar Date: Fri, 24 Oct 2008 22:12:41 +0000 (+0000) Subject: Add initial dependency file generation support. Patch by Kovarththanan X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=750c358049a0e91fd71a8d10a9ac8299c943e238;p=clang 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 0000000000..cd26079f89 --- /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 +#include + +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 +GenerateDependencyFile("MD", + llvm::cl::desc("Generate dependency for main source file " + "(system headers included)")); + +static llvm::cl::opt +GenerateDependencyFileNoSysHeaders("MMD", + llvm::cl::desc("Generate dependency for main source file " + "(no system headers)")); + +static llvm::cl::opt +DependencyOutputFile("MF", + llvm::cl::desc("Specify dependency output file")); + +static llvm::cl::opt +DependencyTarget("MT", + llvm::cl::desc("Specify target for dependency")); + +// FIXME: Implement feature +static llvm::cl::opt +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("", 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(); +} + diff --git a/Driver/clang.cpp b/Driver/clang.cpp index e6c696225d..cd58a05ee7 100644 --- a/Driver/clang.cpp +++ b/Driver/clang.cpp @@ -1084,6 +1084,16 @@ public: return NULL; } + /// FIXME: PP can only handle one callback + if (ProgAction != PrintPreprocessedInput) { + const char* ErrStr; + bool DFG = CreateDependencyFileGen(PP, OutputFile, InFile, ErrStr); + if (!DFG && ErrStr) { + fprintf(stderr, ErrStr); + return NULL; + } + } + InitializeSourceMgr = false; return PP; } diff --git a/Driver/clang.h b/Driver/clang.h index a74a02d408..a3b3bcc68f 100644 --- a/Driver/clang.h +++ b/Driver/clang.h @@ -50,6 +50,13 @@ bool CheckASTConsumer(Preprocessor &PP, ASTConsumer* C); /// CheckDiagnostics - Gather the expected diagnostics and check them. bool CheckDiagnostics(Preprocessor &PP); +/// CreateDependencyFileGen - Create dependency file generator. +/// This is only done if either -MD or -MMD has been specified. +bool CreateDependencyFileGen(Preprocessor *PP, + std::string &OutputFile, + const std::string &InputFile, + const char *&ErrStr); + /// CacheTokens - Cache tokens for use with PCH. void CacheTokens(Preprocessor& PP, const std::string& OutFile); diff --git a/win32/clangDriver/clangDriver.vcproj b/win32/clangDriver/clangDriver.vcproj index 0201c65034..b1bb29562a 100644 --- a/win32/clangDriver/clangDriver.vcproj +++ b/win32/clangDriver/clangDriver.vcproj @@ -207,6 +207,10 @@ RelativePath="..\..\Driver\clang.cpp" > + +