xref: /openbmc/sdbusplus/tools/sdbus++-gen-meson (revision 56eb1775)
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 2"
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    mesondir="$outputdir/$1"
175    yamldir="$(realpath --relative-to="$mesondir" "$rootdir")"
176
177    # Determine the source and output files based on the YAMLs present.
178    sources=""
179    outputs=""
180    for s in ${interfaces[$1]};
181    do
182      sources="'$yamldir/$1.$s', "
183
184        case "$s" in
185            errors.yaml)
186                outputs="${outputs}'error.cpp', 'error.hpp', "
187                ;;
188
189            interface.yaml)
190                outputs="${outputs}'server.cpp', 'server.hpp', "
191                outputs="${outputs}'client.hpp', "
192                ;;
193        esac
194    done
195
196    # Create the target to generate the 'outputs'.
197    cat >> "$mesondir/meson.build" \
198<<EOF
199generated_sources += custom_target(
200    '$1__cpp'.underscorify(),
201    input: [ $sources ],
202    output: [ $outputs ],
203    command: [
204        sdbuspp_gen_meson_prog, '--command', 'cpp',
205        '--output', meson.current_build_dir(),
206        '--tool', sdbusplusplus_prog,
207        '--directory', meson.current_source_dir() / '$yamldir',
208        '$1',
209    ],
210)
211
212EOF
213}
214
215## Generate the meson target for the markdown files from a YAML interface.
216## $1 - The interface to generate a target for.
217function meson_md_target {
218    mesondir="$outputdir/$(dirname "$1")"
219    yamldir="$(realpath --relative-to="$mesondir" "$rootdir")"
220
221    # Determine the source files based on the YAMLs present.
222    sources=""
223    for s in ${interfaces[$1]};
224    do
225        sources="'$yamldir/$1.$s', "
226    done
227
228    # Create the target to generate the interface.md file.
229    cat >> "$mesondir/meson.build" \
230<<EOF
231generated_others += custom_target(
232    '$1__markdown'.underscorify(),
233    input: [ $sources ],
234    output: [ '$(basename "$1").md' ],
235    command: [
236        sdbuspp_gen_meson_prog, '--command', 'markdown',
237        '--output', meson.current_build_dir(),
238        '--tool', sdbusplusplus_prog,
239        '--directory', meson.current_source_dir() / '$yamldir',
240        '$1',
241    ],
242    build_by_default: true,
243)
244
245EOF
246}
247
248## Handle command=meson by generating the tree of meson.build files.
249function cmd_meson {
250    TLDs="com net org xyz"
251    yamls=""
252
253    # Find all the YAML files in the TLD subdirectories.
254    for d in $TLDs;
255    do
256        dir="$rootdir/$d"
257        if [ ! -d "$dir" ];
258        then
259            continue
260        fi
261
262        yamls="\
263            $yamls \
264            $(find "$dir" -name '*.interface.yaml' -o -name '*.errors.yaml') \
265            "
266    done
267
268    # Sort YAMLs
269    yamls="$(echo "$yamls" | tr " " "\n" | sort)"
270
271    # Assign the YAML files into the hash-table by interface name.
272    for y in $yamls;
273    do
274        rel="$(realpath "--relative-to=$rootdir" "$y")"
275        dir="$(dirname "$rel")"
276        ext="${rel#*.}"
277        base="$(basename "$rel" ".$ext")"
278
279        interfaces["$dir/$base"]="${interfaces[$dir/$base]} $ext"
280    done
281
282    # Create the meson.build files.
283    meson_create_root
284    sorted_ifaces="$(echo "${!interfaces[@]}" | tr " " "\n" | sort)"
285    for i in ${sorted_ifaces};
286    do
287        meson_create_path "$i"
288        meson_cpp_target "$i"
289        meson_md_target "$i"
290    done
291}
292
293## Handle command=cpp by calling sdbus++ as appropriate.
294## $1 - interface to generate.
295##
296## For an interface foo/bar, the outputdir is expected to be foo/bar.
297function cmd_cpp {
298
299    if [ "x" == "x$1" ];
300    then
301        show_usage
302        exit 1
303    fi
304
305    if [ ! -e "$rootdir/$1.interface.yaml" ] && \
306        [ ! -e "$rootdir/$1.errors.yaml" ];
307    then
308        echo "Missing YAML for $1."
309        exit 1
310    fi
311
312    mkdir -p "$outputdir"
313
314    sdbusppcmd="$sdbuspp -r $rootdir"
315    intf="${1//\//.}"
316
317    if [ -e "$rootdir/$1.interface.yaml" ];
318    then
319        $sdbusppcmd interface server-header "$intf" > "$outputdir/server.hpp"
320        $sdbusppcmd interface server-cpp "$intf" > "$outputdir/server.cpp"
321        $sdbusppcmd interface client-header "$intf" > "$outputdir/client.hpp"
322    fi
323
324    if [ -e "$rootdir/$1.errors.yaml" ];
325    then
326        $sdbusppcmd error exception-header "$intf" > "$outputdir/error.hpp"
327        $sdbusppcmd error exception-cpp "$intf" > "$outputdir/error.cpp"
328    fi
329}
330
331## Handle command=markdown by calling sdbus++ as appropriate.
332## $1 - interface to generate.
333##
334## For an interface foo/bar, the outputdir is expected to be foo.
335function cmd_markdown {
336
337    if [ "x" == "x$1" ];
338    then
339        show_usage
340        exit 1
341    fi
342
343    if [ ! -e "$rootdir/$1.interface.yaml" ] && \
344        [ ! -e "$rootdir/$1.errors.yaml" ];
345    then
346        echo "Missing YAML for $1."
347        exit 1
348    fi
349
350    mkdir -p "$outputdir"
351
352    sdbusppcmd="$sdbuspp -r $rootdir"
353    intf="${1//\//.}"
354    base="$(basename "$1")"
355
356    echo -n > "$outputdir/$base.md"
357    if [ -e "$rootdir/$1.interface.yaml" ];
358    then
359        $sdbusppcmd interface markdown "$intf" >> "$outputdir/$base.md"
360    fi
361
362    if [ -e "$rootdir/$1.errors.yaml" ];
363    then
364        $sdbusppcmd error markdown "$intf" >> "$outputdir/$base.md"
365    fi
366}
367
368## Handle command=version.
369function cmd_version {
370    show_version
371}
372
373"cmd_$cmd" "$*"
374