Generate markdown manual at build time.

This commit is contained in:
Bartosz Taudul
2026-08-31 18:23:06 +02:00
parent 00e152b97a
commit c6f1cb7585
12 changed files with 325 additions and 5674 deletions

1
.gitignore vendored
View File

@@ -17,6 +17,7 @@ manual/t*.bbl
manual/t*.blg
manual/t*.fdb_latexmk
manual/t*.fls
manual/tracy.md
profiler/build-appimage
.deps/
.dirstamp

View File

@@ -288,7 +288,8 @@ fs.writeFileSync('/tmp/toolbar-zoom.png', buf);
| 1012–1036 | 2142681495 | ▶ next frame — focuses the timeline on the next frame (verified: view range 25.82 s → 203.38 ms) |
| 1052–1072 | 2961929287 | ▼ frame set selection (switch the active frame set) |
Button names and functions: the user manual, *Control menu* (`manual/tracy.md`). The
Button names and functions: the user manual, *Control menu* (`manual/tracy.md`,
generated into the profiler build directory by the build, e.g. `build/profiler/manual/tracy.md`). The
web build omits *Connection* (live capture only) and *Tracy Assist* (desktop only).
## User scale (DPI zoom)

73
cmake/manual.cmake Normal file
View File

@@ -0,0 +1,73 @@
set(PANDOC_VERSION 3.9.0.2)
find_package(Python3 COMPONENTS Interpreter REQUIRED)
find_program(TRACY_PANDOC pandoc)
if(TRACY_PANDOC)
execute_process(
COMMAND ${TRACY_PANDOC} --version
OUTPUT_VARIABLE _pandoc_version
OUTPUT_STRIP_TRAILING_WHITESPACE
RESULT_VARIABLE _pandoc_rc
ERROR_QUIET)
string(REGEX MATCH "pandoc [0-9.]+" _pandoc_version "${_pandoc_version}")
if(NOT _pandoc_rc EQUAL 0 OR NOT _pandoc_version STREQUAL "pandoc ${PANDOC_VERSION}")
set(TRACY_PANDOC "")
endif()
endif()
if(TRACY_PANDOC)
message(STATUS "Using system pandoc ${PANDOC_VERSION}: ${TRACY_PANDOC}")
else()
if(WIN32)
if(NOT CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64)$")
message(FATAL_ERROR "No pinned pandoc ${PANDOC_VERSION} binary for ${CMAKE_HOST_SYSTEM_PROCESSOR}; install pandoc ${PANDOC_VERSION}")
endif()
set(_pandoc_asset pandoc-${PANDOC_VERSION}-windows-x86_64.zip)
set(_pandoc_hash c97542f2800f446e788d9f74237856d995421ad1bb3cc8324286840c5f272d3a)
set(_pandoc_exe pandoc-${PANDOC_VERSION}/pandoc.exe)
elseif(APPLE)
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(arm64|aarch64)$")
set(_pandoc_asset pandoc-${PANDOC_VERSION}-arm64-macOS.zip)
set(_pandoc_hash 6e9eca844076bcbb599bbeebbba78a70f93b5307782b85c2c272872812c88875)
set(_pandoc_exe pandoc-${PANDOC_VERSION}-arm64/bin/pandoc)
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64)$")
set(_pandoc_asset pandoc-${PANDOC_VERSION}-x86_64-macOS.zip)
set(_pandoc_hash b9fbceabccbc8f34ac021a50483fc32f8160568d0b4b2c22d81bb29e3054fd82)
set(_pandoc_exe pandoc-${PANDOC_VERSION}-x86_64/bin/pandoc)
else()
message(FATAL_ERROR "No pinned pandoc ${PANDOC_VERSION} binary for ${CMAKE_HOST_SYSTEM_PROCESSOR}; install pandoc ${PANDOC_VERSION}")
endif()
else()
if(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(aarch64|arm64)$")
set(_pandoc_asset pandoc-${PANDOC_VERSION}-linux-arm64.tar.gz)
set(_pandoc_hash b6d21e8f9c3b15744f5a7ab40248019157ed7793875dbe0383d4c82ff572b528)
set(_pandoc_exe pandoc-${PANDOC_VERSION}/bin/pandoc)
elseif(CMAKE_HOST_SYSTEM_PROCESSOR MATCHES "^(x86_64|AMD64|amd64)$")
set(_pandoc_asset pandoc-${PANDOC_VERSION}-linux-amd64.tar.gz)
set(_pandoc_hash a69abfababda8a56969a254b09f9553a7be89ddec00d4e0fe9fd585d71a67508)
set(_pandoc_exe pandoc-${PANDOC_VERSION}/bin/pandoc)
else()
message(FATAL_ERROR "No pinned pandoc ${PANDOC_VERSION} binary for ${CMAKE_HOST_SYSTEM_PROCESSOR}; install pandoc ${PANDOC_VERSION}")
endif()
endif()
set(TRACY_PANDOC ${CMAKE_BINARY_DIR}/tools/${_pandoc_exe})
if(NOT EXISTS ${TRACY_PANDOC})
message(STATUS "Downloading pandoc ${PANDOC_VERSION} (${_pandoc_asset})")
file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/tools)
file(DOWNLOAD
https://github.com/jgm/pandoc/releases/download/${PANDOC_VERSION}/${_pandoc_asset}
${CMAKE_BINARY_DIR}/tools/pandoc.archive
EXPECTED_HASH SHA256=${_pandoc_hash}
STATUS _pandoc_download)
list(GET _pandoc_download 0 _pandoc_download_rc)
if(NOT _pandoc_download_rc STREQUAL "0")
file(REMOVE ${CMAKE_BINARY_DIR}/tools/pandoc.archive)
message(FATAL_ERROR "Failed to download pandoc ${PANDOC_VERSION}: ${_pandoc_download}")
endif()
file(ARCHIVE_EXTRACT
INPUT ${CMAKE_BINARY_DIR}/tools/pandoc.archive
DESTINATION ${CMAKE_BINARY_DIR}/tools)
file(REMOVE ${CMAKE_BINARY_DIR}/tools/pandoc.archive)
endif()
endif()

