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