74 std::vector<QString>&& sources,
79 std::stable_sort(sources.begin(), sources.end());
82 std::unique(sources.begin(), sources.end()),
86 sources.erase(
std::remove_if(sources.begin(), sources.end(),
87 [](
const QString &source) {
88 return Utilities::isGeneratedFile(source);
93 std::stable_partition(sources.begin(), sources.end(), [](
const QString& source){
94 return CodeParser::parserForSourceFile(source) == CodeParser::parserForLanguage(
"QML");
98 std::for_each(qml_sources, sources.end(),
99 [&source_file_parser, &cpp_code_parser, &error_handler](
const QString& source){
100 qCDebug(lcQdoc,
"Parsing %s", qPrintable(source));
102 auto [untied_documentation, tied_documentation] = source_file_parser(tag_source_file(source));
103 std::vector<FnMatchError> errors{};
105 for (
const auto &untied : std::as_const(untied_documentation)) {
106 auto result = cpp_code_parser.processTopicArgs(untied);
107 tied_documentation.insert(tied_documentation.end(), result.first.begin(), result.first.end());
108 errors.insert(errors.end(), result.second.begin(), result.second.end());
114 for (
const auto &e : errors)
118 std::for_each(sources.begin(), qml_sources, [&cpp_code_parser](
const QString& source){
120 if (!codeParser)
return;
122 qCDebug(lcQdoc,
"Parsing %s", qPrintable(source));
123 codeParser
->parseSourceFile(Config::instance().location(), source, cpp_code_parser);
139 Config &config = Config::instance();
144 config.dependModules() += config.get(
CONFIG_DEPENDS).asStringList();
145 config.dependModules().removeDuplicates();
146 bool useNoSubDirs =
false;
147 QSet<QString> subDirs;
151 for (
const auto &format : formats) {
152 if (config.get(format + Config::dot +
"nosubdirs").asBool()) {
154 const auto singleOutputSubdir{QDir(config.getOutputDir(format)).dirName()};
155 if (!singleOutputSubdir.isEmpty())
156 subDirs << singleOutputSubdir;
160 if (!config.dependModules().empty()) {
161 if (!config.indexDirs().empty()) {
162 for (
auto &dir : config.indexDirs()) {
163 if (dir.startsWith(
"..")) {
164 const QString prefix(QDir(config.currentDir())
165 .relativeFilePath(config.previousCurrentDir()));
166 if (!prefix.isEmpty())
167 dir.prepend(prefix + QLatin1Char(
'/'));
171
172
173
174
175
176
177 bool asteriskUsed =
false;
178 if (config.dependModules().contains(
"*")) {
179 config.dependModules().removeOne(
"*");
182 std::for_each(formats.begin(), formats.end(), [&](
const QString &format) {
183 QDir scanDir(config.getOutputDir(format));
184 QStringList foundModules =
185 scanDir.entryList(QStringList(
"*.index"), QDir::Files);
187 foundModules.begin(), foundModules.end(), foundModules.begin(),
188 [](
const QString &index) {
return QFileInfo(index).baseName(); });
189 config.dependModules() << foundModules;
192 for (
const auto &indexDir : config.indexDirs()) {
193 QDir scanDir = QDir(indexDir);
194 scanDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
195 QFileInfoList dirList = scanDir.entryInfoList();
196 for (
const auto &dir : dirList)
197 config.dependModules().append(dir.fileName());
202 config.dependModules().removeAll(project.toLower());
203 config.dependModules().removeDuplicates();
204 qCCritical(lcQdoc) <<
"Configuration file for"
205 << project <<
"has depends = *; loading all"
206 << config.dependModules().size()
207 <<
"index files found";
209 for (
const auto &module : config.dependModules()) {
210 QList<QFileInfo> foundIndices;
212 bool useModuleSubDir = !subDirs.contains(module);
215 for (
const auto &dir : config.indexDirs()) {
216 for (
const auto &subDir : std::as_const(subDirs)) {
217 QString fileToLookFor = dir + QLatin1Char(
'/') + subDir + QLatin1Char(
'/')
218 + module.toLower() +
".index";
219 if (QFile::exists(fileToLookFor)) {
220 QFileInfo tempFileInfo(fileToLookFor);
221 if (!foundIndices.contains(tempFileInfo))
222 foundIndices.append(tempFileInfo);
228 subDirs.remove(module);
229 std::sort(foundIndices.begin(), foundIndices.end(), creationTimeBefore);
231 if (foundIndices.size() > 1) {
233
234
235
236
237 QStringList indexPaths;
238 indexPaths.reserve(foundIndices.size());
239 for (
const auto &found : std::as_const(foundIndices))
240 indexPaths << found.absoluteFilePath();
243 QString(
"Multiple index files found for dependency \"%1\":\n%2")
244 .arg(module, indexPaths.join(
'\n')));
246 QString(
"Using %1 as index file for dependency \"%2\"")
247 .arg(foundIndices[foundIndices.size() - 1].absoluteFilePath(),
250 indexToAdd = foundIndices[foundIndices.size() - 1].absoluteFilePath();
251 }
else if (foundIndices.size() == 1) {
252 indexToAdd = foundIndices[0].absoluteFilePath();
254 if (!indexToAdd.isEmpty()) {
255 if (!indexFiles.contains(indexToAdd))
256 indexFiles << indexToAdd;
257 }
else if (!asteriskUsed && warn) {
259 QString(R"("%1" Cannot locate index file for dependency "%2")")
265 QLatin1String(
"Dependent modules specified, but no index directories were set. "
266 "There will probably be errors for missing links."));
269 qdb->readIndexes(indexFiles);
352 Config &config = Config::instance();
353 config.setPreviousCurrentDir(QDir::currentPath());
356
357
358
359
360
361
362
363
365 config.load(fileName);
367 if (project.isEmpty()) {
368 qCCritical(lcQdoc) << QLatin1String(
"qdoc can't run; no project set in qdocconf file");
373 config.setCurrentDir(QFileInfo(fileName).path());
374 if (!config.currentDir().isEmpty())
375 QDir::setCurrent(config.currentDir());
377 logStartEndMessage(QLatin1String(
"Start"), config);
380 Utilities::startDebugging(QString(
"command line"));
381 qCDebug(lcQdoc).noquote() <<
"Arguments:" << QCoreApplication::arguments();
398 QStringList image_search_directories{config.getCanonicalPathList(
CONFIG_IMAGEDIRS)};
402 qCDebug(lcQdoc,
"Adding doc/image dirs found in exampledirs to imagedirs");
403 QSet<QString> exampleImageDirs;
404 QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles);
405 for (
const auto &image : exampleImageList) {
406 if (image.contains(
"doc/images")) {
407 QString t = image.left(image.lastIndexOf(
"doc/images") + 10);
408 if (!exampleImageDirs.contains(t))
409 exampleImageDirs.insert(t);
436 std::copy(image_search_directories.begin(), image_search_directories.end(),
std::back_inserter(search_directories));
437 std::copy(exampleImageDirs.begin(), exampleImageDirs.end(),
std::back_inserter(search_directories));
439 std::vector<DirectoryPath> validated_search_directories{};
440 for (
const QString& path : search_directories) {
441 auto maybe_validated_path{DirectoryPath::refine(path)};
442 if (!maybe_validated_path)
444 qCDebug(lcQdoc).noquote() << u"%1 is not a valid path, it will be ignored when resolving a file"_s.arg(path);
445 else validated_search_directories.push_back(*maybe_validated_path);
485#ifdef QDOC_TEMPLATE_GENERATOR_ENABLED
495 auto configuredFormats = config.getOutputFormats().values();
496 std::sort(configuredFormats.begin(), configuredFormats.end(),
497 [](
const QString &a,
const QString &b) {
498 return QString::compare(a, b, Qt::CaseInsensitive) < 0;
500 std::vector<std::unique_ptr<TemplateGenerator>> templateGenerators;
501 templateGenerators.reserve(configuredFormats.size());
502 for (
const auto &fmt : configuredFormats) {
503 if (fmt.startsWith(
"template"_L1, Qt::CaseInsensitive))
504 templateGenerators.push_back(
505 std::make_unique<TemplateGenerator>(file_resolver, *QDocDatabase::qdocDB(), fmt));
512
513
514
515
516
517
518
519
523
524
525 const QSet<QString> outputFormats = config.getOutputFormats();
530 qCDebug(lcQdoc,
" loading index files");
531 loadIndexFiles(outputFormats);
532 qCDebug(lcQdoc,
" done loading index files");
534 qdb->newPrimaryTree(project);
536 qdb->newPrimaryTree(project);
538 qdb->setPrimaryTree(project);
541 if (config.dependModules().isEmpty()) {
542 config.dependModules() = config.get(
CONFIG_DEPENDS).asStringList();
543 config.dependModules().removeDuplicates();
545 qdb->setSearchOrder(config.dependModules());
556 std::vector<QByteArray> include_paths{};
561 args.append(Utilities::getInternalIncludePaths(QStringLiteral(
"clang++")));
562#elif defined(Q_OS_LINUX)
563 args.append(Utilities::getInternalIncludePaths(QStringLiteral(
"g++")));
566 for (
const auto &path : std::as_const(args)) {
568 include_paths.push_back(path.toUtf8());
571 include_paths.erase(
std::unique(include_paths.begin(), include_paths.end()),
572 include_paths.end());
575 QList<QByteArray> clang_defines{};
577 const QStringList config_defines{config.get(
CONFIG_DEFINES).asStringList()};
578 for (
const QString &def : config_defines) {
579 if (!def.contains(QChar(
'*'))) {
580 QByteArray tmp(
"-D");
581 tmp.append(def.toUtf8());
582 clang_defines.append(tmp.constData());
588
589
590
591
592
593
594
595
596
597
598
599
610 QDocDatabase::qdocDB(),
611 moduleHeader.isNull() ? project : moduleHeader,
612 Config::instance().getHeaderFiles(),
615 Config::instance().createInclusionPolicy()
619 ClangCodeParser clangParser(QDocDatabase::qdocDB(), Config::instance(), include_paths, clang_defines, pch);
623 QStringList sourceList;
625 qCDebug(lcQdoc,
"Reading sourcedirs");
634 std::vector<QString> sources{};
635 for (
const auto &source : sourceList) {
636 if (!source.contains(QLatin1String(
"doc/snippets")))
637 sources.emplace_back(source);
640
641
642
643 qCDebug(lcQdoc,
"Reading exampledirs");
644 QStringList exampleQdocList = config.getExampleQdocFiles(excludedDirs, excludedFiles);
645 for (
const auto &example : exampleQdocList) {
646 if (!example.contains(QLatin1String(
"doc/snippets")))
647 sources.emplace_back(example);
651
652
653
655 qCInfo(lcQdoc) <<
"Parse source files for" << project;
658 auto headers = config.getHeaderFiles();
662 parseSourceFiles(
std::move(sources), source_file_parser, cpp_code_parser);
665 qCInfo(lcQdoc) <<
"Source files parsed for" << project;
668
669
670
671
672 qCDebug(lcQdoc,
"Resolving stuff prior to generating docs");
676
677
678
679
680
681
682 qCDebug(lcQdoc,
"Generating docs");
683 int producedFormats = 0;
684 for (
const auto &format : outputFormats) {
685 auto *generator = Generator::generatorForFormat(format);
687 generator->initializeFormat();
688 generator->generateDocs();
690 }
else if (
auto *producer = OutputProducerRegistry::instance().producerForFormat(format)) {
694 producer->finalize();
699 .warning(QStringLiteral(
"QDoc: Unknown output format '%1'; skipping. "
700 "Check for typos or ensure the corresponding "
701 "generator is enabled in this QDoc build.")
706 if (!outputFormats.isEmpty() && producedFormats == 0) {
709 .fatal(QStringLiteral(
"QDoc: None of the configured output formats could be produced; aborting"));
714 qCDebug(lcQdoc,
"Terminating qdoc classes");
718 logStartEndMessage(QLatin1String(
"End"), config);
726 QDir::setCurrent(config.previousCurrentDir());
728 qCDebug(lcQdoc,
"qdoc classes terminated");