blob: 27613f2373be7e5f7b38efafda8dad13620e8266 [file] [log] [blame]
Patrick Williams847a0c32020-06-24 15:18:10 -05001#!/usr/bin/env bash
2
3set -e
4
Patrick Williamsa4c9edc2020-12-17 21:02:36 -06005# Locale can change behavior of utilities like 'sort' but we want the output
6# to be stable on all machines. Force the locale to 'C' for consistency.
7export LC_ALL=C
8
Patrick Williams847a0c32020-06-24 15:18:10 -05009function show_usage {
10 cat \
11<<EOF
12Usage: $(basename "$0") [options] <command-args>*
13
14Generate meson.build files from a directory tree containing YAML files and
15facilitate building the sdbus++ sources.
16
17Options:
18 --help - Display this message
19 --command <cmd> - Command mode to execute (default 'meson').
20 --directory <path> - Root directory of the YAML source (default '.').
21 --output <path> - Root directory of the output (default '.').
22 --tool <path> - Path to the processing tool (default 'sdbus++').
23 --version - Display this tool's version string.
24
25Commands:
26 meson - Generate a tree of meson.build files corresponding
27 to the source YAML files.
28 cpp <intf> - Generate the source files from a YAML interface.
29 markdown <intf> - Generate the markdown files from a YAML interface.
30 version - Display this tool's version string.
31
32EOF
33}
34
35## The version is somewhat arbitrary but is used to create a warning message
36## if a repository contains old copies of the generated meson.build files and
37## needs an update. We should increment the version number whenever the
38## resulting meson.build would change.
39tool_version="sdbus++-gen-meson version 1"
40function show_version {
41 echo "$tool_version"
42}
43
44# Set up defaults.
45sdbuspp="sdbus++"
46outputdir="."
47cmd="meson"
48rootdir="."
49
50# Parse options.
51options="$(getopt -o hc:d:o:t:v --long help,command:,directory:,output:,tool:,version -- "$@")"
52eval set -- "$options"
53
54while true;
55do
56 case "$1" in
57 -h | --help)
58 show_usage
59 exit
60 ;;
61
62 -c | --command)
63 shift
64 cmd="$1"
65 shift
66 ;;
67
68 -d | --directory)
69 shift
70 rootdir="$1"
71 shift
72 ;;
73
74 -o | --output)
75 shift
76 outputdir="$1"
77 shift
78 ;;
79
80 -t | --tool)
81 shift
82 sdbuspp="$1"
83 shift
84 ;;
85
86 -v | --version)
87 show_version
88 exit
89 ;;
90
91 --)
92 shift
93 break
94 ;;
95 esac
96done
97
98## Create an initially empty meson.build file.
99## $1 - path to create meson.build at.
100function meson_empty_file {
101 mkdir -p "$1"
102 echo "# Generated file; do not modify." > "$1/meson.build"
103}
104
105## Create the root-level meson.build
106##
107## Inserts rules to run the available version of this tool to ensure the
108## version has not changed.
109function meson_create_root {
110 meson_empty_file "$outputdir"
111
112 cat >> "$outputdir/meson.build" \
113<<EOF
114sdbuspp_gen_meson_ver = run_command(
115 sdbuspp_gen_meson_prog,
116 '--version',
117).stdout().strip().split('\n')[0]
118
119if sdbuspp_gen_meson_ver != '$tool_version'
120 warning('Generated meson files from wrong version of sdbus++-gen-meson.')
121 warning(
122 'Expected "$tool_version", got:',
123 sdbuspp_gen_meson_ver
124 )
125endif
126
127EOF
128}
129
130## hash-tables to store:
131## meson_paths - list of subdirectory paths for which an empty meson.build
132## has already been created.
133## interfaces - list of interface paths which a YAML has been found and
134## which YAML types (interface, errors, etc.).
135declare -A meson_paths
136declare -A interfaces
137
138## Ensure the meson.build files to a path have been created.
139## $1 - The path requiring to be created.
140function meson_create_path {
141
142 meson_path="$outputdir"
143 prev_meson_path=""
144
145 # Split the path into segments.
146 for part in $(echo "$1" | tr '/' '\n');
147 do
148 prev_meson_path="$meson_path"
149 meson_path="$meson_path/$part"
150
151 # Create the meson.build for this segment if it doesn't already exist.
152 if [ "x" == "x${meson_paths[$meson_path]}" ];
153 then
154 meson_paths["$meson_path"]="1"
155 meson_empty_file "$meson_path"
156
157 # Add the 'subdir' link into the parent's meson.build.
158 # We need to skip adding the links into the 'root' meson.build
159 # because most repositories want to selectively add TLDs based
160 # on config flags. Let them figure out their own logic for that.
161 if [ "x$outputdir" != "x$prev_meson_path" ];
162 then
163 echo "subdir('$part')" >> "$prev_meson_path/meson.build"
164 fi
165 fi
166 done
167}
168
169## Generate the meson target for the source files (.cpp/.hpp) from a YAML
170## interface.
171##
172## $1 - The interface to generate a target for.
173function meson_cpp_target {
174
175 # Determine the source and output files based on the YAMLs present.
176 sources=""
177 outputs=""
178 for s in ${interfaces[$1]};
179 do
180 sources="${sources}meson.source_root() / '$1.$s', "
181
182 case "$s" in
183 errors.yaml)
184 outputs="${outputs}'error.cpp', 'error.hpp', "
185 ;;
186
187 interface.yaml)
188 outputs="${outputs}'server.cpp', 'server.hpp', "
189 outputs="${outputs}'client.hpp', "
190 ;;
191 esac
192 done
193
194 # Create the target to generate the 'outputs'.
195 cat >> "$outputdir/$1/meson.build" \
196<<EOF
197generated_sources += custom_target(
198 '$1__cpp'.underscorify(),
199 input: [ $sources ],
200 output: [ $outputs ],
201 command: [
202 sdbuspp_gen_meson_prog, '--command', 'cpp',
203 '--output', meson.current_build_dir(),
204 '--tool', sdbusplusplus_prog,
205 '--directory', meson.source_root(),
206 '$1',
207 ],
208)
209
210EOF
211}
212
213## Generate the meson target for the markdown files from a YAML interface.
214## $1 - The interface to generate a target for.
215function meson_md_target {
216
217 # Determine the source files based on the YAMLs present.
218 sources=""
219 for s in ${interfaces[$1]};
220 do
221 sources="${sources}meson.source_root() / '$1.$s', "
222 done
223
224 # Create the target to generate the interface.md file.
225 cat >> "$outputdir/$(dirname "$1")/meson.build" \
226<<EOF
227generated_others += custom_target(
228 '$1__markdown'.underscorify(),
229 input: [ $sources ],
230 output: [ '$(basename "$1").md' ],
231 command: [
232 sdbuspp_gen_meson_prog, '--command', 'markdown',
233 '--output', meson.current_build_dir(),
234 '--tool', sdbusplusplus_prog,
235 '--directory', meson.source_root(),
236 '$1',
237 ],
238 build_by_default: true,
239)
240
241EOF
242}
243
244## Handle command=meson by generating the tree of meson.build files.
245function cmd_meson {
246 TLDs="com net org xyz"
247 yamls=""
248
249 # Find all the YAML files in the TLD subdirectories.
250 for d in $TLDs;
251 do
252 dir="$rootdir/$d"
253 if [ ! -d "$dir" ];
254 then
255 continue
256 fi
257
258 yamls="\
259 $yamls \
260 $(find "$dir" -name '*.interface.yaml' -o -name '*.errors.yaml') \
261 "
262 done
263
264 # Sort YAMLs
265 yamls="$(echo "$yamls" | tr " " "\n" | sort)"
266
267 # Assign the YAML files into the hash-table by interface name.
268 for y in $yamls;
269 do
270 rel="$(realpath "--relative-to=$rootdir" "$y")"
271 dir="$(dirname "$rel")"
272 ext="${rel#*.}"
273 base="$(basename "$rel" ".$ext")"
274
275 interfaces["$dir/$base"]="${interfaces[$dir/$base]} $ext"
276 done
277
278 # Create the meson.build files.
279 meson_create_root
280 sorted_ifaces="$(echo "${!interfaces[@]}" | tr " " "\n" | sort)"
281 for i in ${sorted_ifaces};
282 do
283 meson_create_path "$i"
284 meson_cpp_target "$i"
285 meson_md_target "$i"
286 done
287}
288
289## Handle command=cpp by calling sdbus++ as appropriate.
290## $1 - interface to generate.
291##
292## For an interface foo/bar, the outputdir is expected to be foo/bar.
293function cmd_cpp {
294
295 if [ "x" == "x$1" ];
296 then
297 show_usage
298 exit 1
299 fi
300
301 if [ ! -e "$rootdir/$1.interface.yaml" ] && \
302 [ ! -e "$rootdir/$1.errors.yaml" ];
303 then
304 echo "Missing YAML for $1."
305 exit 1
306 fi
307
308 mkdir -p "$outputdir"
309
310 sdbusppcmd="$sdbuspp -r $rootdir"
311 intf="${1//\//.}"
312
313 if [ -e "$rootdir/$1.interface.yaml" ];
314 then
315 $sdbusppcmd interface server-header "$intf" > "$outputdir/server.hpp"
316 $sdbusppcmd interface server-cpp "$intf" > "$outputdir/server.cpp"
317 $sdbusppcmd interface client-header "$intf" > "$outputdir/client.hpp"
318 fi
319
320 if [ -e "$rootdir/$1.errors.yaml" ];
321 then
322 $sdbusppcmd error exception-header "$intf" > "$outputdir/error.hpp"
323 $sdbusppcmd error exception-cpp "$intf" > "$outputdir/error.cpp"
324 fi
325}
326
327## Handle command=markdown by calling sdbus++ as appropriate.
328## $1 - interface to generate.
329##
330## For an interface foo/bar, the outputdir is expected to be foo.
331function cmd_markdown {
332
333 if [ "x" == "x$1" ];
334 then
335 show_usage
336 exit 1
337 fi
338
339 if [ ! -e "$rootdir/$1.interface.yaml" ] && \
340 [ ! -e "$rootdir/$1.errors.yaml" ];
341 then
342 echo "Missing YAML for $1."
343 exit 1
344 fi
345
346 mkdir -p "$outputdir"
347
348 sdbusppcmd="$sdbuspp -r $rootdir"
349 intf="${1//\//.}"
350 base="$(basename "$1")"
351
352 echo -n > "$outputdir/$base.md"
353 if [ -e "$rootdir/$1.interface.yaml" ];
354 then
355 $sdbusppcmd interface markdown "$intf" >> "$outputdir/$base.md"
356 fi
357
358 if [ -e "$rootdir/$1.errors.yaml" ];
359 then
360 $sdbusppcmd error markdown "$intf" >> "$outputdir/$base.md"
361 fi
362}
363
364## Handle command=version.
365function cmd_version {
366 show_version
367}
368
369"cmd_$cmd" "$*"