1 //===--- DependencyFile.cpp - Generate dependency file --------------------===//
2 //
3 //                     The LLVM Compiler Infrastructure
4 //
5 // This file is distributed under the University of Illinois Open Source
6 // License. See LICENSE.TXT for details.
7 //
8 //===----------------------------------------------------------------------===//
9 //
10 // This code generates dependency files.
11 //
12 //===----------------------------------------------------------------------===//
13 
14 #include "clang/Frontend/Utils.h"
15 #include "clang/Basic/FileManager.h"
16 #include "clang/Basic/SourceLocation.h"
17 #include "clang/Basic/SourceManager.h"
18 #include "clang/Frontend/DependencyOutputOptions.h"
19 #include "clang/Frontend/FrontendDiagnostic.h"
20 #include "clang/Lex/DirectoryLookup.h"
21 #include "clang/Lex/PPCallbacks.h"
22 #include "clang/Lex/Preprocessor.h"
23 #include "llvm/ADT/StringSet.h"
24 #include "llvm/Support/raw_ostream.h"
25 #include <string>
26 
27 using namespace clang;
28 
29 namespace {
30 class DependencyFileCallback : public PPCallbacks {
31   std::vector<std::string> Files;
32   llvm::StringSet<> FilesSet;
33   const Preprocessor *PP;
34   std::vector<std::string> Targets;
35   llvm::raw_ostream *OS;
36   bool IncludeSystemHeaders;
37   bool PhonyTarget;
38 private:
39   bool FileMatchesDepCriteria(const char *Filename,
40                               SrcMgr::CharacteristicKind FileType);
41   void OutputDependencyFile();
42 
43 public:
44   DependencyFileCallback(const Preprocessor *_PP,
45                          llvm::raw_ostream *_OS,
46                          const DependencyOutputOptions &Opts)
47     : PP(_PP), Targets(Opts.Targets), OS(_OS),
48       IncludeSystemHeaders(Opts.IncludeSystemHeaders),
49       PhonyTarget(Opts.UsePhonyTargets) {}
50 
51   virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason,
52                            SrcMgr::CharacteristicKind FileType);
53 
54   virtual void EndOfMainFile() {
55     OutputDependencyFile();
56     OS->flush();
57     delete OS;
58     OS = 0;
59   }
60 };
61 }
62 
63 void clang::AttachDependencyFileGen(Preprocessor &PP,
64                                     const DependencyOutputOptions &Opts) {
65   if (Opts.Targets.empty()) {
66     PP.getDiagnostics().Report(diag::err_fe_dependency_file_requires_MT);
67     return;
68   }
69 
70   std::string Err;
71   llvm::raw_ostream *OS(new llvm::raw_fd_ostream(Opts.OutputFile.c_str(), Err));
72   if (!Err.empty()) {
73     PP.getDiagnostics().Report(diag::err_fe_error_opening)
74       << Opts.OutputFile << Err;
75     return;
76   }
77 
78   PP.addPPCallbacks(new DependencyFileCallback(&PP, OS, Opts));
79 }
80 
81 /// FileMatchesDepCriteria - Determine whether the given Filename should be
82 /// considered as a dependency.
83 bool DependencyFileCallback::FileMatchesDepCriteria(const char *Filename,
84                                           SrcMgr::CharacteristicKind FileType) {
85   if (strcmp("<built-in>", Filename) == 0)
86     return false;
87 
88   if (IncludeSystemHeaders)
89     return true;
90 
91   return FileType == SrcMgr::C_User;
92 }
93 
94 void DependencyFileCallback::FileChanged(SourceLocation Loc,
95                                          FileChangeReason Reason,
96                                          SrcMgr::CharacteristicKind FileType) {
97   if (Reason != PPCallbacks::EnterFile)
98     return;
99 
100   // Dependency generation really does want to go all the way to the
101   // file entry for a source location to find out what is depended on.
102   // We do not want #line markers to affect dependency generation!
103   SourceManager &SM = PP->getSourceManager();
104 
105   const FileEntry *FE =
106     SM.getFileEntryForID(SM.getFileID(SM.getInstantiationLoc(Loc)));
107   if (FE == 0) return;
108 
109   const char *Filename = FE->getName();
110   if (!FileMatchesDepCriteria(Filename, FileType))
111     return;
112 
113   // Remove leading "./"
114   if (Filename[0] == '.' && Filename[1] == '/')
115     Filename = &Filename[2];
116 
117   if (FilesSet.insert(Filename))
118     Files.push_back(Filename);
119 }
120 
121 void DependencyFileCallback::OutputDependencyFile() {
122   // Write out the dependency targets, trying to avoid overly long
123   // lines when possible. We try our best to emit exactly the same
124   // dependency file as GCC (4.2), assuming the included files are the
125   // same.
126   const unsigned MaxColumns = 75;
127   unsigned Columns = 0;
128 
129   for (std::vector<std::string>::iterator
130          I = Targets.begin(), E = Targets.end(); I != E; ++I) {
131     unsigned N = I->length();
132     if (Columns == 0) {
133       Columns += N;
134       *OS << *I;
135     } else if (Columns + N + 2 > MaxColumns) {
136       Columns = N + 2;
137       *OS << " \\\n  " << *I;
138     } else {
139       Columns += N + 1;
140       *OS << ' ' << *I;
141     }
142   }
143 
144   *OS << ':';
145   Columns += 1;
146 
147   // Now add each dependency in the order it was seen, but avoiding
148   // duplicates.
149   for (std::vector<std::string>::iterator I = Files.begin(),
150          E = Files.end(); I != E; ++I) {
151     // Start a new line if this would exceed the column limit. Make
152     // sure to leave space for a trailing " \" in case we need to
153     // break the line on the next iteration.
154     unsigned N = I->length();
155     if (Columns + (N + 1) + 2 > MaxColumns) {
156       *OS << " \\\n ";
157       Columns = 2;
158     }
159     *OS << ' ' << *I;
160     Columns += N + 1;
161   }
162   *OS << '\n';
163 
164   // Create phony targets if requested.
165   if (PhonyTarget) {
166     // Skip the first entry, this is always the input file itself.
167     for (std::vector<std::string>::iterator I = Files.begin() + 1,
168            E = Files.end(); I != E; ++I) {
169       *OS << '\n';
170       *OS << *I << ":\n";
171     }
172   }
173 }
174 
175