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