summaryrefslogtreecommitdiffstats
path: root/src/doxygen.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/doxygen.cpp')
-rw-r--r--src/doxygen.cpp638
1 files changed, 370 insertions, 268 deletions
diff --git a/src/doxygen.cpp b/src/doxygen.cpp
index 1824ceb..dab7549 100644
--- a/src/doxygen.cpp
+++ b/src/doxygen.cpp
@@ -104,6 +104,7 @@
#include "plantuml.h"
#include "stlsupport.h"
#include "threadpool.h"
+#include "clangparser.h"
// provided by the generated file resources.cpp
extern void initResources();
@@ -158,9 +159,9 @@ int Doxygen::subpageNestingLevel = 0;
bool Doxygen::userComments = FALSE;
QCString Doxygen::spaces;
bool Doxygen::generatingXmlOutput = FALSE;
-bool Doxygen::markdownSupport = TRUE;
GenericsSDict *Doxygen::genericsDict;
-DefineList Doxygen::macroDefinitions;
+DefinesPerFileList Doxygen::macroDefinitions;
+bool Doxygen::clangAssistedParsing = FALSE;
// locally accessible globals
static std::unordered_map< std::string, const Entry* > g_classEntries;
@@ -628,7 +629,7 @@ static void addIncludeFile(ClassDef *cd,FileDef *ifd,const Entry *root)
iName=fd->name();
}
}
- else if (!Config_getList(STRIP_FROM_INC_PATH).isEmpty())
+ else if (!Config_getList(STRIP_FROM_INC_PATH).empty())
{
iName=stripFromIncludePath(fd->absFilePath());
}
@@ -3443,7 +3444,7 @@ static void buildFunctionList(const Entry *root)
{
// merge argument lists
ArgumentList mergedArgList = root->argList;
- mergeArguments(mdAl,mergedArgList,!root->proto);
+ mergeArguments(mdAl,mergedArgList,!root->doc.isEmpty());
// merge documentation
if (md->documentation().isEmpty() && !root->doc.isEmpty())
{
@@ -4952,7 +4953,7 @@ static void addMemberDocs(const Entry *root,
{
ArgumentList mergedAl = *al;
//printf("merging arguments (1) docs=%d\n",root->doc.isEmpty());
- mergeArguments(mdAl,mergedAl,!root->proto);
+ mergeArguments(mdAl,mergedAl,!root->doc.isEmpty());
}
else
{
@@ -4965,7 +4966,7 @@ static void addMemberDocs(const Entry *root,
{
//printf("merging arguments (2)\n");
ArgumentList mergedArgList = root->argList;
- mergeArguments(mdAl,mergedArgList,!root->proto);
+ mergeArguments(mdAl,mergedArgList,!root->doc.isEmpty());
}
}
if (over_load) // the \overload keyword was used
@@ -5645,7 +5646,7 @@ static void addMemberFunction(const Entry *root,
root->protection,root->stat,root->virt,spec,relates);
return;
}
- if (md->argsString()==argListToString(root->argList,TRUE,FALSE))
+ if (md->argsString()==argListToString(root->argList,FALSE,FALSE))
{ // exact argument list match -> remember
ucd = ecd = ccd;
umd = emd = cmd;
@@ -7478,18 +7479,18 @@ static void generateFileSources()
if (!Doxygen::inputNameLinkedMap->empty())
{
#if USE_LIBCLANG
- static bool clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
- if (clangAssistedParsing)
+ if (Doxygen::clangAssistedParsing)
{
- QDict<void> g_processedFiles(10007);
+ StringUnorderedSet processedFiles;
// create a dictionary with files to process
- QDict<void> g_filesToProcess(10007);
+ StringUnorderedSet filesToProcess;
+
for (const auto &fn : *Doxygen::inputNameLinkedMap)
{
for (const auto &fd : *fn)
{
- g_filesToProcess.insert(fd->absFilePath(),(void*)0x8);
+ filesToProcess.insert(fd->absFilePath().str());
}
}
// process source files (and their include dependencies)
@@ -7499,51 +7500,49 @@ static void generateFileSources()
{
if (fd->isSource() && !fd->isReference())
{
- QStrList filesInSameTu;
- fd->getAllIncludeFilesRecursively(filesInSameTu);
- fd->startParsing();
+ auto clangParser = ClangParser::instance()->createTUParser(fd.get());
if (fd->generateSourceFile() && !g_useOutputTemplate) // sources need to be shown in the output
{
msg("Generating code for file %s...\n",fd->docName().data());
- fd->writeSource(*g_outputList,FALSE,filesInSameTu);
+ clangParser->parse();
+ fd->writeSource(*g_outputList,clangParser.get());
}
else if (!fd->isReference() && Doxygen::parseSourcesNeeded)
// we needed to parse the sources even if we do not show them
{
msg("Parsing code for file %s...\n",fd->docName().data());
- fd->parseSource(FALSE,filesInSameTu);
+ clangParser->parse();
+ fd->parseSource(clangParser.get());
}
- char *incFile = filesInSameTu.first();
- while (incFile && g_filesToProcess.find(incFile))
+ for (auto incFile : clangParser->filesInSameTU())
{
- if (fd->absFilePath()!=incFile && !g_processedFiles.find(incFile))
+ if (filesToProcess.find(incFile)!=filesToProcess.end() && // part of input
+ fd->absFilePath()!=incFile && // not same file
+ processedFiles.find(incFile)==processedFiles.end()) // not yet marked as processed
{
- QStrList moreFiles;
+ StringVector moreFiles;
bool ambig;
- FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile,ambig);
+ FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile.c_str(),ambig);
if (ifd && !ifd->isReference())
{
if (ifd->generateSourceFile() && !g_useOutputTemplate) // sources need to be shown in the output
{
msg(" Generating code for file %s...\n",ifd->docName().data());
- ifd->writeSource(*g_outputList,TRUE,moreFiles);
-
+ ifd->writeSource(*g_outputList,clangParser.get());
}
else if (!ifd->isReference() && Doxygen::parseSourcesNeeded)
// we needed to parse the sources even if we do not show them
{
msg(" Parsing code for file %s...\n",ifd->docName().data());
- ifd->parseSource(TRUE,moreFiles);
+ ifd->parseSource(clangParser.get());
}
- g_processedFiles.insert(incFile,(void*)0x8);
+ processedFiles.insert(incFile);
}
}
- incFile = filesInSameTu.next();
}
- fd->finishParsing();
- g_processedFiles.insert(fd->absFilePath(),(void*)0x8);
+ processedFiles.insert(fd->absFilePath().str());
}
}
}
@@ -7552,23 +7551,23 @@ static void generateFileSources()
{
for (const auto &fd : *fn)
{
- if (!g_processedFiles.find(fd->absFilePath())) // not yet processed
+ if (processedFiles.find(fd->absFilePath().str())==processedFiles.end()) // not yet processed
{
- QStrList filesInSameTu;
- fd->startParsing();
+ auto clangParser = ClangParser::instance()->createTUParser(fd.get());
if (fd->generateSourceFile() && !Htags::useHtags && !g_useOutputTemplate) // sources need to be shown in the output
{
msg("Generating code for file %s...\n",fd->docName().data());
- fd->writeSource(*g_outputList,FALSE,filesInSameTu);
+ clangParser->parse();
+ fd->writeSource(*g_outputList,clangParser.get());
}
else if (!fd->isReference() && Doxygen::parseSourcesNeeded)
// we needed to parse the sources even if we do not show them
{
msg("Parsing code for file %s...\n",fd->docName().data());
- fd->parseSource(FALSE,filesInSameTu);
+ clangParser->parse();
+ fd->parseSource(clangParser.get());
}
- fd->finishParsing();
}
}
}
@@ -7580,21 +7579,20 @@ static void generateFileSources()
{
for (const auto &fd : *fn)
{
- QStrList filesInSameTu;
- fd->startParsing();
+ StringVector filesInSameTu;
+ fd->getAllIncludeFilesRecursively(filesInSameTu);
if (fd->generateSourceFile() && !Htags::useHtags && !g_useOutputTemplate) // sources need to be shown in the output
{
msg("Generating code for file %s...\n",fd->docName().data());
- fd->writeSource(*g_outputList,FALSE,filesInSameTu);
+ fd->writeSource(*g_outputList,nullptr);
}
else if (!fd->isReference() && Doxygen::parseSourcesNeeded)
// we needed to parse the sources even if we do not show them
{
msg("Parsing code for file %s...\n",fd->docName().data());
- fd->parseSource(FALSE,filesInSameTu);
+ fd->parseSource(nullptr);
}
- fd->finishParsing();
}
}
}
@@ -7702,28 +7700,35 @@ static void addSourceReferences()
// add the macro definitions found during preprocessing as file members
static void buildDefineList()
{
- for (const auto &def : Doxygen::macroDefinitions)
+ for (const auto &s : g_inputFiles)
{
- std::unique_ptr<MemberDef> md { createMemberDef(
- def->fileName,def->lineNr,def->columnNr,
- "#define",def->name,def->args,0,
- Public,Normal,FALSE,Member,MemberType_Define,
- ArgumentList(),ArgumentList(),"") };
-
- if (!def->args.isEmpty())
+ auto it = Doxygen::macroDefinitions.find(s);
+ if (it!=Doxygen::macroDefinitions.end())
{
- md->moveArgumentList(stringToArgumentList(SrcLangExt_Cpp, def->args));
- }
- md->setInitializer(def->definition);
- md->setFileDef(def->fileDef);
- md->setDefinition("#define "+def->name);
+ for (const auto &def : it->second)
+ {
+ std::unique_ptr<MemberDef> md { createMemberDef(
+ def->fileName,def->lineNr,def->columnNr,
+ "#define",def->name,def->args,0,
+ Public,Normal,FALSE,Member,MemberType_Define,
+ ArgumentList(),ArgumentList(),"") };
- MemberName *mn=Doxygen::functionNameLinkedMap->add(def->name);
- if (def->fileDef)
- {
- def->fileDef->insertMember(md.get());
+ if (!def->args.isEmpty())
+ {
+ md->moveArgumentList(stringToArgumentList(SrcLangExt_Cpp, def->args));
+ }
+ md->setInitializer(def->definition);
+ md->setFileDef(def->fileDef);
+ md->setDefinition("#define "+def->name);
+
+ MemberName *mn=Doxygen::functionNameLinkedMap->add(def->name);
+ if (def->fileDef)
+ {
+ def->fileDef->insertMember(md.get());
+ }
+ mn->push_back(std::move(md));
+ }
}
- mn->push_back(std::move(md));
}
}
@@ -8632,7 +8637,10 @@ static void generateExampleDocs()
pd->documentation()+"\n\n\\include"+lineNoOptStr+" "+pd->name(), // docs
TRUE, // index words
TRUE, // is example
- pd->name()
+ pd->name(),
+ FALSE,
+ FALSE,
+ Config_getBool(MARKDOWN_SUPPORT)
);
endFile(*g_outputList); // contains g_outputList->endContents()
}
@@ -8856,10 +8864,10 @@ static void readTagFile(const std::shared_ptr<Entry> &root,const char *tl)
//----------------------------------------------------------------------------
static void copyLatexStyleSheet()
{
- QStrList latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
- for (uint i=0; i<latexExtraStyleSheet.count(); ++i)
+ const StringVector &latexExtraStyleSheet = Config_getList(LATEX_EXTRA_STYLESHEET);
+ for (const auto &sheet : latexExtraStyleSheet)
{
- QCString fileName(latexExtraStyleSheet.at(i));
+ QCString fileName = sheet.c_str();
if (!fileName.isEmpty())
{
QFileInfo fi(fileName);
@@ -8883,14 +8891,14 @@ static void copyLatexStyleSheet()
//----------------------------------------------------------------------------
static void copyStyleSheet()
{
- QCString &htmlStyleSheet = Config_getString(HTML_STYLESHEET);
+ QCString htmlStyleSheet = Config_getString(HTML_STYLESHEET);
if (!htmlStyleSheet.isEmpty())
{
QFileInfo fi(htmlStyleSheet);
if (!fi.exists())
{
err("Style sheet '%s' specified by HTML_STYLESHEET does not exist!\n",htmlStyleSheet.data());
- htmlStyleSheet.resize(0); // revert to the default
+ htmlStyleSheet = Config_updateString(HTML_STYLESHEET,""); // revert to the default
}
else
{
@@ -8898,10 +8906,10 @@ static void copyStyleSheet()
copyFile(htmlStyleSheet,destFileName);
}
}
- QStrList htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
- for (uint i=0; i<htmlExtraStyleSheet.count(); ++i)
+ const StringVector &htmlExtraStyleSheet = Config_getList(HTML_EXTRA_STYLESHEET);
+ for (const auto &sheet : htmlExtraStyleSheet)
{
- QCString fileName(htmlExtraStyleSheet.at(i));
+ QCString fileName = sheet.c_str();
if (!fileName.isEmpty())
{
QFileInfo fi(fileName);
@@ -8924,14 +8932,14 @@ static void copyStyleSheet()
static void copyLogo(const QCString &outputOption)
{
- QCString &projectLogo = Config_getString(PROJECT_LOGO);
+ QCString projectLogo = Config_getString(PROJECT_LOGO);
if (!projectLogo.isEmpty())
{
QFileInfo fi(projectLogo);
if (!fi.exists())
{
err("Project logo '%s' specified by PROJECT_LOGO does not exist!\n",projectLogo.data());
- projectLogo.resize(0); // revert to the default
+ projectLogo = Config_updateString(PROJECT_LOGO,""); // revert to the default
}
else
{
@@ -8942,13 +8950,11 @@ static void copyLogo(const QCString &outputOption)
}
}
-static void copyExtraFiles(QStrList files,const QCString &filesOption,const QCString &outputOption)
+static void copyExtraFiles(const StringVector &files,const QCString &filesOption,const QCString &outputOption)
{
- uint i;
- for (i=0; i<files.count(); ++i)
+ for (const auto &file : files)
{
- QCString fileName(files.at(i));
-
+ QCString fileName = file.c_str();
if (!fileName.isEmpty())
{
QFileInfo fi(fileName);
@@ -9054,13 +9060,8 @@ static std::unique_ptr<OutlineParserInterface> getParserForFile(const char *fn)
static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
FileDef *fd,const char *fn,
- bool sameTu,QStrList &filesInSameTu)
+ ClangTUParser *clangParser,bool newTU)
{
-#if USE_LIBCLANG
- static bool clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
-#else
- static bool clangAssistedParsing = FALSE;
-#endif
QCString fileName=fn;
QCString extension;
int ei = fileName.findRev('.');
@@ -9080,12 +9081,10 @@ static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
parser.needsPreprocessing(extension))
{
Preprocessor preprocessor;
- QStrList &includePath = Config_getList(INCLUDE_PATH);
- char *s=includePath.first();
- while (s)
+ const StringVector &includePath = Config_getList(INCLUDE_PATH);
+ for (const auto &s : includePath)
{
- preprocessor.addSearchDir(QFileInfo(s).absFilePath().utf8());
- s=includePath.next();
+ preprocessor.addSearchDir(QFileInfo(s.c_str()).absFilePath().utf8());
}
BufStr inBuf(fi.size()+4096);
msg("Preprocessing %s...\n",fn);
@@ -9109,35 +9108,41 @@ static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser,
convBuf.addChar('\0');
- if (clangAssistedParsing && !sameTu)
- {
- fd->getAllIncludeFilesRecursively(filesInSameTu);
- }
-
std::shared_ptr<Entry> fileRoot = std::make_shared<Entry>();
// use language parse to parse the file
- parser.parseInput(fileName,convBuf.data(),fileRoot,sameTu,filesInSameTu);
+ if (clangParser)
+ {
+ if (newTU) clangParser->parse();
+ clangParser->switchToFile(fd);
+ }
+ parser.parseInput(fileName,convBuf.data(),fileRoot,clangParser);
fileRoot->setFileDef(fd);
return fileRoot;
}
+#if MULTITHREADED_INPUT
+
//! parse the list of input files
static void parseFiles(const std::shared_ptr<Entry> &root)
{
#if USE_LIBCLANG
- static bool clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
- if (clangAssistedParsing)
+ if (Doxygen::clangAssistedParsing)
{
- QDict<void> g_processedFiles(10007);
+ StringUnorderedSet processedFiles;
// create a dictionary with files to process
- QDict<void> g_filesToProcess(10007);
+ StringUnorderedSet filesToProcess;
for (const auto &s : g_inputFiles)
{
- g_filesToProcess.insert(s.c_str(),(void*)0x8);
+ filesToProcess.insert(s);
}
+ std::mutex processedFilesLock;
// process source files (and their include dependencies)
+ std::size_t numThreads = std::thread::hardware_concurrency();
+ msg("Processing input using %lu threads.\n",numThreads);
+ ThreadPool threadPool(numThreads);
+ std::vector< std::future< std::vector< std::shared_ptr<Entry> > > > results;
for (const auto &s : g_inputFiles)
{
bool ambig;
@@ -9145,70 +9150,89 @@ static void parseFiles(const std::shared_ptr<Entry> &root)
ASSERT(fd!=0);
if (fd->isSource() && !fd->isReference()) // this is a source file
{
- QStrList filesInSameTu;
- std::unique_ptr<OutlineParserInterface> parser { getParserForFile(s.c_str()) };
- parser->startTranslationUnit(s.c_str());
- std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,s.c_str(),FALSE,filesInSameTu);
- root->moveToSubEntryAndKeep(fileRoot);
- //printf(" got %d extra files in tu\n",filesInSameTu.count());
-
- // Now process any include files in the same translation unit
- // first. When libclang is used this is much more efficient.
- char *incFile = filesInSameTu.first();
- while (incFile && g_filesToProcess.find(incFile))
- {
- if (qstrcmp(incFile,s.c_str()) && !g_processedFiles.find(incFile))
+ // lambda representing the work to executed by a thread
+ auto processFile = [s,&filesToProcess,&processedFilesLock,&processedFiles]() {
+ bool ambig;
+ std::vector< std::shared_ptr<Entry> > roots;
+ FileDef *fd = findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
+ auto clangParser = ClangParser::instance()->createTUParser(fd);
+ auto parser = getParserForFile(s.c_str());
+ auto fileRoot { parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true) };
+ roots.push_back(fileRoot);
+
+ // Now process any include files in the same translation unit
+ // first. When libclang is used this is much more efficient.
+ for (auto incFile : clangParser->filesInSameTU())
{
- FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile,ambig);
- if (ifd && !ifd->isReference())
+ if (filesToProcess.find(incFile)!=filesToProcess.end())
{
- QStrList moreFiles;
- //printf(" Processing %s in same translation unit as %s\n",incFile,s->c_str());
- fileRoot = parseFile(*parser.get(),ifd,incFile,TRUE,moreFiles);
- root->moveToSubEntryAndKeep(fileRoot);
- g_processedFiles.insert(incFile,(void*)0x8);
+ bool needsToBeProcessed;
+ {
+ std::lock_guard<std::mutex> lock(processedFilesLock);
+ needsToBeProcessed = processedFiles.find(incFile)==processedFiles.end();
+ if (needsToBeProcessed) processedFiles.insert(incFile);
+ }
+ if (incFile!=s && needsToBeProcessed)
+ {
+ FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile.c_str(),ambig);
+ if (ifd && !ifd->isReference())
+ {
+ //printf(" Processing %s in same translation unit as %s\n",incFile,s->c_str());
+ fileRoot = parseFile(*parser.get(),ifd,incFile.c_str(),clangParser.get(),false);
+ roots.push_back(fileRoot);
+ }
+ }
}
}
- incFile = filesInSameTu.next();
- }
- parser->finishTranslationUnit();
- g_processedFiles.insert(s.c_str(),(void*)0x8);
+ return roots;
+ };
+ // dispatch the work and collect the future results
+ results.emplace_back(threadPool.queue(processFile));
+ }
+ }
+ // synchronise with the Entry result lists produced and add them to the root
+ for (auto &f : results)
+ {
+ auto l = f.get();
+ for (auto &e : l)
+ {
+ root->moveToSubEntryAndKeep(e);
}
}
// process remaining files
+ results.clear();
for (const auto &s : g_inputFiles)
{
- if (!g_processedFiles.find(s.c_str())) // not yet processed
+ if (processedFiles.find(s)==processedFiles.end()) // not yet processed
{
- bool ambig;
- QStrList filesInSameTu;
- FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
- ASSERT(fd!=0);
- std::unique_ptr<OutlineParserInterface> parser { getParserForFile(s.c_str()) };
- parser->startTranslationUnit(s.c_str());
- std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,s.c_str(),FALSE,filesInSameTu);
- root->moveToSubEntryAndKeep(fileRoot);
- parser->finishTranslationUnit();
- g_processedFiles.insert(s.c_str(),(void*)0x8);
+ // lambda representing the work to executed by a thread
+ auto processFile = [s]() {
+ bool ambig;
+ std::vector< std::shared_ptr<Entry> > roots;
+ FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
+ auto clangParser = ClangParser::instance()->createTUParser(fd);
+ auto parser { getParserForFile(s.c_str()) };
+ auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
+ roots.push_back(fileRoot);
+ return roots;
+ };
+ // dispatch the work and collect the future results
+ results.emplace_back(threadPool.queue(processFile));
+ }
+ }
+ // synchronise with the Entry result lists produced and add them to the root
+ for (auto &f : results)
+ {
+ auto l = f.get();
+ for (auto &e : l)
+ {
+ root->moveToSubEntryAndKeep(e);
}
}
}
else // normal processing
#endif
{
-#if !MULTITHREADED_INPUT
- for (const auto &s : g_inputFiles)
- {
- bool ambig;
- QStrList filesInSameTu;
- FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
- ASSERT(fd!=0);
- std::unique_ptr<OutlineParserInterface> parser { getParserForFile(s.c_str()) };
- parser->startTranslationUnit(s.c_str());
- std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,s.c_str(),FALSE,filesInSameTu);
- root->moveToSubEntryAndKeep(fileRoot);
- }
-#else
std::size_t numThreads = std::thread::hardware_concurrency();
msg("Processing input using %lu threads.\n",numThreads);
ThreadPool threadPool(numThreads);
@@ -9218,12 +9242,10 @@ static void parseFiles(const std::shared_ptr<Entry> &root)
// lambda representing the work to executed by a thread
auto processFile = [s]() {
bool ambig;
- QStrList filesInSameTu;
FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
- ASSERT(fd!=0);
- std::unique_ptr<OutlineParserInterface> parser { getParserForFile(s.c_str()) };
- parser->startTranslationUnit(s.c_str());
- std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,s.c_str(),FALSE,filesInSameTu);
+ auto clangParser = ClangParser::instance()->createTUParser(fd);
+ auto parser = getParserForFile(s.c_str());
+ auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
return fileRoot;
};
// dispatch the work and collect the future results
@@ -9235,10 +9257,92 @@ static void parseFiles(const std::shared_ptr<Entry> &root)
root->moveToSubEntryAndKeep(f.get());
}
#warning "Multi-threaded input enabled. This is a highly experimental feature. Only use for doxygen development."
+ }
+}
+
+#else // !MULTITHREADED_INPUT
+
+//! parse the list of input files
+static void parseFiles(const std::shared_ptr<Entry> &root)
+{
+#if USE_LIBCLANG
+ if (Doxygen::clangAssistedParsing)
+ {
+ StringUnorderedSet processedFiles;
+
+ // create a dictionary with files to process
+ StringUnorderedSet filesToProcess;
+ for (const auto &s : g_inputFiles)
+ {
+ filesToProcess.insert(s);
+ }
+
+ // process source files (and their include dependencies)
+ for (const auto &s : g_inputFiles)
+ {
+ bool ambig;
+ FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
+ ASSERT(fd!=0);
+ if (fd->isSource() && !fd->isReference()) // this is a source file
+ {
+ auto clangParser = ClangParser::instance()->createTUParser(fd);
+ auto parser { getParserForFile(s.c_str()) };
+ auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
+ root->moveToSubEntryAndKeep(fileRoot);
+ processedFiles.insert(s);
+
+ // Now process any include files in the same translation unit
+ // first. When libclang is used this is much more efficient.
+ for (auto incFile : clangParser->filesInSameTU())
+ {
+ //printf(" file %s\n",incFile.c_str());
+ if (filesToProcess.find(incFile)!=filesToProcess.end() && // file need to be processed
+ processedFiles.find(incFile)==processedFiles.end()) // and is not processed already
+ {
+ FileDef *ifd=findFileDef(Doxygen::inputNameLinkedMap,incFile.c_str(),ambig);
+ if (ifd && !ifd->isReference())
+ {
+ //printf(" Processing %s in same translation unit as %s\n",incFile.c_str(),s.c_str());
+ fileRoot = parseFile(*parser.get(),ifd,incFile.c_str(),clangParser.get(),false);
+ root->moveToSubEntryAndKeep(fileRoot);
+ processedFiles.insert(incFile);
+ }
+ }
+ }
+ }
+ }
+ // process remaining files
+ for (const auto &s : g_inputFiles)
+ {
+ if (processedFiles.find(s)==processedFiles.end()) // not yet processed
+ {
+ bool ambig;
+ FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
+ auto clangParser = ClangParser::instance()->createTUParser(fd);
+ auto parser { getParserForFile(s.c_str()) };
+ auto fileRoot = parseFile(*parser.get(),fd,s.c_str(),clangParser.get(),true);
+ root->moveToSubEntryAndKeep(fileRoot);
+ processedFiles.insert(s);
+ }
+ }
+ }
+ else // normal processing
#endif
+ {
+ for (const auto &s : g_inputFiles)
+ {
+ bool ambig;
+ FileDef *fd=findFileDef(Doxygen::inputNameLinkedMap,s.c_str(),ambig);
+ ASSERT(fd!=0);
+ std::unique_ptr<OutlineParserInterface> parser { getParserForFile(s.c_str()) };
+ std::shared_ptr<Entry> fileRoot = parseFile(*parser.get(),fd,s.c_str(),nullptr,true);
+ root->moveToSubEntryAndKeep(fileRoot);
+ }
}
}
+#endif
+
// resolves a path that may include symlinks, if a recursive symlink is
// found an empty string is returned.
static QCString resolveSymlink(QCString path)
@@ -9318,8 +9422,8 @@ static QDict<void> g_pathsVisited(1009);
static int readDir(QFileInfo *fi,
FileNameLinkedMap *fnMap,
StringUnorderedSet *exclSet,
- QStrList *patList,
- QStrList *exclPatList,
+ const StringVector *patList,
+ const StringVector *exclPatList,
StringVector *resultList,
StringUnorderedSet *resultSet,
bool errorIfNotExist,
@@ -9366,8 +9470,8 @@ static int readDir(QFileInfo *fi,
}
else if (cfi->isFile() &&
(!Config_getBool(EXCLUDE_SYMLINKS) || !cfi->isSymLink()) &&
- (patList==0 || patternMatch(*cfi,patList)) &&
- !patternMatch(*cfi,exclPatList) &&
+ (patList==0 || patternMatch(*cfi,*patList)) &&
+ (exclPatList==0 || !patternMatch(*cfi,*exclPatList)) &&
(killSet==0 || killSet->find(cfi->absFilePath().utf8().data())==killSet->end())
)
{
@@ -9391,7 +9495,7 @@ static int readDir(QFileInfo *fi,
else if (recursive &&
(!Config_getBool(EXCLUDE_SYMLINKS) || !cfi->isSymLink()) &&
cfi->isDir() &&
- !patternMatch(*cfi,exclPatList) &&
+ (exclPatList==0 || !patternMatch(*cfi,*exclPatList)) &&
cfi->fileName().at(0)!='.') // skip "." ".." and ".dir"
{
cfi->setFile(cfi->absFilePath());
@@ -9414,8 +9518,8 @@ static int readDir(QFileInfo *fi,
int readFileOrDirectory(const char *s,
FileNameLinkedMap *fnMap,
StringUnorderedSet *exclSet,
- QStrList *patList,
- QStrList *exclPatList,
+ const StringVector *patList,
+ const StringVector *exclPatList,
StringVector *resultList,
StringUnorderedSet *resultSet,
bool recursive,
@@ -9524,7 +9628,7 @@ static void escapeAliases()
value.mid(in,14)!="\\nosubgrouping"
)
{
- newValue+="\\_linebr ";
+ newValue+="\\ilinebr ";
}
else
{
@@ -9539,7 +9643,7 @@ static void escapeAliases()
while ((in=value.find("^^",p))!=-1)
{
newValue+=value.mid(p,in-p);
- newValue+="\\\\_linebr ";
+ newValue+="\\ilinebr ";
p=in+2;
}
newValue+=value.mid(p,value.length()-p);
@@ -9554,13 +9658,12 @@ void readAliases()
{
// add aliases to a dictionary
Doxygen::aliasDict.setAutoDelete(TRUE);
- QStrList &aliasList = Config_getList(ALIASES);
- const char *s=aliasList.first();
- while (s)
+ const StringVector &aliasList = Config_getList(ALIASES);
+ for (const auto &s : aliasList)
{
- if (Doxygen::aliasDict[s]==0)
+ QCString alias=s.c_str();
+ if (Doxygen::aliasDict[alias]==0)
{
- QCString alias=s;
int i=alias.find('=');
if (i>0)
{
@@ -9581,7 +9684,6 @@ void readAliases()
}
}
}
- s=aliasList.next();
}
expandAliases();
escapeAliases();
@@ -9709,9 +9811,7 @@ static const char *getArg(int argc,char **argv,int &optind)
class NullOutlineParser : public OutlineParserInterface
{
public:
- void startTranslationUnit(const char *) {}
- void finishTranslationUnit() {}
- void parseInput(const char *, const char *,const std::shared_ptr<Entry> &, bool, QStrList &) {}
+ void parseInput(const char *, const char *,const std::shared_ptr<Entry> &, ClangTUParser*) {}
bool needsPreprocessing(const QCString &) const { return FALSE; }
void parsePrototype(const char *) {}
};
@@ -10302,21 +10402,17 @@ void adjustConfiguration()
Config_getBool(REFERENCES_RELATION) ||
Config_getBool(REFERENCED_BY_RELATION);
- Doxygen::markdownSupport = Config_getBool(MARKDOWN_SUPPORT);
-
/**************************************************************************
* Add custom extension mappings
**************************************************************************/
- QStrList &extMaps = Config_getList(EXTENSION_MAPPING);
- char *mapping = extMaps.first();
- while (mapping)
+ const StringVector &extMaps = Config_getList(EXTENSION_MAPPING);
+ for (const auto &mapping : extMaps)
{
- QCString mapStr = mapping;
+ QCString mapStr = mapping.c_str();
int i=mapStr.find('=');
if (i==-1)
{
- mapping = extMaps.next();
continue;
}
else
@@ -10325,7 +10421,6 @@ void adjustConfiguration()
QCString language = mapStr.mid(i+1).stripWhiteSpace().lower();
if (ext.isEmpty() || language.isEmpty())
{
- mapping = extMaps.next();
continue;
}
@@ -10341,23 +10436,20 @@ void adjustConfiguration()
ext.data(),language.data());
}
}
- mapping = extMaps.next();
}
// add predefined macro name to a dictionary
- QStrList &expandAsDefinedList =Config_getList(EXPAND_AS_DEFINED);
- char *s=expandAsDefinedList.first();
- while (s)
+ const StringVector &expandAsDefinedList =Config_getList(EXPAND_AS_DEFINED);
+ for (const auto &s : expandAsDefinedList)
{
- Doxygen::expandAsDefinedSet.insert(s);
- s=expandAsDefinedList.next();
+ Doxygen::expandAsDefinedSet.insert(s.c_str());
}
// read aliases and store them in a dictionary
readAliases();
// store number of spaces in a tab into Doxygen::spaces
- int &tabSize = Config_getInt(TAB_SIZE);
+ int tabSize = Config_getInt(TAB_SIZE);
Doxygen::spaces.resize(tabSize+1);
int sp;for (sp=0;sp<tabSize;sp++) Doxygen::spaces.at(sp)=' ';
Doxygen::spaces.at(tabSize)='\0';
@@ -10387,7 +10479,7 @@ static void stopDoxygen(int)
static void writeTagFile()
{
- QCString &generateTagFile = Config_getString(GENERATE_TAGFILE);
+ QCString generateTagFile = Config_getString(GENERATE_TAGFILE);
if (generateTagFile.isEmpty()) return;
QFile tag(generateTagFile);
@@ -10490,26 +10582,26 @@ static void exitDoxygen()
}
static QCString createOutputDirectory(const QCString &baseDirName,
- QCString &formatDirName,
+ const QCString &formatDirName,
const char *defaultDirName)
{
- // Note the & on the next line, we modify the formatDirOption!
- if (formatDirName.isEmpty())
+ QCString result = formatDirName;
+ if (result.isEmpty())
{
- formatDirName = baseDirName + defaultDirName;
+ result = baseDirName + defaultDirName;
}
else if (formatDirName[0]!='/' && (formatDirName.length()==1 || formatDirName[1]!=':'))
{
- formatDirName.prepend(baseDirName+'/');
+ result.prepend(baseDirName+'/');
}
- QDir formatDir(formatDirName);
- if (!formatDir.exists() && !formatDir.mkdir(formatDirName))
+ QDir formatDir(result);
+ if (!formatDir.exists() && !formatDir.mkdir(result))
{
- err("Could not create output directory %s\n", formatDirName.data());
+ err("Could not create output directory %s\n", result.data());
cleanUpDoxygen();
exit(1);
}
- return formatDirName;
+ return result;
}
static QCString getQchFileName()
@@ -10533,23 +10625,20 @@ void searchInputFiles()
{
StringUnorderedSet killSet;
- QStrList &exclPatterns = Config_getList(EXCLUDE_PATTERNS);
+ const StringVector &exclPatterns = Config_getList(EXCLUDE_PATTERNS);
bool alwaysRecursive = Config_getBool(RECURSIVE);
StringUnorderedSet excludeNameSet;
// gather names of all files in the include path
g_s.begin("Searching for include files...\n");
killSet.clear();
- QStrList &includePathList = Config_getList(INCLUDE_PATH);
- char *s=includePathList.first();
- while (s)
+ const StringVector &includePathList = Config_getList(INCLUDE_PATH);
+ for (const auto &s : includePathList)
{
- QStrList &pl = Config_getList(INCLUDE_FILE_PATTERNS);
- if (pl.count()==0)
- {
- pl = Config_getList(FILE_PATTERNS);
- }
- readFileOrDirectory(s, // s
+ size_t plSize = Config_getList(INCLUDE_FILE_PATTERNS).size();
+ const StringVector &pl = plSize==0 ? Config_getList(FILE_PATTERNS) :
+ Config_getList(INCLUDE_FILE_PATTERNS);
+ readFileOrDirectory(s.c_str(), // s
Doxygen::includeNameLinkedMap, // fnDict
0, // exclSet
&pl, // patList
@@ -10559,17 +10648,15 @@ void searchInputFiles()
alwaysRecursive, // recursive
TRUE, // errorIfNotExist
&killSet); // killSet
- s=includePathList.next();
}
g_s.end();
g_s.begin("Searching for example files...\n");
killSet.clear();
- QStrList &examplePathList = Config_getList(EXAMPLE_PATH);
- s=examplePathList.first();
- while (s)
+ const StringVector &examplePathList = Config_getList(EXAMPLE_PATH);
+ for (const auto &s : examplePathList)
{
- readFileOrDirectory(s, // s
+ readFileOrDirectory(s.c_str(), // s
Doxygen::exampleNameLinkedMap, // fnDict
0, // exclSet
&Config_getList(EXAMPLE_PATTERNS), // patList
@@ -10579,17 +10666,15 @@ void searchInputFiles()
(alwaysRecursive || Config_getBool(EXAMPLE_RECURSIVE)), // recursive
TRUE, // errorIfNotExist
&killSet); // killSet
- s=examplePathList.next();
}
g_s.end();
g_s.begin("Searching for images...\n");
killSet.clear();
- QStrList &imagePathList=Config_getList(IMAGE_PATH);
- s=imagePathList.first();
- while (s)
+ const StringVector &imagePathList=Config_getList(IMAGE_PATH);
+ for (const auto &s : imagePathList)
{
- readFileOrDirectory(s, // s
+ readFileOrDirectory(s.c_str(), // s
Doxygen::imageNameLinkedMap, // fnDict
0, // exclSet
0, // patList
@@ -10599,17 +10684,15 @@ void searchInputFiles()
alwaysRecursive, // recursive
TRUE, // errorIfNotExist
&killSet); // killSet
- s=imagePathList.next();
}
g_s.end();
g_s.begin("Searching for dot files...\n");
killSet.clear();
- QStrList &dotFileList=Config_getList(DOTFILE_DIRS);
- s=dotFileList.first();
- while (s)
+ const StringVector &dotFileList=Config_getList(DOTFILE_DIRS);
+ for (const auto &s : dotFileList)
{
- readFileOrDirectory(s, // s
+ readFileOrDirectory(s.c_str(), // s
Doxygen::dotFileNameLinkedMap, // fnDict
0, // exclSet
0, // patList
@@ -10619,17 +10702,15 @@ void searchInputFiles()
alwaysRecursive, // recursive
TRUE, // errorIfNotExist
&killSet); // killSet
- s=dotFileList.next();
}
g_s.end();
g_s.begin("Searching for msc files...\n");
killSet.clear();
- QStrList &mscFileList=Config_getList(MSCFILE_DIRS);
- s=mscFileList.first();
- while (s)
+ const StringVector &mscFileList=Config_getList(MSCFILE_DIRS);
+ for (const auto &s : mscFileList)
{
- readFileOrDirectory(s, // s
+ readFileOrDirectory(s.c_str(), // s
Doxygen::mscFileNameLinkedMap, // fnDict
0, // exclSet
0, // patList
@@ -10639,17 +10720,15 @@ void searchInputFiles()
alwaysRecursive, // recursive
TRUE, // errorIfNotExist
&killSet); // killSet
- s=mscFileList.next();
}
g_s.end();
g_s.begin("Searching for dia files...\n");
killSet.clear();
- QStrList &diaFileList=Config_getList(DIAFILE_DIRS);
- s=diaFileList.first();
- while (s)
+ const StringVector &diaFileList=Config_getList(DIAFILE_DIRS);
+ for (const auto &s : diaFileList)
{
- readFileOrDirectory(s, // s
+ readFileOrDirectory(s.c_str(), // s
Doxygen::diaFileNameLinkedMap, // fnDict
0, // exclSet
0, // patList
@@ -10659,16 +10738,14 @@ void searchInputFiles()
alwaysRecursive, // recursive
TRUE, // errorIfNotExist
&killSet); // killSet
- s=diaFileList.next();
}
g_s.end();
g_s.begin("Searching for files to exclude\n");
- QStrList &excludeList = Config_getList(EXCLUDE);
- s=excludeList.first();
- while (s)
+ const StringVector &excludeList = Config_getList(EXCLUDE);
+ for (const auto &s : excludeList)
{
- readFileOrDirectory(s, // s
+ readFileOrDirectory(s.c_str(), // s
0, // fnDict
0, // exclSet
&Config_getList(FILE_PATTERNS), // patList
@@ -10677,7 +10754,6 @@ void searchInputFiles()
&excludeNameSet, // resultSet
alwaysRecursive, // recursive
FALSE); // errorIfNotExist
- s=excludeList.next(); // killSet
}
g_s.end();
@@ -10688,11 +10764,10 @@ void searchInputFiles()
g_s.begin("Searching INPUT for files to process...\n");
killSet.clear();
Doxygen::inputPaths.clear();
- QStrList &inputList=Config_getList(INPUT);
- s=inputList.first();
- while (s)
+ const StringVector &inputList=Config_getList(INPUT);
+ for (const auto &s : inputList)
{
- QCString path=s;
+ QCString path=s.c_str();
uint l = path.length();
if (l>0)
{
@@ -10712,7 +10787,6 @@ void searchInputFiles()
&killSet, // killSet
&Doxygen::inputPaths); // paths
}
- s=inputList.next();
}
std::sort(Doxygen::inputNameLinkedMap->begin(),
Doxygen::inputNameLinkedMap->end(),
@@ -10730,6 +10804,10 @@ void parseInput()
{
atexit(exitDoxygen);
+#if USE_LIBCLANG
+ Doxygen::clangAssistedParsing = Config_getBool(CLANG_ASSISTED_PARSING);
+#endif
+
// we would like to show the versionString earlier, but we first have to handle the configuration file
// to know the value of the QUIET setting.
QCString versionString = getFullVersion();
@@ -10738,10 +10816,10 @@ void parseInput()
/**************************************************************************
* Make sure the output directory exists
**************************************************************************/
- QCString &outputDirectory = Config_getString(OUTPUT_DIRECTORY);
+ QCString outputDirectory = Config_getString(OUTPUT_DIRECTORY);
if (outputDirectory.isEmpty())
{
- outputDirectory=QDir::currentDirPath().utf8();
+ outputDirectory = Config_updateString(OUTPUT_DIRECTORY,QDir::currentDirPath().utf8());
}
else
{
@@ -10763,7 +10841,7 @@ void parseInput()
}
dir.cd(outputDirectory);
}
- outputDirectory=dir.absPath().utf8();
+ outputDirectory = Config_updateString(OUTPUT_DIRECTORY,dir.absPath().utf8());
}
/**************************************************************************
@@ -10795,39 +10873,62 @@ void parseInput()
**************************************************************************/
QCString htmlOutput;
- bool &generateHtml = Config_getBool(GENERATE_HTML);
+ bool generateHtml = Config_getBool(GENERATE_HTML);
if (generateHtml || g_useOutputTemplate /* TODO: temp hack */)
+ {
htmlOutput = createOutputDirectory(outputDirectory,Config_getString(HTML_OUTPUT),"/html");
+ Config_updateString(HTML_OUTPUT,htmlOutput);
+ }
QCString docbookOutput;
- bool &generateDocbook = Config_getBool(GENERATE_DOCBOOK);
+ bool generateDocbook = Config_getBool(GENERATE_DOCBOOK);
if (generateDocbook)
+ {
docbookOutput = createOutputDirectory(outputDirectory,Config_getString(DOCBOOK_OUTPUT),"/docbook");
+ Config_updateString(DOCBOOK_OUTPUT,docbookOutput);
+ }
QCString xmlOutput;
- bool &generateXml = Config_getBool(GENERATE_XML);
+ bool generateXml = Config_getBool(GENERATE_XML);
if (generateXml)
+ {
xmlOutput = createOutputDirectory(outputDirectory,Config_getString(XML_OUTPUT),"/xml");
+ Config_updateString(XML_OUTPUT,xmlOutput);
+ }
QCString latexOutput;
- bool &generateLatex = Config_getBool(GENERATE_LATEX);
+ bool generateLatex = Config_getBool(GENERATE_LATEX);
if (generateLatex)
- latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT),"/latex");
+ {
+ latexOutput = createOutputDirectory(outputDirectory,Config_getString(LATEX_OUTPUT), "/latex");
+ Config_updateString(LATEX_OUTPUT,latexOutput);
+ }
QCString rtfOutput;
- bool &generateRtf = Config_getBool(GENERATE_RTF);
+ bool generateRtf = Config_getBool(GENERATE_RTF);
if (generateRtf)
+ {
rtfOutput = createOutputDirectory(outputDirectory,Config_getString(RTF_OUTPUT),"/rtf");
+ Config_updateString(RTF_OUTPUT,rtfOutput);
+ }
QCString manOutput;
- bool &generateMan = Config_getBool(GENERATE_MAN);
+ bool generateMan = Config_getBool(GENERATE_MAN);
if (generateMan)
+ {
manOutput = createOutputDirectory(outputDirectory,Config_getString(MAN_OUTPUT),"/man");
+ Config_updateString(MAN_OUTPUT,manOutput);
+ }
- //QCString sqlOutput;
- //bool &generateSql = Config_getBool(GENERATE_SQLITE3);
- //if (generateSql)
- // sqlOutput = createOutputDirectory(outputDirectory,"SQLITE3_OUTPUT","/sqlite3");
+#if USE_SQLITE3
+ QCString sqlOutput;
+ bool generateSql = Config_getBool(GENERATE_SQLITE3);
+ if (generateSql)
+ {
+ sqlOutput = createOutputDirectory(outputDirectory,Config_getString(SQLITE3_OUTPUT),"/sqlite3");
+ Config_updateString(SQLITE3_OUTPUT,sqlOutput);
+ }
+#endif
if (Config_getBool(HAVE_DOT))
{
@@ -10856,11 +10957,11 @@ void parseInput()
**************************************************************************/
LayoutDocManager::instance().init();
- QCString &layoutFileName = Config_getString(LAYOUT_FILE);
+ QCString layoutFileName = Config_getString(LAYOUT_FILE);
bool defaultLayoutUsed = FALSE;
if (layoutFileName.isEmpty())
{
- layoutFileName = "DoxygenLayout.xml";
+ layoutFileName = Config_updateString(LAYOUT_FILE,"DoxygenLayout.xml");
defaultLayoutUsed = TRUE;
}
@@ -10880,13 +10981,14 @@ void parseInput()
**************************************************************************/
// prevent search in the output directories
- QStrList &exclPatterns = Config_getList(EXCLUDE_PATTERNS);
- if (generateHtml) exclPatterns.append(htmlOutput);
- if (generateDocbook) exclPatterns.append(docbookOutput);
- if (generateXml) exclPatterns.append(xmlOutput);
- if (generateLatex) exclPatterns.append(latexOutput);
- if (generateRtf) exclPatterns.append(rtfOutput);
- if (generateMan) exclPatterns.append(manOutput);
+ StringVector exclPatterns = Config_getList(EXCLUDE_PATTERNS);
+ if (generateHtml) exclPatterns.push_back(htmlOutput.data());
+ if (generateDocbook) exclPatterns.push_back(docbookOutput.data());
+ if (generateXml) exclPatterns.push_back(xmlOutput.data());
+ if (generateLatex) exclPatterns.push_back(latexOutput.data());
+ if (generateRtf) exclPatterns.push_back(rtfOutput.data());
+ if (generateMan) exclPatterns.push_back(manOutput.data());
+ Config_updateList(EXCLUDE_PATTERNS,exclPatterns);
searchInputFiles();
@@ -10919,12 +11021,10 @@ void parseInput()
std::shared_ptr<Entry> root = std::make_shared<Entry>();
msg("Reading and parsing tag files\n");
- QStrList &tagFileList = Config_getList(TAGFILES);
- char *s=tagFileList.first();
- while (s)
+ const StringVector &tagFileList = Config_getList(TAGFILES);
+ for (const auto &s : tagFileList)
{
- readTagFile(root,s);
- s=tagFileList.next();
+ readTagFile(root,s.c_str());
}
/**************************************************************************
@@ -11445,12 +11545,14 @@ void generateOutput()
Doxygen::generatingXmlOutput=FALSE;
g_s.end();
}
- if (USE_SQLITE3)
+#if USE_SQLITE3
+ if (Config_getBool(GENERATE_SQLITE3))
{
g_s.begin("Generating SQLITE3 output...\n");
generateSqlite3();
g_s.end();
}
+#endif
if (Config_getBool(GENERATE_AUTOGEN_DEF))
{
@@ -11588,7 +11690,7 @@ void generateOutput()
if (Debug::isFlagSet(Debug::Time))
{
msg("Total elapsed time: %.3f seconds\n(of which %.3f seconds waiting for external tools to finish)\n",
- ((double)Debug::elapsedTime())/1000.0,
+ ((double)Debug::elapsedTime()),
Portable::getSysElapsedTime()
);
g_s.print();