doxygen使用: 跨平台方式让markdown文件包含另一个文件

在这里插入图片描述

1. 目的和问题

项目代码的 changelog.md 有特殊用处和格式, 用它生成 doxygen 的 html 网页时, 内容不足, 需要添加额外内容; 这些额外内容直接放在 changelog.md 里会导致整个工程的编译、打包出错。

解决思路是另外写一个 docs/changelog.md 文件, 让它包含根目录的 changelog.md:

ChangeLog {#changelog}
====
This page lists change logs for each released version.<br/>

#include "../CHANGELOG.md"

其中 #include "../CHANGELOG.md" 意思是要用 C/C++ 编译器(其实是C预处理器),把内容替换过来。

原本的 changelog.md 内容为 :
在这里插入图片描述

预期结果如下,其中头部的 ChangeLog 标题和 This page lists chagne logs for each released version. 是增加的内容
在这里插入图片描述

在 Windows 上则是得到了非预期结果:
在这里插入图片描述

2. 解决思路

2.1 FILTER_PATTERNS 选项

容易在 StackOverflow 找到解决方案的大致方向: 在 Doxyfile 里配置 FILTER_PATTERNS 选项:

FILTER_PATTERNS = *.md="cpp -P "

这样的写法可以在 Linux / MacOSX 系统上生成预期结果, 但在 Windows 下没有 cpp 这个预处理命令使用。

其中 -P 意思是把替换后的内容输出到屏幕。

尝试过一些方法, 不过失败了, 报错内容是一个 doxygen 警告:

explicit link request to ‘include’ could not be resolved

简单罗列这些失败的方法:

  1. 直接用 VS2022 的 cl.exe 的路径

失败: 可能是路径有空格,也可能是 cl.exe 的 /P 参数不支持导致的。

  1. 找到了 mcpp 这个开源项目和它的魔改版本(https://github.com/zeroc-ice/mcpp)

失败: mcpp 的魔改版本不支持 -P 参数

  1. 封装 VS2022 的 cl.exe 为 cpp.cmd 脚本

失败: 和方法1 差不多。

2.2 基于 Python 的 FILTER_PATTERNS 选项

cross platform FILTER_PATTERNS in doxygen 这个问答中提示, 可以用 Python 脚本作为 FILTER_PATTERNS 的参数。于是可以自行实现一个替代 cpp 命令的 Python 脚本, 让它支持 #include "xxx" 替换为 xxx 文件的具体内容即可。

2.3 sledcpp.py 脚本

我实现的替代 cpp 命令的脚本名为 sledcpp.py, 内容如下

# sledcpp:  a naive C Pre-Processor implementation
# Author:   Zhuo Zhang <imzhuo#foxmail.com>
#
# Currently only support expanding `#include` directive once
# Used in generating doxygen webpages from markdown files which include another file
#
# Created:  2023-05-30 10:38:29
# Modified: 2023-05-30 10:38:48

import sys
import os

def getLinesOfTextFile(filepath):
    lines = []
    with open(filepath, encoding='utf-8') as fin:
        for line in fin.readlines():
            lines.append(line.strip())
    return lines


def expand_file_once(filepath):
    fileDir = os.path.dirname(filepath)
    with open(filepath) as fin:
        for line in fin.readlines():
            line = line.strip()
            if line.startswith('#include'):
                startPos = len('#include')
                depfile = line[startPos:].strip().split('"')[1]
                deppath = os.path.join(fileDir, depfile)
                deplines = getLinesOfTextFile(deppath)
                for item in deplines:
                    print(item)
            else:
                print(line)


if __name__ == '__main__':
    if len(sys.argv) != 2:
        print('Usage: python {:s} filepath'.format(sys.argv[0]))
        exit(1)
    
    filepath = sys.argv[1]
    expand_file_once(filepath)

3. 完整工程

3.1 目录结构

➜  b tree
.
├── CHANGELOG.md
├── docs
│   ├── changelog.md
│   └── root.md
├── Doxyfile
├── generate_doxyfile.py
├── hello.h
├── html
└── sledcpp.py

3.2 hello.h 文件内容

#pragma once

#ifdef __cplusplus
extern "C" {
#endif

void hello(const char* name);

#ifdef __cplusplus
}
#endif

3.3 CHANGELOG.md 文件内容

## 0.0.1
- [feat] Add `hello()`.
- [docs] Generate doxygen html website.

3.4 generate_doxyfile.py 文件内容

由于 Doxyfile 中需要配置 markdown 文件的绝对路径(尝试过相对路径,无法得到正确结果),而每个人电脑上的路径不一样, 这里提供 python 脚本 generate_doxyfile.py 来生成 Doxyfile 来解决路径问题。

此外还需要注意, 路径里不能带中文, 即使是 Linux 下也不能有中文。

generate_doxyfile.py 内容如下

import os

class CodeWriter(object):
    def __init__(self, indent_len):
        self.lines = []
        self.indent_num = 0
        self.indent_len = indent_len

    def write(self, content):
        padding = (self.indent_len * self.indent_num) * ' '
        line = padding + content
        self.lines.append(line)

    def save(self, filename):
        with open(filename, 'w') as fout:
            for line in self.lines:
                fout.write(line + "\n")

    def tab(self):
        self.indent_num += 1

    def backspace(self):
        if (self.indent_num > 0):
            self.indent_num -= 1


def generate_doxyfile():
    w = CodeWriter(8)
    w.write('PROJECT_NAME = "x"')
    w.write('GENERATE_TREEVIEW = YES')
    w.write('GENERATE_HTML = YES')
    w.write('GENERATE_LATEX = NO')
    w.write('')
    w.write('EXTRACT_ALL = YES')
    w.write('EXTRACT_STATIC = YES')
    w.write('')
    w.write('INPUT = hello.h \\')
    w.tab()

    curDir = os.getcwd().replace('\\', '/')
    line = '{:s}/docs/root.md \\'.format(curDir)
    w.write(line)

    line = '{:s}/docs/changelog.md'.format(curDir)
    w.write(line)

    w.backspace()

    w.write('# cpp command, the C Pre Processor, works on Linux/MacOSX, but failed on Windows')
    line = '# FILTER_PATTERNS = {:s}/docs/changelog.md="cpp -P "'.format(curDir)
    w.write(line)

    w.write('# Implement an alternative of cpp with Python wors for Windows, Linux and MacOSX')
    line = 'FILTER_PATTERNS = {:s}/docs/changelog.md="python {:s}/sledcpp.py "'.format(curDir, curDir)
    w.write(line)

    w.save("Doxyfile")


if __name__ == '__main__':
    generate_doxyfile()

例如我生成的 Doxyfile 内容为

PROJECT_NAME = "x"
GENERATE_TREEVIEW = YES
GENERATE_HTML = YES
GENERATE_LATEX = NO

EXTRACT_ALL = YES
EXTRACT_STATIC = YES

INPUT = hello.h \
        D:/dbg/a/docs/root.md \
        D:/dbg/a/docs/changelog.md
# cpp command, the C Pre Processor, works on Linux/MacOSX, but failed on Windows
# FILTER_PATTERNS = D:/dbg/a/docs/changelog.md="cpp -P "
# Implement an alternative of cpp with Python wors for Windows, Linux and MacOSX
FILTER_PATTERNS = D:/dbg/a/docs/changelog.md="python D:/dbg/a/sledcpp.py "

3.5 docs/root.md

是生成的网页的主页。

# MainPage

Hello, World!

3.6 docs/changelog.md

这个文件是关键文件。最关键一行是 #include "../CHANGELOG.md".

ChangeLog {#changelog}
====
This page lists change logs for each released version.<br/>

#include "../CHANGELOG.md"

3.7 运行整个工程

python generate_doxyfile.py
doxygen
cd html
python -m http.server 7080
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值