xref: /openbmc/sdbusplus/tools/sdbus++-gen-meson (revision c8447d52)
1#!/usr/bin/env bash
2
3set -e
4
5# 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
9function 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 3"
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; do
55    case "$1" in
56        -h | --help)
57            show_usage
58            exit
59            ;;
60
61        -c | --command)
62            shift
63            cmd="$1"
64            shift
65            ;;
66
67        -d | --directory)
68            shift
69            rootdir="$1"
70            shift
71            ;;
72
73        -o | --output)
74            shift
75            outputdir="$1"
76            shift
77            ;;
78
79        -t | --tool)
80            shift
81            sdbuspp="$1"
82            shift
83            ;;
84
85        -v | --version)
86            show_version
87            exit
88            ;;
89
90        --)
91            shift
92            break
93            ;;
94    esac
95done
96
97## Create an initially empty meson.build file.
98## $1 - path to create meson.build at.
99function meson_empty_file {
100    mkdir -p "$1"
101    echo "# Generated file; do not modify." > "$1/meson.build"
102}
103
104## Create the root-level meson.build
105##
106## Inserts rules to run the available version of this tool to ensure the
107## version has not changed.
108function meson_create_root {
109    meson_empty_file "$outputdir"
110
111    cat >> "$outputdir/meson.build" \
112        << EOF
113sdbuspp_gen_meson_ver = run_command(
114    sdbuspp_gen_meson_prog,
115    '--version',
116).stdout().strip().split('\n')[0]
117
118if sdbuspp_gen_meson_ver != '$tool_version'
119    warning('Generated meson files from wrong version of sdbus++-gen-meson.')
120    warning(
121        'Expected "$tool_version", got:',
122        sdbuspp_gen_meson_ver
123    )
124endif
125
126EOF
127}
128
129## hash-tables to store:
130##      meson_paths - list of subdirectory paths for which an empty meson.build
131##                    has already been created.
132##      interfaces - list of interface paths which a YAML has been found and
133##                   which YAML types (interface, errors, etc.).
134declare -A meson_paths
135declare -A interfaces
136
137## Ensure the meson.build files to a path have been created.
138## $1 - The path requiring to be created.
139function meson_create_path {
140
141    meson_path="$outputdir"
142    prev_meson_path=""
143
144    # Split the path into segments.
145    for part in $(echo "$1" | tr '/' '\n'); do
146        prev_meson_path="$meson_path"
147        meson_path="$meson_path/$part"
148
149        # Create the meson.build for this segment if it doesn't already exist.
150        if [ "" == "${meson_paths[$meson_path]}" ]; then
151            meson_paths["$meson_path"]="1"
152            meson_empty_file "$meson_path"
153
154            # Add the 'subdir' link into the parent's meson.build.
155            # We need to skip adding the links into the 'root' meson.build
156            # because most repositories want to selectively add TLDs based
157            # on config flags.  Let them figure out their own logic for that.
158            if [ "$outputdir" != "$prev_meson_path" ]; then
159                echo "subdir('$part')" >> "$prev_meson_path/meson.build"
160            fi
161        fi
162    done
163}
164
165## Generate the meson target for the source files (.cpp/.hpp) from a YAML
166## interface.
167##
168## $1 - The interface to generate a target for.
169function meson_cpp_target {
170    mesondir="$outputdir/$1"
171    yamldir="$(realpath --relative-to="$mesondir" "$rootdir")"
172
173    # Determine the source and output files based on the YAMLs present.
174    sources=""
175    outputs=""
176    for s in ${interfaces[$1]}; do
177        sources="'$yamldir/$1.$s', "
178
179        case "$s" in
180            errors.yaml)
181                outputs="${outputs}'error.cpp', 'error.hpp', "
182                ;;
183
184            interface.yaml)
185                outputs="${outputs}'server.cpp', 'server.hpp', "
186                outputs="${outputs}'client.hpp', "
187                ;;
188        esac
189    done
190
191    # Create the target to generate the 'outputs'.
192    cat >> "$mesondir/meson.build" \
193        << EOF
194generated_sources += custom_target(
195    '$1__cpp'.underscorify(),
196    input: [ $sources ],
197    output: [ $outputs ],
198    command: [
199        sdbuspp_gen_meson_prog, '--command', 'cpp',
200        '--output', meson.current_build_dir(),
201        '--tool', sdbusplusplus_prog,
202        '--directory', meson.current_source_dir() / '$yamldir',
203        '$1',
204    ],
205)
206
207EOF
208}
209
210## Generate the meson target for the markdown files from a YAML interface.
211## $1 - The interface to generate a target for.
212function meson_md_target {
213    mesondir="$outputdir/$(dirname "$1")"
214    yamldir="$(realpath --relative-to="$mesondir" "$rootdir")"
215
216    # Determine the source files based on the YAMLs present.
217    sources=""
218    for s in ${interfaces[$1]}; do
219        sources="'$yamldir/$1.$s', "
220    done
221
222    # Create the target to generate the interface.md file.
223    cat >> "$mesondir/meson.build" \
224        << EOF
225generated_others += custom_target(
226    '$1__markdown'.underscorify(),
227    input: [ $sources ],
228    output: [ '$(basename "$1").md' ],
229    command: [
230        sdbuspp_gen_meson_prog, '--command', 'markdown',
231        '--output', meson.current_build_dir(),
232        '--tool', sdbusplusplus_prog,
233        '--directory', meson.current_source_dir() / '$yamldir',
234        '$1',
235    ],
236)
237
238EOF
239}
240
241## Handle command=meson by generating the tree of meson.build files.
242function cmd_meson {
243    TLDs="com net org xyz"
244    yamls=""
245
246    # Find all the YAML files in the TLD subdirectories.
247    for d in $TLDs; do
248        dir="$rootdir/$d"
249        if [ ! -d "$dir" ]; then
250            continue
251        fi
252
253        yamls="\
254            $yamls \
255            $(find "$dir" -name '*.interface.yaml' -o -name '*.errors.yaml') \
256            "
257    done
258
259    # Sort YAMLs
260    yamls="$(echo "$yamls" | tr " " "\n" | sort)"
261
262    # Assign the YAML files into the hash-table by interface name.
263    for y in $yamls; do
264        rel="$(realpath "--relative-to=$rootdir" "$y")"
265        dir="$(dirname "$rel")"
266        ext="${rel#*.}"
267        base="$(basename "$rel" ".$ext")"
268        key="$dir/$base"
269
270        interfaces["$key"]="${interfaces[$key]} $ext"
271    done
272
273    # Create the meson.build files.
274    meson_create_root
275    sorted_ifaces="$(echo "${!interfaces[@]}" | tr " " "\n" | sort)"
276    for i in ${sorted_ifaces}; do
277        meson_create_path "$i"
278        meson_cpp_target "$i"
279        meson_md_target "$i"
280    done
281}
282
283## Handle command=cpp by calling sdbus++ as appropriate.
284## $1 - interface to generate.
285##
286## For an interface foo/bar, the outputdir is expected to be foo/bar.
287function cmd_cpp {
288
289    if [ "" == "$1" ]; then
290        show_usage
291        exit 1
292    fi
293
294    if [ ! -e "$rootdir/$1.interface.yaml" ] &&
295        [ ! -e "$rootdir/$1.errors.yaml" ]; then
296        echo "Missing YAML for $1."
297        exit 1
298    fi
299
300    mkdir -p "$outputdir"
301
302    sdbusppcmd="$sdbuspp -r $rootdir"
303    intf="${1//\//.}"
304
305    if [ -e "$rootdir/$1.interface.yaml" ]; then
306        $sdbusppcmd interface server-header "$intf" > "$outputdir/server.hpp"
307        $sdbusppcmd interface server-cpp "$intf" > "$outputdir/server.cpp"
308        $sdbusppcmd interface client-header "$intf" > "$outputdir/client.hpp"
309    fi
310
311    if [ -e "$rootdir/$1.errors.yaml" ]; then
312        $sdbusppcmd error exception-header "$intf" > "$outputdir/error.hpp"
313        $sdbusppcmd error exception-cpp "$intf" > "$outputdir/error.cpp"
314    fi
315}
316
317## Handle command=markdown by calling sdbus++ as appropriate.
318## $1 - interface to generate.
319##
320## For an interface foo/bar, the outputdir is expected to be foo.
321function cmd_markdown {
322
323    if [ "" == "$1" ]; then
324        show_usage
325        exit 1
326    fi
327
328    if [ ! -e "$rootdir/$1.interface.yaml" ] &&
329        [ ! -e "$rootdir/$1.errors.yaml" ]; then
330        echo "Missing YAML for $1."
331        exit 1
332    fi
333
334    mkdir -p "$outputdir"
335
336    sdbusppcmd="$sdbuspp -r $rootdir"
337    intf="${1//\//.}"
338    base="$(basename "$1")"
339
340    echo -n > "$outputdir/$base.md"
341    if [ -e "$rootdir/$1.interface.yaml" ]; then
342        $sdbusppcmd interface markdown "$intf" >> "$outputdir/$base.md"
343    fi
344
345    if [ -e "$rootdir/$1.errors.yaml" ]; then
346        $sdbusppcmd error markdown "$intf" >> "$outputdir/$base.md"
347    fi
348}
349
350## Handle command=version.
351function cmd_version {
352    show_version
353}
354
355"cmd_$cmd" "$*"
356