File indexing completed on 2024-05-19 05:42:05
0001 // ct_lvtclp_toolexecutor.cpp -*-C++-*- 0002 0003 /* 0004 // Copyright 2023 Codethink Ltd <codethink@codethink.co.uk> 0005 // SPDX-License-Identifier: Apache-2.0 0006 // 0007 // Licensed under the Apache License, Version 2.0 (the "License"); 0008 // you may not use this file except in compliance with the License. 0009 // You may obtain a copy of the License at 0010 // 0011 // http://www.apache.org/licenses/LICENSE-2.0 0012 // 0013 // Unless required by applicable law or agreed to in writing, software 0014 // distributed under the License is distributed on an "AS IS" BASIS, 0015 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 0016 // See the License for the specific language governing permissions and 0017 // limitations under the License. 0018 */ 0019 0020 #include <ct_lvtclp_diagnostic_consumer.h> 0021 #include <ct_lvtclp_toolexecutor.h> 0022 0023 #include <QRunnable> 0024 #include <QThreadPool> 0025 0026 #include <llvm/Support/VirtualFileSystem.h> 0027 0028 #include <climits> 0029 #include <mutex> 0030 #include <utility> 0031 #include <vector> 0032 0033 // much of this component is taken straight from clang source 0034 // that source is licenced as Apache-2.0 with llvm-exception 0035 // See https://llvm.org/LICENSE.txt 0036 // clang/lib/Tooling/AllTUsExecution.cpp 0037 0038 namespace { 0039 0040 int unsignedToInt(unsigned i) 0041 { 0042 if (i > (unsigned) INT_MAX) { 0043 return INT_MAX; 0044 } 0045 return static_cast<int>(i); 0046 } 0047 0048 llvm::Error makeError(const llvm::Twine& msg) 0049 { 0050 return llvm::make_error<llvm::StringError>(msg, llvm::inconvertibleErrorCode()); 0051 } 0052 0053 class ThreadSafeToolResults : public clang::tooling::ToolResults { 0054 private: 0055 // DATA 0056 clang::tooling::InMemoryToolResults d_results; 0057 std::mutex d_mutex; 0058 0059 public: 0060 // MODIFIERS 0061 void addResult(llvm::StringRef key, llvm::StringRef value) override 0062 { 0063 std::unique_lock<std::mutex> guard(d_mutex); 0064 d_results.addResult(key, value); 0065 } 0066 0067 std::vector<std::pair<llvm::StringRef, llvm::StringRef>> AllKVResults() override 0068 { 0069 return d_results.AllKVResults(); 0070 } 0071 0072 void forEachResult(llvm::function_ref<void(llvm::StringRef key, llvm::StringRef value)> callback) override 0073 { 0074 d_results.forEachResult(callback); 0075 } 0076 }; 0077 0078 } // unnamed namespace 0079 0080 namespace Codethink::lvtclp { 0081 0082 struct ToolExecutor::Private { 0083 const clang::tooling::CompilationDatabase& compilations; 0084 0085 ThreadSafeToolResults results; 0086 clang::tooling::ExecutionContext context; 0087 0088 llvm::StringMap<std::string> overlayFiles; 0089 0090 unsigned threadCount; 0091 0092 std::function<void(std::string, long)> messageCallback; 0093 lvtmdb::ObjectStore& memDb; 0094 0095 QThreadPool pool; 0096 0097 Private(const clang::tooling::CompilationDatabase& compDb, 0098 unsigned threadCount, 0099 std::function<void(std::string, long)> messageCallback, 0100 lvtmdb::ObjectStore& memDb): 0101 compilations(compDb), 0102 context(&results), 0103 threadCount(threadCount), 0104 messageCallback(std::move(messageCallback)), 0105 memDb(memDb) 0106 { 0107 } 0108 }; 0109 0110 class ToolExecutor::RunnableThread : public QRunnable { 0111 // The ancient version of Qt in appimage (5.11) doesn't support creating a 0112 // QRunnable from a lambda so we have to write a lambda by hand here :( 0113 0114 public: 0115 // PUBLIC TYPES 0116 using Action = std::pair<std::unique_ptr<clang::tooling::FrontendActionFactory>, clang::tooling::ArgumentsAdjuster>; 0117 0118 private: 0119 // PRIVATE DATA 0120 const std::string d_file; 0121 ToolExecutor *d_executor; 0122 const Action& d_action; 0123 std::function<void(llvm::Twine)> d_appendError; 0124 std::function<void(std::string, long)> d_messageCallback; 0125 lvtmdb::ObjectStore& d_memDb; 0126 0127 public: 0128 // CREATORS 0129 RunnableThread(std::string file, 0130 ToolExecutor *executor, 0131 const Action& action, 0132 std::function<void(llvm::Twine)> appendError, 0133 std::function<void(std::string, long)> messageCallback, 0134 lvtmdb::ObjectStore& memDb): 0135 d_file(std::move(file)), 0136 d_executor(executor), 0137 d_action(action), 0138 d_appendError(std::move(appendError)), 0139 d_messageCallback(std::move(messageCallback)), 0140 d_memDb(memDb) 0141 { 0142 setAutoDelete(true); 0143 } 0144 0145 ~RunnableThread() override = default; 0146 0147 // MUTATORS 0148 void run() override 0149 { 0150 clang::tooling::ClangTool tool(d_executor->d->compilations, 0151 {d_file}, 0152 std::make_shared<clang::PCHContainerOperations>(), 0153 llvm::vfs::createPhysicalFileSystem()); 0154 auto consumer = lvtclp::DiagnosticConsumer(d_memDb, d_messageCallback); 0155 tool.setDiagnosticConsumer(&consumer); 0156 for (const auto& fileAndContent : d_executor->d->overlayFiles) { 0157 tool.mapVirtualFile(fileAndContent.first(), fileAndContent.second); 0158 } 0159 0160 // 0 on success; 0161 // 1 if any error occurred; 0162 // 2 if there is no error but some files are skipped due to missing compile commands. 0163 auto ret = tool.run(d_action.first.get()); 0164 0165 if (ret == 1) { 0166 d_appendError(llvm::Twine("Failed to run action on ") + d_file + "\n"); 0167 } 0168 } 0169 }; 0170 0171 ToolExecutor::ToolExecutor(const clang::tooling::CompilationDatabase& compDb, 0172 unsigned threadCount, 0173 std::function<void(std::string, long)> messageCallback, 0174 lvtmdb::ObjectStore& memDb): 0175 d(std::make_unique<Private>(compDb, threadCount, std::move(messageCallback), memDb)) 0176 { 0177 } 0178 0179 ToolExecutor::~ToolExecutor() noexcept = default; 0180 0181 llvm::Error ToolExecutor::execute( 0182 llvm::ArrayRef<std::pair<std::unique_ptr<clang::tooling::FrontendActionFactory>, clang::tooling::ArgumentsAdjuster>> 0183 actions) 0184 { 0185 if (actions.empty()) { 0186 return makeError("No action to execute."); 0187 } 0188 0189 if (actions.size() != 1) { 0190 return makeError("Only support executing 1 action"); 0191 } 0192 0193 std::string errorMsg; 0194 std::mutex logMutex; 0195 auto appendError = [&errorMsg, &logMutex](llvm::Twine err) { 0196 std::unique_lock<std::mutex> lock(logMutex); 0197 errorMsg += err.str(); 0198 }; 0199 0200 const auto& action = actions.front(); 0201 0202 { 0203 d->pool.setMaxThreadCount(unsignedToInt(d->threadCount)); 0204 0205 for (const std::string& file : d->compilations.getAllFiles()) { 0206 // deleted automatically by QThreadPool 0207 // see QRunnable::setAutoDelete 0208 QRunnable *runnable = new RunnableThread(file, this, action, appendError, d->messageCallback, d->memDb); 0209 d->pool.start(runnable); 0210 } 0211 0212 // make sure all tasks have finished 0213 d->pool.waitForDone(); 0214 } 0215 0216 if (!errorMsg.empty()) { 0217 return makeError(errorMsg); 0218 } 0219 0220 return llvm::Error::success(); 0221 } 0222 0223 clang::tooling::ExecutionContext *ToolExecutor::getExecutionContext() 0224 { 0225 return &d->context; 0226 } 0227 0228 clang::tooling::ToolResults *ToolExecutor::getToolResults() 0229 { 0230 return &d->results; 0231 } 0232 0233 void ToolExecutor::mapVirtualFile(llvm::StringRef filePath, llvm::StringRef content) 0234 { 0235 d->overlayFiles[filePath] = std::string(content); 0236 } 0237 0238 void ToolExecutor::cancelRun() 0239 { 0240 d->pool.clear(); 0241 } 0242 0243 llvm::StringRef ToolExecutor::getExecutorName() const 0244 { 0245 return "Codethink::lvtclp::ToolExecutor"; 0246 } 0247 0248 } // namespace Codethink::lvtclp