项目文档结构
├──.github/workflows/dbook.yml
├── book.toml
├── src/
│ ├── SUMMARY.md # 目录结构定义
│ ├── index.md
│ ├── chapter_1.md
│ ├── chapter_2.md
│ ├── images/
│ │ ├── example.png
│ ├── theme/
│ │ ├── custom.css
├── book/
简要说明:
1、book.toml中是用于设置文档/书籍、输出形式的相关设置
2、src子目录下存放源文件,包括源文档(.md),静态资源(images目录下)、模板文件(theme目录下),其中必须有一个SUMMARY.md文档,用于存储文档的目录结构,必须有一个index.md,用于指定进入页
3、.github/workflows/dbook.yml用于github进行action自动化进程,用于检查、解决打开生成的html文档时的一些问题
4、book是文档生成的一些结果信息
5、整个项目利用mdbook进行,mdbook的下载需要Rust编译环境,流程如下
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh
这一步时会询问下载方式一般选择-立即安装Rust、Cargo-直接点Enter
此时rust会安装到~/.cargo/bin/ 此时还需要手动加入到PATH
export PATH="$HOME/.cargo/bin:$PATH" >> ~/.bashrc
source ~/.bashrc
此时就可以用cargo工具安装mdbook,需要注意,cargo指令是用户级管理,不要用sudo
cargo install mdbook
cargo install mdbook-pdf
book.toml
[book]
authors = ["upsetgrass"]
language = "zh"
multilingual = false
src = "docs/"
title = "note_test"
[output.html]
git-repository-url = "https://upsetgrass.github.io/note/"
mathjax-support = true
[output.pdf]
optional = true
这是一个示例,有三个部分book、output.html、output.pdf
[book] - 用于设置书籍,作者是upsetgrass,该文档的语言是中文,不支持多语言,src源文件目录,所有的markdown章节都放在docs目录下,书籍的标题是note_test,显示在index.html页面的 <\title> 标签中
[output.html] - 配置 mdBook 生成 HTML 格式文档的参数,git-repository-url,会在生成的 HTML 文档的右上角添加一个指向该 Git 仓库的链接,mathjax-support用于是否启用MathJax公式支持,Makrdown中使用LateX数学公式
对于每一个md生成的html会生成在本地的book路径下
[output.pdf] - 控制pdf的输出行为,如果 pdf 生成器(mdbook-pdf)不可用,跳过 pdf 生成,而不会报错,mdbook-pdf生成的pdf会放在book/output路径下
src源文件
SUMMARY.md是书籍的目录
# Summary
# ChiselAIA
* [😺AIA](./index.md)
* [📩IMSIC](./imsic.md)
* [🧶APLIC](./aplic.md)
* [🧭集成指南(Integration Guide)](./integration.md)
mdbook会分析SUMMARY.md中指向的.md,只有加在了SUMMARY.md中的章节,才会生成对应的html最终在书籍中才能看到对应的章节,下图框出部分就是SUMMARY.md的内容展示,右侧就是index.md的内容,index.md会作为进入页(初始页)
对于.md中需要使用到的图片,需要存放到src/images下,并在.md中以的形式引用进来
ps:[]() - 超链接,![]() - 图片插入
mdbook指令
mdbook init
:对mdbook所需要的book.toml book/ .gitignore src/ SUMMARY.md进行简单设置,也可以自行mkdir touch
mdbook build
:读取book.toml配置文件,解析 src/SUMMARY.md 生成目录结构;将 src/ 目录中的 .md 文件转换为 HTML(默认存放在 book/);复制 theme/、static/ 目录的内容到 book/。
mdbook serve -p 3000
:运行一个本地的http服务器,默认为http://127.0.0.1:3000,-p可以指定为3000还是4000...,用于本地展示生成的书籍
github_action
workflows中的yml是自动构建工具,和makefile mill等工具类似,只不过worflows是github中action组件自动执行的,这一部分用于创建分支gh-pages,单独存储mdbook build构建出来的那些html还有一些依赖文件,以保持其他分支的洁净,并设定自动更新文档的条件,具体示例如下,基本可以直接使用
name: deploy-github-pages # 定义workflow或步骤名称
on: # on:触发条件
push:
branches:
- main # 当main分支有新的push,触发该github action
jobs:
deploy-github-pages: # 使用最新的Ubuntu作为执行环境
runs-on: ubuntu-latest # 运行环境
permissions: # 设置权限,使得action可以向gh-pages分支写入
contents: write
steps: # 以下都是各个任务
- name: Checkout repository # 步骤名称
uses: actions/checkout@v4 # uses-使用github action中写好的脚本,这里是在拉取我的代码
- name: Setup mdBook
if: ${{ github.ref == 'refs/heads/main' }}
uses: peaceiris/actions-mdbook@v1.2.0 # 主分支上安装mdbook
with:
mdbook-version: '0.4.5'
- name: Build site
if: ${{ github.ref == 'refs/heads/main' }}
working-directory: .
run: mdbook build # 主分支上运行mdbook build指令
- name: Move HTML files to root
run: | # run: | 运行多行shell脚本
mv ./book/html/* ./book/
rm -r ./book/html
- name: Deploy to GitHub Pages
uses: peaceiris/actions-gh-pages@v3 # 使用该actions用于自动部署github pages
if: ${{ github.ref == 'refs/heads/main' }}
with:
github_token: ${{ secrets.GITHUB_TOKEN }}
publish_dir: ./book # 指令主分支的./book内容作为gh-pages分支的内容
force_orphan: true # 删除gh-pages的历史记录 ,只保留最新版本
本质:workflows中的操作就是github帮我们在某种触发下,自动执行某些指令。比如上面就是push到主分支时,执行后面的操作,后面的操作就是github把我们的主分支的代码pull下来,然后下载mdbook执行mdbook build,然后创建gh-pages分支,将生成的/book推送到我的仓库的gh-pages分支上。
github操作
需要在github仓库的settings里面的Pages项去修改这些内容
这里your site is live at https://upsetgrass.github.io/note/ 就是github给我们的文档分配的地址
格式为:https://<用户名/组织名>.github.io/<仓库名>
我们在Readme.md中就可以以 [文档](https://upsetgrass.github.io/note/) 这种形式超链接到我们写的书籍
下面Branch中设置gh-pages/root的前提是需要先把.github/workflows/mdbook.yml git到该仓库,此时github就会自动出现gh-pages分支,用于处理Github Pages,也就是会把这个分支作为书籍的源文件
这里需要注意的是,我们之前在.github/workflows/mdbook的生成/book的操作是在主分支,但是生成的结果放在了gh-pages分支,所以是以gh-pages分支作为源文件
插图
有多种方式生成我们所需要的图,1、graphviz 2、drawio
第一种方式是以代码的形式存储,第二种方式是以图形化、交互式的方式进行,.drawio->.svg
简要针对第一种方式进行讲解
Graphviz是一个底层图形可视软件,通过命令行和编程接口来生成图表,通过解析DOT语言,自动布局绘制图,而直接写DOT语言会比较困难,所以有人将DOT接口进行优化封装,即pydot,我们通过执行python脚本,自动生成.dot,又由Graphviz将.dot解析成.svg等图片格式
所以我们只需要专注于pydot,pydot中有三个主要的类node、Subgraph、Graph
1、node是pydot最基本的元素,表示一个独立的实体,其中不能包含其他的节点,需要加入到Graph/SubGraph中,否则不会显示在最终的图中
2、Subgraph子图是一组节点的集合,其本身不是节点,可以包含多个 Node,并且可以用边连接节点
3、Graph是整个图,是最终的可视化对象。是最高级的容器,可以包含:节点、子图、边,只有Graph、DiGraph对象可以被write_png() write_svg()输出为最终图像
那么在pydot中就是创建这些类,然后将node、Subgraph、Graph连接起来就构成了图,然后根据不同的需求,用不同的布局引擎来使用不同的图形排版,如下所示
dot -Tpng example.dot -o example_dot.png
fdp -Tpng example.dot -o example_fdp.png
从理解层面,排除掉边,就可以把Graph理解为根,Subgraph理解为非叶子节点,node就是叶子节点
# Dot就是主图,Edge用于连线,Node就是节点,Subgraph就是子图
from pydot import Dot, Edge, Node, Subgraph
# 创建一个空的有向图
graph = pydot.Dot(graph_type="digraph")
# 创建节点,节点在DOT语言中的唯一ID为msi_device_0,其中文字显示为MSI Device0
msi = Node("msi_device_0", label="MSI Device0")
# 创建一个子图
subgraph = pydot.Subgraph("cluster_1")
# main.py
from arch_common import *
###############################################################################
# Graph
###############################################################################
graph = AIADot(label="Configuration Paths in an AIA System", rankdir="RL")
configure = graph.main
###############################################################################
# Nodes and Subgraphs
###############################################################################
configure.add_subgraph(aplic)
configure.add_node(bus_network)
for imsic_hart in imsic_harts:
configure.add_subgraph(imsic_hart)
###############################################################################
# Edges
###############################################################################
for domain in aplic.domains:
configure.add_edge(MessageEdge(bus_network, domain))
for imsic_hart in imsic_harts:
imsic = imsic_hart.imsic
hart = imsic_hart.hart
for intFile in imsic.intFiles:
configure.add_edge(WireEdge(hart, intFile))
configure.add_edge(Edge(intFile, bus_network, color="transparent"))
configure.add_edge(MessageEdge(hart, bus_network))
###############################################################################
# Output
###############################################################################
graph.write(__file__.replace("_dot.py", "_py.dot"))
# arch_common.py
from pydot import Dot, Edge, Node, Subgraph
###############################################################################
# Nodes and Subgraphs
###############################################################################
msi_devices = [
Node("msi_device_0", label="MSI Device 0"),
Node("msi_device_1", label="MSI Device 1"),
Node("msi_device__", label="MSI Device ..."),
]
class APLIC(Subgraph):
def __init__(self):
Subgraph.__init__(self, "aplic", label="APLIC", cluster=True,
style='filled', bgcolor="#F8CECC", pencolor="#B85450",
)
self.domains = [
Node("m_domain", label="M Domain", height=1.5),
Node("s_domain", label="S Domain", height=1.5),
]
for domain in self.domains:
self.add_node(domain)
aplic = APLIC()
wired_devices = [
Node("wired_device_0", label="Wired Device 0"),
Node("wired_device_1", label="Wired Device 1"),
Node("wired_device__", label="Wired Device ..."),
]
bus_network = Node("bus_network", label="Bus", height=7)
class IMSICHart(Subgraph):
def __init__(self, id, suffix):
Subgraph.__init__(self, f"imsic_hart_{suffix}", label="", cluster=True,
pencolor="transparent",
)
self.imsic = self.IMSIC(id, suffix)
self.add_subgraph(self.imsic)
self.hart = Node(f"hart_{suffix}", label=f"Hart {id}", height=3.2)
self.add_node(self.hart)
class IMSIC(Subgraph):
def __init__(self, id, suffix):
Subgraph.__init__(self, f"imsic_{suffix}", label=f"IMSIC {id}", cluster=True,
style="filled", bgcolor="#F8CECC", pencolor="#B85450",
)
self.intFiles = [
Node(f"imsic_{suffix}_mint_file", label="M IntFile"),
Node(f"imsic_{suffix}_sint_file", label="S IntFile"),
]
self.intFiles += [
Node(f"imsic_{suffix}_vsint_file_0", label=f"VS IntFile 0"),
Node(f"imsic_{suffix}_vsint_file__", label=f"VS IntFile ..."),
]
for intFile in self.intFiles:
self.add_node(intFile)
imsic_harts = [IMSICHart(0, 0), IMSICHart("...", "_")]
###############################################################################
# Edges
###############################################################################
class MessageEdge(Edge):
def __init__(self, src, dst, obj_dict=None, **attrs):
Edge.__init__(self, src, dst, obj_dict, **attrs, color='"black:invis:black"')
class WireEdge(Edge):
def __init__(self, src, dst, obj_dict=None, **attrs):
Edge.__init__(self, src, dst, obj_dict, **attrs)
###############################################################################
# Graph
###############################################################################
class AIADot(Dot):
class Legend(Subgraph):
def __init__(self):
Subgraph.__init__(self, "legend", label="Legend", cluster=True, pencolor="gray")
def add_edge_legend(self, EdgeClass, label):
src = Node(f"legend_${label}_edge_src", shape="plain", label=label)
self.add_node(src)
dst = Node(f"legend_${label}_edge_dst", shape="plain", label=" ")
self.add_node(dst)
self.add_edge(EdgeClass(src, dst))
def __init__(self, *argsl, **argsd):
Dot.__init__(self, *argsl, **argsd,
splines="ortho",
bgcolor="transparent",
)
self.main = Subgraph("main", label="", cluster=True, pencolor="transparent")
self.main.set_node_defaults(shape="box")
self.add_subgraph(self.main)
self.legend = self.Legend()
self.add_subgraph(self.legend)
self.legend.add_edge_legend(WireEdge, "wire")
self.legend.add_edge_legend(MessageEdge, "message")
此时python main.py即可生成.dot,然后用前面说的布局引擎即可生成图片,上例生成的图片如下图所示