View File

@@ -1 +0,0 @@
The LaTeX source file (tracy.tex) and the resulting PDF file (tracy.pdf) are the only authorative version of the user manual. Do NOT modify the Markdown user manual (tracy.md) by hand. It is only meant to be updated via the latex2md.sh script.

View File

@@ -1,35 +0,0 @@
/\\begin\{bclogo\}\[/ {
in_bclogo = 1
bclogo_type = ""
next
}
in_bclogo && /logo=/ {
if (/\\bcbombe/) bclogo_type = "bcbombe"
else if (/\\bcattention/) bclogo_type = "bcattention"
else if (/\\bclampe/) bclogo_type = "bclampe"
else if (/\\bcquestion/) bclogo_type = "bcquestion"
next
}
in_bclogo && /noborder|couleur/ {
next
}
in_bclogo {
line = $0
sub(/^[ \t]*\]?\{/, "", line)
sub(/\}.*$/, "", line)
bclogo_title = line
if (bclogo_type == "bcbombe") prefix = "IMPORTANT"
else if (bclogo_type == "bcattention") prefix = "CAUTION"
else if (bclogo_type == "bclampe") prefix = "TIP"
else prefix = "NOTE"
printf "\\begin{quote}\\textbf{%s:%s}\\par\n", prefix, bclogo_title
in_bclogo = 0
next
}
/\\end\{bclogo\}/ {
printf "\\end{quote}\n"
next
}
{ print }

View File

@@ -1,64 +0,0 @@
#!/usr/bin/env python3
"""Replace Font Awesome icon macros in LaTeX with Unicode codepoints."""
import re
import sys
def pascal_to_snake(name):
"""Convert PascalCase to UPPER_SNAKE_CASE."""
result = name[0]
for i in range(1, len(name)):
if name[i].isupper() and name[i - 1].islower():
result += '_'
result += name[i]
return result.upper()
def main():
if len(sys.argv) < 3:
print(f"Usage: {sys.argv[0]} <header_path> <tex_path>", file=sys.stderr)
sys.exit(1)
header_path = sys.argv[1]
tex_path = sys.argv[2]
# Parse header: ICON_FA_SNAKE_CASE -> Unicode char
icons = {}
with open(header_path) as f:
for line in f:
m = re.match(
r'#define\s+ICON_FA_(\w+)\s+.*?//\s*(U\+([0-9a-fA-F]+))', line
)
if m:
snake = m.group(1)
parts = snake.split('_')
pascal = ''.join(p.capitalize() for p in parts)
codepoint = int(m.group(3), 16)
icons[pascal] = chr(codepoint)
# Read tex file
with open(tex_path) as f:
text = f.read()
# Find all \faXxx used in the text (uppercase first letter excludes \fancyhead etc.)
used = set()
for m in re.finditer(r'\\fa([A-Z][a-zA-Z0-9]*)', text):
used.add(m.group(1))
# Replace each used icon, longest names first to avoid prefix conflicts
for name in sorted(used, key=lambda n: (-len(n), n)):
if name not in icons:
print(f"Warning: \\fa{name} not found in header", file=sys.stderr)
continue
char = icons[name]
# Order matters: more specific patterns first
text = text.replace(f'\\fa{name}{{}}~', f'{char} ')
text = text.replace(f'\\fa{name}{{}}', char)
text = text.replace(f'\\fa{name}~', f'{char} ')
text = text.replace(f'\\fa{name}', char)
# Write back
with open(tex_path, 'w') as f:
f.write(text)
if __name__ == '__main__':
main()

