66 std::vector<QString>&& sources,
71 std::stable_sort(sources.begin(), sources.end());
74 std::unique(sources.begin(), sources.end()),
78 sources.erase(
std::remove_if(sources.begin(), sources.end(),
79 [](
const QString &source) {
80 return Utilities::isGeneratedFile(source);
85 std::stable_partition(sources.begin(), sources.end(), [](
const QString& source){
86 return CodeParser::parserForSourceFile(source) == CodeParser::parserForLanguage(
"QML");
90 std::for_each(qml_sources, sources.end(),
91 [&source_file_parser, &cpp_code_parser, &error_handler](
const QString& source){
92 qCDebug(lcQdoc,
"Parsing %s", qPrintable(source));
94 auto [untied_documentation, tied_documentation] = source_file_parser(tag_source_file(source));
95 std::vector<FnMatchError> errors{};
97 for (
const auto &untied : std::as_const(untied_documentation)) {
98 auto result = cpp_code_parser.processTopicArgs(untied);
99 tied_documentation.insert(tied_documentation.end(), result.first.begin(), result.first.end());
100 errors.insert(errors.end(), result.second.begin(), result.second.end());
106 for (
const auto &e : errors)
110 std::for_each(sources.begin(), qml_sources, [&cpp_code_parser](
const QString& source){
112 if (!codeParser)
return;
114 qCDebug(lcQdoc,
"Parsing %s", qPrintable(source));
115 codeParser->parseSourceFile(Config::instance().location(), source, cpp_code_parser);
131 Config &config = Config::instance();
133 QStringList indexFiles;
134 const QStringList configIndexes{config.get(
CONFIG_INDEXES).asStringList()};
137 for (
const auto &index : configIndexes) {
139 if (fi.exists() && fi.isFile())
142 Location().warning(QString(
"Index file not found: %1").arg(index));
145 config.dependModules() += config.get(
CONFIG_DEPENDS).asStringList();
146 config.dependModules().removeDuplicates();
147 bool useNoSubDirs =
false;
148 QSet<QString> subDirs;
152 for (
const auto &format : formats) {
153 if (config.get(format + Config::dot +
"nosubdirs").asBool()) {
155 const auto singleOutputSubdir{QDir(config.getOutputDir(format)).dirName()};
156 if (!singleOutputSubdir.isEmpty())
157 subDirs << singleOutputSubdir;
161 if (!config.dependModules().empty()) {
162 if (!config.indexDirs().empty()) {
163 for (
auto &dir : config.indexDirs()) {
164 if (dir.startsWith(
"..")) {
165 const QString prefix(QDir(config.currentDir())
166 .relativeFilePath(config.previousCurrentDir()));
167 if (!prefix.isEmpty())
168 dir.prepend(prefix + QLatin1Char(
'/'));
172
173
174
175
176
177
178 bool asteriskUsed =
false;
179 if (config.dependModules().contains(
"*")) {
180 config.dependModules().removeOne(
"*");
183 std::for_each(formats.begin(), formats.end(), [&](
const QString &format) {
184 QDir scanDir(config.getOutputDir(format));
185 QStringList foundModules =
186 scanDir.entryList(QStringList(
"*.index"), QDir::Files);
188 foundModules.begin(), foundModules.end(), foundModules.begin(),
189 [](
const QString &index) {
return QFileInfo(index).baseName(); });
190 config.dependModules() << foundModules;
193 for (
const auto &indexDir : config.indexDirs()) {
194 QDir scanDir = QDir(indexDir);
195 scanDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
196 QFileInfoList dirList = scanDir.entryInfoList();
197 for (
const auto &dir : dirList)
198 config.dependModules().append(dir.fileName());
203 config.dependModules().removeAll(project.toLower());
204 config.dependModules().removeDuplicates();
205 qCCritical(lcQdoc) <<
"Configuration file for"
206 << project <<
"has depends = *; loading all"
207 << config.dependModules().size()
208 <<
"index files found";
210 for (
const auto &module : config.dependModules()) {
211 QList<QFileInfo> foundIndices;
213 bool useModuleSubDir = !subDirs.contains(module);
216 for (
const auto &dir : config.indexDirs()) {
217 for (
const auto &subDir : std::as_const(subDirs)) {
218 QString fileToLookFor = dir + QLatin1Char(
'/') + subDir + QLatin1Char(
'/')
220 if (QFile::exists(fileToLookFor)) {
221 QFileInfo tempFileInfo(fileToLookFor);
222 if (!foundIndices.contains(tempFileInfo))
223 foundIndices.append(tempFileInfo);
229 subDirs.remove(module);
230 std::sort(foundIndices.begin(), foundIndices.end(), creationTimeBefore);
232 if (foundIndices.size() > 1) {
234
235
236
237
238 QStringList indexPaths;
239 indexPaths.reserve(foundIndices.size());
240 for (
const auto &found : std::as_const(foundIndices))
241 indexPaths << found.absoluteFilePath();
244 QString(
"Multiple index files found for dependency \"%1\":\n%2")
245 .arg(module, indexPaths.join(
'\n')));
247 QString(
"Using %1 as index file for dependency \"%2\"")
248 .arg(foundIndices[foundIndices.size() - 1].absoluteFilePath(),
251 indexToAdd = foundIndices[foundIndices.size() - 1].absoluteFilePath();
252 }
else if (foundIndices.size() == 1) {
253 indexToAdd = foundIndices[0].absoluteFilePath();
255 if (!indexToAdd.isEmpty()) {
256 if (!indexFiles.contains(indexToAdd))
257 indexFiles << indexToAdd;
258 }
else if (!asteriskUsed && warn) {
260 QString(R"("%1" Cannot locate index file for dependency "%2")")
266 QLatin1String(
"Dependent modules specified, but no index directories were set. "
267 "There will probably be errors for missing links."));
270 qdb->readIndexes(indexFiles);
305 Config &config = Config::instance();
306 config.setPreviousCurrentDir(QDir::currentPath());
309
310
311
312
313
314
315
316
318 config.load(fileName);
320 if (project.isEmpty()) {
321 qCCritical(lcQdoc) << QLatin1String(
"qdoc can't run; no project set in qdocconf file");
326 config.setCurrentDir(QFileInfo(fileName).path());
327 if (!config.currentDir().isEmpty())
328 QDir::setCurrent(config.currentDir());
330 logStartEndMessage(QLatin1String(
"Start"), config);
333 Utilities::startDebugging(QString(
"command line"));
334 qCDebug(lcQdoc).noquote() <<
"Arguments:" << QCoreApplication::arguments();
351 QStringList image_search_directories{config.getCanonicalPathList(
CONFIG_IMAGEDIRS)};
355 qCDebug(lcQdoc,
"Adding doc/image dirs found in exampledirs to imagedirs");
356 QSet<QString> exampleImageDirs;
357 QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles);
358 for (
const auto &image : exampleImageList) {
359 if (image.contains(
"doc/images")) {
360 QString t = image.left(image.lastIndexOf(
"doc/images") + 10);
361 if (!exampleImageDirs.contains(t))
362 exampleImageDirs.insert(t);
389 std::copy(image_search_directories.begin(), image_search_directories.end(),
std::back_inserter(search_directories));
390 std::copy(exampleImageDirs.begin(), exampleImageDirs.end(),
std::back_inserter(search_directories));
392 std::vector<DirectoryPath> validated_search_directories{};
393 for (
const QString& path : search_directories) {
394 auto maybe_validated_path{DirectoryPath::refine(path)};
395 if (!maybe_validated_path)
397 qCDebug(lcQdoc).noquote() << u"%1 is not a valid path, it will be ignored when resolving a file"_s.arg(path);
398 else validated_search_directories.push_back(*maybe_validated_path);
442
443
444
445
446
447
448
449
453
454
455 const QSet<QString> outputFormats = config.getOutputFormats();
460 qCDebug(lcQdoc,
" loading index files");
461 loadIndexFiles(outputFormats);
462 qCDebug(lcQdoc,
" done loading index files");
464 qdb->newPrimaryTree(project);
466 qdb->newPrimaryTree(project);
468 qdb->setPrimaryTree(project);
471 if (config.dependModules().isEmpty()) {
472 config.dependModules() = config.get(
CONFIG_DEPENDS).asStringList();
473 config.dependModules().removeDuplicates();
475 qdb->setSearchOrder(config.dependModules());
486 std::vector<QByteArray> include_paths{};
491 args.append(Utilities::getInternalIncludePaths(QStringLiteral(
"clang++")));
492#elif defined(Q_OS_LINUX)
493 args.append(Utilities::getInternalIncludePaths(QStringLiteral(
"g++")));
496 for (
const auto &path : std::as_const(args)) {
498 include_paths.push_back(path.toUtf8());
501 include_paths.erase(
std::unique(include_paths.begin(), include_paths.end()),
502 include_paths.end());
505 QList<QByteArray> clang_defines{};
507 const QStringList config_defines{config.get(
CONFIG_DEFINES).asStringList()};
508 for (
const QString &def : config_defines) {
509 if (!def.contains(QChar(
'*'))) {
510 QByteArray tmp(
"-D");
511 tmp.append(def.toUtf8());
512 clang_defines.append(tmp.constData());
521 QDocDatabase::qdocDB(),
522 moduleHeader.isNull() ? project : moduleHeader,
523 Config::instance().getHeaderFiles(),
526 Config::instance().createInclusionPolicy()
530 ClangCodeParser clangParser(QDocDatabase::qdocDB(), Config::instance(), include_paths, clang_defines, pch);
534
535
536
537
538
539
547 QStringList sourceList;
549 qCDebug(lcQdoc,
"Reading sourcedirs");
558 std::vector<QString> sources{};
559 for (
const auto &source : sourceList) {
560 if (!source.contains(QLatin1String(
"doc/snippets")))
561 sources.emplace_back(source);
564
565
566
567 qCDebug(lcQdoc,
"Reading exampledirs");
568 QStringList exampleQdocList = config.getExampleQdocFiles(excludedDirs, excludedFiles);
569 for (
const auto &example : exampleQdocList) {
570 if (!example.contains(QLatin1String(
"doc/snippets")))
571 sources.emplace_back(example);
575
576
577
579 qCInfo(lcQdoc) <<
"Parse source files for" << project;
582 auto headers = config.getHeaderFiles();
586 parseSourceFiles(
std::move(sources), source_file_parser, cpp_code_parser);
589 qCInfo(lcQdoc) <<
"Source files parsed for" << project;
592
593
594
595
596 qCDebug(lcQdoc,
"Resolving stuff prior to generating docs");
600
601
602
603
604
605
606 qCDebug(lcQdoc,
"Generating docs");
607 for (
const auto &format : outputFormats) {
608 auto *generator = Generator::generatorForFormat(format);
610 generator->initializeFormat();
611 generator->generateDocs();
615 .fatal(QStringLiteral(
"QDoc: Unknown output format '%1'").arg(format));
619 qCDebug(lcQdoc,
"Terminating qdoc classes");
623 logStartEndMessage(QLatin1String(
"End"), config);
631 QDir::setCurrent(config.previousCurrentDir());
633 qCDebug(lcQdoc,
"qdoc classes terminated");