65 std::vector<QString>&& sources,
70 std::stable_sort(sources.begin(), sources.end());
73 std::unique(sources.begin(), sources.end()),
78 std::stable_partition(sources.begin(), sources.end(), [](
const QString& source){
79 return CodeParser::parserForSourceFile(source) == CodeParser::parserForLanguage(
"QML");
83 std::for_each(qml_sources, sources.end(),
84 [&source_file_parser, &cpp_code_parser, &error_handler](
const QString& source){
85 qCDebug(lcQdoc,
"Parsing %s", qPrintable(source));
87 auto [untied_documentation, tied_documentation] = source_file_parser(tag_source_file(source));
88 std::vector<FnMatchError> errors{};
90 for (
auto untied : untied_documentation) {
91 auto result = cpp_code_parser.processTopicArgs(untied);
92 tied_documentation.insert(tied_documentation.end(), result.first.begin(), result.first.end());
98 for (
const auto &e : errors)
102 std::for_each(sources.begin(), qml_sources, [&cpp_code_parser](
const QString& source){
104 if (!codeParser)
return;
106 qCDebug(lcQdoc,
"Parsing %s", qPrintable(source));
107 codeParser->parseSourceFile(Config::instance().location(), source, cpp_code_parser);
123 Config &config = Config::instance();
125 QStringList indexFiles;
126 const QStringList configIndexes{config.get(
CONFIG_INDEXES).asStringList()};
129 for (
const auto &index : configIndexes) {
131 if (fi.exists() && fi.isFile())
134 Location().warning(QString(
"Index file not found: %1").arg(index));
137 config.dependModules() += config.get(
CONFIG_DEPENDS).asStringList();
138 config.dependModules().removeDuplicates();
139 bool useNoSubDirs =
false;
140 QSet<QString> subDirs;
144 for (
const auto &format : formats) {
145 if (config.get(format + Config::dot +
"nosubdirs").asBool()) {
147 const auto singleOutputSubdir{QDir(config.getOutputDir(format)).dirName()};
148 if (!singleOutputSubdir.isEmpty())
149 subDirs << singleOutputSubdir;
153 if (!config.dependModules().empty()) {
154 if (!config.indexDirs().empty()) {
155 for (
auto &dir : config.indexDirs()) {
156 if (dir.startsWith(
"..")) {
157 const QString prefix(QDir(config.currentDir())
158 .relativeFilePath(config.previousCurrentDir()));
159 if (!prefix.isEmpty())
160 dir.prepend(prefix + QLatin1Char(
'/'));
164
165
166
167
168
169
170 bool asteriskUsed =
false;
171 if (config.dependModules().contains(
"*")) {
172 config.dependModules().removeOne(
"*");
175 std::for_each(formats.begin(), formats.end(), [&](
const QString &format) {
176 QDir scanDir(config.getOutputDir(format));
177 QStringList foundModules =
178 scanDir.entryList(QStringList(
"*.index"), QDir::Files);
180 foundModules.begin(), foundModules.end(), foundModules.begin(),
181 [](
const QString &index) {
return QFileInfo(index).baseName(); });
182 config.dependModules() << foundModules;
185 for (
const auto &indexDir : config.indexDirs()) {
186 QDir scanDir = QDir(indexDir);
187 scanDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
188 QFileInfoList dirList = scanDir.entryInfoList();
189 for (
const auto &dir : dirList)
190 config.dependModules().append(dir.fileName());
195 config.dependModules().removeAll(project.toLower());
196 config.dependModules().removeDuplicates();
197 qCCritical(lcQdoc) <<
"Configuration file for"
198 << project <<
"has depends = *; loading all"
199 << config.dependModules().size()
200 <<
"index files found";
202 for (
const auto &module : config.dependModules()) {
203 QList<QFileInfo> foundIndices;
205 bool useModuleSubDir = !subDirs.contains(module);
208 for (
const auto &dir : config.indexDirs()) {
209 for (
const auto &subDir : std::as_const(subDirs)) {
210 QString fileToLookFor = dir + QLatin1Char(
'/') + subDir + QLatin1Char(
'/')
212 if (QFile::exists(fileToLookFor)) {
213 QFileInfo tempFileInfo(fileToLookFor);
214 if (!foundIndices.contains(tempFileInfo))
215 foundIndices.append(tempFileInfo);
221 subDirs.remove(module);
222 std::sort(foundIndices.begin(), foundIndices.end(), creationTimeBefore);
224 if (foundIndices.size() > 1) {
226
227
228
229
230 QStringList indexPaths;
231 indexPaths.reserve(foundIndices.size());
232 for (
const auto &found : std::as_const(foundIndices))
233 indexPaths << found.absoluteFilePath();
236 QString(
"Multiple index files found for dependency \"%1\":\n%2")
237 .arg(module, indexPaths.join(
'\n')));
239 QString(
"Using %1 as index file for dependency \"%2\"")
240 .arg(foundIndices[foundIndices.size() - 1].absoluteFilePath(),
243 indexToAdd = foundIndices[foundIndices.size() - 1].absoluteFilePath();
244 }
else if (foundIndices.size() == 1) {
245 indexToAdd = foundIndices[0].absoluteFilePath();
247 if (!indexToAdd.isEmpty()) {
248 if (!indexFiles.contains(indexToAdd))
249 indexFiles << indexToAdd;
250 }
else if (!asteriskUsed && warn) {
252 QString(R"("%1" Cannot locate index file for dependency "%2")")
258 QLatin1String(
"Dependent modules specified, but no index directories were set. "
259 "There will probably be errors for missing links."));
262 qdb->readIndexes(indexFiles);
297 Config &config = Config::instance();
298 config.setPreviousCurrentDir(QDir::currentPath());
301
302
303
304
305
306
307
308
310 config.load(fileName);
312 if (project.isEmpty()) {
313 qCCritical(lcQdoc) << QLatin1String(
"qdoc can't run; no project set in qdocconf file");
318 config.setCurrentDir(QFileInfo(fileName).path());
319 if (!config.currentDir().isEmpty())
320 QDir::setCurrent(config.currentDir());
322 logStartEndMessage(QLatin1String(
"Start"), config);
325 Utilities::startDebugging(QString(
"command line"));
326 qCDebug(lcQdoc).noquote() <<
"Arguments:" << QCoreApplication::arguments();
343 QStringList image_search_directories{config.getCanonicalPathList(
CONFIG_IMAGEDIRS)};
347 qCDebug(lcQdoc,
"Adding doc/image dirs found in exampledirs to imagedirs");
348 QSet<QString> exampleImageDirs;
349 QStringList exampleImageList = config.getExampleImageFiles(excludedDirs, excludedFiles);
350 for (
const auto &image : exampleImageList) {
351 if (image.contains(
"doc/images")) {
352 QString t = image.left(image.lastIndexOf(
"doc/images") + 10);
353 if (!exampleImageDirs.contains(t))
354 exampleImageDirs.insert(t);
381 std::copy(image_search_directories.begin(), image_search_directories.end(),
std::back_inserter(search_directories));
382 std::copy(exampleImageDirs.begin(), exampleImageDirs.end(),
std::back_inserter(search_directories));
384 std::vector<DirectoryPath> validated_search_directories{};
385 for (
const QString& path : search_directories) {
386 auto maybe_validated_path{DirectoryPath::refine(path)};
387 if (!maybe_validated_path)
389 qCDebug(lcQdoc).noquote() << u"%1 is not a valid path, it will be ignored when resolving a file"_s.arg(path);
390 else validated_search_directories.push_back(*maybe_validated_path);
434
435
436
437
438
439
440
441
445
446
447 const QSet<QString> outputFormats = config.getOutputFormats();
452 qCDebug(lcQdoc,
" loading index files");
453 loadIndexFiles(outputFormats);
454 qCDebug(lcQdoc,
" done loading index files");
456 qdb->newPrimaryTree(project);
458 qdb->newPrimaryTree(project);
460 qdb->setPrimaryTree(project);
463 if (config.dependModules().isEmpty()) {
464 config.dependModules() = config.get(
CONFIG_DEPENDS).asStringList();
465 config.dependModules().removeDuplicates();
467 qdb->setSearchOrder(config.dependModules());
478 std::vector<QByteArray> include_paths{};
483 args.append(Utilities::getInternalIncludePaths(QStringLiteral(
"clang++")));
484#elif defined(Q_OS_LINUX)
485 args.append(Utilities::getInternalIncludePaths(QStringLiteral(
"g++")));
488 for (
const auto &path : std::as_const(args)) {
490 include_paths.push_back(path.toUtf8());
493 include_paths.erase(
std::unique(include_paths.begin(), include_paths.end()),
494 include_paths.end());
497 QList<QByteArray> clang_defines{};
499 const QStringList config_defines{config.get(
CONFIG_DEFINES).asStringList()};
500 for (
const QString &def : config_defines) {
501 if (!def.contains(QChar(
'*'))) {
502 QByteArray tmp(
"-D");
503 tmp.append(def.toUtf8());
504 clang_defines.append(tmp.constData());
513 QDocDatabase::qdocDB(),
514 moduleHeader.isNull() ? project : moduleHeader,
515 Config::instance().getHeaderFiles(),
521 ClangCodeParser clangParser(QDocDatabase::qdocDB(), Config::instance(), include_paths, clang_defines, pch);
525
526
527
528
529
530
538 QStringList sourceList;
540 qCDebug(lcQdoc,
"Reading sourcedirs");
549 std::vector<QString> sources{};
550 for (
const auto &source : sourceList) {
551 if (source.contains(QLatin1String(
"doc/snippets")))
553 sources.emplace_back(source);
556
557
558
559 qCDebug(lcQdoc,
"Reading exampledirs");
560 QStringList exampleQdocList = config.getExampleQdocFiles(excludedDirs, excludedFiles);
561 for (
const auto &example : exampleQdocList) {
562 sources.emplace_back(example);
566
567
568
570 qCInfo(lcQdoc) <<
"Parse source files for" << project;
573 auto headers = config.getHeaderFiles();
577 parseSourceFiles(
std::move(sources), source_file_parser, cpp_code_parser);
580 qCInfo(lcQdoc) <<
"Source files parsed for" << project;
583
584
585
586
587 qCDebug(lcQdoc,
"Resolving stuff prior to generating docs");
591
592
593
594
595
596
597 qCDebug(lcQdoc,
"Generating docs");
598 for (
const auto &format : outputFormats) {
599 auto *generator = Generator::generatorForFormat(format);
601 generator->initializeFormat();
602 generator->generateDocs();
606 .fatal(QStringLiteral(
"QDoc: Unknown output format '%1'").arg(format));
610 qCDebug(lcQdoc,
"Terminating qdoc classes");
614 logStartEndMessage(QLatin1String(
"End"), config);
622 QDir::setCurrent(config.previousCurrentDir());
624 qCDebug(lcQdoc,
"qdoc classes terminated");