View File

@@ -1,77 +0,0 @@
#!/usr/bin/env python3
"""Append icon legend blocks to each markdown section containing Font Awesome icons."""
import re
import sys
def _extract_icons(lines):
"""Return deduplicated icon chars from lines, in order of first appearance."""
seen = set()
icons = []
for line in lines:
for ch in line:
cp = ord(ch)
if 0xE000 <= cp <= 0xF8FF and ch not in seen:
seen.add(ch)
icons.append(ch)
return icons
def _append_legend(result_lines, icons, icon_names):
"""Append a legend block for the given icons."""
result_lines.append('')
result_lines.append('-----')
result_lines.append('')
for ch in icons:
name = icon_names.get(ch, f'Unknown(U+{ord(ch):04X})')
result_lines.append(f'{ch} - {name} icon')
result_lines.append('')
def main():
if len(sys.argv) < 3:
print(f"Usage: {sys.argv[0]} <header_path> <md_path>", file=sys.stderr)
sys.exit(1)
header_path = sys.argv[1]
md_path = sys.argv[2]
# Build char -> name mapping from header
icon_names = {}
with open(header_path) as f:
for line in f:
m = re.match(
r'#define\s+ICON_FA_(\w+)\s+.*?//\s*(U\+([0-9a-fA-F]+))', line
)
if m:
snake = m.group(1)
parts = snake.split('_')
pascal = ' '.join(p.capitalize() for p in parts)
codepoint = int(m.group(3), 16)
icon_names[chr(codepoint)] = pascal
with open(md_path, encoding='utf-8') as f:
lines = f.read().split('\n')
# Build chunk boundaries: header lines and EOF
chunk_starts = [i for i, line in enumerate(lines) if line.startswith('#')]
# Also add index 0 as a chunk start if there's pre-header content
if chunk_starts and chunk_starts[0] > 0:
chunk_starts.insert(0, 0)
result_lines = []
for ci, start in enumerate(chunk_starts):
end = chunk_starts[ci + 1] if ci + 1 < len(chunk_starts) else len(lines)
icons = _extract_icons(lines[start:end])
result_lines.extend(lines[start:end])
if icons:
_append_legend(result_lines, icons, icon_names)
with open(md_path, 'w', encoding='utf-8') as f:
f.write('\n'.join(result_lines))
if __name__ == '__main__':
main()

232
manual/latex2md.py Normal file
View File

