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 DString &content, ImageFormat imageFormat,
72 const DString &srcFile, int srcLine)
73{
74 DString outDir(outDirArg);
75 DString baseName;
76
77 // strip any trailing slashes and backslashes
78 while (!outDir.empty() && (outDir.at(outDir.length()-1)=='/' || outDir.at(outDir.length()-1)=='\\'))
79 {
80 outDir = outDir.left(outDir.length()-1);
81 }
82
83 if (fileName.empty())
84 {
85 std::lock_guard<std::mutex> lock(g_mermaidMutex);
86 baseName = outDir + "/inline_mermaid_" + DString().setNum(g_mermaidIndex++);
87 }
88 else
89 {
90 baseName = fileName;
91 if (size_t i = baseName.rfind('.'); i!=DString::npos) baseName = baseName.left(i);
92 baseName.prepend(outDir + "/");
93 }
94
95 DString mmdName = baseName + ".mmd";
96
97 Debug::print(Debug::Mermaid, 0, "*** writeMermaidSource baseName: {}\n", baseName);
98 Debug::print(Debug::Mermaid, 0, "*** writeMermaidSource mmdName: {}\n", mmdName);
99
100 // Write the .mmd source file
101 std::ofstream file = Portable::openOutputStream(mmdName);
102 if (!file.is_open())
103 {
104 err_full(srcFile, srcLine, "Could not open file {} for writing", mmdName);
105 return baseName;
106 }
107 file.write(content.data(), content.length());
108 file.close();
109
110 // Store for batch processing in run()
111 m_diagrams.emplace_back(imageFormat,MermaidDiagramInfo(baseName, content, outDir, srcFile, srcLine));
112
113 return baseName;
114}
115
116void MermaidManager::generateMermaidOutput(const DString &baseName, const DString &/*outDir*/,
117 ImageFormat imageFormat, bool toIndex)
118{
119 if (!toIndex) return;
120 DString imgName = baseName;
121 if (size_t i = imgName.rfind('/'); i!=DString::npos)
122 {
123 imgName = imgName.mid(i + 1);
124 }
125 imgName += "." + imageExtension(imageFormat);
127}
128
129static void runMermaid(const MermaidManager::DiagramList &diagrams)
130{
131 //printf("runMermaidContent for %zu images\n",contentList.size());
132 if (diagrams.empty()) return;
133
134 DString mmdc = Config_getString(MERMAID_PATH);
135 if (!mmdc.empty() && mmdc.at(mmdc.length()-1) != '/' && mmdc.at(mmdc.length()-1) != '\\')
136 {
137 mmdc += "/";
138 }
139 mmdc += "mmdc";
140
141 DString mermaidConfigFile = Config_getString(MERMAID_CONFIG_FILE);
142
143 struct MermaidCmd
144 {
145 MermaidCmd(const DString &mmdc_,const DString &args_,const DString &ext_,const DString &srcFile_,int srcLine_) :
146 mmdc(mmdc_), args(args_), ext(ext_), srcFile(srcFile_), srcLine(srcLine_) {}
147 DString mmdc;
148 DString args;
149 DString ext;
150 DString srcFile;
151 int srcLine;
152 };
153 std::vector<MermaidCmd> mermaidCmds;
154
155 for (const auto &diagram : diagrams)
156 {
157 //printf("content=%s\n",qPrint(mc.content));
158 if (diagram.info.content.empty()) continue;
159
160 DString ext = MermaidManager::imageExtension(diagram.imageFormat);
161
162 DString inputFile = diagram.info.baseName + ".mmd";
163 DString outputFile = diagram.info.baseName + "." + ext;
164
165 // Check if content has changed since last run (caching)
166 FileInfo fi(outputFile.str());
167 if (fi.exists())
168 {
169 DString cachedContent = fileToString(inputFile);
170 if (cachedContent == diagram.info.content)
171 {
172 continue;
173 }
174 }
175
176 // Build the mmdc command arguments
177 DString args;
178 args += "-q -i \"" + inputFile + "\" ";
179 args += "-o \"" + outputFile + "\" ";
180
181 if (!mermaidConfigFile.empty())
182 {
183 args += "-c \"" + mermaidConfigFile + "\" ";
184 }
185
186 mermaidCmds.emplace_back(mmdc, args,ext, diagram.info.srcFile, diagram.info.srcLine);
187 }
188
189 std::size_t numThreads = static_cast<std::size_t>(Config_getInt(DOT_NUM_THREADS));
190 size_t offset=0;
191 size_t total=mermaidCmds.size();
192 msg("Generating {} Mermaid files using {} threads\n", total, numThreads);
193 if (numThreads>1) // multi threaded version
194 {
195 ThreadPool threadPool(numThreads);
196 std::vector< std::future<int> > results;
197
198 // queue the work
199 for (const auto &cmd : mermaidCmds)
200 {
201 auto processFile = [&cmd]()
202 {
203 Debug::print(Debug::Mermaid, 0, "*** MermaidManager::run Running: {} {}\n", cmd.mmdc, cmd.args);
204 int exitCode = Portable::system(cmd.mmdc.data(), cmd.args.data(), true);
205 if (exitCode != 0)
206 {
207 err_full(cmd.srcFile, cmd.srcLine,
208 "Problems running Mermaid (mmdc). Verify that the command '{} {}' works from the command line. Exit code: {}.",
209 cmd.mmdc, cmd.args, exitCode);
210 }
211 return exitCode;
212 };
213 results.emplace_back(threadPool.queue(processFile));
214 }
215
216 // wait for the results
217 for (auto &f : results)
218 {
219 offset++;
220 msg("Generating Mermaid file {}/{}\n", offset, total);
221 f.get();
222 }
223 }
224 else // single threaded version
225 {
226 for (const auto &cmd : mermaidCmds)
227 {
228 offset++;
229 msg("Generating Mermaid file {}/{}\n", offset, total);
230 Debug::print(Debug::Mermaid, 0, "*** MermaidManager::run Running: {} {}\n", cmd.mmdc, cmd.args);
231
232 int exitCode = Portable::system(cmd.mmdc.data(), cmd.args.data(), true);
233 if (exitCode != 0)
234 {
235 err_full(cmd.srcFile, cmd.srcLine,
236 "Problems running Mermaid (mmdc). Verify that the command '{} {}' works from the command line. Exit code: {}.",
237 cmd.mmdc, cmd.args, exitCode);
238 }
239 }
240 }
241}
242
244{
245 // CLI mode creates images locally, other modes create inline diagram descriptions
246 // in the HTML output and rely on rendering them in the browser.
247 m_hasInlineDiagrams=Config_getEnum(MERMAID_RENDER_MODE)!=MERMAID_RENDER_MODE_t::CLI;
248}
249
251{
252 Debug::print(Debug::Mermaid, 0, "*** MermaidManager::run\n");
254}
A String class for use with Doxygen wrapping std::string and adding some additional functionality off...
Definition dstring.h:89
DString & setNum(short n)
Definition dstring.h:541
DString()=default
size_t rfind(char c, size_t pos=npos) const
Definition dstring.h:249
DString mid(size_t index, size_t len=npos) const
Definition dstring.h:323
bool empty() const
Returns true iff the string is empty (std::string compatible alias for isEmpty()).
Definition dstring.h:153
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:183
char & at(size_t i)
Returns a reference to the character at index i.
Definition dstring.h:675
DString & prepend(const char *s)
Definition dstring.h:504
DString left(size_t len) const
Definition dstring.h:311
const std::string & str() const
Definition dstring.h:634
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:162
size_t length() const
Returns the length of the string, not counting the 0-terminator.
Definition dstring.h:156
@ 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 addImageFile(const DString &name)
Definition indexlist.h:123
void run()
Run mmdc tool for all collected diagrams.
Definition mermaid.cpp:250
OutputFormat
Mermaid output image formats.
Definition mermaid.h:44
bool m_hasInlineDiagrams
Definition mermaid.h:92
static MermaidManager & instance()
Definition mermaid.cpp:33
DiagramList m_diagrams
Definition mermaid.h:91
void setHasInlineDiagrams()
Definition mermaid.cpp:243
static DString imageExtension(ImageFormat imageFormat)
Definition mermaid.cpp:43
static ImageFormat convertToImageFormat(OutputFormat outputFormat)
Definition mermaid.cpp:54
void generateMermaidOutput(const DString &baseName, const DString &outDir, ImageFormat format, bool toIndex)
Register a generated Mermaid image with the index.
Definition mermaid.cpp:116
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:70
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:129
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
int system(const DString &command, const DString &args, bool commandHasConsole=true)
Definition portable.cpp:105
std::ofstream openOutputStream(const DString &name, bool append=false)
Definition portable.cpp:648
Portable versions of functions that are platform dependent.
MermaidDiagramInfo(const DString &baseName_, const DString &content_, const DString &outDir_, const DString &srcFile_, int srcLine_)
Definition mermaid.h:28
DString fileToString(const DString &name, bool filter, bool isSourceCode)
Definition util.cpp:1120
DString getDotImageExtension()
Definition util.cpp:5498
A bunch of utility functions.