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 // 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 |