blob: ba7aa9380947e68eda1e666bb736232bc98f25e2 [file] [log] [blame]
#!/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] <command-args>*
Generate meson.build files from a directory tree containing YAML files and
facilitate building the sdbus++ sources.
Options:
--help - Display this message
--command <cmd> - Command mode to execute (default 'meson').
--directory <path> - Root directory of the YAML source (default '.').
--output <path> - Root directory of the output (default '.').
--tool <path> - 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 <intf> - Generate the source files from a YAML interface.
markdown <intf> - 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 4"
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
;;
*)
echo "Invalid argument $1"
exit 1
;;
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', "
;;
*)
echo "Unknown interface type: ${s}"
exit 1
;;
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} ],
depend_files: sdbusplusplus_depfiles,
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' ],
depend_files: sdbusplusplus_depfiles,
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() {
# Find and sort all the YAML files
yamls="$(find "${rootdir}" -name '*.interface.yaml' -o -name '*.errors.yaml')"
yamls="$(echo "${yamls}" | 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
# shellcheck disable=SC2312
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}" "$*"