Doxygen
Loading...
Searching...
No Matches
dot.cpp
Go to the documentation of this file.
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#include <mutex>
21
22#include "config.h"
23#include "dot.h"
24#include "dotrunner.h"
25#include "dotfilepatcher.h"
26#include "util.h"
27#include "portable.h"
28#include "message.h"
29#include "doxygen.h"
30#include "language.h"
31#include "indexlist.h"
32#include "dir.h"
33
34#define MAP_CMD "cmapx"
35
36//--------------------------------------------------------------------
37
39
40static std::mutex g_dotManagerMutex;
41
42static void setDotFontPath(const QCString &path)
43{
44 ASSERT(g_dotFontPath.isEmpty());
45 g_dotFontPath = Portable::getenv("DOTFONTPATH");
46 QCString newFontPath = Config_getString(DOT_FONTPATH);
47 if (!newFontPath.isEmpty() && !path.isEmpty())
48 {
49 newFontPath.prepend(path+Portable::pathListSeparator());
50 }
51 else if (newFontPath.isEmpty() && !path.isEmpty())
52 {
53 newFontPath=path;
54 }
55 else
56 {
57 Portable::unsetenv("DOTFONTPATH");
58 return;
59 }
60 Portable::setenv("DOTFONTPATH",newFontPath);
61}
62
63static void unsetDotFontPath()
64{
65 if (g_dotFontPath.isEmpty())
66 {
67 Portable::unsetenv("DOTFONTPATH");
68 }
69 else
70 {
71 Portable::setenv("DOTFONTPATH",g_dotFontPath);
72 }
74}
75
76//--------------------------------------------------------------------
77
79{
80 static DotManager theInstance;
81 return &theInstance;
82}
83
84DotManager::DotManager() : m_runners(), m_filePatchers(), m_workers(static_cast<size_t>(Config_getInt(DOT_NUM_THREADS)))
85{
86}
87
91
92DotRunner* DotManager::createRunner(const QCString &absDotName, const QCString& md5Hash)
93{
94 std::lock_guard<std::mutex> lock(g_dotManagerMutex);
95 DotRunner* rv = nullptr;
96 auto const runit = m_runners.find(absDotName.str());
97 if (runit == m_runners.end())
98 {
99 auto insobj = std::make_unique<DotRunner>(absDotName, md5Hash);
100 rv = insobj.get();
101 m_runners.emplace(absDotName.str(), std::move(insobj));
102 }
103 else
104 {
105 // we have a match
106 if (md5Hash != runit->second->getMd5Hash())
107 {
108 err("md5 hash does not match for two different runs of %s !\n", qPrint(absDotName));
109 }
110 rv = runit->second.get();
111 }
112 assert(rv);
113 return rv;
114}
115
117{
118 std::lock_guard<std::mutex> lock(g_dotManagerMutex);
119 auto patcher = m_filePatchers.find(fileName.str());
120
121 if (patcher != m_filePatchers.end()) return &(patcher->second);
122
123 auto rv = m_filePatchers.emplace(fileName.str(), fileName);
124 assert(rv.second);
125 return &(rv.first->second);
126}
127
129{
130 size_t numDotRuns = m_runners.size();
131 size_t numFilePatchers = m_filePatchers.size();
132 if (numDotRuns+numFilePatchers>1)
133 {
134 if (Config_getInt(DOT_NUM_THREADS)<=1)
135 {
136 msg("Generating dot graphs in single threaded mode...\n");
137 }
138 else
139 {
140 msg("Generating dot graphs using %d parallel threads...\n",Config_getInt(DOT_NUM_THREADS));
141 }
142 }
143 size_t i=1;
144
145 bool setPath=FALSE;
146 if (Config_getBool(GENERATE_HTML))
147 {
148 setDotFontPath(Config_getString(HTML_OUTPUT));
149 setPath=TRUE;
150 }
151 else if (Config_getBool(GENERATE_LATEX))
152 {
153 setDotFontPath(Config_getString(LATEX_OUTPUT));
154 setPath=TRUE;
155 }
156 else if (Config_getBool(GENERATE_RTF))
157 {
158 setDotFontPath(Config_getString(RTF_OUTPUT));
159 setPath=TRUE;
160 }
161 else if (Config_getBool(GENERATE_DOCBOOK))
162 {
163 setDotFontPath(Config_getString(DOCBOOK_OUTPUT));
164 setPath=TRUE;
165 }
166 // fill work queue with dot operations
167 size_t prev=1;
168 if (Config_getInt(DOT_NUM_THREADS)<=1) // no threads to work with
169 {
170 for (auto & dr : m_runners)
171 {
172 msg("Running dot for graph %zu/%zu\n",prev,numDotRuns);
173 dr.second->run();
174 prev++;
175 }
176 }
177 else // use multiple threads to run instances of dot in parallel
178 {
179 std::vector< std::future<void> > results;
180 for (auto & dr: m_runners)
181 {
182 DotRunner *runner = dr.second.get();
183 auto process = [runner]()
184 {
185 runner->run();
186 };
187 results.emplace_back(m_workers.queue(process));
188 }
189 for (auto &f : results)
190 {
191 f.get();
192 msg("Running dot for graph %zu/%zu\n",prev,numDotRuns);
193 prev++;
194 }
195 }
196 if (setPath)
197 {
199 }
200
201 // patch the output file and insert the maps and figures
202 i=1;
203 // since patching the svg files may involve patching the header of the SVG
204 // (for zoomable SVGs), and patching the .html files requires reading that
205 // header after the SVG is patched, we first process the .svg files and
206 // then the other files.
207 for (auto & fp : m_filePatchers)
208 {
209 if (fp.second.isSVGFile())
210 {
211 msg("Patching output file %zu/%zu\n",i,numFilePatchers);
212 if (!fp.second.run()) return FALSE;
213 i++;
214 }
215 }
216 for (auto& fp : m_filePatchers)
217 {
218 if (!fp.second.isSVGFile())
219 {
220 msg("Patching output file %zu/%zu\n",i,numFilePatchers);
221 if (!fp.second.run()) return FALSE;
222 i++;
223 }
224 }
225 return TRUE;
226}
227
228//--------------------------------------------------------------------
229
230void writeDotGraphFromFile(const QCString &inFile,const QCString &outDir,
231 const QCString &outFile,GraphOutputFormat format,
232 const QCString &srcFile,int srcLine)
233{
234 Dir d(outDir.str());
235 if (!d.exists())
236 {
237 term("Output dir %s does not exist!\n",qPrint(outDir));
238 }
239
241 QCString imgName = QCString(outFile)+"."+imgExt;
242 QCString absImgName = QCString(d.absPath())+"/"+imgName;
243 QCString absOutFile = QCString(d.absPath())+"/"+outFile;
244
245 DotRunner dotRun(inFile);
246 if (format==GraphOutputFormat::BITMAP)
247 {
248 dotRun.addJob(Config_getEnumAsString(DOT_IMAGE_FORMAT),absImgName,srcFile,srcLine);
249 }
250 else // format==GraphOutputFormat::EPS
251 {
252 if (Config_getBool(USE_PDFLATEX))
253 {
254 dotRun.addJob("pdf",absOutFile+".pdf",srcFile,srcLine);
255 }
256 else
257 {
258 dotRun.addJob("ps",absOutFile+".eps",srcFile,srcLine);
259 }
260 }
261
262 dotRun.preventCleanUp();
263 if (!dotRun.run())
264 {
265 return;
266 }
267
268 Doxygen::indexList->addImageFile(imgName);
269
270}
271
272/*! Writes user defined image map to the output.
273 * \param t text stream to write to
274 * \param inFile just the basename part of the filename
275 * \param outDir output directory
276 * \param relPath relative path the to root of the output dir
277 * \param baseName the base name of the output files
278 * \param context the scope in which this graph is found (for resolving links)
279 * \param graphId a unique id for this graph, use for dynamic sections
280 * \param srcFile the source file
281 * \param srcLine the line number in the source file
282 */
284 const QCString &inFile, const QCString &outDir,
285 const QCString &relPath, const QCString &baseName,
286 const QCString &context,int graphId,
287 const QCString &srcFile,int srcLine)
288{
289
290 Dir d(outDir.str());
291 if (!d.exists())
292 {
293 term("Output dir %s does not exist!\n",qPrint(outDir));
294 }
295
296 QCString mapName = baseName+".map";
298 QCString imgName = baseName+"."+imgExt;
299 QCString absOutFile = QCString(d.absPath())+"/"+mapName;
300
301 DotRunner dotRun(inFile);
302 dotRun.addJob(MAP_CMD,absOutFile,srcFile,srcLine);
303 dotRun.preventCleanUp();
304 if (!dotRun.run())
305 {
306 return;
307 }
308
309 if (imgExt=="svg") // vector graphics
310 {
311 QCString svgName = outDir+"/"+baseName+".svg";
312 DotFilePatcher::writeSVGFigureLink(t,relPath,baseName,svgName);
313 DotFilePatcher patcher(svgName);
314 patcher.addSVGConversion("",TRUE,context,TRUE,graphId);
315 patcher.run();
316 }
317 else // bitmap graphics
318 {
319 TextStream tt;
320 t << "<img src=\"" << relPath << imgName << "\" alt=\""
321 << imgName << "\" border=\"0\" usemap=\"#" << mapName << "\"/>\n";
322 DotFilePatcher::convertMapFile(tt, absOutFile, relPath ,TRUE, context);
323 if (!tt.empty())
324 {
325 t << "<map name=\"" << mapName << "\" id=\"" << mapName << "\">";
326 t << tt.str();
327 t << "</map>\n";
328 }
329 }
330 d.remove(absOutFile.str());
331}
Class representing a directory in the file system.
Definition dir.h:75
std::string absPath() const
Definition dir.cpp:363
bool remove(const std::string &path, bool acceptsAbsPath=true) const
Definition dir.cpp:314
bool exists() const
Definition dir.cpp:257
Helper class to insert a set of map file into an output file.
static bool convertMapFile(TextStream &t, const QCString &mapName, const QCString &relPath, bool urlOnly=FALSE, const QCString &context=QCString())
int addSVGConversion(const QCString &relPath, bool urlOnly, const QCString &context, bool zoomable, int graphId)
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.
DotManager()
Definition dot.cpp:84
std::map< std::string, DotFilePatcher > m_filePatchers
Definition dot.h:48
DotFilePatcher * createFilePatcher(const QCString &fileName)
Definition dot.cpp:116
bool run()
Definition dot.cpp:128
std::map< std::string, std::unique_ptr< DotRunner > > m_runners
Definition dot.h:47
ThreadPool m_workers
Definition dot.h:49
virtual ~DotManager()
Definition dot.cpp:88
static DotManager * instance()
Definition dot.cpp:78
DotRunner * createRunner(const QCString &absDotName, const QCString &md5Hash)
Definition dot.cpp:92
Helper class to run dot from doxygen from multiple threads.
Definition dotrunner.h:31
void preventCleanUp()
Prevent cleanup of the dot file (for user provided dot files)
Definition dotrunner.h:54
void addJob(const QCString &format, const QCString &output, const QCString &srcFile, int srcLine)
Adds an additional job to the run.
bool run()
Runs dot for all jobs added.
static IndexList * indexList
Definition doxygen.h:134
This is an alternative implementation of QCString.
Definition qcstring.h:101
QCString & prepend(const char *s)
Definition qcstring.h:407
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:150
const std::string & str() const
Definition qcstring.h:526
Text streaming class that buffers data.
Definition textstream.h:36
bool empty() const
Returns true iff the buffer is empty.
Definition textstream.h:253
std::string str() const
Return the contents of the buffer as a std::string object.
Definition textstream.h:229
#define Config_getInt(name)
Definition config.h:34
#define Config_getEnumAsString(name)
Definition config.h:36
#define Config_getBool(name)
Definition config.h:33
#define Config_getString(name)
Definition config.h:32
static void setDotFontPath(const QCString &path)
Definition dot.cpp:42
static QCString g_dotFontPath
Definition dot.cpp:38
static void unsetDotFontPath()
Definition dot.cpp:63
#define MAP_CMD
Definition dot.cpp:34
static std::mutex g_dotManagerMutex
Definition dot.cpp:40
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:283
void writeDotGraphFromFile(const QCString &inFile, const QCString &outDir, const QCString &outFile, GraphOutputFormat format, const QCString &srcFile, int srcLine)
Definition dot.cpp:230
GraphOutputFormat
Definition dotgraph.h:29
void msg(const char *fmt,...)
Definition message.cpp:98
#define err(fmt,...)
Definition message.h:84
#define term(fmt,...)
Definition message.h:94
QCString pathListSeparator()
Definition portable.cpp:400
void setenv(const QCString &variable, const QCString &value)
Definition portable.cpp:303
void unsetenv(const QCString &variable)
Definition portable.cpp:318
QCString getenv(const QCString &variable)
Definition portable.cpp:338
Portable versions of functions that are platform dependent.
const char * qPrint(const char *s)
Definition qcstring.h:661
#define TRUE
Definition qcstring.h:37
#define FALSE
Definition qcstring.h:34
#define ASSERT(x)
Definition qcstring.h:39
QCString getDotImageExtension()
Definition util.cpp:6616
A bunch of utility functions.