forked from toeb/cmakepp
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathREADME.md.in
173 lines (128 loc) · 6.24 KB
/
README.md.in
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
# Templating
<%
assign(function_files = glob("**.cmake" --relative))
assign(function_files[] = glob("template/**cmake" --relative))
%>
While working on the documentation of `cmakepp` I grew wary of always doubling the documentation - one in the source code and a mirror in the `README.md`. Also I was frustrated when I refactored and renamed functions or changed signatures which is not uncommon - especially when working on a new feature. So I needed a tool which could generate the documentation by copying the comment header and the function signature from my `CMake` files.
While the comment/signature/cmake script parsing is done by `cmake_script_parse(...)` and is another subject, I still stood before the problem of combining all the information into useful documentation. I especially did not want a too special solution which does not allow for special cases / different formatting. So I took inspiration from some of existing template engines like `razor` or `asp` and managed to implement the core logic in less than 60 lines of `CMake` code.
While my original thought was only 'how is the best way to generate markdown' I soon realized that This kind of templating would allow for quite some more domains to be served. For Example generating `C++` source code (i.e. data structures) - any kind of text based format could easily be generated.
`CMake` itself allows you to do something not quite similar. You can use the `string(CONFIGURE)` and `file(CONFIGURE)` functions to replace `@@vars@@` within text with values that you have specified. This howeverwill never allow you to generate complex structures as simple as an inline `foreach()` loop.
When I was finished with implementing my solution I noticed I had a far more mighty tool at hand than I anticipated: I could let the functions which I was documenting run and add the result to my documentation. For Example see the comments for [ls](#) and the resulting [README.md](#)
## Template Process
The template process is as follows:
```
|cmake scope|
vvv
(|file| =read=>) |string| =template_compile=> |cmake code| =eval=> |string| (=write=> |file|)
```
## Template Syntax
The template syntax is quite easy:
* `<%%` `%%>` encloses cmake code
* `<%%%` and `%%%>` escape `<%%` and `%%>` resp.
* `<%%=` runs the function specified if possible (only single line function calls allowed) or formats the following string using the `format()` function (allows things like `<%%="${var} {format.expression[3].navigation.key}%%>`)
* single line function calls are `func(.....)` but not `func(... \n ....)`
* shorthands
* `@@<cmake function call>` is replaced with `<%%= <cmake function call> %%>`
* `@@<navigation expression>` is replaced with `<%%= {<navigation expression>}%%>`
## Example
This example takes an template input file and creates output using some given data. You can also check out my [Samples Repository](#samples) for templating.
*Input file - mytemplate.in*
```
Hello this is <%%={user.first_name}%%>,
I am writing you from within a template file! Here are some things I like:
<%% foreach(thing ${things}) %%>* The Thing: <%%={thing.name}%%> - <%%={thing.description}%%>
<%% endforeach() %%>
Thanks for Reading! Write me at <%%={user.email}%%>
(path of this file: <%%=${template_path}%%>)
```
*CMake*
```cmake
## create a user object
data("{
first_name:'Tobias',
last_name:'Becker',
email:'toeb@@thetoeb.de'
}")
ans(user)
## create a list of things
data("[
{
name:'Swimming',
decsription:'humanoid aquatic propulsion'
},
{
name:'Programming',
decsription:'converting ideas to instructions'
},
{
name:'Squash',
decsription:'destroying your body while chasing a ball'
}
]")
ans(things)
## now run the template file
template_run_file("mytemplate.in")
ans(generated_content)
message("${generated_content}")
```
*stdout*
```cmake
<%
## create a user object
data("{
first_name:'Tobias',
last_name:'Becker',
email:'toeb@@thetoeb.de'
}")
ans(user)
## create a list of things
data("[
{
name:'Swimming',
description:'humanoid aquatic propulsion'
},
{
name:'Programming',
description:'converting ideas to instructions'
},
{
name:'Squash',
description:'destroying your body while chasing a ball'
}
]")
ans(things)
template_run("Hello this is <%%={user.first_name}%%>,
I am writing you from within a template file! Here are some things I like:
<%% foreach(thing ${things}) %%>* The Thing: <%%={thing.name}%%> - <%%={thing.description}%%>
<%% endforeach() %%>
Thanks for Reading! Write me at <%%={user.email}%%>
(path of this file: <%%=${template_path}%%>)")
ans(generated_content)
template_out("${generated_content}")
%>
```
## Implementation
The implemenation is straightforward: Using `CMake`'s regex functionality I split the input template into `code fragment` s and `literal fragment`s source fragments are such which start with `<%%` and end with `%%>`. Out of these fragments I generate a `cmake code` listing which can be executed. This is done by appending `code fragments` directly and appending a `template_out(<literal fragment>)` for every `literal fragment`. The template itself works by creating an accumulator variable which can be appended to via `template_out` .
Some more transformations are performed but the gist ist captured in this description.
*Example*
template string
```
Hello <%%={user.name}%%>,
<%%foreach(i RANGE 1 3)%%>* <%%=${i}%%>
<%%endforeach()%%>
```
result of `template_compile` **Note** *the strings seem may seem strange. That is because they are `cmake_escape`d.*
```cmake
<%
template_compile("Hello <%%={user.name}%%>,
<%%foreach(i RANGE 1 3)%%>* <%%=\${i}%%>
<%%endforeach()%%>")
template_out("${__ans}")
%>
```
## Caveats
There is one major caveat at the moment. Files larger than `500 kB` lead to memory exhaustion in cmake. I know how to alleviate this problem but I will not until it is necessary.
### Function List
<%= markdown_template_function_list(${function_files}) %>
### Function Descriptions
<%= markdown_template_function_descriptions(${function_files}) %>