blob: 282164fdd6cc819a8260fc4901c76afb6e18181f [file] [log] [blame]
Daniel Dunbar750c3582008-10-24 22:12:41 +00001//===--- 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.h"
15#include "clang/Basic/SourceManager.h"
16#include "clang/Lex/Preprocessor.h"
17#include "clang/Lex/PPCallbacks.h"
18#include "clang/Lex/DirectoryLookup.h"
19#include "clang/Basic/SourceLocation.h"
20#include "llvm/ADT/StringSet.h"
21#include "llvm/System/Path.h"
22#include "llvm/Support/CommandLine.h"
23#include "llvm/Support/Compiler.h"
Daniel Dunbar7e9f1f72008-10-27 20:01:06 +000024#include "llvm/Support/raw_ostream.h"
Daniel Dunbar750c3582008-10-24 22:12:41 +000025#include <fstream>
26#include <string>
27
28using namespace clang;
29
30namespace {
31class VISIBILITY_HIDDEN DependencyFileCallback : public PPCallbacks {
Daniel Dunbar7e9f1f72008-10-27 20:01:06 +000032 std::vector<std::string> Files;
33 llvm::StringSet<> FilesSet;
Daniel Dunbar750c3582008-10-24 22:12:41 +000034 const Preprocessor *PP;
35 std::ofstream OS;
36 const std::string &InputFile;
Chris Lattner02fbb252009-01-11 19:28:34 +000037 std::vector<std::string> Targets;
Daniel Dunbar750c3582008-10-24 22:12:41 +000038
39private:
40 bool FileMatchesDepCriteria(const char *Filename,
Chris Lattner9d728512008-10-27 01:19:25 +000041 SrcMgr::CharacteristicKind FileType);
Daniel Dunbar750c3582008-10-24 22:12:41 +000042 void OutputDependencyFile();
43
44public:
45 DependencyFileCallback(const Preprocessor *PP,
46 const std::string &InputFile,
47 const std::string &DepFile,
Chris Lattner02fbb252009-01-11 19:28:34 +000048 const std::vector<std::string> &Targets,
Daniel Dunbar750c3582008-10-24 22:12:41 +000049 const char *&ErrStr);
50 ~DependencyFileCallback();
51 virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason,
Chris Lattner9d728512008-10-27 01:19:25 +000052 SrcMgr::CharacteristicKind FileType);
Daniel Dunbar750c3582008-10-24 22:12:41 +000053};
54}
55
56static const char *DependencyFileExt = "d";
57static const char *ObjectFileExt = "o";
58
59//===----------------------------------------------------------------------===//
60// Dependency file options
61//===----------------------------------------------------------------------===//
62static llvm::cl::opt<bool>
63GenerateDependencyFile("MD",
64 llvm::cl::desc("Generate dependency for main source file "
65 "(system headers included)"));
66
67static llvm::cl::opt<bool>
68GenerateDependencyFileNoSysHeaders("MMD",
69 llvm::cl::desc("Generate dependency for main source file "
70 "(no system headers)"));
71
72static llvm::cl::opt<std::string>
73DependencyOutputFile("MF",
74 llvm::cl::desc("Specify dependency output file"));
75
Chris Lattner02fbb252009-01-11 19:28:34 +000076static llvm::cl::list<std::string>
77DependencyTargets("MT",
Daniel Dunbar750c3582008-10-24 22:12:41 +000078 llvm::cl::desc("Specify target for dependency"));
79
80// FIXME: Implement feature
81static llvm::cl::opt<bool>
82PhonyDependencyTarget("MP",
83 llvm::cl::desc("Create phony target for each dependency "
84 "(other than main file)"));
85
86bool clang::CreateDependencyFileGen(Preprocessor *PP,
87 std::string &OutputFile,
88 const std::string &InputFile,
89 const char *&ErrStr) {
90 assert(!InputFile.empty() && "No file given");
91
92 ErrStr = NULL;
93
94 if (!GenerateDependencyFile && !GenerateDependencyFileNoSysHeaders) {
Chris Lattner02fbb252009-01-11 19:28:34 +000095 if (!DependencyOutputFile.empty() || !DependencyTargets.empty() ||
Daniel Dunbar750c3582008-10-24 22:12:41 +000096 PhonyDependencyTarget)
97 ErrStr = "Error: to generate dependencies you must specify -MD or -MMD\n";
98 return false;
99 }
100
101 // Handle conflicting options
102 if (GenerateDependencyFileNoSysHeaders)
103 GenerateDependencyFile = false;
104
105 // Determine name of dependency output filename
106 llvm::sys::Path DepFile;
107 if (!DependencyOutputFile.empty())
108 DepFile = DependencyOutputFile;
109 else if (!OutputFile.empty()) {
110 DepFile = OutputFile;
111 DepFile.eraseSuffix();
112 DepFile.appendSuffix(DependencyFileExt);
113 }
114 else {
115 DepFile = InputFile;
116 DepFile.eraseSuffix();
117 DepFile.appendSuffix(DependencyFileExt);
118 }
119
Chris Lattner02fbb252009-01-11 19:28:34 +0000120 std::vector<std::string> Targets(DependencyTargets);
121
122 // Infer target name if unspecified
123 if (Targets.empty()) {
124 if (!OutputFile.empty()) {
125 llvm::sys::Path TargetPath(OutputFile);
126 TargetPath.eraseSuffix();
127 TargetPath.appendSuffix(ObjectFileExt);
128 Targets.push_back(TargetPath.toString());
129 } else {
130 llvm::sys::Path TargetPath(InputFile);
131 TargetPath.eraseSuffix();
132 TargetPath.appendSuffix(ObjectFileExt);
133 Targets.push_back(TargetPath.toString());
134 }
Daniel Dunbar750c3582008-10-24 22:12:41 +0000135 }
136
137 DependencyFileCallback *PPDep =
138 new DependencyFileCallback(PP, InputFile, DepFile.toString(),
Chris Lattner02fbb252009-01-11 19:28:34 +0000139 Targets, ErrStr);
Daniel Dunbar750c3582008-10-24 22:12:41 +0000140 if (ErrStr){
141 delete PPDep;
142 return false;
143 }
144 else {
145 PP->setPPCallbacks(PPDep);
146 return true;
147 }
148}
149
150/// FileMatchesDepCriteria - Determine whether the given Filename should be
151/// considered as a dependency.
152bool DependencyFileCallback::FileMatchesDepCriteria(const char *Filename,
Chris Lattner9d728512008-10-27 01:19:25 +0000153 SrcMgr::CharacteristicKind FileType) {
Daniel Dunbar750c3582008-10-24 22:12:41 +0000154 if (strcmp(InputFile.c_str(), Filename) != 0 &&
155 strcmp("<predefines>", Filename) != 0) {
156 if (GenerateDependencyFileNoSysHeaders)
157 return FileType == SrcMgr::C_User;
158 else
159 return true;
160 }
161
162 return false;
163}
164
165void DependencyFileCallback::FileChanged(SourceLocation Loc,
166 FileChangeReason Reason,
Chris Lattner9d728512008-10-27 01:19:25 +0000167 SrcMgr::CharacteristicKind FileType) {
Daniel Dunbar750c3582008-10-24 22:12:41 +0000168 if (Reason != PPCallbacks::EnterFile)
169 return;
170
171 const char *Filename = PP->getSourceManager().getSourceName(Loc);
172 if (!FileMatchesDepCriteria(Filename, FileType))
173 return;
174
175 // Remove leading "./"
Daniel Dunbar7e9f1f72008-10-27 20:01:06 +0000176 if (Filename[0] == '.' && Filename[1] == '/')
Daniel Dunbar750c3582008-10-24 22:12:41 +0000177 Filename = &Filename[2];
178
Daniel Dunbar7e9f1f72008-10-27 20:01:06 +0000179 if (FilesSet.insert(Filename))
180 Files.push_back(Filename);
Daniel Dunbar750c3582008-10-24 22:12:41 +0000181}
182
183void DependencyFileCallback::OutputDependencyFile() {
Daniel Dunbar7e9f1f72008-10-27 20:01:06 +0000184 // Write out the dependency targets, trying to avoid overly long
185 // lines when possible. We try our best to emit exactly the same
186 // dependency file as GCC (4.2), assuming the included files are the
187 // same.
188 const unsigned MaxColumns = 75;
Chris Lattner02fbb252009-01-11 19:28:34 +0000189 unsigned Columns = 0;
190
191 for (std::vector<std::string>::iterator
192 I = Targets.begin(), E = Targets.end(); I != E; ++I) {
193 unsigned N = I->length();
194 if (Columns == 0) {
195 Columns += N;
196 OS << *I;
197 } else if (Columns + N + 2 > MaxColumns) {
198 Columns = N + 2;
199 OS << " \\\n " << *I;
200 } else {
201 Columns += N + 1;
202 OS << " " << *I;
203 }
204 }
205
206 OS << ":";
207 Columns += 1;
Daniel Dunbar7e9f1f72008-10-27 20:01:06 +0000208
209 // Now add each dependency in the order it was seen, but avoiding
210 // duplicates.
211 for (std::vector<std::string>::iterator I = Files.begin(),
212 E = Files.end(); I != E; ++I) {
213 // Start a new line if this would exceed the column limit. Make
214 // sure to leave space for a trailing " \" in case we need to
215 // break the line on the next iteration.
216 unsigned N = I->length();
217 if (Columns + (N + 1) + 2 > MaxColumns) {
218 OS << " \\\n ";
219 Columns = 2;
220 }
221 OS << " " << *I;
222 Columns += N + 1;
Daniel Dunbar750c3582008-10-24 22:12:41 +0000223 }
Daniel Dunbar7e9f1f72008-10-27 20:01:06 +0000224 OS << "\n";
Daniel Dunbar750c3582008-10-24 22:12:41 +0000225
Daniel Dunbar7e9f1f72008-10-27 20:01:06 +0000226 // Create phony targets if requested.
227 if (PhonyDependencyTarget) {
228 // Skip the first entry, this is always the input file itself.
229 for (std::vector<std::string>::iterator I = Files.begin() + 1,
230 E = Files.end(); I != E; ++I) {
231 OS << "\n";
232 OS << *I << ":\n";
233 }
234 }
Daniel Dunbar750c3582008-10-24 22:12:41 +0000235}
236
237DependencyFileCallback::DependencyFileCallback(const Preprocessor *PP,
238 const std::string &InputFile,
239 const std::string &DepFile,
Chris Lattner02fbb252009-01-11 19:28:34 +0000240 const std::vector<std::string>
241 &Targets,
Daniel Dunbar750c3582008-10-24 22:12:41 +0000242 const char *&ErrStr)
Chris Lattner02fbb252009-01-11 19:28:34 +0000243 : PP(PP), InputFile(InputFile), Targets(Targets) {
Daniel Dunbar750c3582008-10-24 22:12:41 +0000244
245 OS.open(DepFile.c_str());
246 if (OS.fail())
247 ErrStr = "Could not open dependency output file\n";
248 else
249 ErrStr = NULL;
Daniel Dunbar7e9f1f72008-10-27 20:01:06 +0000250
251 Files.push_back(InputFile);
Daniel Dunbar750c3582008-10-24 22:12:41 +0000252}
253
254DependencyFileCallback::~DependencyFileCallback() {
255 if ((!GenerateDependencyFile && !GenerateDependencyFileNoSysHeaders) ||
256 OS.fail())
257 return;
258
259 OutputDependencyFile();
260 OS.close();
261}
262