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