@@ -0,0 +1,232 @@
#!/usr/bin/env python3
"""Generate the Markdown user manual from the LaTeX source.
Cross-platform replacement for the previous sh/sed/awk pipeline. pandoc is
invoked as a subprocess; its Lua filter (filter.lua, next to the .tex file)
stays with pandoc.
"""
import argparse
import os
import re
import subprocess
import sys
PANDOC_MARKDOWN = 'markdown-simple_tables-multiline_tables-grid_tables+pipe_tables'
NAMES = (
('quicklook', 'A quick look at Tracy Profiler'),
('firststeps', 'First steps'),
('client', 'Client markup'),
('capturing', 'Capturing the data'),
('analyzingdata', 'Analyzing captured data'),
('tracyassist', 'Tracy Assist'),
('csvexport', 'Exporting zone statistics to CSV'),
('importingdata', 'Importing external profiling data'),
('configurationfiles', 'Configuration files'),
)
def parse_icon_header(path):
"""Return (pascal_name -> char, char -> 'Word Word') maps from the icon header."""
fa = {}
names = {}
with open(path, encoding='utf-8') as f:
for line in f:
m = re.match(
r'#define\s+ICON_FA_(\w+)\s+.*?//\s*(U\+([0-9a-fA-F]+))', line
)
if m:
parts = m.group(1).split('_')
ch = chr(int(m.group(3), 16))
fa[''.join(p.capitalize() for p in parts)] = ch
names[ch] = ' '.join(p.capitalize() for p in parts)
return fa, names
def substitute_icons(text, fa):
used = set(re.findall(r'\\fa([A-Z][a-zA-Z0-9]*)', text))
for name in sorted(used, key=lambda n: (-len(n), n)):
if name not in fa:
print(f"Warning: \\fa{name} not found in header", file=sys.stderr)
continue
ch = fa[name]
text = text.replace(f'\\fa{name}{{}}~', f'{ch} ')
text = text.replace(f'\\fa{name}{{}}', ch)
text = text.replace(f'\\fa{name}~', f'{ch} ')
text = text.replace(f'\\fa{name}', ch)
return text
def pre_sed(text):
text = text.replace('\\menu[,]', '')
text = text.replace('\\keys', '')
text = text.replace('\\ctrl', 'Ctrl')
text = text.replace('\\shift', 'Shift')
text = text.replace('\\Alt', 'Alt')
text = text.replace('\\del', 'Delete')
return text
def post_sed(text):
for m in ('LMB', 'MMB', 'RMB', 'Scroll'):
text = text.replace(f'\\{m}{{}}~', '')
text = text.replace('\\textsigma', 'σ')
for l in 'abc':
text = text.replace(f'@\\circled{{{l}}}@', f'({l})')
for l in 'abc':
text = text.replace(f'\\circled{{{l}}}', f'({l})')
text = text.replace('@\\ldots@', '…')
text = re.sub(r',?escapeinside=\{\}\{\}', '', text)
for label, name in NAMES:
text = text.replace(f'\\nameref{{{label}}}', name)
return text
def bclogo2quote(text):
"""Convert \\begin{bclogo} admonition blocks to \\begin{quote}\\textbf{PREFIX:...}."""
prefix = {'bcbombe': 'IMPORTANT', 'bcattention': 'CAUTION', 'bclampe': 'TIP'}
out = []
in_bclogo = False
bclogo_type = ''
for line in text.split('\n'):
if not in_bclogo and r'\begin{bclogo}[' in line:
in_bclogo = True
bclogo_type = ''
continue
if in_bclogo and 'logo=' in line:
if r'\bcbombe' in line:
bclogo_type = 'bcbombe'
elif r'\bcattention' in line:
bclogo_type = 'bcattention'
elif r'\bclampe' in line:
bclogo_type = 'bclampe'
elif r'\bcquestion' in line:
bclogo_type = 'bcquestion'
continue
if in_bclogo and ('noborder' in line or 'couleur' in line):
continue
if in_bclogo:
title = re.sub(r'\}.*$', '', re.sub(r'^[ \t]*\]?\{', '', line))
out.append(f"\\begin{{quote}}\\textbf{{{prefix.get(bclogo_type, 'NOTE')}:{title}}}\\par")
in_bclogo = False
continue
if r'\end{bclogo}' in line:
out.append('\\end{quote}')
continue
out.append(line)
return '\n'.join(out)
def run_pandoc(pandoc, filter_path, tex):
args = [
pandoc,
'--wrap=none',
'--reference-location=block',
'--number-sections',
'-f', 'latex',
'-t', PANDOC_MARKDOWN,
'-s',
'-L', filter_path,
'-',
]
proc = subprocess.run(args, input=tex.encode('utf-8'), capture_output=True)
if proc.returncode != 0:
sys.stderr.write(proc.stderr.decode('utf-8', 'replace'))
sys.exit(proc.returncode)
return proc.stdout.decode('utf-8')
def tablecaption(text):
"""Pandoc emits table captions as ": ..." lines; italicize them instead."""
out = []
incap = False
for line in text.split('\n'):
if not incap and line.startswith(': '):
incap = True
line = '_' + line[2:]
if incap and not line.endswith('\\'):
out.append(line + '_')
incap = False
continue
out.append(line)
return '\n'.join(out)
def admonitions(text):
for kind in ('IMPORTANT', 'TIP', 'CAUTION', 'NOTE'):
text = re.sub(
rf'^> \*\*{kind}:([^*]*)\*\*',
f'> [!{kind}]\n> **\\1**',
text,
flags=re.M,
)
return text
def icon_explain(text, names):
"""Append an icon legend to each top-level section containing FA icons."""
lines = text.split('\n')
chunk_starts = [i for i, line in enumerate(lines) if line.startswith('#')]
if chunk_starts and chunk_starts[0] > 0:
chunk_starts.insert(0, 0)
out = []
for ci, start in enumerate(chunk_starts):
end = chunk_starts[ci + 1] if ci + 1 < len(chunk_starts) else len(lines)
chunk = lines[start:end]
icons = []
seen = set()
for line in chunk:
for ch in line:
cp = ord(ch)
if 0xE000 <= cp <= 0xF8FF and ch not in seen:
seen.add(ch)
icons.append(ch)
out.extend(chunk)
if icons:
out.append('')
out.append('-----')
out.append('')
for ch in icons:
out.append(f"{ch} - {names.get(ch, f'Unknown(U+{ord(ch):04X})')} icon")
out.append('')
return '\n'.join(out)
def main():
ap = argparse.ArgumentParser(description='Generate the Markdown user manual')
ap.add_argument('--tex', required=True, help='path to tracy.tex')
ap.add_argument('--out', required=True, help='path of the generated tracy.md')
ap.add_argument('--pandoc', required=True, help='path to the pandoc executable')
ap.add_argument('--icons', required=True, help='path to IconsFontAwesome7.h')
args = ap.parse_args()
fa, names = parse_icon_header(args.icons)
with open(args.tex, encoding='utf-8') as f:
text = f.read()
text = pre_sed(text)
text = substitute_icons(text, fa)
text = post_sed(text)
text = bclogo2quote(text)
md = run_pandoc(
args.pandoc,
os.path.join(os.path.dirname(os.path.abspath(args.tex)), 'filter.lua'),
text,
)
md = tablecaption(md)
md = admonitions(md)
md = icon_explain(md, names)
out_dir = os.path.dirname(os.path.abspath(args.out))
if out_dir:
os.makedirs(out_dir, exist_ok=True)
with open(args.out, 'w', encoding='utf-8', newline='\n') as f:
f.write(md)
if __name__ == '__main__':
main()

