diff options
Diffstat (limited to 'src/doxygen.cpp')
-rw-r--r-- | src/doxygen.cpp | 276 |
1 files changed, 189 insertions, 87 deletions
diff --git a/src/doxygen.cpp b/src/doxygen.cpp index 5ac1980..8c739ad 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(); @@ -7499,50 +7500,48 @@ 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 && filesToProcess.find(incFile)!=filesToProcess.end()) + for (auto incFile : clangParser->filesInSameTU()) { - if (fd->absFilePath()!=incFile && processedFiles.find(incFile)==processedFiles.end()) + 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()); } processedFiles.insert(incFile); } } - incFile = filesInSameTu.next(); } - fd->finishParsing(); processedFiles.insert(fd->absFilePath().str()); } } @@ -7554,21 +7553,21 @@ static void generateFileSources() { 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(); } } } @@ -9062,7 +9060,7 @@ 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) { QCString fileName=fn; QCString extension; @@ -9110,18 +9108,20 @@ static std::shared_ptr<Entry> parseFile(OutlineParserInterface &parser, convBuf.addChar('\0'); - if (Doxygen::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) { @@ -9137,7 +9137,12 @@ static void parseFiles(const std::shared_ptr<Entry> &root) 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 && filesToProcess.find(incFile)!=filesToProcess.end()) - { - if (qstrcmp(incFile,s.c_str()) && processedFiles.find(incFile)==processedFiles.end()) + // 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); - processedFiles.insert(incFile); + 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(); - processedFiles.insert(s); + 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 (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(); - processedFiles.insert(s); + // 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) @@ -9707,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 *) {} }; |