1// Copyright (C) 2016 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
3
4#include "qdocgenerator.h"
5
6#include <QtCore/qdir.h>
7
8#include <iostream>
9
10using namespace Qt::Literals::StringLiterals;
11
12namespace QDocGenerator {
13
14// See definition of idstring and licenseid in https://spdx.org/spdx-specification-21-web-version
15static bool isSpdxLicenseId(const QString &str) {
16 if (str.isEmpty())
17 return false;
18 for (auto iter(str.cbegin()); iter != str.cend(); ++iter) {
19 const QChar c = *iter;
20 if (!((c >= QLatin1Char('A') && c <= QLatin1Char('Z'))
21 || (c >= QLatin1Char('a') && c <= QLatin1Char('z'))
22 || (c >= QLatin1Char('0') && c <= QLatin1Char('9'))
23 || (c == QLatin1Char('-')) || (c == QLatin1Char('.'))))
24 return false;
25 }
26 return true;
27}
28
29static QString languageJoin(const QStringList &list)
30{
31 QString result;
32 for (int i = 0; i < list.size(); ++i) {
33 QString delimiter = u", "_s;
34 if (i == list.size() - 1) // last item
35 delimiter.clear();
36 else if (list.size() == 2)
37 delimiter = u" and "_s;
38 else if (list.size() > 2 && i == list.size() - 2)
39 delimiter = u", and "_s; // oxford comma
40 result += list[i] + delimiter;
41 }
42
43 return result;
44}
45
46// Embed source code between \badcode ... \endbadcode
47// Also, avoid '*/' breaking qdoc by passing the star as argument
48static void sourceCode(QTextStream &out, const QString &src)
49{
50 out << "\\badcode *\n";
51 out << QString(src).replace(before: u"*/"_s, after: u"\\1/"_s);
52 out << "\n\\endcode\n\n";
53}
54
55static void generate(QTextStream &out, const Package &package, const QDir &baseDir)
56{
57 out << "/*!\n\n";
58 for (const QString &part : package.qtParts) {
59 out << "\\ingroup attributions-" << package.qdocModule << "-" << part << "\n";
60 out << "\\ingroup attributions-" << part << "\n";
61 }
62
63 if (package.qtParts.contains(str: "libs"_L1)) {
64 // show up in xxx-index.html page of module
65 out << "\\ingroup attributions-" << package.qdocModule << "\n";
66 // include in '\generatelist annotatedattributions'
67 out << "\\page " << package.qdocModule << "-attribution-" << package.id
68 << ".html\n";
69 out << "\\attribution\n";
70 } else {
71 out << "\\page " << package.qdocModule << "-attribution-" << package.id
72 << ".html \n";
73 }
74
75 out << "\\target " << package.id << "\n\n";
76 out << "\\title " << package.name;
77 if (!package.version.isEmpty())
78 out << ", version " << package.version;
79 out << "\n\n\\brief " << package.license << "\n\n";
80
81 if (!package.description.isEmpty())
82 out << package.description << "\n\n";
83
84 if (!package.qtUsage.isEmpty())
85 out << package.qtUsage << "\n\n";
86
87 QStringList sourcePaths;
88 if (package.files.isEmpty()) {
89 sourcePaths << baseDir.relativeFilePath(fileName: package.path);
90 } else {
91 const QDir packageDir(package.path);
92 for (const QString &filePath: package.files) {
93 const QString absolutePath = packageDir.absoluteFilePath(fileName: filePath);
94 sourcePaths << baseDir.relativeFilePath(fileName: absolutePath);
95 }
96 }
97
98 out << "The sources can be found in " << languageJoin(list: sourcePaths) << ".\n\n";
99
100 const bool hasPackageVersion = !package.version.isEmpty();
101 const bool hasPackageDownloadLocation = !package.downloadLocation.isEmpty();
102 if (!package.homepage.isEmpty()) {
103 out << "\\l{" << package.homepage << "}{Project Homepage}";
104 if (hasPackageVersion)
105 out << ", ";
106 }
107 if (hasPackageVersion) {
108 out << "upstream version: ";
109 if (hasPackageDownloadLocation)
110 out << "\\l{" << package.downloadLocation << "}{";
111 out << package.version;
112 if (hasPackageDownloadLocation)
113 out << "}";
114 }
115
116 out << "\n\n";
117
118 QString copyright;
119 if (!package.copyright.isEmpty())
120 copyright = package.copyright;
121 else if (!package.copyrightFileContents.isEmpty())
122 copyright = package.copyrightFileContents;
123
124 if (!copyright.isEmpty()) {
125 out << "\n";
126 sourceCode(out, src: copyright);
127 }
128
129 if (isSpdxLicenseId(str: package.licenseId) && package.licenseId != "NONE"_L1) {
130 out << "\\l{https://spdx.org/licenses/" << package.licenseId << ".html}"
131 << "{" << package.license << "}.\n\n";
132 } else if (package.licenseId.startsWith(s: "urn:dje:license:"_L1)) {
133 out << "\\l{https://enterprise.dejacode.com/licenses/public/" << package.licenseId.mid(position: 16)
134 << "/}{" << package.license << "}.\n\n";
135 } else {
136 out << package.license << ".\n\n";
137 }
138
139 foreach (const QString &license, package.licenseFilesContents)
140 sourceCode(out, src: license);
141
142 out << "*/\n";
143}
144
145void generate(QTextStream &out, const QList<Package> &packages, const QString &baseDirectory,
146 LogLevel logLevel)
147{
148 if (logLevel == VerboseLog)
149 std::cerr << qPrintable(tr("Generating qdoc file...")) << std::endl;
150
151 QDir baseDir(baseDirectory);
152 for (const Package &package : packages)
153 generate(out, package, baseDir);
154}
155
156} // namespace QDocGenerator
157

source code of qttools/src/qtattributionsscanner/qdocgenerator.cpp