View File

@@ -1,54 +0,0 @@
#!/bin/sh
cp -f tracy.tex _tmp.tex
sed -i -e 's@\\menu\[,\]@@g' _tmp.tex
sed -i -e 's@\\keys@@g' _tmp.tex
sed -i -e 's@\\ctrl@Ctrl@g' _tmp.tex
sed -i -e 's@\\shift@Shift@g' _tmp.tex
sed -i -e 's@\\Alt@Alt@g' _tmp.tex
sed -i -e 's@\\del@Delete@g' _tmp.tex
python3 fa-icons.py ../profiler/src/profiler/IconsFontAwesome7.h _tmp.tex
sed -i -e 's@\\LMB{}~@@g' _tmp.tex
sed -i -e 's@\\MMB{}~@@g' _tmp.tex
sed -i -e 's@\\RMB{}~@@g' _tmp.tex
sed -i -e 's@\\Scroll{}~@@g' _tmp.tex
sed -i -e 's@\\textsigma@σ@g' _tmp.tex
# Resolve \circled{} markers and lstlisting escapeinside (@...@) snippets, which
# pandoc would otherwise emit verbatim or drop, to their Unicode equivalents.
sed -i -e 's|@\\circled{a}@|(a)|g' -e 's|@\\circled{b}@|(b)|g' -e 's|@\\circled{c}@|(c)|g' _tmp.tex
sed -i -e 's|\\circled{a}|(a)|g' -e 's|\\circled{b}|(b)|g' -e 's|\\circled{c}|(c)|g' _tmp.tex
sed -i -e 's|@\\ldots@|…|g' _tmp.tex
# pandoc doens't recognize this lstlisting option and emits it verbatim
sed -i -e 's|,\?escapeinside={}{}||g' _tmp.tex
sed -i -e 's@\\nameref{quicklook}@A quick look at Tracy Profiler@g' _tmp.tex
sed -i -e 's@\\nameref{firststeps}@First steps@g' _tmp.tex
sed -i -e 's@\\nameref{client}@Client markup@g' _tmp.tex
sed -i -e 's@\\nameref{capturing}@Capturing the data@g' _tmp.tex
sed -i -e 's@\\nameref{analyzingdata}@Analyzing captured data@g' _tmp.tex
sed -i -e 's@\\nameref{tracyassist}@Tracy Assist@g' _tmp.tex
sed -i -e 's@\\nameref{csvexport}@Exporting zone statistics to CSV@g' _tmp.tex
sed -i -e 's@\\nameref{importingdata}@Importing external profiling data@g' _tmp.tex
sed -i -e 's@\\nameref{configurationfiles}@Configuration files@g' _tmp.tex
awk -f bclogo2quote.awk _tmp.tex > _tmp_quoted.tex
mv _tmp_quoted.tex _tmp.tex
pandoc --wrap=none --reference-location=block --number-sections -L filter.lua -t 'markdown-simple_tables-multiline_tables-grid_tables+pipe_tables' -s _tmp.tex -o tracy.md
awk -f tablecaption.awk tracy.md > _tmp_caption.md
mv _tmp_caption.md tracy.md
sed -i -e 's/^> \*\*IMPORTANT:\([^*]*\)\*\*/> [!IMPORTANT]\
> **\1**/' tracy.md
sed -i -e 's/^> \*\*TIP:\([^*]*\)\*\*/> [!TIP]\
> **\1**/' tracy.md
sed -i -e 's/^> \*\*CAUTION:\([^*]*\)\*\*/> [!CAUTION]\
> **\1**/' tracy.md
sed -i -e 's/^> \*\*NOTE:\([^*]*\)\*\*/> [!NOTE]\
> **\1**/' tracy.md
python3 icon-explain.py ../profiler/src/profiler/IconsFontAwesome7.h tracy.md
rm -f _tmp.tex

