qdoc: Added support for modularized example doc
[profile/ivi/qtbase.git] / src / tools / qdoc / main.cpp
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** GNU Lesser General Public License Usage
10 ** This file may be used under the terms of the GNU Lesser General Public
11 ** License version 2.1 as published by the Free Software Foundation and
12 ** appearing in the file LICENSE.LGPL included in the packaging of this
13 ** file. Please review the following information to ensure the GNU Lesser
14 ** General Public License version 2.1 requirements will be met:
15 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
16 **
17 ** In addition, as a special exception, Nokia gives you certain additional
18 ** rights. These rights are described in the Nokia Qt LGPL Exception
19 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
20 **
21 ** GNU General Public License Usage
22 ** Alternatively, this file may be used under the terms of the GNU General
23 ** Public License version 3.0 as published by the Free Software Foundation
24 ** and appearing in the file LICENSE.GPL included in the packaging of this
25 ** file. Please review the following information to ensure the GNU General
26 ** Public License version 3.0 requirements will be met:
27 ** http://www.gnu.org/copyleft/gpl.html.
28 **
29 ** Other Usage
30 ** Alternatively, this file may be used in accordance with the terms and
31 ** conditions contained in a signed written agreement between you and Nokia.
32 **
33 **
34 **
35 **
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41
42 /*
43   main.cpp
44 */
45
46 #include <qglobal.h>
47 #include <stdlib.h>
48 #include "codemarker.h"
49 #include "codeparser.h"
50 #include "config.h"
51 #include "cppcodemarker.h"
52 #include "cppcodeparser.h"
53 #include "ditaxmlgenerator.h"
54 #include "doc.h"
55 #include "htmlgenerator.h"
56 #include "plaincodemarker.h"
57 #include "puredocparser.h"
58 #include "tokenizer.h"
59 #include "tree.h"
60
61 #include "jscodemarker.h"
62 #include "qmlcodemarker.h"
63 #include "qmlcodeparser.h"
64
65 #include <qdatetime.h>
66 #include <qdebug.h>
67
68 #include "qtranslator.h"
69 #ifndef QT_BOOTSTRAPPED
70 #  include "qcoreapplication.h"
71 #endif
72
73 QT_BEGIN_NAMESPACE
74
75 /*
76   The default indent for code is 4.
77   The default value for false is 0.
78   The default supported file extensions are cpp, h, qdoc and qml.
79   The default language is c++.
80   The default output format is html.
81   The default tab size is 8.
82   And those are all the default values for configuration variables.
83  */
84 static const struct {
85     const char *key;
86     const char *value;
87 } defaults[] = {
88     { CONFIG_CODEINDENT, "4" },
89     { CONFIG_FALSEHOODS, "0" },
90     { CONFIG_FILEEXTENSIONS, "*.cpp *.h *.qdoc *.qml"},
91     { CONFIG_LANGUAGE, "Cpp" },
92     { CONFIG_OUTPUTFORMATS, "HTML" },
93     { CONFIG_TABSIZE, "8" },
94     { 0, 0 }
95 };
96
97 bool creationTimeBefore(const QFileInfo &fi1, const QFileInfo &fi2)
98 {
99     return fi1.lastModified() < fi2.lastModified();
100 }
101
102 static bool highlighting = false;
103 static bool showInternal = false;
104 static bool obsoleteLinks = false;
105 static QStringList defines;
106 static QStringList dependModules;
107 static QStringList indexDirs;
108 static QHash<QString, Tree *> trees;
109
110 /*!
111   Print the help message to \c stdout.
112  */
113 static void printHelp()
114 {
115     Location::information(tr("Usage: qdoc [options] file1.qdocconf ...\n"
116                              "Options:\n"
117                              "    -D<name>       "
118                              "Define <name> as a macro while parsing sources\n"
119                              "    -depends       "
120                              "Specify dependant modules\n"
121                              "    -help          "
122                              "Display this information and exit\n"
123                              "    -highlighting  "
124                              "Turn on syntax highlighting (makes qdoc run slower)\n"
125                              "    -indexdir      "
126                              "Specify a directory where QDoc should search for indices to link to\n"
127                              "    -installdir    "
128                              "Specify the directory where the output will be after running \"make install\"\n"
129                              "    -no-examples   "
130                              "Do not generate documentation for examples\n"
131                              "    -obsoletelinks "
132                              "Report links from obsolete items to non-obsolete items\n"
133                              "    -outputdir     "
134                              "Specify output directory, overrides setting in qdocconf file\n"
135                              "    -outputformat  "
136                              "Specify output format, overrides setting in qdocconf file\n"
137                              "    -showinternal  "
138                              "Include content marked internal\n"
139                              "    -version       "
140                              "Display version of qdoc and exit\n") );
141 }
142
143 /*!
144   Prints the qdoc version number to stdout.
145  */
146 static void printVersion()
147 {
148     QString s = tr("qdoc version %1").arg(QT_VERSION_STR);
149     Location::information(s);
150 }
151
152 /*!
153   Processes the qdoc config file \a fileName. This is the
154   controller for all of qdoc.
155  */
156 static void processQdocconfFile(const QString &fileName)
157 {
158 #ifndef QT_NO_TRANSLATION
159     QList<QTranslator *> translators;
160 #endif
161
162     /*
163       The Config instance represents the configuration data for qdoc.
164       All the other classes are initialized with the config. Here we
165       initialize the configuration with some default values.
166      */
167     Config config(tr("qdoc"));
168     int i = 0;
169     while (defaults[i].key) {
170         config.setStringList(defaults[i].key,
171                              QStringList() << defaults[i].value);
172         ++i;
173     }
174     config.setStringList(CONFIG_SYNTAXHIGHLIGHTING, QStringList(highlighting ? "true" : "false"));
175     config.setStringList(CONFIG_SHOWINTERNAL,
176                          QStringList(showInternal ? "true" : "false"));
177     config.setStringList(CONFIG_OBSOLETELINKS,
178                          QStringList(obsoleteLinks ? "true" : "false"));
179
180     /*
181       With the default configuration values in place, load
182       the qdoc configuration file. Note that the configuration
183       file may include other configuration files.
184
185       The Location class keeps track of the current location
186       in the file being processed, mainly for error reporting
187       purposes.
188      */
189     Location::initialize(config);
190     config.load(fileName);
191
192     /*
193       Add the defines to the configuration variables.
194      */
195     QStringList defs = defines + config.getStringList(CONFIG_DEFINES);
196     config.setStringList(CONFIG_DEFINES,defs);
197     Location::terminate();
198
199     QString prevCurrentDir = QDir::currentPath();
200     QString dir = QFileInfo(fileName).path();
201     if (!dir.isEmpty())
202         QDir::setCurrent(dir);
203
204     /*
205       Initialize all the classes and data structures with the
206       qdoc configuration.
207      */
208     Location::initialize(config);
209     Tokenizer::initialize(config);
210     Doc::initialize(config);
211     CodeMarker::initialize(config);
212     CodeParser::initialize(config);
213     Generator::initialize(config);
214
215 #ifndef QT_NO_TRANSLATION
216     /*
217       Load the language translators, if the configuration specifies any.
218      */
219     QStringList fileNames = config.getStringList(CONFIG_TRANSLATORS);
220     QStringList::Iterator fn = fileNames.constBegin();
221     while (fn != fileNames.constEnd()) {
222         QTranslator *translator = new QTranslator(0);
223         if (!translator->load(*fn))
224             config.lastLocation().error(tr("Cannot load translator '%1'")
225                                         .arg(*fn));
226         QCoreApplication::instance()->installTranslator(translator);
227         translators.append(translator);
228         ++fn;
229     }
230 #endif
231
232     //QSet<QString> outputLanguages = config.getStringSet(CONFIG_OUTPUTLANGUAGES);
233
234     /*
235       Get the source language (Cpp) from the configuration
236       and the location in the configuration file where the
237       source language was set.
238      */
239     QString lang = config.getString(CONFIG_LANGUAGE);
240     Location langLocation = config.lastLocation();
241
242     /*
243       Initialize the tree where all the parsed sources will be stored.
244       The tree gets built as the source files are parsed, and then the
245       documentation output is generated by traversing the tree.
246      */
247     Tree *tree = new Tree;
248     tree->setVersion(config.getString(CONFIG_VERSION));
249
250     /*
251       By default, the only output format is HTML.
252      */
253     QSet<QString> outputFormats = config.getOutputFormats();
254     Location outputFormatsLocation = config.lastLocation();
255
256     /*
257       Read some XML indexes containing definitions from other documentation sets.
258      */
259     QStringList indexFiles = config.getStringList(CONFIG_INDEXES);
260
261     dependModules += config.getStringList(CONFIG_DEPENDS);
262
263     if (dependModules.size() > 0) {
264         if (indexDirs.size() > 0) {
265             for (int i = 0; i < indexDirs.size(); i++) {
266                 if (indexDirs[i].startsWith("..")) {
267                     indexDirs[i].prepend(QDir(dir).relativeFilePath(prevCurrentDir));
268                 }
269             }
270             /*
271                 Add all subdirectories of the indexdirs as dependModules when an asterisk is used in
272                 the 'depends' list.
273             */
274             if (dependModules.contains("*")) {
275                 dependModules.removeOne("*");
276                 for (int i = 0; i < indexDirs.size(); i++) {
277                     QDir scanDir = QDir(indexDirs[i]);
278                     scanDir.setFilter(QDir::Dirs | QDir::NoDotAndDotDot);
279                     QFileInfoList dirList = scanDir.entryInfoList();
280                     for (int j = 0; j < dirList.size(); j++) {
281                         if (dirList[j].fileName().toLower() != config.getString(CONFIG_PROJECT).toLower())
282                             dependModules.append(dirList[j].fileName());
283                     }
284                 }
285             }
286             for (int i = 0; i < dependModules.size(); i++) {
287                 QString indexToAdd;
288                 QList<QFileInfo> foundIndices;
289                 for (int j = 0; j < indexDirs.size(); j++) {
290                     QString fileToLookFor = indexDirs[j] + QLatin1Char('/') + dependModules[i] +
291                             QLatin1Char('/') + dependModules[i] + QLatin1String(".index");
292                     if (QFile::exists(fileToLookFor)) {
293                         QFileInfo tempFileInfo(fileToLookFor);
294                         if (!foundIndices.contains(tempFileInfo))
295                             foundIndices.append(tempFileInfo);
296                     }
297                 }
298                 qSort(foundIndices.begin(), foundIndices.end(), creationTimeBefore);
299                 if (foundIndices.size() > 1) {
300                     /*
301                         QDoc should always use the last entry in the multimap when there are
302                         multiple index files for a module, since the last modified file has the
303                         highest UNIX timestamp.
304                     */
305                     qDebug() << "Multiple indices found for dependency:" << dependModules[i] << "\nFound:";
306                     for (int k = 0; k < foundIndices.size(); k++)
307                         qDebug() << foundIndices[k].absoluteFilePath();
308                     qDebug() << "Using" << foundIndices[foundIndices.size() - 1].absoluteFilePath()
309                             << "as index for" << dependModules[i];
310                     indexToAdd = foundIndices[foundIndices.size() - 1].absoluteFilePath();
311                 }
312                 else if (foundIndices.size() == 1) {
313                     indexToAdd = foundIndices[0].absoluteFilePath();
314                 }
315                 else {
316                     qDebug() << "No indices for" << dependModules[i] <<
317                                 "could be found in the specified index directories.";
318                 }
319                 if (!indexToAdd.isEmpty() && !indexFiles.contains(indexToAdd))
320                     indexFiles << indexToAdd;
321             }
322         }
323         else {
324             qDebug() << "Dependant modules specified, but no index directories or install directory were set."
325                      << "There will probably be errors for missing links.";
326         }
327     }
328     tree->readIndexes(indexFiles);
329
330     QSet<QString> excludedDirs;
331     QSet<QString> excludedFiles;
332     QStringList headerList;
333     QStringList sourceList;
334     QStringList excludedDirsList;
335     QStringList excludedFilesList;
336
337     excludedDirsList = config.getCleanPathList(CONFIG_EXCLUDEDIRS);
338     foreach (const QString &excludeDir, excludedDirsList) {
339         QString p = QDir::fromNativeSeparators(excludeDir);
340         excludedDirs.insert(p);
341     }
342
343     excludedFilesList = config.getCleanPathList(CONFIG_EXCLUDEFILES);
344     foreach (const QString& excludeFile, excludedFilesList) {
345         QString p = QDir::fromNativeSeparators(excludeFile);
346         excludedFiles.insert(p);
347     }
348
349     headerList = config.getAllFiles(CONFIG_HEADERS,CONFIG_HEADERDIRS,excludedDirs,excludedFiles);
350     QMap<QString,QString> headers;
351     QMultiMap<QString,QString> headerFileNames;
352     for (int i=0; i<headerList.size(); ++i) {
353         headers.insert(headerList[i],headerList[i]);
354         QString t = headerList[i].mid(headerList[i].lastIndexOf('/')+1);
355         headerFileNames.insert(t,t);
356     }
357
358     sourceList = config.getAllFiles(CONFIG_SOURCES,CONFIG_SOURCEDIRS,excludedDirs,excludedFiles);
359     QMap<QString,QString> sources;
360     QMultiMap<QString,QString> sourceFileNames;
361     for (int i=0; i<sourceList.size(); ++i) {
362         sources.insert(sourceList[i],sourceList[i]);
363         QString t = sourceList[i].mid(sourceList[i].lastIndexOf('/')+1);
364         sourceFileNames.insert(t,t);
365     }
366     /*
367       Find all the qdoc files in the example dirs, and add
368       them to the source files to be parsed.
369      */
370     QStringList exampleQdocList = config.getExampleQdocFiles();
371     for (int i=0; i<exampleQdocList.size(); ++i) {
372         sources.insert(exampleQdocList[i],exampleQdocList[i]);
373         QString t = exampleQdocList[i].mid(exampleQdocList[i].lastIndexOf('/')+1);
374         sourceFileNames.insert(t,t);
375     }
376
377     /*
378       Parse each header file in the set using the appropriate parser and add it
379       to the big tree.
380      */
381     QSet<CodeParser *> usedParsers;
382
383     int parsed = 0;
384     QMap<QString,QString>::ConstIterator h = headers.constBegin();
385     while (h != headers.constEnd()) {
386         CodeParser *codeParser = CodeParser::parserForHeaderFile(h.key());
387         if (codeParser) {
388             ++parsed;
389             codeParser->parseHeaderFile(config.location(), h.key(), tree);
390             usedParsers.insert(codeParser);
391         }
392         ++h;
393     }
394
395     foreach (CodeParser *codeParser, usedParsers)
396         codeParser->doneParsingHeaderFiles(tree);
397
398     usedParsers.clear();
399     /*
400       Parse each source text file in the set using the appropriate parser and
401       add it to the big tree.
402      */
403     parsed = 0;
404     QMap<QString,QString>::ConstIterator s = sources.constBegin();
405     while (s != sources.constEnd()) {
406         CodeParser *codeParser = CodeParser::parserForSourceFile(s.key());
407         if (codeParser) {
408             ++parsed;
409             codeParser->parseSourceFile(config.location(), s.key(), tree);
410             usedParsers.insert(codeParser);
411         }
412         ++s;
413     }
414
415     foreach (CodeParser *codeParser, usedParsers)
416         codeParser->doneParsingSourceFiles(tree);
417
418     /*
419       Now the big tree has been built from all the header and
420       source files. Resolve all the class names, function names,
421       targets, URLs, links, and other stuff that needs resolving.
422      */
423     tree->resolveGroups();
424     tree->resolveTargets(tree->root());
425     tree->resolveCppToQmlLinks();
426     tree->resolveQmlInheritance();
427
428     /*
429       The tree is built and all the stuff that needed resolving
430       has been resolved. Now traverse the tree and generate the
431       documentation output. More than one output format can be
432       requested. The tree is traversed for each one.
433      */
434     QSet<QString>::ConstIterator of = outputFormats.constBegin();
435     while (of != outputFormats.constEnd()) {
436         Generator* generator = Generator::generatorForFormat(*of);
437         if (generator == 0)
438             outputFormatsLocation.fatal(tr("Unknown output format '%1'").arg(*of));
439         generator->generateTree(tree);
440         ++of;
441     }
442
443     /*
444       Generate the XML tag file, if it was requested.
445      */
446     QString tagFile = config.getString(CONFIG_TAGFILE);
447     if (!tagFile.isEmpty()) {
448         tree->generateTagFile(tagFile);
449     }
450
451     //Generator::writeOutFileNames();
452
453     tree->setVersion(QString());
454     Generator::terminate();
455     CodeParser::terminate();
456     CodeMarker::terminate();
457     Doc::terminate();
458     Tokenizer::terminate();
459     Location::terminate();
460     QDir::setCurrent(prevCurrentDir);
461
462 #ifndef QT_NO_TRANSLATION
463     qDeleteAll(translators);
464 #endif
465 #ifdef DEBUG_SHUTDOWN_CRASH
466     qDebug() << "main(): Delete tree";
467 #endif
468     delete tree;
469 #ifdef DEBUG_SHUTDOWN_CRASH
470     qDebug() << "main(): Tree deleted";
471 #endif
472 }
473
474 QT_END_NAMESPACE
475
476 int main(int argc, char **argv)
477 {
478     QT_USE_NAMESPACE
479
480 #ifndef QT_BOOTSTRAPPED
481     QCoreApplication app(argc, argv);
482 #endif
483
484     /*
485       Create code parsers for the languages to be parsed,
486       and create a tree for C++.
487      */
488     CppCodeParser cppParser;
489     QmlCodeParser qmlParser;
490     PureDocParser docParser;
491
492     /*
493       Create code markers for plain text, C++,
494       javascript, and QML.
495      */
496     PlainCodeMarker plainMarker;
497     CppCodeMarker cppMarker;
498     JsCodeMarker jsMarker;
499     QmlCodeMarker qmlMarker;
500
501     HtmlGenerator htmlGenerator;
502     DitaXmlGenerator ditaxmlGenerator;
503
504     QStringList qdocFiles;
505     QString opt;
506     int i = 1;
507
508     while (i < argc) {
509         opt = argv[i++];
510
511         if (opt == "-help") {
512             printHelp();
513             return EXIT_SUCCESS;
514         }
515         else if (opt == "-version") {
516             printVersion();
517             return EXIT_SUCCESS;
518         }
519         else if (opt == "--") {
520             while (i < argc)
521                 qdocFiles.append(argv[i++]);
522         }
523         else if (opt.startsWith("-D")) {
524             QString define = opt.mid(2);
525             defines += define;
526         }
527         else if (opt == "-depends") {
528             dependModules += argv[i];
529             i++;
530         }
531         else if (opt == "-highlighting") {
532             highlighting = true;
533         }
534         else if (opt == "-showinternal") {
535             showInternal = true;
536         }
537         else if (opt == "-no-examples") {
538             Config::generateExamples = false;
539         }
540         else if (opt == "-indexdir") {
541             if (QFile::exists(argv[i])) {
542                 indexDirs += argv[i];
543             }
544             else {
545                 qDebug() << "Cannot find index directory" << argv[i];
546             }
547             i++;
548         }
549         else if (opt == "-installdir") {
550             Config::installDir = argv[i];
551             indexDirs += argv[i];
552             i++;
553         }
554         else if (opt == "-obsoletelinks") {
555             obsoleteLinks = true;
556         }
557         else if (opt == "-outputdir") {
558             Config::overrideOutputDir = argv[i];
559             i++;
560         }
561         else if (opt == "-outputformat") {
562             Config::overrideOutputFormats.insert(argv[i]);
563             i++;
564         }
565         else {
566             qdocFiles.append(opt);
567         }
568     }
569
570     if (qdocFiles.isEmpty()) {
571         printHelp();
572         return EXIT_FAILURE;
573     }
574
575     /*
576       Main loop.
577      */
578     foreach (const QString &qf, qdocFiles) {
579         //qDebug() << "PROCESSING:" << qf;
580         processQdocconfFile(qf);
581     }
582
583     qDeleteAll(trees);
584     return EXIT_SUCCESS;
585 }
586