70 std::vector<QString>&& sources,
75 std::stable_sort(sources.begin(), sources.end());
78 std::unique(sources.begin(), sources.end()),
82 sources.erase(
std::remove_if(sources.begin(), sources.end(),
83 [](
const QString &source) {
84 return Utilities::isGeneratedFile(source);
89 std::stable_partition(sources.begin(), sources.end(), [](
const QString& source){
90 return CodeParser::parserForSourceFile(source) == CodeParser::parserForLanguage(
"QML");
94 std::for_each(qml_sources, sources.end(),
95 [&source_file_parser, &cpp_code_parser, &error_handler](
const QString& source){
96 qCDebug(lcQdoc,
"Parsing %s", qPrintable(source));
98 auto [untied_documentation, tied_documentation] = source_file_parser(tag_source_file(source));
99 std::vector<FnMatchError> errors{};
101 for (
const auto &untied : std::as_const(untied_documentation)) {
102 auto result = cpp_code_parser.processTopicArgs(untied);
103 tied_documentation.insert(tied_documentation.end(), result.first.begin(), result.first.end());
104 errors.insert(errors.end(), result.second.begin(), result.second.end());
110 for (
const auto &e : errors)
114 std::for_each(sources.begin(), qml_sources, [&cpp_code_parser](
const QString& source){
116 if (!codeParser)
return;
118 qCDebug(lcQdoc,
"Parsing %s", qPrintable(source));
119 codeParser
->parseSourceFile(Config::instance().location(), source, cpp_code_parser);
135 Config &config = Config::instance();
137 QStringList indexFiles;
138 const QStringList configIndexes{config.get(
CONFIG_INDEXES).asStringList()};
141 for (
const auto &index : configIndexes) {
143 if (fi.exists() && fi.isFile())
146 Location().warning(QString(
"Index file not found: %1").arg(index));
149 config.dependModules() += config.get(
CONFIG_DEPENDS).asStringList();
150 config.dependModules().removeDuplicates();
151 bool useNoSubDirs =
false;
152 QSet<QString> subDirs;
156 for (
const auto &format : formats) {
157 if (config.get(format + Config::dot +
"nosubdirs").asBool()) {
159 const auto singleOutputSubdir{QDir(config.getOutputDir(format)).dirName()};
160 if (!singleOutputSubdir.isEmpty())
161 subDirs << singleOutputSubdir;
165 if (!config.dependModules().empty()) {
166 if (!config.indexDirs().empty()) {
167 for (
auto &dir : config.indexDirs()) {
168 if (dir.startsWith(
"..")) {
169 const QString prefix(QDir(config.currentDir())
170 .relativeFilePath(config.previousCurrentDir()));
171 if (!prefix.isEmpty())
172 dir.prepend(prefix + QLatin1Char(
'/'));
176
177
178
179
180
181
182 bool asteriskUsed =
false;
183 if (config.dependModules().contains(
"*")) {
184 config.dependModules().removeOne(
"*");
187 std::for_each(formats.begin(), formats.end(), [&](
const QString &format) {
188 QDir scanDir(config.getOutputDir(format));
189 QStringList foundModules =
190 scanDir.entryList(QStringList(
"*.index"), QDir::Files);
192 foundModules.begin(), foundModules.end(), foundModules.begin(),
193 [](
const QString &index) {
return QFileInfo(index).baseName(); });
194 config.dependModules() << foundModules;
197 for (
const auto &indexDir : config.indexDirs()) {
198 QDir scanDir = QDir(indexDir);
199 scanDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
200 QFileInfoList dirList = scanDir.entryInfoList();
201 for (
const auto &dir : dirList)
202 config.dependModules().append(dir.fileName());
207 config.dependModules().removeAll(project.toLower());
208 config.dependModules().removeDuplicates();
209 qCCritical(lcQdoc) <<
"Configuration file for"
210 << project <<
"has depends = *; loading all"
211 << config.dependModules().size()
212 <<
"index files found";
214 for (
const auto &module : config.dependModules()) {
215 QList<QFileInfo> foundIndices;
217 bool useModuleSubDir = !subDirs.contains(module);
220 for (
const auto &dir : config.indexDirs()) {
221 for (
const auto &subDir : std::as_const(subDirs)) {
222 QString fileToLookFor = dir + QLatin1Char(
'/') + subDir + QLatin1Char(
'/')
223 + module.toLower() +
".index";
224 if (QFile::exists(fileToLookFor)) {
225 QFileInfo tempFileInfo(fileToLookFor);
226 if (!foundIndices.contains(tempFileInfo))
227 foundIndices.append(tempFileInfo);
233 subDirs.remove(module);
234 std::sort(foundIndices.begin(), foundIndices.end(), creationTimeBefore);
236 if (foundIndices.size() > 1) {
238
239
240
241
242 QStringList indexPaths;
243 indexPaths.reserve(foundIndices.size());
244 for (
const auto &found : std::as_const(foundIndices))
245 indexPaths << found.absoluteFilePath();
248 QString(
"Multiple index files found for dependency \"%1\":\n%2")
249 .arg(module, indexPaths.join(
'\n')));
251 QString(
"Using %1 as index file for dependency \"%2\"")
252 .arg(foundIndices[foundIndices.size() - 1].absoluteFilePath(),
255 indexToAdd = foundIndices[foundIndices.size() - 1].absoluteFilePath();
256 }
else if (foundIndices.size() == 1) {
257 indexToAdd = foundIndices[0].absoluteFilePath();
259 if (!indexToAdd.isEmpty()) {
260 if (!indexFiles.contains(indexToAdd))
261 indexFiles << indexToAdd;
262 }
else if (!asteriskUsed && warn) {
264 QString(R"("%1" Cannot locate index file for dependency "%2")")
270 QLatin1String(
"Dependent modules specified, but no index directories were set. "
271 "There will probably be errors for missing links."));
274 qdb->readIndexes(indexFiles);
357 Config &config = Config::instance();
358 config.setPreviousCurrentDir(QDir::currentPath());
361
362
363
364
365
366
367
368
370 config.load(fileName);
372 if (project.isEmpty()) {
373 qCCritical(lcQdoc) << QLatin1String(
"qdoc can't run; no project set in qdocconf file");
378 config.setCurrentDir(QFileInfo(fileName).path());
379 if (!config.currentDir().isEmpty())
380 QDir::setCurrent(config.currentDir());
382 logStartEndMessage(QLatin1String(
"Start"), config);
385 Utilities::startDebugging(QString(
"command line"));
386 qCDebug(lcQdoc).noquote() <<
"Arguments:" << QCoreApplication::arguments();
403 QStringList image_search_directories{config.getCanonicalPathList(
CONFIG_IMAGEDIRS)};
407 qCDebug(lcQdoc,
"Adding doc/image dirs found in exampledirs to imagedirs");
408 QSet<QString> exampleImageDirs;
409 QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles);
410 for (
const auto &image : exampleImageList) {
411 if (image.contains(
"doc/images")) {
412 QString t = image.left(image.lastIndexOf(
"doc/images") + 10);
413 if (!exampleImageDirs.contains(t))
414 exampleImageDirs.insert(t);
441 std::copy(image_search_directories.begin(), image_search_directories.end(),
std::back_inserter(search_directories));
442 std::copy(exampleImageDirs.begin(), exampleImageDirs.end(),
std::back_inserter(search_directories));
444 std::vector<DirectoryPath> validated_search_directories{};
445 for (
const QString& path : search_directories) {
446 auto maybe_validated_path{DirectoryPath::refine(path)};
447 if (!maybe_validated_path)
449 qCDebug(lcQdoc).noquote() << u"%1 is not a valid path, it will be ignored when resolving a file"_s.arg(path);
450 else validated_search_directories.push_back(*maybe_validated_path);
490#ifdef QDOC_TEMPLATE_GENERATOR_ENABLED
491 TemplateGenerator templateGenerator{file_resolver};
497
498
499
500
501
502
503
504
508
509
510 const QSet<QString> outputFormats = config.getOutputFormats();
515 qCDebug(lcQdoc,
" loading index files");
516 loadIndexFiles(outputFormats);
517 qCDebug(lcQdoc,
" done loading index files");
519 qdb->newPrimaryTree(project);
521 qdb->newPrimaryTree(project);
523 qdb->setPrimaryTree(project);
526 if (config.dependModules().isEmpty()) {
527 config.dependModules() = config.get(
CONFIG_DEPENDS).asStringList();
528 config.dependModules().removeDuplicates();
530 qdb->setSearchOrder(config.dependModules());
541 std::vector<QByteArray> include_paths{};
546 args.append(Utilities::getInternalIncludePaths(QStringLiteral(
"clang++")));
547#elif defined(Q_OS_LINUX)
548 args.append(Utilities::getInternalIncludePaths(QStringLiteral(
"g++")));
551 for (
const auto &path : std::as_const(args)) {
553 include_paths.push_back(path.toUtf8());
556 include_paths.erase(
std::unique(include_paths.begin(), include_paths.end()),
557 include_paths.end());
560 QList<QByteArray> clang_defines{};
562 const QStringList config_defines{config.get(
CONFIG_DEFINES).asStringList()};
563 for (
const QString &def : config_defines) {
564 if (!def.contains(QChar(
'*'))) {
565 QByteArray tmp(
"-D");
566 tmp.append(def.toUtf8());
567 clang_defines.append(tmp.constData());
573
574
575
576
577
578
579
580
581
582
583
584
595 QDocDatabase::qdocDB(),
596 moduleHeader.isNull() ? project : moduleHeader,
597 Config::instance().getHeaderFiles(),
600 Config::instance().createInclusionPolicy()
604 ClangCodeParser clangParser(QDocDatabase::qdocDB(), Config::instance(), include_paths, clang_defines, pch);
608 QStringList sourceList;
610 qCDebug(lcQdoc,
"Reading sourcedirs");
619 std::vector<QString> sources{};
620 for (
const auto &source : sourceList) {
621 if (!source.contains(QLatin1String(
"doc/snippets")))
622 sources.emplace_back(source);
625
626
627
628 qCDebug(lcQdoc,
"Reading exampledirs");
629 QStringList exampleQdocList = config.getExampleQdocFiles(excludedDirs, excludedFiles);
630 for (
const auto &example : exampleQdocList) {
631 if (!example.contains(QLatin1String(
"doc/snippets")))
632 sources.emplace_back(example);
636
637
638
640 qCInfo(lcQdoc) <<
"Parse source files for" << project;
643 auto headers = config.getHeaderFiles();
647 parseSourceFiles(
std::move(sources), source_file_parser, cpp_code_parser);
650 qCInfo(lcQdoc) <<
"Source files parsed for" << project;
653
654
655
656
657 qCDebug(lcQdoc,
"Resolving stuff prior to generating docs");
661
662
663
664
665
666
667 qCDebug(lcQdoc,
"Generating docs");
668 for (
const auto &format : outputFormats) {
669 auto *generator = Generator::generatorForFormat(format);
671 generator->initializeFormat();
672 generator->generateDocs();
676 .fatal(QStringLiteral(
"QDoc: Unknown output format '%1'").arg(format));
682 qCDebug(lcQdoc,
"Terminating qdoc classes");
686 logStartEndMessage(QLatin1String(
"End"), config);
694 QDir::setCurrent(config.previousCurrentDir());
696 qCDebug(lcQdoc,
"qdoc classes terminated");