forked from eunomia-bpf/bpf-developer-tutorial
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathgenerate_toc.py
More file actions
329 lines (274 loc) · 14.7 KB
/
Copy pathgenerate_toc.py
File metadata and controls
329 lines (274 loc) · 14.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
import os
import re
# Define a function to walk through the directory and generate the TOC structure
def generate_toc(base_dir, project_root, output_file_dir):
toc = "## Table of Contents\n\n"
section_headers = {
"Basic": "### Getting Started Examples\n\nThis section contains simple eBPF program examples and introductions. It primarily utilizes the `eunomia-bpf` framework to simplify development and introduces the basic usage and development process of eBPF.\n\n",
"Advance": "### Advanced Documents and Examples\n\nWe start to build complete eBPF projects mainly based on `libbpf` and combine them with various application scenarios for practical use.\n\n",
"Depth": "### In-Depth Topics\n\nThis section covers advanced topics related to eBPF, including using eBPF programs on Android, possible attacks and defenses using eBPF programs, and complex tracing. Combining the user-mode and kernel-mode aspects of eBPF can bring great power (as well as security risks).\n\n"
}
subsection_titles = {
"Android": "\n\nAndroid:\n\n",
"GPU": "\n\nGPU:\n\n",
"Scheduler": "\n\nScheduler:\n\n",
"Networking": "\n\nNetworking:\n\n",
"Tracing": "\n\nTracing:\n\n",
"Security": "\n\nSecurity:\n\n",
"Features": "\n\nFeatures:\n\n",
"Other": "\nOther:\n\n"
}
subsection_order = ['GPU', 'Scheduler', 'Networking', 'Tracing', 'Security', 'Features', 'Other', 'Android']
# To ensure numeric sorting of directories
def sort_key(directory_name):
return list(map(int, re.findall(r'\d+', directory_name)))
sections = {} # {section_level: {subsection_type: [lessons]}}
# Collect all directories including subdirectories
all_dirs = []
for item in os.listdir(base_dir):
item_path = os.path.join(base_dir, item)
if os.path.isdir(item_path):
# Add numbered directories directly
if re.match(r'^\d+', item):
all_dirs.append(item)
# Also add non-numbered directories that have .config file
elif os.path.exists(os.path.join(item_path, ".config")):
all_dirs.append(item)
# Also scan subdirectories (like features/, xpu/)
else:
for subitem in os.listdir(item_path):
subitem_path = os.path.join(item_path, subitem)
if os.path.isdir(subitem_path):
all_dirs.append(os.path.join(item, subitem))
# Sort directories properly by numeric order (non-numeric dirs go to end)
all_dirs = sorted(all_dirs, key=lambda d: sort_key(d) if re.search(r'\d+', d) else [999999])
# Loop over the sorted directories
for directory in all_dirs:
lesson_path = os.path.join(base_dir, directory)
config_path = os.path.join(lesson_path, ".config")
readme_path = os.path.join(lesson_path, "README.md")
if os.path.exists(config_path) and os.path.exists(readme_path):
# Read the .config file for 'level', 'type', and 'desc'
with open(config_path, 'r') as config_file:
config_lines = config_file.readlines()
level = None
lesson_type = None
desc = None
for line in config_lines:
if line.startswith("level="):
level = line.split("=",1)[1].strip()
elif line.startswith("type="):
lesson_type = line.split("=",1)[1].strip()
elif line.startswith("desc="):
desc = line.split("=",1)[1].strip()
# Extract the first markdown title in README_en.md
with open(readme_path, 'r') as readme_file:
first_title = None
for line in readme_file:
if line.startswith("#"):
first_title = line.strip().lstrip("#").strip()
break
# If title starts with "eBPF", remove the part before the colon
if first_title and first_title.startswith("eBPF"):
if ":" in first_title:
first_title = first_title.split(":", 1)[1].strip()
# Get the relative path for the lesson (relative to the output file's directory)
lesson_rel_path = os.path.relpath(readme_path, output_file_dir)
# Prepare lesson data
# Handle both numbered lessons (e.g., "12-profile") and named lessons (e.g., "features/bpf_arena")
if '-' in os.path.basename(directory):
lesson_number = directory.split('-')[0]
lesson_name = directory.split('-', 1)[1]
link_text = f"lesson {lesson_number}-{lesson_name}"
else:
# For non-numbered directories, use the full path as name
link_text = directory.replace('/', ' ')
link = f"{lesson_rel_path}"
# Use description if available, else use first title
lesson_desc = desc if desc else first_title
lesson_entry = {
'link_text': link_text,
'link': link,
'desc': lesson_desc
}
# Organize lessons into sections and subsections
sections.setdefault(level, {}).setdefault(lesson_type, []).append(lesson_entry)
# Now, output the TOC in the desired order
section_order = ['Basic', 'Advance', 'Depth']
for level in section_order:
if level in sections:
toc += section_headers.get(level, "")
# For Basic and Advance sections, no subsections
if level != 'Depth':
for lesson in sum(sections[level].values(), []): # Flatten the list
toc += f"- [{lesson['link_text']}]({lesson['link']}) {lesson['desc']}\n"
else:
# For Depth section, output subsections in the desired order
for subsection in subsection_order:
if subsection in sections[level]:
toc += subsection_titles.get(subsection, "")
for lesson in sections[level][subsection]:
toc += f"- [{lesson['link_text']}]({lesson['link']}) {lesson['desc']}\n"
toc += "\nContinuously updating..."
return toc
# Define a function to walk through the directory and generate the TOC structure in Chinese
def generate_toc_cn(base_dir, project_root, output_file_dir):
toc = "## 目录\n\n"
section_headers = {
"Basic": "### 入门示例\n\n这一部分包含简单的 eBPF 程序示例和介绍。主要利用 `eunomia-bpf` 框架简化开发,介绍 eBPF 的基本用法和开发流程。\n\n",
"Advance": "### 高级文档和示例\n\n我们开始构建完整的 eBPF 项目,主要基于 `libbpf`,并将其与各种应用场景结合起来,以便实际使用。\n\n",
"Depth": "### 深入主题\n\n这一部分涵盖了与 eBPF 相关的高级主题,包括在 Android 上使用 eBPF 程序、利用 eBPF 程序进行的潜在攻击和防御以及复杂的追踪。结合用户模式和内核模式的 eBPF 可以带来强大的能力(也可能带来安全风险)。\n\n"
}
subsection_titles = {
"Android": "Android:\n\n",
"GPU": "GPU:\n\n",
"Scheduler": "调度器:\n\n",
"Networking": "网络:\n\n",
"tracing": "Tracing:\n\n",
"Security": "安全:\n\n",
"Features": "特性:\n\n",
"Other": "特性:\n\n"
}
subsection_order = ['GPU', 'Scheduler', 'Networking', 'tracing', 'Security', 'Features', 'Other', 'Android']
# To ensure numeric sorting of directories
def sort_key(directory_name):
return list(map(int, re.findall(r'\d+', directory_name)))
sections = {} # {section_level: {subsection_type: [lessons]}}
# Collect all directories including subdirectories
all_dirs = []
for item in os.listdir(base_dir):
item_path = os.path.join(base_dir, item)
if os.path.isdir(item_path):
# Add numbered directories directly
if re.match(r'^\d+', item):
all_dirs.append(item)
# Also add non-numbered directories that have .config file
elif os.path.exists(os.path.join(item_path, ".config")):
all_dirs.append(item)
# Also scan subdirectories (like features/, xpu/)
else:
for subitem in os.listdir(item_path):
subitem_path = os.path.join(item_path, subitem)
if os.path.isdir(subitem_path):
all_dirs.append(os.path.join(item, subitem))
# Sort directories properly by numeric order (non-numeric dirs go to end)
all_dirs = sorted(all_dirs, key=lambda d: sort_key(d) if re.search(r'\d+', d) else [999999])
# Loop over the sorted directories
for directory in all_dirs:
lesson_path = os.path.join(base_dir, directory)
config_path = os.path.join(lesson_path, ".config")
readme_path = os.path.join(lesson_path, "README.zh.md")
if os.path.exists(config_path) and os.path.exists(readme_path):
# Read the .config file for 'level', 'type', and 'desc'
with open(config_path, 'r') as config_file:
config_lines = config_file.readlines()
level = None
lesson_type = None
desc = None
for line in config_lines:
if line.startswith("level="):
level = line.split("=",1)[1].strip()
elif line.startswith("type="):
lesson_type = line.split("=",1)[1].strip()
elif line.startswith("desc="):
desc = line.split("=",1)[1].strip()
# Extract the first markdown title in README.md
with open(readme_path, 'r') as readme_file:
first_title = None
for line in readme_file:
if line.startswith("#"):
first_title = line.strip().lstrip("#").strip()
break
# If title starts with "eBPF", remove the part before the colon
if first_title and first_title.startswith("eBPF"):
if ":" in first_title:
first_title = first_title.split(":", 1)[1].strip()
# Get the relative path for the lesson (relative to the output file's directory)
lesson_rel_path = os.path.relpath(readme_path, output_file_dir)
# Prepare lesson data
# Handle both numbered lessons (e.g., "12-profile") and named lessons (e.g., "features/bpf_arena")
if '-' in os.path.basename(directory):
lesson_number = directory.split('-')[0]
lesson_name = directory.split('-', 1)[1]
link_text = f"lesson {lesson_number}-{lesson_name}"
else:
# For non-numbered directories, use the full path as name
link_text = directory.replace('/', ' ')
link = f"{lesson_rel_path}"
# Use description if available, else use first title
lesson_desc = desc if desc else first_title
lesson_entry = {
'link_text': link_text,
'link': link,
'desc': lesson_desc
}
# Organize lessons into sections and subsections
sections.setdefault(level, {}).setdefault(lesson_type, []).append(lesson_entry)
# Now, output the TOC in the desired order
section_order = ['Basic', 'Advance', 'Depth']
for level in section_order:
if level in sections:
toc += section_headers.get(level, "")
# For Basic and Advance sections, no subsections
if level != 'Depth':
for lesson in sum(sections[level].values(), []): # Flatten the list
toc += f"- [{lesson['link_text']}]({lesson['link']}) {lesson['desc']}\n"
else:
# For Depth section, output subsections in the desired order
for subsection in subsection_order:
if subsection in sections[level]:
toc += subsection_titles.get(subsection, "")
for lesson in sections[level][subsection]:
toc += f"- [{lesson['link_text']}]({lesson['link']}) {lesson['desc']}\n"
toc += "\n持续更新中..."
return toc
def load_template(template_path):
"""Load a template file and return its content"""
with open(template_path, 'r', encoding='utf-8') as f:
return f.read()
def generate_file_from_template(template_path, output_path, toc_content):
"""Generate a file from template by replacing {{TOC_CONTENT}} placeholder"""
template = load_template(template_path)
output_content = template.replace('{{TOC_CONTENT}}', toc_content)
with open(output_path, 'w', encoding='utf-8') as f:
f.write(output_content)
print(f"Generated: {output_path}")
# Main execution
if __name__ == "__main__":
# Get the absolute path to the script's directory
script_dir = os.path.dirname(os.path.abspath(__file__))
# Get the project root (parent of scripts directory)
project_root = os.path.dirname(script_dir)
base_directory = os.path.join(project_root, "src")
scripts_dir = os.path.join(project_root, "scripts")
# Generate TOC content for SUMMARY.md files (output in src/ directory)
toc_summary_en = generate_toc(base_directory, project_root, os.path.join(project_root, 'src'))
toc_summary_cn = generate_toc_cn(base_directory, project_root, os.path.join(project_root, 'src'))
# Generate TOC content for README.md files (output in project root)
toc_readme_en = generate_toc(base_directory, project_root, project_root)
toc_readme_cn = generate_toc_cn(base_directory, project_root, project_root)
# Generate SUMMARY.md from template
generate_file_from_template(
os.path.join(scripts_dir, 'SUMMARY.md.template'),
os.path.join(project_root, 'src', 'SUMMARY.md'),
toc_summary_en
)
# Generate SUMMARY.zh.md from template
generate_file_from_template(
os.path.join(scripts_dir, 'SUMMARY.zh.md.template'),
os.path.join(project_root, 'src', 'SUMMARY.zh.md'),
toc_summary_cn
)
# Generate README.md from template
generate_file_from_template(
os.path.join(scripts_dir, 'README.md.template'),
os.path.join(project_root, 'README.md'),
toc_readme_en
)
# Generate README.zh.md from template
generate_file_from_template(
os.path.join(scripts_dir, 'README.zh.md.template'),
os.path.join(project_root, 'README.zh.md'),
toc_readme_cn
)
print("\nAll files generated successfully!")