Doxygen
dot.cpp
浏览该文件的文档.
1 /*****************************************************************************
2  *
3  * Copyright (C) 1997-2019 by Dimitri van Heesch.
4  *
5  * Permission to use, copy, modify, and distribute this software and its
6  * documentation under the terms of the GNU General Public License is hereby
7  * granted. No representations are made about the suitability of this software
8  * for any purpose. It is provided "as is" without express or implied warranty.
9  * See the GNU General Public License for more details.
10  *
11  * Documents produced by Doxygen are derivative works derived from the
12  * input used in their production; they are not affected by this license.
13  *
14  */
15 
16 #include <cstdlib>
17 #include <cassert>
18 #include <sstream>
19 #include <algorithm>
20 
21 #include "config.h"
22 #include "dot.h"
23 #include "dotrunner.h"
24 #include "dotfilepatcher.h"
25 #include "util.h"
26 #include "portable.h"
27 #include "message.h"
28 #include "doxygen.h"
29 #include "language.h"
30 #include "index.h"
31 #include "dir.h"
32 
33 #define MAP_CMD "cmapx"
34 
35 //--------------------------------------------------------------------
36 
38 
39 static void setDotFontPath(const QCString &path)
40 {
42  g_dotFontPath = Portable::getenv("DOTFONTPATH");
43  QCString newFontPath = Config_getString(DOT_FONTPATH);
44  if (!newFontPath.isEmpty() && !path.isEmpty())
45  {
46  newFontPath.prepend(path+Portable::pathListSeparator());
47  }
48  else if (newFontPath.isEmpty() && !path.isEmpty())
49  {
50  newFontPath=path;
51  }
52  else
53  {
54  Portable::unsetenv("DOTFONTPATH");
55  return;
56  }
57  Portable::setenv("DOTFONTPATH",newFontPath);
58 }
59 
60 static void unsetDotFontPath()
61 {
62  if (g_dotFontPath.isEmpty())
63  {
64  Portable::unsetenv("DOTFONTPATH");
65  }
66  else
67  {
68  Portable::setenv("DOTFONTPATH",g_dotFontPath);
69  }
70  g_dotFontPath="";
71 }
72 
73 //--------------------------------------------------------------------
74 
76 
78 {
79  if (!m_theInstance)
80  {
82  }
83  return m_theInstance;
84 }
85 
87 {
88  delete m_theInstance;
89  m_theInstance=0;
90 }
91 
92 DotManager::DotManager() : m_runners(), m_filePatchers()
93 {
94  m_queue = new DotRunnerQueue;
95  int i;
96  int dotNumThreads = Config_getInt(DOT_NUM_THREADS);
97  if (dotNumThreads!=1)
98  {
99  for (i=0;i<dotNumThreads;i++)
100  {
101  std::unique_ptr<DotWorkerThread> thread = std::make_unique<DotWorkerThread>(m_queue);
102  thread->start();
103  if (thread->isRunning())
104  {
105  m_workers.push_back(std::move(thread));
106  }
107  else // no more threads available!
108  {
109  }
110  }
111  ASSERT(m_workers.size()>0);
112  }
113 }
114 
116 {
117  delete m_queue;
118 }
119 
120 DotRunner* DotManager::createRunner(const QCString &absDotName, const QCString& md5Hash)
121 {
122  DotRunner* rv = nullptr;
123  auto const runit = m_runners.find(absDotName.str());
124  if (runit == m_runners.end())
125  {
126  auto insobj = std::make_unique<DotRunner>(absDotName, md5Hash);
127  rv = insobj.get();
128  m_runners.emplace(absDotName.str(), std::move(insobj));
129  }
130  else
131  {
132  // we have a match
133  if (md5Hash != runit->second->getMd5Hash())
134  {
135  err("md5 hash does not match for two different runs of %s !\n", qPrint(absDotName));
136  }
137  rv = runit->second.get();
138  }
139  assert(rv);
140  return rv;
141 }
142 
144 {
145  auto patcher = m_filePatchers.find(fileName.str());
146 
147  if (patcher != m_filePatchers.end()) return &(patcher->second);
148 
149  auto rv = m_filePatchers.emplace(std::make_pair(fileName.str(), fileName));
150  assert(rv.second);
151  return &(rv.first->second);
152 }
153 
154 bool DotManager::run() const
155 {
156  size_t numDotRuns = m_runners.size();
157  size_t numFilePatchers = m_filePatchers.size();
158  if (numDotRuns+numFilePatchers>1)
159  {
160  if (m_workers.size()==0)
161  {
162  msg("Generating dot graphs in single threaded mode...\n");
163  }
164  else
165  {
166  msg("Generating dot graphs using %zu parallel threads...\n",std::min(numDotRuns+numFilePatchers,m_workers.size()));
167  }
168  }
169  size_t i=1;
170 
171  bool setPath=FALSE;
172  if (Config_getBool(GENERATE_HTML))
173  {
174  setDotFontPath(Config_getString(HTML_OUTPUT));
175  setPath=TRUE;
176  }
177  else if (Config_getBool(GENERATE_LATEX))
178  {
179  setDotFontPath(Config_getString(LATEX_OUTPUT));
180  setPath=TRUE;
181  }
182  else if (Config_getBool(GENERATE_RTF))
183  {
184  setDotFontPath(Config_getString(RTF_OUTPUT));
185  setPath=TRUE;
186  }
187  else if (Config_getBool(GENERATE_DOCBOOK))
188  {
189  setDotFontPath(Config_getString(DOCBOOK_OUTPUT));
190  setPath=TRUE;
191  }
193  // fill work queue with dot operations
194  size_t prev=1;
195  if (m_workers.size()==0) // no threads to work with
196  {
197  for (auto & dr : m_runners)
198  {
199  msg("Running dot for graph %zu/%zu\n",prev,numDotRuns);
200  dr.second->run();
201  prev++;
202  }
203  }
204  else // use multiple threads to run instances of dot in parallel
205  {
206  for (auto & dr: m_runners)
207  {
208  m_queue->enqueue(dr.second.get());
209  }
210  // wait for the queue to become empty
211  while ((i=m_queue->size())>0)
212  {
213  i = numDotRuns - i;
214  while (i>=prev)
215  {
216  msg("Running dot for graph %zu/%zu\n",prev,numDotRuns);
217  prev++;
218  }
219  Portable::sleep(100);
220  }
221  while (numDotRuns>=prev)
222  {
223  msg("Running dot for graph %zu/%zu\n",prev,numDotRuns);
224  prev++;
225  }
226  // signal the workers we are done
227  for (i=0;i<m_workers.size();i++)
228  {
229  m_queue->enqueue(0); // add terminator for each worker
230  }
231  // wait for the workers to finish
232  for (i=0;i<m_workers.size();i++)
233  {
234  m_workers.at(i)->wait();
235  }
236  }
238  if (setPath)
239  {
241  }
242 
243  // patch the output file and insert the maps and figures
244  i=1;
245  // since patching the svg files may involve patching the header of the SVG
246  // (for zoomable SVGs), and patching the .html files requires reading that
247  // header after the SVG is patched, we first process the .svg files and
248  // then the other files.
249  for (auto & fp : m_filePatchers)
250  {
251  if (fp.second.isSVGFile())
252  {
253  msg("Patching output file %zu/%zu\n",i,numFilePatchers);
254  if (!fp.second.run()) return FALSE;
255  i++;
256  }
257  }
258  for (auto& fp : m_filePatchers)
259  {
260  if (!fp.second.isSVGFile())
261  {
262  msg("Patching output file %zu/%zu\n",i,numFilePatchers);
263  if (!fp.second.run()) return FALSE;
264  i++;
265  }
266  }
267  return TRUE;
268 }
269 
270 //--------------------------------------------------------------------
271 
272 void writeDotGraphFromFile(const QCString &inFile,const QCString &outDir,
273  const QCString &outFile,GraphOutputFormat format,
274  const QCString &srcFile,int srcLine)
275 {
276  Dir d(outDir.str());
277  if (!d.exists())
278  {
279  term("Output dir %s does not exist!\n",qPrint(outDir));
280  }
281 
282  QCString imgExt = getDotImageExtension();
283  QCString imgName = (QCString)outFile+"."+imgExt;
284  QCString absImgName = QCString(d.absPath())+"/"+imgName;
285  QCString absOutFile = QCString(d.absPath())+"/"+outFile;
286 
287  DotRunner dotRun(inFile);
288  if (format==GOF_BITMAP)
289  {
290  dotRun.addJob(Config_getEnumAsString(DOT_IMAGE_FORMAT),absImgName,srcFile,srcLine);
291  }
292  else // format==GOF_EPS
293  {
294  if (Config_getBool(USE_PDFLATEX))
295  {
296  dotRun.addJob("pdf",absOutFile+".pdf",srcFile,srcLine);
297  }
298  else
299  {
300  dotRun.addJob("ps",absOutFile+".eps",srcFile,srcLine);
301  }
302  }
303 
304  dotRun.preventCleanUp();
305  if (!dotRun.run())
306  {
307  return;
308  }
309 
311 
312 }
313 
314 /*! Writes user defined image map to the output.
315  * \param t text stream to write to
316  * \param inFile just the basename part of the filename
317  * \param outDir output directory
318  * \param relPath relative path the to root of the output dir
319  * \param baseName the base name of the output files
320  * \param context the scope in which this graph is found (for resolving links)
321  * \param graphId a unique id for this graph, use for dynamic sections
322  * \param srcFile the source file
323  * \param srcLine the line number in the source file
324  */
326  const QCString &inFile, const QCString &outDir,
327  const QCString &relPath, const QCString &baseName,
328  const QCString &context,int graphId,
329  const QCString &srcFile,int srcLine)
330 {
331 
332  Dir d(outDir.str());
333  if (!d.exists())
334  {
335  term("Output dir %s does not exist!\n",qPrint(outDir));
336  }
337 
338  QCString mapName = baseName+".map";
339  QCString imgExt = getDotImageExtension();
340  QCString imgName = baseName+"."+imgExt;
341  QCString absOutFile = QCString(d.absPath())+"/"+mapName;
342 
343  DotRunner dotRun(inFile);
344  dotRun.addJob(MAP_CMD,absOutFile,srcFile,srcLine);
345  dotRun.preventCleanUp();
346  if (!dotRun.run())
347  {
348  return;
349  }
350 
351  if (imgExt=="svg") // vector graphics
352  {
353  QCString svgName = outDir+"/"+baseName+".svg";
354  DotFilePatcher::writeSVGFigureLink(t,relPath,baseName,svgName);
355  DotFilePatcher patcher(svgName);
356  patcher.addSVGConversion("",TRUE,context,TRUE,graphId);
357  patcher.run();
358  }
359  else // bitmap graphics
360  {
361  TextStream tt;
362  t << "<img src=\"" << relPath << imgName << "\" alt=\""
363  << imgName << "\" border=\"0\" usemap=\"#" << mapName << "\"/>\n";
364  DotFilePatcher::convertMapFile(tt, absOutFile, relPath ,TRUE, context);
365  if (!tt.empty())
366  {
367  t << "<map name=\"" << mapName << "\" id=\"" << mapName << "\">";
368  t << tt.str();
369  t << "</map>\n";
370  }
371  }
372  d.remove(absOutFile.str());
373 }
DotManager
Singleton that manages parallel dot invocations and patching files for embedding image maps
Definition: dot.h:31
getDotImageExtension
QCString getDotImageExtension()
Definition: util.cpp:7032
DotRunner::run
bool run()
Runs dot for all jobs added.
Definition: dotrunner.cpp:172
DotManager::instance
static DotManager * instance()
Definition: dot.cpp:77
DotManager::createFilePatcher
DotFilePatcher * createFilePatcher(const QCString &fileName)
Definition: dot.cpp:143
DotManager::run
bool run() const
Definition: dot.cpp:154
g_dotFontPath
static QCString g_dotFontPath
Definition: dot.cpp:37
Dir::remove
bool remove(const std::string &path, bool acceptsAbsPath=true) const
Definition: dir.cpp:256
Dir
Class representing a directory in the file system
Definition: dir.h:68
DotRunner
Helper class to run dot from doxygen from multiple threads.
Definition: dotrunner.h:30
DotManager::deleteInstance
static void deleteInstance()
Definition: dot.cpp:86
DotManager::m_queue
DotRunnerQueue * m_queue
Definition: dot.h:47
QCString::isEmpty
bool isEmpty() const
Returns TRUE iff the string is empty
Definition: qcstring.h:144
index.h
Doxygen::indexList
static IndexList * indexList
Definition: doxygen.h:114
DotManager::createRunner
DotRunner * createRunner(const QCString &absDotName, const QCString &md5Hash)
Definition: dot.cpp:120
DotRunnerQueue::size
size_t size() const
Definition: dotrunner.cpp:277
QCString::str
std::string str() const
Definition: qcstring.h:442
err
void err(const char *fmt,...)
Definition: message.cpp:203
TextStream
Text streaming class that buffers data.
Definition: textstream.h:33
TextStream::empty
bool empty() const
Returns true iff the buffer is empty
Definition: textstream.h:232
Portable::getenv
QCString getenv(const QCString &variable)
Definition: portable.cpp:279
DotFilePatcher
Helper class to insert a set of map file into an output file
Definition: dotfilepatcher.h:26
Portable::unsetenv
void unsetenv(const QCString &variable)
Definition: portable.cpp:260
DotRunner::addJob
void addJob(const QCString &format, const QCString &output, const QCString &srcFile, int srcLine)
Adds an additional job to the run.
Definition: dotrunner.cpp:150
DotManager::m_workers
std::vector< std::unique_ptr< DotWorkerThread > > m_workers
Definition: dot.h:48
DotRunner::preventCleanUp
void preventCleanUp()
Prevent cleanup of the dot file (for user provided dot files)
Definition: dotrunner.h:67
dot.h
Config_getInt
#define Config_getInt(name)
Definition: config.h:34
DotManager::DotManager
DotManager()
Definition: dot.cpp:92
message.h
writeDotGraphFromFile
void writeDotGraphFromFile(const QCString &inFile, const QCString &outDir, const QCString &outFile, GraphOutputFormat format, const QCString &srcFile, int srcLine)
Definition: dot.cpp:272
Portable::sysTimerStart
void sysTimerStart()
Definition: portable.cpp:470
IndexList::addImageFile
void addImageFile(const QCString &name)
Definition: index.h:105
Dir::absPath
std::string absPath() const
Definition: dir.cpp:305
DotRunnerQueue::enqueue
void enqueue(DotRunner *runner)
Definition: dotrunner.cpp:258
doxygen.h
Config_getEnumAsString
#define Config_getEnumAsString(name)
Definition: config.h:36
DotRunnerQueue
Queue of dot jobs to run.
Definition: dotrunner.h:73
language.h
DotFilePatcher::writeSVGFigureLink
static bool writeSVGFigureLink(TextStream &out, const QCString &relPath, const QCString &baseName, const QCString &absImgName)
Check if a reference to a SVG figure can be written and do so if possible.
Definition: dotfilepatcher.cpp:539
dotfilepatcher.h
DotFilePatcher::run
bool run() const
Definition: dotfilepatcher.cpp:292
TRUE
#define TRUE
Definition: qcstring.h:36
writeDotImageMapFromFile
void writeDotImageMapFromFile(TextStream &t, const QCString &inFile, const QCString &outDir, const QCString &relPath, const QCString &baseName, const QCString &context, int graphId, const QCString &srcFile, int srcLine)
Definition: dot.cpp:325
dotrunner.h
TextStream::str
std::string str() const
Return the contents of the buffer as a std::string object
Definition: textstream.h:208
MAP_CMD
#define MAP_CMD
Definition: dot.cpp:33
DotManager::m_filePatchers
std::map< std::string, DotFilePatcher > m_filePatchers
Definition: dot.h:45
DotManager::m_theInstance
static DotManager * m_theInstance
Definition: dot.h:46
Portable::pathListSeparator
QCString pathListSeparator()
Definition: portable.cpp:356
setDotFontPath
static void setDotFontPath(const QCString &path)
Definition: dot.cpp:39
GOF_BITMAP
@ GOF_BITMAP
Definition: dotgraph.h:27
Config_getBool
#define Config_getBool(name)
Definition: config.h:33
msg
void msg(const char *fmt,...)
Definition: message.cpp:53
term
void term(const char *fmt,...)
Definition: message.cpp:220
qPrint
const char * qPrint(const char *s)
Definition: qcstring.h:589
Config_getString
#define Config_getString(name)
Definition: config.h:32
DotFilePatcher::addSVGConversion
int addSVGConversion(const QCString &relPath, bool urlOnly, const QCString &context, bool zoomable, int graphId)
Definition: dotfilepatcher.cpp:274
config.h
Portable::sysTimerStop
void sysTimerStop()
Definition: portable.cpp:475
ASSERT
#define ASSERT(x)
Definition: qcstring.h:44
DotManager::~DotManager
virtual ~DotManager()
Definition: dot.cpp:115
DotManager::m_runners
std::map< std::string, std::unique_ptr< DotRunner > > m_runners
Definition: dot.h:44
Dir::exists
bool exists() const
Definition: dir.cpp:199
GraphOutputFormat
GraphOutputFormat
Definition: dotgraph.h:27
Portable::setenv
void setenv(const QCString &variable, const QCString &value)
Definition: portable.cpp:246
Portable::sleep
void sleep(int ms)
Definition: portable.cpp:487
unsetDotFontPath
static void unsetDotFontPath()
Definition: dot.cpp:60
DotFilePatcher::convertMapFile
static bool convertMapFile(TextStream &t, const QCString &mapName, const QCString &relPath, bool urlOnly=FALSE, const QCString &context=QCString())
Definition: dotfilepatcher.cpp:214
portable.h
Portable versions of functions that are platform dependent.
dir.h
util.h
A bunch of utility functions.
QCString::prepend
QCString & prepend(const char *s)
Definition: qcstring.h:339
FALSE
#define FALSE
Definition: qcstring.h:33
QCString
This is an alternative implementation of QCString.
Definition: qcstring.h:108