#!/usr/bin/env bash set -e # Locale can change behavior of utilities like 'sort' but we want the output # to be stable on all machines. Force the locale to 'C' for consistency. export LC_ALL=C function show_usage { cat \ << EOF Usage: $(basename "$0") [options] * Generate meson.build files from a directory tree containing YAML files and facilitate building the sdbus++ sources. Options: --help - Display this message --command - Command mode to execute (default 'meson'). --directory - Root directory of the YAML source (default '.'). --output - Root directory of the output (default '.'). --tool - Path to the processing tool (default 'sdbus++'). --version - Display this tool's version string. Commands: meson - Generate a tree of meson.build files corresponding to the source YAML files. cpp - Generate the source files from a YAML interface. markdown - Generate the markdown files from a YAML interface. version - Display this tool's version string. EOF } ## The version is somewhat arbitrary but is used to create a warning message ## if a repository contains old copies of the generated meson.build files and ## needs an update. We should increment the version number whenever the ## resulting meson.build would change. tool_version="sdbus++-gen-meson version 3" function show_version { echo "$tool_version" } # Set up defaults. sdbuspp="sdbus++" outputdir="." cmd="meson" rootdir="." # Parse options. options="$(getopt -o hc:d:o:t:v --long help,command:,directory:,output:,tool:,version -- "$@")" eval set -- "$options" while true; do case "$1" in -h | --help) show_usage exit ;; -c | --command) shift cmd="$1" shift ;; -d | --directory) shift rootdir="$1" shift ;; -o | --output) shift outputdir="$1" shift ;; -t | --tool) shift sdbuspp="$1" shift ;; -v | --version) show_version exit ;; --) shift break ;; esac done ## Create an initially empty meson.build file. ## $1 - path to create meson.build at. function meson_empty_file { mkdir -p "$1" echo "# Generated file; do not modify." > "$1/meson.build" } ## Create the root-level meson.build ## ## Inserts rules to run the available version of this tool to ensure the ## version has not changed. function meson_create_root { meson_empty_file "$outputdir" cat >> "$outputdir/meson.build" \ << EOF sdbuspp_gen_meson_ver = run_command( sdbuspp_gen_meson_prog, '--version', ).stdout().strip().split('\n')[0] if sdbuspp_gen_meson_ver != '$tool_version' warning('Generated meson files from wrong version of sdbus++-gen-meson.') warning( 'Expected "$tool_version", got:', sdbuspp_gen_meson_ver ) endif EOF } ## hash-tables to store: ## meson_paths - list of subdirectory paths for which an empty meson.build ## has already been created. ## interfaces - list of interface paths which a YAML has been found and ## which YAML types (interface, errors, etc.). declare -A meson_paths declare -A interfaces ## Ensure the meson.build files to a path have been created. ## $1 - The path requiring to be created. function meson_create_path { meson_path="$outputdir" prev_meson_path="" # Split the path into segments. for part in $(echo "$1" | tr '/' '\n'); do prev_meson_path="$meson_path" meson_path="$meson_path/$part" # Create the meson.build for this segment if it doesn't already exist. if [ "" == "${meson_paths[$meson_path]}" ]; then meson_paths["$meson_path"]="1" meson_empty_file "$meson_path" # Add the 'subdir' link into the parent's meson.build. # We need to skip adding the links into the 'root' meson.build # because most repositories want to selectively add TLDs based # on config flags. Let them figure out their own logic for that. if [ "$outputdir" != "$prev_meson_path" ]; then echo "subdir('$part')" >> "$prev_meson_path/meson.build" fi fi done } ## Generate the meson target for the source files (.cpp/.hpp) from a YAML ## interface. ## ## $1 - The interface to generate a target for. function meson_cpp_target { mesondir="$outputdir/$1" yamldir="$(realpath --relative-to="$mesondir" "$rootdir")" # Determine the source and output files based on the YAMLs present. sources="" outputs="" for s in ${interfaces[$1]}; do sources="'$yamldir/$1.$s', " case "$s" in errors.yaml) outputs="${outputs}'error.cpp', 'error.hpp', " ;; interface.yaml) outputs="${outputs}'server.cpp', 'server.hpp', " outputs="${outputs}'client.hpp', " ;; esac done # Create the target to generate the 'outputs'. cat >> "$mesondir/meson.build" \ << EOF generated_sources += custom_target( '$1__cpp'.underscorify(), input: [ $sources ], output: [ $outputs ], command: [ sdbuspp_gen_meson_prog, '--command', 'cpp', '--output', meson.current_build_dir(), '--tool', sdbusplusplus_prog, '--directory', meson.current_source_dir() / '$yamldir', '$1', ], ) EOF } ## Generate the meson target for the markdown files from a YAML interface. ## $1 - The interface to generate a target for. function meson_md_target { mesondir="$outputdir/$(dirname "$1")" yamldir="$(realpath --relative-to="$mesondir" "$rootdir")" # Determine the source files based on the YAMLs present. sources="" for s in ${interfaces[$1]}; do sources="'$yamldir/$1.$s', " done # Create the target to generate the interface.md file. cat >> "$mesondir/meson.build" \ << EOF generated_others += custom_target( '$1__markdown'.underscorify(), input: [ $sources ], output: [ '$(basename "$1").md' ], command: [ sdbuspp_gen_meson_prog, '--command', 'markdown', '--output', meson.current_build_dir(), '--tool', sdbusplusplus_prog, '--directory', meson.current_source_dir() / '$yamldir', '$1', ], ) EOF } ## Handle command=meson by generating the tree of meson.build files. function cmd_meson { TLDs="com net org xyz" yamls="" # Find all the YAML files in the TLD subdirectories. for d in $TLDs; do dir="$rootdir/$d" if [ ! -d "$dir" ]; then continue fi yamls="\ $yamls \ $(find "$dir" -name '*.interface.yaml' -o -name '*.errors.yaml') \ " done # Sort YAMLs yamls="$(echo "$yamls" | tr " " "\n" | sort)" # Assign the YAML files into the hash-table by interface name. for y in $yamls; do rel="$(realpath "--relative-to=$rootdir" "$y")" dir="$(dirname "$rel")" ext="${rel#*.}" base="$(basename "$rel" ".$ext")" key="$dir/$base" interfaces["$key"]="${interfaces[$key]} $ext" done # Create the meson.build files. meson_create_root sorted_ifaces="$(echo "${!interfaces[@]}" | tr " " "\n" | sort)" for i in ${sorted_ifaces}; do meson_create_path "$i" meson_cpp_target "$i" meson_md_target "$i" done } ## Handle command=cpp by calling sdbus++ as appropriate. ## $1 - interface to generate. ## ## For an interface foo/bar, the outputdir is expected to be foo/bar. function cmd_cpp { if [ "" == "$1" ]; then show_usage exit 1 fi if [ ! -e "$rootdir/$1.interface.yaml" ] && [ ! -e "$rootdir/$1.errors.yaml" ]; then echo "Missing YAML for $1." exit 1 fi mkdir -p "$outputdir" sdbusppcmd="$sdbuspp -r $rootdir" intf="${1//\//.}" if [ -e "$rootdir/$1.interface.yaml" ]; then $sdbusppcmd interface server-header "$intf" > "$outputdir/server.hpp" $sdbusppcmd interface server-cpp "$intf" > "$outputdir/server.cpp" $sdbusppcmd interface client-header "$intf" > "$outputdir/client.hpp" fi if [ -e "$rootdir/$1.errors.yaml" ]; then $sdbusppcmd error exception-header "$intf" > "$outputdir/error.hpp" $sdbusppcmd error exception-cpp "$intf" > "$outputdir/error.cpp" fi } ## Handle command=markdown by calling sdbus++ as appropriate. ## $1 - interface to generate. ## ## For an interface foo/bar, the outputdir is expected to be foo. function cmd_markdown { if [ "" == "$1" ]; then show_usage exit 1 fi if [ ! -e "$rootdir/$1.interface.yaml" ] && [ ! -e "$rootdir/$1.errors.yaml" ]; then echo "Missing YAML for $1." exit 1 fi mkdir -p "$outputdir" sdbusppcmd="$sdbuspp -r $rootdir" intf="${1//\//.}" base="$(basename "$1")" echo -n > "$outputdir/$base.md" if [ -e "$rootdir/$1.interface.yaml" ]; then $sdbusppcmd interface markdown "$intf" >> "$outputdir/$base.md" fi if [ -e "$rootdir/$1.errors.yaml" ]; then $sdbusppcmd error markdown "$intf" >> "$outputdir/$base.md" fi } ## Handle command=version. function cmd_version { show_version } "cmd_$cmd" "$*"