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();
141 QStringList indexFiles;
142 const QStringList configIndexes{config.get(
CONFIG_INDEXES).asStringList()};
145 for (
const auto &index : configIndexes) {
147 if (fi.exists() && fi.isFile())
150 Location().warning(QString(
"Index file not found: %1").arg(index));
153 config.dependModules() += config.get(
CONFIG_DEPENDS).asStringList();
154 config.dependModules().removeDuplicates();
155 bool useNoSubDirs =
false;
156 QSet<QString> subDirs;
160 for (
const auto &format : formats) {
161 if (config.get(format + Config::dot +
"nosubdirs").asBool()) {
163 const auto singleOutputSubdir{QDir(config.getOutputDir(format)).dirName()};
164 if (!singleOutputSubdir.isEmpty())
165 subDirs << singleOutputSubdir;
169 if (!config.dependModules().empty()) {
170 if (!config.indexDirs().empty()) {
171 for (
auto &dir : config.indexDirs()) {
172 if (dir.startsWith(
"..")) {
173 const QString prefix(QDir(config.currentDir())
174 .relativeFilePath(config.previousCurrentDir()));
175 if (!prefix.isEmpty())
176 dir.prepend(prefix + QLatin1Char(
'/'));
180
181
182
183
184
185
186 bool asteriskUsed =
false;
187 if (config.dependModules().contains(
"*")) {
188 config.dependModules().removeOne(
"*");
191 std::for_each(formats.begin(), formats.end(), [&](
const QString &format) {
192 QDir scanDir(config.getOutputDir(format));
193 QStringList foundModules =
194 scanDir.entryList(QStringList(
"*.index"), QDir::Files);
196 foundModules.begin(), foundModules.end(), foundModules.begin(),
197 [](
const QString &index) {
return QFileInfo(index).baseName(); });
198 config.dependModules() << foundModules;
201 for (
const auto &indexDir : config.indexDirs()) {
202 QDir scanDir = QDir(indexDir);
203 scanDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
204 QFileInfoList dirList = scanDir.entryInfoList();
205 for (
const auto &dir : dirList)
206 config.dependModules().append(dir.fileName());
211 config.dependModules().removeAll(project.toLower());
212 config.dependModules().removeDuplicates();
213 qCCritical(lcQdoc) <<
"Configuration file for"
214 << project <<
"has depends = *; loading all"
215 << config.dependModules().size()
216 <<
"index files found";
218 for (
const auto &module : config.dependModules()) {
219 QList<QFileInfo> foundIndices;
221 bool useModuleSubDir = !subDirs.contains(module);
224 for (
const auto &dir : config.indexDirs()) {
225 for (
const auto &subDir : std::as_const(subDirs)) {
226 QString fileToLookFor = dir + QLatin1Char(
'/') + subDir + QLatin1Char(
'/')
227 + module.toLower() +
".index";
228 if (QFile::exists(fileToLookFor)) {
229 QFileInfo tempFileInfo(fileToLookFor);
230 if (!foundIndices.contains(tempFileInfo))
231 foundIndices.append(tempFileInfo);
237 subDirs.remove(module);
238 std::sort(foundIndices.begin(), foundIndices.end(), creationTimeBefore);
240 if (foundIndices.size() > 1) {
242
243
244
245
246 QStringList indexPaths;
247 indexPaths.reserve(foundIndices.size());
248 for (
const auto &found : std::as_const(foundIndices))
249 indexPaths << found.absoluteFilePath();
252 QString(
"Multiple index files found for dependency \"%1\":\n%2")
253 .arg(module, indexPaths.join(
'\n')));
255 QString(
"Using %1 as index file for dependency \"%2\"")
256 .arg(foundIndices[foundIndices.size() - 1].absoluteFilePath(),
259 indexToAdd = foundIndices[foundIndices.size() - 1].absoluteFilePath();
260 }
else if (foundIndices.size() == 1) {
261 indexToAdd = foundIndices[0].absoluteFilePath();
263 if (!indexToAdd.isEmpty()) {
264 if (!indexFiles.contains(indexToAdd))
265 indexFiles << indexToAdd;
266 }
else if (!asteriskUsed && warn) {
268 QString(R"("%1" Cannot locate index file for dependency "%2")")
274 QLatin1String(
"Dependent modules specified, but no index directories were set. "
275 "There will probably be errors for missing links."));
278 qdb->readIndexes(indexFiles);
361 Config &config = Config::instance();
362 config.setPreviousCurrentDir(QDir::currentPath());
365
366
367
368
369
370
371
372
374 config.load(fileName);
376 if (project.isEmpty()) {
377 qCCritical(lcQdoc) << QLatin1String(
"qdoc can't run; no project set in qdocconf file");
382 config.setCurrentDir(QFileInfo(fileName).path());
383 if (!config.currentDir().isEmpty())
384 QDir::setCurrent(config.currentDir());
386 logStartEndMessage(QLatin1String(
"Start"), config);
389 Utilities::startDebugging(QString(
"command line"));
390 qCDebug(lcQdoc).noquote() <<
"Arguments:" << QCoreApplication::arguments();
407 QStringList image_search_directories{config.getCanonicalPathList(
CONFIG_IMAGEDIRS)};
411 qCDebug(lcQdoc,
"Adding doc/image dirs found in exampledirs to imagedirs");
412 QSet<QString> exampleImageDirs;
413 QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles);
414 for (
const auto &image : exampleImageList) {
415 if (image.contains(
"doc/images")) {
416 QString t = image.left(image.lastIndexOf(
"doc/images") + 10);
417 if (!exampleImageDirs.contains(t))
418 exampleImageDirs.insert(t);
445 std::copy(image_search_directories.begin(), image_search_directories.end(),
std::back_inserter(search_directories));
446 std::copy(exampleImageDirs.begin(), exampleImageDirs.end(),
std::back_inserter(search_directories));
448 std::vector<DirectoryPath> validated_search_directories{};
449 for (
const QString& path : search_directories) {
450 auto maybe_validated_path{DirectoryPath::refine(path)};
451 if (!maybe_validated_path)
453 qCDebug(lcQdoc).noquote() << u"%1 is not a valid path, it will be ignored when resolving a file"_s.arg(path);
454 else validated_search_directories.push_back(*maybe_validated_path);
494#ifdef QDOC_TEMPLATE_GENERATOR_ENABLED
504 auto configuredFormats = config.getOutputFormats().values();
505 std::sort(configuredFormats.begin(), configuredFormats.end(),
506 [](
const QString &a,
const QString &b) {
507 return QString::compare(a, b, Qt::CaseInsensitive) < 0;
509 std::vector<std::unique_ptr<TemplateGenerator>> templateGenerators;
510 templateGenerators.reserve(configuredFormats.size());
511 for (
const auto &fmt : configuredFormats) {
512 if (fmt.startsWith(
"template"_L1, Qt::CaseInsensitive))
513 templateGenerators.push_back(
514 std::make_unique<TemplateGenerator>(file_resolver, *QDocDatabase::qdocDB(), fmt));
521
522
523
524
525
526
527
528
532
533
534 const QSet<QString> outputFormats = config.getOutputFormats();
539 qCDebug(lcQdoc,
" loading index files");
540 loadIndexFiles(outputFormats);
541 qCDebug(lcQdoc,
" done loading index files");
543 qdb->newPrimaryTree(project);
545 qdb->newPrimaryTree(project);
547 qdb->setPrimaryTree(project);
550 if (config.dependModules().isEmpty()) {
551 config.dependModules() = config.get(
CONFIG_DEPENDS).asStringList();
552 config.dependModules().removeDuplicates();
554 qdb->setSearchOrder(config.dependModules());
565 std::vector<QByteArray> include_paths{};
570 args.append(Utilities::getInternalIncludePaths(QStringLiteral(
"clang++")));
571#elif defined(Q_OS_LINUX)
572 args.append(Utilities::getInternalIncludePaths(QStringLiteral(
"g++")));
575 for (
const auto &path : std::as_const(args)) {
577 include_paths.push_back(path.toUtf8());
580 include_paths.erase(
std::unique(include_paths.begin(), include_paths.end()),
581 include_paths.end());
584 QList<QByteArray> clang_defines{};
586 const QStringList config_defines{config.get(
CONFIG_DEFINES).asStringList()};
587 for (
const QString &def : config_defines) {
588 if (!def.contains(QChar(
'*'))) {
589 QByteArray tmp(
"-D");
590 tmp.append(def.toUtf8());
591 clang_defines.append(tmp.constData());
597
598
599
600
601
602
603
604
605
606
607
608
619 QDocDatabase::qdocDB(),
620 moduleHeader.isNull() ? project : moduleHeader,
621 Config::instance().getHeaderFiles(),
624 Config::instance().createInclusionPolicy()
628 ClangCodeParser clangParser(QDocDatabase::qdocDB(), Config::instance(), include_paths, clang_defines, pch);
632 QStringList sourceList;
634 qCDebug(lcQdoc,
"Reading sourcedirs");
643 std::vector<QString> sources{};
644 for (
const auto &source : sourceList) {
645 if (!source.contains(QLatin1String(
"doc/snippets")))
646 sources.emplace_back(source);
649
650
651
652 qCDebug(lcQdoc,
"Reading exampledirs");
653 QStringList exampleQdocList = config.getExampleQdocFiles(excludedDirs, excludedFiles);
654 for (
const auto &example : exampleQdocList) {
655 if (!example.contains(QLatin1String(
"doc/snippets")))
656 sources.emplace_back(example);
660
661
662
664 qCInfo(lcQdoc) <<
"Parse source files for" << project;
667 auto headers = config.getHeaderFiles();
671 parseSourceFiles(
std::move(sources), source_file_parser, cpp_code_parser);
674 qCInfo(lcQdoc) <<
"Source files parsed for" << project;
677
678
679
680
681 qCDebug(lcQdoc,
"Resolving stuff prior to generating docs");
685
686
687
688
689
690
691 qCDebug(lcQdoc,
"Generating docs");
692 for (
const auto &format : outputFormats) {
693 auto *generator = Generator::generatorForFormat(format);
695 generator->initializeFormat();
696 generator->generateDocs();
697 }
else if (
auto *producer = OutputProducerRegistry::instance().producerForFormat(format)) {
701 producer->finalize();
705 .fatal(QStringLiteral(
"QDoc: Unknown output format '%1'").arg(format));
711 qCDebug(lcQdoc,
"Terminating qdoc classes");
715 logStartEndMessage(QLatin1String(
"End"), config);
723 QDir::setCurrent(config.previousCurrentDir());
725 qCDebug(lcQdoc,
"qdoc classes terminated");