OLD | NEW |
---|---|
1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 // | 4 // |
5 // This implements a Clang tool to generate compilation information that is | 5 // This implements a Clang tool to generate compilation information that is |
6 // sufficient to recompile the code with clang. For each compilation unit, all | 6 // sufficient to recompile the code with clang. For each compilation unit, all |
7 // source files which are necessary for compiling it are determined. For each | 7 // source files which are necessary for compiling it are determined. For each |
8 // compilation unit, a file is created containing a list of all file paths of | 8 // compilation unit, a file is created containing a list of all file paths of |
9 // included files. | 9 // included files. |
10 | 10 |
11 #include <assert.h> | 11 #include <assert.h> |
12 #include <unistd.h> | 12 #include <unistd.h> |
13 #include <fstream> | 13 #include <fstream> |
14 #include <iostream> | 14 #include <iostream> |
15 #include <memory> | 15 #include <memory> |
16 #include <set> | 16 #include <set> |
17 #include <stack> | 17 #include <stack> |
18 #include <string> | 18 #include <string> |
19 #include <vector> | 19 #include <vector> |
20 | 20 |
21 #include "clang/Basic/Diagnostic.h" | 21 #include "clang/Basic/Diagnostic.h" |
22 #include "clang/Basic/FileManager.h" | 22 #include "clang/Basic/FileManager.h" |
23 #include "clang/Basic/SourceManager.h" | 23 #include "clang/Basic/SourceManager.h" |
24 #include "clang/Frontend/CompilerInstance.h" | 24 #include "clang/Frontend/CompilerInstance.h" |
25 #include "clang/Frontend/FrontendActions.h" | 25 #include "clang/Frontend/FrontendActions.h" |
26 #include "clang/Lex/HeaderSearchOptions.h" | |
26 #include "clang/Lex/PPCallbacks.h" | 27 #include "clang/Lex/PPCallbacks.h" |
27 #include "clang/Lex/Preprocessor.h" | 28 #include "clang/Lex/Preprocessor.h" |
28 #include "clang/Tooling/CommonOptionsParser.h" | 29 #include "clang/Tooling/CommonOptionsParser.h" |
29 #include "clang/Tooling/CompilationDatabase.h" | 30 #include "clang/Tooling/CompilationDatabase.h" |
30 #include "clang/Tooling/Refactoring.h" | 31 #include "clang/Tooling/Refactoring.h" |
31 #include "clang/Tooling/Tooling.h" | 32 #include "clang/Tooling/Tooling.h" |
32 #include "llvm/Support/CommandLine.h" | 33 #include "llvm/Support/CommandLine.h" |
33 | 34 |
35 using clang::HeaderSearchOptions; | |
34 using clang::tooling::CommonOptionsParser; | 36 using clang::tooling::CommonOptionsParser; |
35 using std::set; | 37 using std::set; |
36 using std::stack; | 38 using std::stack; |
37 using std::string; | 39 using std::string; |
38 using std::vector; | 40 using std::vector; |
39 | 41 |
40 namespace { | 42 namespace { |
41 // Set of preprocessor callbacks used to record files included. | 43 // Set of preprocessor callbacks used to record files included. |
42 class IncludeFinderPPCallbacks : public clang::PPCallbacks { | 44 class IncludeFinderPPCallbacks : public clang::PPCallbacks { |
43 public: | 45 public: |
44 IncludeFinderPPCallbacks(clang::SourceManager* source_manager, | 46 IncludeFinderPPCallbacks(clang::SourceManager* source_manager, |
45 string* main_source_file, | 47 string* main_source_file, |
46 set<string>* source_file_paths) | 48 set<string>* source_file_paths, |
47 : source_manager_(source_manager), | 49 const HeaderSearchOptions* header_search_options); |
48 main_source_file_(main_source_file), | |
49 source_file_paths_(source_file_paths) {} | |
50 void FileChanged(clang::SourceLocation /*loc*/, | 50 void FileChanged(clang::SourceLocation /*loc*/, |
51 clang::PPCallbacks::FileChangeReason reason, | 51 clang::PPCallbacks::FileChangeReason reason, |
52 clang::SrcMgr::CharacteristicKind /*file_type*/, | 52 clang::SrcMgr::CharacteristicKind /*file_type*/, |
53 clang::FileID /*prev_fid*/) override; | 53 clang::FileID /*prev_fid*/) override; |
54 void AddFile(const string& path); | 54 void AddFile(const string& path); |
55 void InclusionDirective(clang::SourceLocation hash_loc, | 55 void InclusionDirective(clang::SourceLocation hash_loc, |
56 const clang::Token& include_tok, | 56 const clang::Token& include_tok, |
57 llvm::StringRef file_name, | 57 llvm::StringRef file_name, |
58 bool is_angled, | 58 bool is_angled, |
59 clang::CharSourceRange range, | 59 clang::CharSourceRange range, |
60 const clang::FileEntry* file, | 60 const clang::FileEntry* file, |
61 llvm::StringRef search_path, | 61 llvm::StringRef search_path, |
62 llvm::StringRef relative_path, | 62 llvm::StringRef relative_path, |
63 const clang::Module* imported) override; | 63 const clang::Module* imported) override; |
64 void EndOfMainFile() override; | 64 void EndOfMainFile() override; |
65 | 65 |
66 private: | 66 private: |
67 string DoubleSlashSystemHeaders(const string& search_path, | |
68 const string& relative_path) const; | |
69 | |
67 clang::SourceManager* const source_manager_; | 70 clang::SourceManager* const source_manager_; |
68 string* const main_source_file_; | 71 string* const main_source_file_; |
69 set<string>* const source_file_paths_; | 72 set<string>* const source_file_paths_; |
73 set<string> system_header_prefixes_; | |
70 // The path of the file that was last referenced by an inclusion directive, | 74 // The path of the file that was last referenced by an inclusion directive, |
71 // normalized for includes that are relative to a different source file. | 75 // normalized for includes that are relative to a different source file. |
72 string last_inclusion_directive_; | 76 string last_inclusion_directive_; |
73 // The stack of currently parsed files. top() gives the current file. | 77 // The stack of currently parsed files. top() gives the current file. |
74 stack<string> current_files_; | 78 stack<string> current_files_; |
75 }; | 79 }; |
76 | 80 |
81 IncludeFinderPPCallbacks::IncludeFinderPPCallbacks( | |
82 clang::SourceManager* source_manager, | |
83 string* main_source_file, | |
84 set<string>* source_file_paths, | |
85 const HeaderSearchOptions* header_search_options) | |
86 : source_manager_(source_manager), | |
87 main_source_file_(main_source_file), | |
88 source_file_paths_(source_file_paths) { | |
89 // In practice this list seems to be empty, but add it anyway just in case. | |
90 for (const auto& prefix : header_search_options->SystemHeaderPrefixes) { | |
91 system_header_prefixes_.insert(prefix.Prefix); | |
92 } | |
93 | |
94 // This list contains all the include directories of different type. We add | |
95 // all system headers to the set - excluding the Quoted and Angled groups | |
96 // which are from -iquote and -I flags. | |
97 for (const auto& entry : header_search_options->UserEntries) { | |
98 switch (entry.Group) { | |
99 case clang::frontend::System: | |
100 case clang::frontend::ExternCSystem: | |
101 case clang::frontend::CSystem: | |
102 case clang::frontend::CXXSystem: | |
103 case clang::frontend::ObjCSystem: | |
104 case clang::frontend::ObjCXXSystem: | |
105 case clang::frontend::After: | |
106 system_header_prefixes_.insert(entry.Path); | |
107 break; | |
108 default: | |
109 break; | |
110 } | |
111 } | |
112 } | |
113 | |
77 void IncludeFinderPPCallbacks::FileChanged( | 114 void IncludeFinderPPCallbacks::FileChanged( |
78 clang::SourceLocation /*loc*/, | 115 clang::SourceLocation /*loc*/, |
79 clang::PPCallbacks::FileChangeReason reason, | 116 clang::PPCallbacks::FileChangeReason reason, |
80 clang::SrcMgr::CharacteristicKind /*file_type*/, | 117 clang::SrcMgr::CharacteristicKind /*file_type*/, |
81 clang::FileID /*prev_fid*/) { | 118 clang::FileID /*prev_fid*/) { |
82 if (reason == clang::PPCallbacks::EnterFile) { | 119 if (reason == clang::PPCallbacks::EnterFile) { |
83 if (!last_inclusion_directive_.empty()) { | 120 if (!last_inclusion_directive_.empty()) { |
84 current_files_.push(last_inclusion_directive_); | 121 current_files_.push(last_inclusion_directive_); |
85 } else { | 122 } else { |
86 current_files_.push( | 123 current_files_.push( |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
128 string parent = | 165 string parent = |
129 llvm::sys::path::parent_path(current_files_.top().c_str()).str(); | 166 llvm::sys::path::parent_path(current_files_.top().c_str()).str(); |
130 | 167 |
131 // If the file is a top level file ("file.cc"), we normalize to a path | 168 // If the file is a top level file ("file.cc"), we normalize to a path |
132 // relative to "./". | 169 // relative to "./". |
133 if (parent.empty() || parent == "/") | 170 if (parent.empty() || parent == "/") |
134 parent = "."; | 171 parent = "."; |
135 | 172 |
136 // Otherwise we take the literal path as we stored it for the current | 173 // Otherwise we take the literal path as we stored it for the current |
137 // file, and append the relative path. | 174 // file, and append the relative path. |
138 last_inclusion_directive_ = parent + "/" + relative_path.str(); | 175 last_inclusion_directive_ = |
176 DoubleSlashSystemHeaders(parent, relative_path.str()); | |
139 } else if (!search_path.empty()) { | 177 } else if (!search_path.empty()) { |
140 // We want to be able to extract the search path relative to which the | 178 last_inclusion_directive_ = |
141 // include statement is defined. Therefore if search_path is an absolute | 179 DoubleSlashSystemHeaders(search_path.str(), relative_path.str()); |
142 // path (indicating it is most likely a system header) we use "//" as a | |
143 // separator between the search path and the relative path. | |
144 last_inclusion_directive_ = search_path.str() + | |
145 (llvm::sys::path::is_absolute(search_path) ? "//" : "/") + | |
146 relative_path.str(); | |
147 } else { | 180 } else { |
148 last_inclusion_directive_ = file_name.str(); | 181 last_inclusion_directive_ = file_name.str(); |
149 } | 182 } |
150 AddFile(last_inclusion_directive_); | 183 AddFile(last_inclusion_directive_); |
151 } | 184 } |
152 | 185 |
186 string IncludeFinderPPCallbacks::DoubleSlashSystemHeaders( | |
187 const string& search_path, | |
188 const string& relative_path) const { | |
189 // We want to be able to extract the search path relative to which the | |
190 // include statement is defined. Therefore if search_path is a system header | |
191 // we use "//" as a separator between the search path and the relative path. | |
192 const bool is_system_header = | |
193 system_header_prefixes_.find(search_path) != | |
194 system_header_prefixes_.end(); | |
195 | |
196 return search_path + (is_system_header ? "//" : "/") + relative_path; | |
197 } | |
198 | |
153 void IncludeFinderPPCallbacks::EndOfMainFile() { | 199 void IncludeFinderPPCallbacks::EndOfMainFile() { |
154 const clang::FileEntry* main_file = | 200 const clang::FileEntry* main_file = |
155 source_manager_->getFileEntryForID(source_manager_->getMainFileID()); | 201 source_manager_->getFileEntryForID(source_manager_->getMainFileID()); |
156 assert(*main_source_file_ == main_file->getName()); | 202 assert(*main_source_file_ == main_file->getName()); |
157 AddFile(main_file->getName()); | 203 AddFile(main_file->getName()); |
158 } | 204 } |
159 | 205 |
160 class CompilationIndexerAction : public clang::PreprocessorFrontendAction { | 206 class CompilationIndexerAction : public clang::PreprocessorFrontendAction { |
161 public: | 207 public: |
162 CompilationIndexerAction() {} | 208 CompilationIndexerAction() {} |
(...skipping 20 matching lines...) Expand all Loading... | |
183 main_source_file_ = inputs[0].getFile(); | 229 main_source_file_ = inputs[0].getFile(); |
184 | 230 |
185 Preprocess(); | 231 Preprocess(); |
186 } | 232 } |
187 | 233 |
188 void CompilationIndexerAction::Preprocess() { | 234 void CompilationIndexerAction::Preprocess() { |
189 clang::Preprocessor& preprocessor = getCompilerInstance().getPreprocessor(); | 235 clang::Preprocessor& preprocessor = getCompilerInstance().getPreprocessor(); |
190 preprocessor.addPPCallbacks(llvm::make_unique<IncludeFinderPPCallbacks>( | 236 preprocessor.addPPCallbacks(llvm::make_unique<IncludeFinderPPCallbacks>( |
191 &getCompilerInstance().getSourceManager(), | 237 &getCompilerInstance().getSourceManager(), |
192 &main_source_file_, | 238 &main_source_file_, |
193 &source_file_paths_)); | 239 &source_file_paths_, |
240 &getCompilerInstance().getHeaderSearchOpts())); | |
194 preprocessor.getDiagnostics().setIgnoreAllWarnings(true); | 241 preprocessor.getDiagnostics().setIgnoreAllWarnings(true); |
195 preprocessor.SetSuppressIncludeNotFoundError(true); | 242 preprocessor.SetSuppressIncludeNotFoundError(true); |
196 preprocessor.EnterMainSourceFile(); | 243 preprocessor.EnterMainSourceFile(); |
197 clang::Token token; | 244 clang::Token token; |
198 do { | 245 do { |
199 preprocessor.Lex(token); | 246 preprocessor.Lex(token); |
200 } while (token.isNot(clang::tok::eof)); | 247 } while (token.isNot(clang::tok::eof)); |
201 } | 248 } |
202 | 249 |
203 void CompilationIndexerAction::EndSourceFileAction() { | 250 void CompilationIndexerAction::EndSourceFileAction() { |
204 std::ofstream out(main_source_file_ + ".filepaths"); | 251 std::ofstream out(main_source_file_ + ".filepaths"); |
205 for (string path : source_file_paths_) { | 252 for (const string& path : source_file_paths_) { |
dcheng
2016/02/03 05:18:33
Oops.
| |
206 out << path << std::endl; | 253 out << path << std::endl; |
207 } | 254 } |
208 } | 255 } |
209 } // namespace | 256 } // namespace |
210 | 257 |
211 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); | 258 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); |
212 | 259 |
213 int main(int argc, const char* argv[]) { | 260 int main(int argc, const char* argv[]) { |
214 llvm::cl::OptionCategory category("TranslationUnitGenerator Tool"); | 261 llvm::cl::OptionCategory category("TranslationUnitGenerator Tool"); |
215 CommonOptionsParser options(argc, argv, category); | 262 CommonOptionsParser options(argc, argv, category); |
216 std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory = | 263 std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory = |
217 clang::tooling::newFrontendActionFactory<CompilationIndexerAction>(); | 264 clang::tooling::newFrontendActionFactory<CompilationIndexerAction>(); |
218 clang::tooling::ClangTool tool(options.getCompilations(), | 265 clang::tooling::ClangTool tool(options.getCompilations(), |
219 options.getSourcePathList()); | 266 options.getSourcePathList()); |
220 // This clang tool does not actually produce edits, but run_tool.py expects | 267 // This clang tool does not actually produce edits, but run_tool.py expects |
221 // this. So we just print an empty edit block. | 268 // this. So we just print an empty edit block. |
222 llvm::outs() << "==== BEGIN EDITS ====\n"; | 269 llvm::outs() << "==== BEGIN EDITS ====\n"; |
223 llvm::outs() << "==== END EDITS ====\n"; | 270 llvm::outs() << "==== END EDITS ====\n"; |
224 return tool.run(frontend_factory.get()); | 271 return tool.run(frontend_factory.get()); |
225 } | 272 } |
OLD | NEW |