File indexing completed on 2024-05-19 04:39:57
0001 /* 0002 SPDX-FileCopyrightText: 2009 David Nolden <david.nolden.kdevelop@art-master.de> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "main.h" 0008 0009 #include <shell/core.h> 0010 #include <shell/shellextension.h> 0011 0012 #include <language/backgroundparser/backgroundparser.h> 0013 #include <language/duchain/definitions.h> 0014 #include <language/duchain/duchain.h> 0015 #include <language/duchain/duchainlock.h> 0016 #include <language/duchain/duchaindumper.h> 0017 #include <language/duchain/dumpdotgraph.h> 0018 #include <language/duchain/problem.h> 0019 #include <language/duchain/persistentsymboltable.h> 0020 0021 #include <interfaces/ilanguagecontroller.h> 0022 #include <tests/autotestshell.h> 0023 #include <tests/testcore.h> 0024 0025 #include <QCoreApplication> 0026 #include <QCommandLineParser> 0027 #include <QCommandLineOption> 0028 #include <QDebug> 0029 #include <QDirIterator> 0030 #include <QStringList> 0031 #include <QTimer> 0032 0033 #include <cstdio> 0034 0035 #include <KAboutData> 0036 #include <KLocalizedString> 0037 0038 bool verbose = false, warnings = false; 0039 0040 using namespace KDevelop; 0041 0042 void messageOutput(QtMsgType type, const QMessageLogContext& context, const QString& msg) 0043 { 0044 Q_UNUSED(context); 0045 0046 switch (type) { 0047 case QtInfoMsg: // fall-through 0048 case QtDebugMsg: 0049 if (verbose) 0050 std::cerr << qPrintable(msg) << std::endl; 0051 break; 0052 case QtWarningMsg: 0053 if (warnings) 0054 std::cerr << qPrintable(msg) << std::endl; 0055 break; 0056 case QtCriticalMsg: 0057 std::cerr << qPrintable(msg) << std::endl; 0058 break; 0059 case QtFatalMsg: 0060 std::cerr << qPrintable(msg) << std::endl; 0061 abort(); 0062 } 0063 } 0064 0065 Manager::Manager(QCommandLineParser* args) : m_total(0) 0066 , m_args(args) 0067 , m_allFilesAdded(0) 0068 { 0069 } 0070 0071 void Manager::init() 0072 { 0073 if (m_args->positionalArguments().isEmpty()) { 0074 std::cerr << "Need file or directory to duchainify" << std::endl; 0075 QCoreApplication::exit(1); 0076 } 0077 0078 TopDUContext::Features features = TopDUContext::VisibleDeclarationsAndContexts; 0079 if (m_args->isSet(QStringLiteral("features"))) { 0080 QString featuresStr = m_args->value(QStringLiteral("features")); 0081 if (featuresStr == QLatin1String("visible-declarations")) { 0082 features = TopDUContext::VisibleDeclarationsAndContexts; 0083 } else if (featuresStr == QLatin1String("all-declarations")) { 0084 features = TopDUContext::AllDeclarationsAndContexts; 0085 } else if (featuresStr == QLatin1String("all-declarations-and-uses")) { 0086 features = TopDUContext::AllDeclarationsContextsAndUses; 0087 } else if (featuresStr == QLatin1String("all-declarations-and-uses-and-AST")) { 0088 features = TopDUContext::AllDeclarationsContextsAndUses | TopDUContext::AST; 0089 } else if (featuresStr == QLatin1String("empty")) { 0090 features = TopDUContext::Empty; 0091 } else if (featuresStr == QLatin1String("simplified-visible-declarations")) { 0092 features = TopDUContext::SimplifiedVisibleDeclarationsAndContexts; 0093 } else { 0094 std::cerr << "Wrong feature-string given\n"; 0095 QCoreApplication::exit(2); 0096 return; 0097 } 0098 } 0099 if (m_args->isSet(QStringLiteral("force-update"))) 0100 features |= TopDUContext::ForceUpdate; 0101 if (m_args->isSet(QStringLiteral("force-update-recursive"))) 0102 features |= TopDUContext::ForceUpdateRecursive; 0103 0104 if (m_args->isSet(QStringLiteral("threads"))) { 0105 bool ok = false; 0106 int count = m_args->value(QStringLiteral("threads")).toInt(&ok); 0107 ICore::self()->languageController()->backgroundParser()->setThreadCount(count); 0108 if (!ok) { 0109 std::cerr << "bad thread count\n"; 0110 QCoreApplication::exit(3); 0111 return; 0112 } 0113 } 0114 0115 // quit when everything is done 0116 // background parser emits hideProgress() signal in two situations: 0117 // when everything is done and when bgparser is suspended 0118 // later doesn't happen in duchain, so just rely on hideProgress() 0119 // and quit when it's emitted 0120 connect( 0121 ICore::self()->languageController()->backgroundParser(), &BackgroundParser::hideProgress, this, 0122 [this]() { 0123 if (ICore::self()->languageController()->backgroundParser()->isIdle()) 0124 QTimer::singleShot(0, this, &Manager::finish); 0125 }); 0126 0127 const auto files = m_args->positionalArguments(); 0128 for (const auto& file : files) { 0129 addToBackgroundParser(file, features); 0130 } 0131 0132 m_allFilesAdded = 1; 0133 0134 if (m_total) { 0135 std::cerr << "Added " << m_total << " files to the background parser" << std::endl; 0136 const int threads = ICore::self()->languageController()->backgroundParser()->threadCount(); 0137 std::cerr << "parsing with " << threads << " threads" << std::endl; 0138 ICore::self()->languageController()->backgroundParser()->parseDocuments(); 0139 } else { 0140 std::cerr << "no files added to the background parser" << std::endl; 0141 QCoreApplication::exit(0); 0142 return; 0143 } 0144 } 0145 0146 void Manager::updateReady(const IndexedString& url, const ReferencedTopDUContext& topContext) 0147 { 0148 qDebug() << "finished" << url.toUrl().toLocalFile() << "success: " << ( bool )topContext; 0149 0150 m_waiting.remove(url.toUrl()); 0151 0152 std::cerr << "processed " << (m_total - m_waiting.size()) << " out of " << m_total << "\n"; 0153 dump(topContext); 0154 } 0155 0156 void Manager::dump(const ReferencedTopDUContext& topContext) 0157 { 0158 if (!topContext) { 0159 return; 0160 } 0161 0162 QTextStream stream(stdout); 0163 0164 std::cerr << "\n"; 0165 0166 if (m_args->isSet(QStringLiteral("dump-definitions"))) { 0167 DUChainReadLocker lock; 0168 std::cerr << "Definitions:" << std::endl; 0169 DUChain::definitions()->dump(stream); 0170 std::cerr << std::endl; 0171 } 0172 0173 if (m_args->isSet(QStringLiteral("dump-symboltable"))) { 0174 DUChainReadLocker lock; 0175 std::cerr << "PersistentSymbolTable:" << std::endl; 0176 PersistentSymbolTable::self().dump(stream); 0177 std::cerr << std::endl; 0178 } 0179 0180 DUChainDumper::Features features; 0181 if (m_args->isSet(QStringLiteral("dump-context"))) { 0182 features |= DUChainDumper::DumpContext; 0183 } 0184 if (m_args->isSet(QStringLiteral("dump-errors"))) { 0185 features |= DUChainDumper::DumpProblems; 0186 } 0187 0188 if (auto depth = m_args->value(QStringLiteral("dump-depth")).toInt()) { 0189 DUChainReadLocker lock; 0190 std::cerr << "Context:" << std::endl; 0191 DUChainDumper dumpChain(features); 0192 dumpChain.dump(topContext, depth); 0193 } 0194 0195 if (m_args->isSet(QStringLiteral("dump-graph"))) { 0196 DUChainReadLocker lock; 0197 DumpDotGraph dumpGraph; 0198 const QString dotOutput = dumpGraph.dotGraph(topContext); 0199 std::cout << qPrintable(dotOutput) << std::endl; 0200 } 0201 0202 if (m_args->isSet(QStringLiteral("dump-imported-errors"))) { 0203 DUChainReadLocker lock; 0204 const auto imports = topContext->importedParentContexts(); 0205 for (const auto& import : imports) { 0206 auto top = dynamic_cast<TopDUContext*>(import.indexedContext().context()); 0207 if (top && top != topContext && !top->problems().isEmpty()) { 0208 DUChainDumper dumpChain(DUChainDumper::DumpProblems); 0209 dumpChain.dump(top, 0); 0210 } 0211 } 0212 } 0213 } 0214 0215 void Manager::addToBackgroundParser(const QString& path, TopDUContext::Features features) 0216 { 0217 QFileInfo info(path); 0218 0219 if (info.isFile()) { 0220 qDebug() << "adding file" << path; 0221 QUrl pathUrl = QUrl::fromLocalFile(info.canonicalFilePath()); 0222 0223 m_waiting << pathUrl; 0224 ++m_total; 0225 0226 KDevelop::DUChain::self()->updateContextForUrl(KDevelop::IndexedString(pathUrl), features, this); 0227 0228 } else if (info.isDir()) { 0229 QDirIterator contents(path); 0230 while (contents.hasNext()) { 0231 QString newPath = contents.next(); 0232 if (!newPath.endsWith(QLatin1Char('.'))) 0233 addToBackgroundParser(newPath, features); 0234 } 0235 } 0236 } 0237 0238 QSet<QUrl> Manager::waiting() 0239 { 0240 return m_waiting; 0241 } 0242 0243 void Manager::finish() 0244 { 0245 std::cerr << "ready" << std::endl; 0246 QCoreApplication::quit(); 0247 } 0248 0249 using namespace KDevelop; 0250 0251 int main(int argc, char** argv) 0252 { 0253 QCoreApplication app(argc, argv); 0254 0255 KAboutData aboutData(QStringLiteral("duchainify"), i18n("duchainify"), 0256 QStringLiteral("1"), i18n("DUChain builder application"), KAboutLicense::GPL, 0257 i18n("(c) 2009 David Nolden"), QString(), QStringLiteral("https://www.kdevelop.org/")); 0258 KAboutData::setApplicationData(aboutData); 0259 0260 QCommandLineParser parser; 0261 aboutData.setupCommandLine(&parser); 0262 0263 parser.addPositionalArgument(QStringLiteral("paths"), i18n("file or directory"), QStringLiteral("[PATH...]")); 0264 0265 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("w"), QStringLiteral("warnings")}, 0266 i18n("Show warnings")}); 0267 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("V"), QStringLiteral("verbose")}, 0268 i18n("Show warnings and debug output")}); 0269 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("u"), QStringLiteral("force-update")}, 0270 i18n("Enforce an update of the top-contexts corresponding to the given files")}); 0271 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("r"), QStringLiteral( 0272 "force-update-recursive")}, 0273 i18n( 0274 "Enforce an update of the top-contexts corresponding to the given files and all included files")}); 0275 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("t"), QStringLiteral("threads")}, 0276 i18n("Number of threads to use"), QStringLiteral("count")}); 0277 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("f"), QStringLiteral("features")}, 0278 i18n( 0279 "Features to build. Options: empty, simplified-visible-declarations, visible-declarations (default), all-declarations, all-declarations-and-uses, all-declarations-and-uses-and-AST"), 0280 QStringLiteral("features")}); 0281 0282 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-context")}, 0283 i18n("Print complete Definition-Use Chain on successful parse")}); 0284 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-definitions")}, 0285 i18n("Print complete DUChain Definitions repository on successful parse")}); 0286 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-symboltable")}, 0287 i18n( 0288 "Print complete DUChain PersistentSymbolTable repository on successful parse")}); 0289 parser.addOption( 0290 QCommandLineOption { QStringList { QStringLiteral("dump-depth") }, 0291 i18n("Number defining the maximum depth where declaration details are printed"), 0292 QStringLiteral("depth"), QStringLiteral("100") }); 0293 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-graph")}, 0294 i18n("Dump DUChain graph (in .dot format)")}); 0295 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("d"), QStringLiteral("dump-errors")}, 0296 i18n("Print problems encountered during parsing")}); 0297 parser.addOption(QCommandLineOption{QStringList{QStringLiteral("dump-imported-errors")}, 0298 i18n("Recursively dump errors from imported contexts.")}); 0299 0300 parser.process(app); 0301 0302 aboutData.processCommandLine(&parser); 0303 0304 verbose = parser.isSet(QStringLiteral("verbose")); 0305 warnings = parser.isSet(QStringLiteral("warnings")); 0306 qInstallMessageHandler(messageOutput); 0307 0308 AutoTestShell::init(); 0309 TestCore::initialize(Core::NoUi, QStringLiteral("duchainify")); 0310 Manager manager(&parser); 0311 0312 QTimer::singleShot(0, &manager, &Manager::init); 0313 int ret = app.exec(); 0314 0315 TestCore::shutdown(); 0316 0317 return ret; 0318 } 0319 0320 #include "moc_main.cpp"