blob: cd26079f89ec3e28482bd4a314ab178a5c357e34 [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"
24#include <fstream>
25#include <string>
26
27using namespace clang;
28
29namespace {
30class VISIBILITY_HIDDEN DependencyFileCallback : public PPCallbacks {
31 llvm::StringSet<> Files;
32 const Preprocessor *PP;
33 std::ofstream OS;
34 const std::string &InputFile;
35 std::string Target;
36
37private:
38 bool FileMatchesDepCriteria(const char *Filename,
39 SrcMgr::Characteristic_t FileType);
40 void OutputDependencyFile();
41
42public:
43 DependencyFileCallback(const Preprocessor *PP,
44 const std::string &InputFile,
45 const std::string &DepFile,
46 const std::string &Target,
47 const char *&ErrStr);
48 ~DependencyFileCallback();
49 virtual void FileChanged(SourceLocation Loc, FileChangeReason Reason,
50 SrcMgr::Characteristic_t FileType);
51};
52}
53
54static const char *DependencyFileExt = "d";
55static const char *ObjectFileExt = "o";
56
57//===----------------------------------------------------------------------===//
58// Dependency file options
59//===----------------------------------------------------------------------===//
60static llvm::cl::opt<bool>
61GenerateDependencyFile("MD",
62 llvm::cl::desc("Generate dependency for main source file "
63 "(system headers included)"));
64
65static llvm::cl::opt<bool>
66GenerateDependencyFileNoSysHeaders("MMD",
67 llvm::cl::desc("Generate dependency for main source file "
68 "(no system headers)"));
69
70static llvm::cl::opt<std::string>
71DependencyOutputFile("MF",
72 llvm::cl::desc("Specify dependency output file"));
73
74static llvm::cl::opt<std::string>
75DependencyTarget("MT",
76 llvm::cl::desc("Specify target for dependency"));
77
78// FIXME: Implement feature
79static llvm::cl::opt<bool>
80PhonyDependencyTarget("MP",
81 llvm::cl::desc("Create phony target for each dependency "
82 "(other than main file)"));
83
84bool clang::CreateDependencyFileGen(Preprocessor *PP,
85 std::string &OutputFile,
86 const std::string &InputFile,
87 const char *&ErrStr) {
88 assert(!InputFile.empty() && "No file given");
89
90 ErrStr = NULL;
91
92 if (!GenerateDependencyFile && !GenerateDependencyFileNoSysHeaders) {
93 if (!DependencyOutputFile.empty() || !DependencyTarget.empty() ||
94 PhonyDependencyTarget)
95 ErrStr = "Error: to generate dependencies you must specify -MD or -MMD\n";
96 return false;
97 }
98
99 // Handle conflicting options
100 if (GenerateDependencyFileNoSysHeaders)
101 GenerateDependencyFile = false;
102
103 // Determine name of dependency output filename
104 llvm::sys::Path DepFile;
105 if (!DependencyOutputFile.empty())
106 DepFile = DependencyOutputFile;
107 else if (!OutputFile.empty()) {
108 DepFile = OutputFile;
109 DepFile.eraseSuffix();
110 DepFile.appendSuffix(DependencyFileExt);
111 }
112 else {
113 DepFile = InputFile;
114 DepFile.eraseSuffix();
115 DepFile.appendSuffix(DependencyFileExt);
116 }
117
118 // Determine name of target
119 std::string Target;
120 if (!DependencyTarget.empty())
121 Target = DependencyTarget;
122 else if (!OutputFile.empty()) {
123 llvm::sys::Path TargetPath(OutputFile);
124 TargetPath.eraseSuffix();
125 TargetPath.appendSuffix(ObjectFileExt);
126 Target = TargetPath.toString();
127 }
128 else {
129 llvm::sys::Path TargetPath(InputFile);
130 TargetPath.eraseSuffix();
131 TargetPath.appendSuffix(ObjectFileExt);
132 Target = TargetPath.toString();
133 }
134
135 DependencyFileCallback *PPDep =
136 new DependencyFileCallback(PP, InputFile, DepFile.toString(),
137 Target, ErrStr);
138 if (ErrStr){
139 delete PPDep;
140 return false;
141 }
142 else {
143 PP->setPPCallbacks(PPDep);
144 return true;
145 }
146}
147
148/// FileMatchesDepCriteria - Determine whether the given Filename should be
149/// considered as a dependency.
150bool DependencyFileCallback::FileMatchesDepCriteria(const char *Filename,
151 SrcMgr::Characteristic_t FileType) {
152 if (strcmp(InputFile.c_str(), Filename) != 0 &&
153 strcmp("<predefines>", Filename) != 0) {
154 if (GenerateDependencyFileNoSysHeaders)
155 return FileType == SrcMgr::C_User;
156 else
157 return true;
158 }
159
160 return false;
161}
162
163void DependencyFileCallback::FileChanged(SourceLocation Loc,
164 FileChangeReason Reason,
165 SrcMgr::Characteristic_t FileType) {
166 if (Reason != PPCallbacks::EnterFile)
167 return;
168
169 const char *Filename = PP->getSourceManager().getSourceName(Loc);
170 if (!FileMatchesDepCriteria(Filename, FileType))
171 return;
172
173 // Remove leading "./"
174 if(Filename[0] == '.' && Filename[1] == '/')
175 Filename = &Filename[2];
176
177 Files.insert(Filename);
178}
179
180void DependencyFileCallback::OutputDependencyFile() {
181 std::string Output;
182 // Add "target: mainfile"
183 Output += Target;
184 Output += ": ";
185 Output += InputFile;
186
187 // Now add each dependency
188 for (llvm::StringSet<>::iterator I = Files.begin(),
189 E = Files.end(); I != E; ++I) {
190 // FIXME: Wrap lines
191 Output += " ";
192 Output += I->getKeyData();
193 }
194
195 OS << Output << "\n";
196}
197
198DependencyFileCallback::DependencyFileCallback(const Preprocessor *PP,
199 const std::string &InputFile,
200 const std::string &DepFile,
201 const std::string &Target,
202 const char *&ErrStr)
203 : PP(PP), InputFile(InputFile), Target(Target) {
204
205 OS.open(DepFile.c_str());
206 if (OS.fail())
207 ErrStr = "Could not open dependency output file\n";
208 else
209 ErrStr = NULL;
210}
211
212DependencyFileCallback::~DependencyFileCallback() {
213 if ((!GenerateDependencyFile && !GenerateDependencyFileNoSysHeaders) ||
214 OS.fail())
215 return;
216
217 OutputDependencyFile();
218 OS.close();
219}
220