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