Chromium Code Reviews| 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 |