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 for (const auto& prefix : header_search_options->SystemHeaderPrefixes) { | |
Adrian Kuegel
2016/02/01 09:46:25
In the documentation this is saying "User-specifie
Adrian Kuegel
2016/02/01 09:54:10
I just asked a team mate, and he told me that it i
dsansome
2016/02/02 06:48:58
It seems to be empty for me anyway. All the syste
Adrian Kuegel
2016/02/02 08:37:19
Maybe I misunderstood him, or he was wrong. Anyway
| |
90 system_header_prefixes_.insert(prefix.Prefix); | |
91 } | |
92 | |
93 for (const auto& entry : header_search_options->UserEntries) { | |
Adrian Kuegel
2016/02/01 09:46:25
I am not so sure about if this works correctly. If
dsansome
2016/02/02 06:48:58
This list has all the paths in, but ones from -I h
Adrian Kuegel
2016/02/02 08:37:19
Interesting; that seems to indicate that Chromium
| |
94 switch (entry.Group) { | |
95 case clang::frontend::System: | |
96 case clang::frontend::ExternCSystem: | |
97 case clang::frontend::CSystem: | |
98 case clang::frontend::CXXSystem: | |
99 case clang::frontend::ObjCSystem: | |
100 case clang::frontend::ObjCXXSystem: | |
101 case clang::frontend::After: | |
102 system_header_prefixes_.insert(entry.Path); | |
103 break; | |
104 default: | |
105 break; | |
106 } | |
107 } | |
108 } | |
109 | |
77 void IncludeFinderPPCallbacks::FileChanged( | 110 void IncludeFinderPPCallbacks::FileChanged( |
78 clang::SourceLocation /*loc*/, | 111 clang::SourceLocation /*loc*/, |
79 clang::PPCallbacks::FileChangeReason reason, | 112 clang::PPCallbacks::FileChangeReason reason, |
80 clang::SrcMgr::CharacteristicKind /*file_type*/, | 113 clang::SrcMgr::CharacteristicKind /*file_type*/, |
81 clang::FileID /*prev_fid*/) { | 114 clang::FileID /*prev_fid*/) { |
82 if (reason == clang::PPCallbacks::EnterFile) { | 115 if (reason == clang::PPCallbacks::EnterFile) { |
83 if (!last_inclusion_directive_.empty()) { | 116 if (!last_inclusion_directive_.empty()) { |
84 current_files_.push(last_inclusion_directive_); | 117 current_files_.push(last_inclusion_directive_); |
85 } else { | 118 } else { |
86 current_files_.push( | 119 current_files_.push( |
(...skipping 41 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
128 string parent = | 161 string parent = |
129 llvm::sys::path::parent_path(current_files_.top().c_str()).str(); | 162 llvm::sys::path::parent_path(current_files_.top().c_str()).str(); |
130 | 163 |
131 // If the file is a top level file ("file.cc"), we normalize to a path | 164 // If the file is a top level file ("file.cc"), we normalize to a path |
132 // relative to "./". | 165 // relative to "./". |
133 if (parent.empty() || parent == "/") | 166 if (parent.empty() || parent == "/") |
134 parent = "."; | 167 parent = "."; |
135 | 168 |
136 // Otherwise we take the literal path as we stored it for the current | 169 // Otherwise we take the literal path as we stored it for the current |
137 // file, and append the relative path. | 170 // file, and append the relative path. |
138 last_inclusion_directive_ = parent + "/" + relative_path.str(); | 171 last_inclusion_directive_ = |
172 DoubleSlashSystemHeaders(parent, relative_path.str()); | |
139 } else if (!search_path.empty()) { | 173 } else if (!search_path.empty()) { |
140 // We want to be able to extract the search path relative to which the | 174 last_inclusion_directive_ = |
141 // include statement is defined. Therefore if search_path is an absolute | 175 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 { | 176 } else { |
148 last_inclusion_directive_ = file_name.str(); | 177 last_inclusion_directive_ = file_name.str(); |
149 } | 178 } |
150 AddFile(last_inclusion_directive_); | 179 AddFile(last_inclusion_directive_); |
151 } | 180 } |
152 | 181 |
182 string IncludeFinderPPCallbacks::DoubleSlashSystemHeaders( | |
183 const string& search_path, | |
184 const string& relative_path) const { | |
185 // We want to be able to extract the search path relative to which the | |
186 // include statement is defined. Therefore if search_path is a system header | |
187 // we use "//" as a separator between the search path and the relative path. | |
188 const bool is_system_header = | |
189 system_header_prefixes_.find(search_path) != | |
190 system_header_prefixes_.end(); | |
191 | |
192 return search_path + (is_system_header ? "//" : "/") + relative_path; | |
193 } | |
194 | |
153 void IncludeFinderPPCallbacks::EndOfMainFile() { | 195 void IncludeFinderPPCallbacks::EndOfMainFile() { |
154 const clang::FileEntry* main_file = | 196 const clang::FileEntry* main_file = |
155 source_manager_->getFileEntryForID(source_manager_->getMainFileID()); | 197 source_manager_->getFileEntryForID(source_manager_->getMainFileID()); |
156 assert(*main_source_file_ == main_file->getName()); | 198 assert(*main_source_file_ == main_file->getName()); |
157 AddFile(main_file->getName()); | 199 AddFile(main_file->getName()); |
158 } | 200 } |
159 | 201 |
160 class CompilationIndexerAction : public clang::PreprocessorFrontendAction { | 202 class CompilationIndexerAction : public clang::PreprocessorFrontendAction { |
161 public: | 203 public: |
162 CompilationIndexerAction() {} | 204 CompilationIndexerAction() {} |
(...skipping 20 matching lines...) Expand all Loading... | |
183 main_source_file_ = inputs[0].getFile(); | 225 main_source_file_ = inputs[0].getFile(); |
184 | 226 |
185 Preprocess(); | 227 Preprocess(); |
186 } | 228 } |
187 | 229 |
188 void CompilationIndexerAction::Preprocess() { | 230 void CompilationIndexerAction::Preprocess() { |
189 clang::Preprocessor& preprocessor = getCompilerInstance().getPreprocessor(); | 231 clang::Preprocessor& preprocessor = getCompilerInstance().getPreprocessor(); |
190 preprocessor.addPPCallbacks(llvm::make_unique<IncludeFinderPPCallbacks>( | 232 preprocessor.addPPCallbacks(llvm::make_unique<IncludeFinderPPCallbacks>( |
191 &getCompilerInstance().getSourceManager(), | 233 &getCompilerInstance().getSourceManager(), |
192 &main_source_file_, | 234 &main_source_file_, |
193 &source_file_paths_)); | 235 &source_file_paths_, |
236 &getCompilerInstance().getHeaderSearchOpts())); | |
194 preprocessor.getDiagnostics().setIgnoreAllWarnings(true); | 237 preprocessor.getDiagnostics().setIgnoreAllWarnings(true); |
195 preprocessor.SetSuppressIncludeNotFoundError(true); | 238 preprocessor.SetSuppressIncludeNotFoundError(true); |
196 preprocessor.EnterMainSourceFile(); | 239 preprocessor.EnterMainSourceFile(); |
197 clang::Token token; | 240 clang::Token token; |
198 do { | 241 do { |
199 preprocessor.Lex(token); | 242 preprocessor.Lex(token); |
200 } while (token.isNot(clang::tok::eof)); | 243 } while (token.isNot(clang::tok::eof)); |
201 } | 244 } |
202 | 245 |
203 void CompilationIndexerAction::EndSourceFileAction() { | 246 void CompilationIndexerAction::EndSourceFileAction() { |
204 std::ofstream out(main_source_file_ + ".filepaths"); | 247 std::ofstream out(main_source_file_ + ".filepaths"); |
205 for (string path : source_file_paths_) { | 248 for (const string& path : source_file_paths_) { |
206 out << path << std::endl; | 249 out << path << std::endl; |
207 } | 250 } |
208 } | 251 } |
209 } // namespace | 252 } // namespace |
210 | 253 |
211 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); | 254 static llvm::cl::extrahelp common_help(CommonOptionsParser::HelpMessage); |
212 | 255 |
213 int main(int argc, const char* argv[]) { | 256 int main(int argc, const char* argv[]) { |
214 llvm::cl::OptionCategory category("TranslationUnitGenerator Tool"); | 257 llvm::cl::OptionCategory category("TranslationUnitGenerator Tool"); |
215 CommonOptionsParser options(argc, argv, category); | 258 CommonOptionsParser options(argc, argv, category); |
216 std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory = | 259 std::unique_ptr<clang::tooling::FrontendActionFactory> frontend_factory = |
217 clang::tooling::newFrontendActionFactory<CompilationIndexerAction>(); | 260 clang::tooling::newFrontendActionFactory<CompilationIndexerAction>(); |
218 clang::tooling::ClangTool tool(options.getCompilations(), | 261 clang::tooling::ClangTool tool(options.getCompilations(), |
219 options.getSourcePathList()); | 262 options.getSourcePathList()); |
220 // This clang tool does not actually produce edits, but run_tool.py expects | 263 // This clang tool does not actually produce edits, but run_tool.py expects |
221 // this. So we just print an empty edit block. | 264 // this. So we just print an empty edit block. |
222 llvm::outs() << "==== BEGIN EDITS ====\n"; | 265 llvm::outs() << "==== BEGIN EDITS ====\n"; |
223 llvm::outs() << "==== END EDITS ====\n"; | 266 llvm::outs() << "==== END EDITS ====\n"; |
224 return tool.run(frontend_factory.get()); | 267 return tool.run(frontend_factory.get()); |
225 } | 268 } |
OLD | NEW |