//===--- 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 "llvm/Support/raw_ostream.h" #include #include using namespace clang; namespace { class VISIBILITY_HIDDEN DependencyFileCallback : public PPCallbacks { std::vector Files; llvm::StringSet<> FilesSet; const Preprocessor *PP; std::ofstream OS; const std::string &InputFile; std::string Target; 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::string &Target, 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 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::CharacteristicKind 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::CharacteristicKind 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]; 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; OS << Target << ":"; unsigned Columns = Target.length() + 1; // Now add each dependency in the order it was seen, but avoiding // duplicates. for (std::vector::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::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::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; Files.push_back(InputFile); } DependencyFileCallback::~DependencyFileCallback() { if ((!GenerateDependencyFile && !GenerateDependencyFileNoSysHeaders) || OS.fail()) return; OutputDependencyFile(); OS.close(); }