View File

@@ -1,16 +0,0 @@
# Pandoc emits table captions as a line beginning with ": ", which GitHub
# renders literally instead of as a caption. Strip the marker and italicize
# the caption instead. Captions may span several physical lines when they
# contain a hard line break (a trailing backslash). Underscores are used for
# the emphasis so captions that already contain "*...*" markup are left intact.
!incap && /^: / {
incap = 1
$0 = "_" substr($0, 3)
}
incap && !/\\$/ {
print $0 "_"
incap = 0
next
}
incap { print; next }
{ print }

File diff suppressed because it is too large Load Diff

View File

@@ -39,6 +39,7 @@ include(${CMAKE_CURRENT_LIST_DIR}/../cmake/config.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/vendor.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/server.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/GitRef.cmake)
include(${CMAKE_CURRENT_LIST_DIR}/../cmake/manual.cmake)
include(ExternalProject)
ExternalProject_Add(embed
@@ -181,8 +182,23 @@ Embed(PROFILER_FILES FontBold src/font/Roboto-Bold.ttf)
Embed(PROFILER_FILES FontItalic src/font/Roboto-Italic.ttf)
Embed(PROFILER_FILES FontBoldItalic src/font/Roboto-BoldItalic.ttf)
Embed(PROFILER_FILES FontEmoji src/font/NotoEmoji-Regular.ttf)
set(MANUAL_MD ${CMAKE_CURRENT_BINARY_DIR}/manual/tracy.md)
add_custom_command(
OUTPUT ${MANUAL_MD}
COMMAND ${Python3_EXECUTABLE} ${CMAKE_CURRENT_LIST_DIR}/../manual/latex2md.py
--tex ${CMAKE_CURRENT_LIST_DIR}/../manual/tracy.tex
--out ${MANUAL_MD}
--pandoc ${TRACY_PANDOC}
--icons ${CMAKE_CURRENT_LIST_DIR}/src/profiler/IconsFontAwesome7.h
DEPENDS
${CMAKE_CURRENT_LIST_DIR}/../manual/tracy.tex
${CMAKE_CURRENT_LIST_DIR}/../manual/filter.lua
${CMAKE_CURRENT_LIST_DIR}/../manual/latex2md.py
${CMAKE_CURRENT_LIST_DIR}/src/profiler/IconsFontAwesome7.h
COMMENT "Generating user manual"
VERBATIM)
Embed(PROFILER_FILES Manual ../manual/tracy.md TEXT)
Embed(PROFILER_FILES Manual manual/tracy.md TEXT BUILD)
Embed(PROFILER_FILES Text100Million src/achievements/100Million.md TEXT)
Embed(PROFILER_FILES TextConnectToClient src/achievements/ConnectToClient.md TEXT)