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