Doxygen
Loading...
Searching...
No Matches
mermaid.cpp
Go to the documentation of this file.
1/******************************************************************************
2 *
3 * Copyright (C) 1997-2026 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 <mutex>
17#include <fstream>
18
19#include "mermaid.h"
20#include "util.h"
21#include "portable.h"
22#include "config.h"
23#include "doxygen.h"
24#include "message.h"
25#include "debug.h"
26#include "dir.h"
27#include "indexlist.h"
28#include "threadpool.h"
29
30static std::mutex g_mermaidMutex;
31static int g_mermaidIndex = 1;
32
34{
35 static MermaidManager theInstance;
36 return theInstance;
37}
38
42
44{
45 switch (format)
46 {
47 case ImageFormat::PNG: return "png";
48 case ImageFormat::SVG: return "svg";
49 case ImageFormat::PDF: return "pdf";
50 }
51 return "png";
52}
53
55{
56 switch(outputFormat)
57 {
59 // fall through
61 // fall through
65 return Config_getBool(USE_PDFLATEX) ? ImageFormat::PDF : ImageFormat::PNG;
66 }
67 return ImageFormat::PNG;
68}
69
71 const QCString &content, ImageFormat imageFormat,
72 const QCString &srcFile, int srcLine)
73{
74 QCString outDir(outDirArg);
75 QCString baseName;
76
77 // strip any trailing slashes and backslashes
78 while (!outDir.isEmpty() && (outDir.at(outDir.length()-1)=='/' || outDir.at(outDir.length()-1)=='\\'))
79 {
80 outDir = outDir.left(outDir.length()-1);
81 }
82
83 if (fileName.isEmpty())
84 {
85 std::lock_guard<std::mutex> lock(g_mermaidMutex);
86 baseName = outDir + "/inline_mermaid_" + QCString().setNum(g_mermaidIndex++);
87 }
88 else
89 {
90 baseName = fileName;
91 int i = baseName.findRev('.');
92 if (i != -1) baseName = baseName.left(i);
93 baseName.prepend(outDir + "/");
94 }
95
96 QCString mmdName = baseName + ".mmd";
97
98 Debug::print(Debug::Mermaid, 0, "*** writeMermaidSource baseName: {}\n", baseName);
99 Debug::print(Debug::Mermaid, 0, "*** writeMermaidSource mmdName: {}\n", mmdName);
100
101 // Write the .mmd source file
102 std::ofstream file = Portable::openOutputStream(mmdName);
103 if (!file.is_open())
104 {
105 err_full(srcFile, srcLine, "Could not open file {} for writing", mmdName);
106 return baseName;
107 }
108 file.write(content.data(), content.length());
109 file.close();
110
111 // Store for batch processing in run()
112 m_diagrams.emplace_back(imageFormat,MermaidDiagramInfo(baseName, content, outDir, srcFile, srcLine));
113
114 return baseName;
115}
116
117void MermaidManager::generateMermaidOutput(const QCString &baseName, const QCString &/*outDir*/,
118 ImageFormat imageFormat, bool toIndex)
119{
120 if (!toIndex) return;
121 QCString imgName = baseName;
122 int i = imgName.findRev('/');
123 if (i != -1)
124 {
125 imgName = imgName.mid(i + 1);
126 }
127 imgName += "." + imageExtension(imageFormat);
128 Doxygen::indexList->addImageFile(imgName);
129}
130
131static void runMermaid(const MermaidManager::DiagramList &diagrams)
132{
133 //printf("runMermaidContent for %zu images\n",contentList.size());
134 if (diagrams.empty()) return;
135
136 QCString mmdc = Config_getString(MERMAID_PATH);
137 if (!mmdc.isEmpty() && mmdc.at(mmdc.length()-1) != '/' && mmdc.at(mmdc.length()-1) != '\\')
138 {
139 mmdc += "/";
140 }
141 mmdc += "mmdc";
142
143 QCString mermaidConfigFile = Config_getString(MERMAID_CONFIG_FILE);
144
145 struct MermaidCmd
146 {
147 MermaidCmd(const QCString &mmdc_,const QCString &args_,const QCString &ext_,const QCString &srcFile_,int srcLine_) :
148 mmdc(mmdc_), args(args_), ext(ext_), srcFile(srcFile_), srcLine(srcLine_) {}
149 QCString mmdc;
150 QCString args;
151 QCString ext;
152 QCString srcFile;
153 int srcLine;
154 };
155 std::vector<MermaidCmd> mermaidCmds;
156
157 for (const auto &diagram : diagrams)
158 {
159 //printf("content=%s\n",qPrint(mc.content));
160 if (diagram.info.content.isEmpty()) continue;
161
162 QCString ext = MermaidManager::imageExtension(diagram.imageFormat);
163
164 QCString inputFile = diagram.info.baseName + ".mmd";
165 QCString outputFile = diagram.info.baseName + "." + ext;
166
167 // Check if content has changed since last run (caching)
168 FileInfo fi(outputFile.str());
169 if (fi.exists())
170 {
171 QCString cachedContent = fileToString(inputFile);
172 if (cachedContent == diagram.info.content)
173 {
174 continue;
175 }
176 }
177
178 // Build the mmdc command arguments
179 QCString args;
180 args += "-q -i \"" + inputFile + "\" ";
181 args += "-o \"" + outputFile + "\" ";
182
183 if (!mermaidConfigFile.isEmpty())
184 {
185 args += "-c \"" + mermaidConfigFile + "\" ";
186 }
187
188 mermaidCmds.emplace_back(mmdc, args,ext, diagram.info.srcFile, diagram.info.srcLine);
189 }
190
191 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(DOT_NUM_THREADS));
192 size_t offset=0;
193 size_t total=mermaidCmds.size();
194 msg("Generating {} Mermaid files using {} threads\n", total, numThreads);
195 if (numThreads>1) // multi threaded version
196 {
197 ThreadPool threadPool(numThreads);
198 std::vector< std::future<int> > results;
199
200 // queue the work
201 for (const auto &cmd : mermaidCmds)
202 {
203 auto processFile = [&cmd]()
204 {
205 Debug::print(Debug::Mermaid, 0, "*** MermaidManager::run Running: {} {}\n", cmd.mmdc, cmd.args);
206 int exitCode = Portable::system(cmd.mmdc.data(), cmd.args.data(), TRUE);
207 if (exitCode != 0)
208 {
209 err_full(cmd.srcFile, cmd.srcLine,
210 "Problems running Mermaid (mmdc). Verify that the command '{} {}' works from the command line. Exit code: {}.",
211 cmd.mmdc, cmd.args, exitCode);
212 }
213 return exitCode;
214 };
215 results.emplace_back(threadPool.queue(processFile));
216 }
217
218 // wait for the results
219 for (auto &f : results)
220 {
221 offset++;
222 msg("Generating Mermaid file {}/{}\n", offset, total);
223 f.get();
224 }
225 }
226 else // single threaded version
227 {
228 for (const auto &cmd : mermaidCmds)
229 {
230 offset++;
231 msg("Generating Mermaid file {}/{}\n", offset, total);
232 Debug::print(Debug::Mermaid, 0, "*** MermaidManager::run Running: {} {}\n", cmd.mmdc, cmd.args);
233
234 int exitCode = Portable::system(cmd.mmdc.data(), cmd.args.data(), TRUE);
235 if (exitCode != 0)
236 {
237 err_full(cmd.srcFile, cmd.srcLine,
238 "Problems running Mermaid (mmdc). Verify that the command '{} {}' works from the command line. Exit code: {}.",
239 cmd.mmdc, cmd.args, exitCode);
240 }
241 }
242 }
243}
244
246{
247 Debug::print(Debug::Mermaid, 0, "*** MermaidManager::run\n");
249}
@ Mermaid
Definition debug.h:51
static void print(DebugMask mask, int prio, fmt::format_string< Args... > fmt, Args &&... args)
Definition debug.h:77
static IndexList * indexList
Definition doxygen.h:132
Minimal replacement for QFileInfo.
Definition fileinfo.h:23
bool exists() const
Definition fileinfo.cpp:30
void run()
Run mmdc tool for all collected diagrams.
Definition mermaid.cpp:245
OutputFormat
Mermaid output image formats.
Definition mermaid.h:44
void generateMermaidOutput(const QCString &baseName, const QCString &outDir, ImageFormat format, bool toIndex)
Register a generated Mermaid image with the index.
Definition mermaid.cpp:117
static QCString imageExtension(ImageFormat imageFormat)
Definition mermaid.cpp:43
static MermaidManager & instance()
Definition mermaid.cpp:33
DiagramList m_diagrams
Definition mermaid.h:87
static ImageFormat convertToImageFormat(OutputFormat outputFormat)
Definition mermaid.cpp:54
std::vector< MermaidDiagram > DiagramList
Definition mermaid.h:79
QCString writeMermaidSource(const QCString &outDirArg, const QCString &fileName, const QCString &content, ImageFormat format, const QCString &srcFile, int srcLine)
Write a Mermaid source file and register it for CLI rendering.
Definition mermaid.cpp:70
This is an alternative implementation of QCString.
Definition qcstring.h:101
QCString & prepend(const char *s)
Definition qcstring.h:422
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition qcstring.h:166
QCString mid(size_t index, size_t len=static_cast< size_t >(-1)) const
Definition qcstring.h:241
char & at(size_t i)
Returns a reference to the character at index i.
Definition qcstring.h:593
bool isEmpty() const
Returns TRUE iff the string is empty.
Definition qcstring.h:163
const std::string & str() const
Definition qcstring.h:552
QCString & setNum(short n)
Definition qcstring.h:459
int findRev(char c, int index=-1, bool cs=TRUE) const
Definition qcstring.cpp:96
const char * data() const
Returns a pointer to the contents of the string in the form of a 0-terminated C string.
Definition qcstring.h:172
QCString left(size_t len) const
Definition qcstring.h:229
Class managing a pool of worker threads.
Definition threadpool.h:48
auto queue(F &&f, Args &&... args) -> std::future< decltype(f(args...))>
Queue the callable function f for the threads to execute.
Definition threadpool.h:77
#define Config_getInt(name)
Definition config.h:34
#define Config_getBool(name)
Definition config.h:33
#define Config_getString(name)
Definition config.h:32
static void runMermaid(const MermaidManager::DiagramList &diagrams)
Definition mermaid.cpp:131
static std::mutex g_mermaidMutex
Definition mermaid.cpp:30
static int g_mermaidIndex
Definition mermaid.cpp:31
#define msg(fmt,...)
Definition message.h:94
#define err_full(file, line, fmt,...)
Definition message.h:132
std::ofstream openOutputStream(const QCString &name, bool append=false)
Definition portable.cpp:648
int system(const QCString &command, const QCString &args, bool commandHasConsole=true)
Definition portable.cpp:105
Portable versions of functions that are platform dependent.
#define TRUE
Definition qcstring.h:37
QCString fileToString(const QCString &name, bool filter, bool isSourceCode)
Definition util.cpp:1494
QCString getDotImageExtension()
Definition util.cpp:6307
A bunch of utility functions.