diff options
Diffstat (limited to 'libs')
-rwxr-xr-x | libs/common | 379 | ||||
-rwxr-xr-x | libs/git | 634 | ||||
-rwxr-xr-x | libs/project | 1471 | ||||
-rwxr-xr-x | libs/tool | 287 |
4 files changed, 2771 insertions, 0 deletions
diff --git a/libs/common b/libs/common new file mode 100755 index 00000000..d375ef4e --- /dev/null +++ b/libs/common @@ -0,0 +1,379 @@ +#!/bin/bash + +# Copyright (C) 2016 Paul Kocialkowski <contact@paulk.fr> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +BUILD_SYSTEM="libreboot" + +PROJECTS="projects" +SOURCES="sources" +BUILD="build" +INSTALL="install" +RELEASE="release" +SYSTEMS="systems" +IMAGES="images" +TOOLS="tools" +DOCS="docs" + +CONFIGS="configs" +PATCHES="patches" +TARGETS="targets" +REVISION="revision" +BLOBS="blobs" +BLOBS_IGNORE="blobs-ignore" +BLOBS_DISCOVER="blobs-discover" + +DOTEPOCH=".epoch" +DOTVERSION=".version" +DOTREVISION=".revision" +DOTTARFILES=".tarfiles" +TAR_XZ="tar.xz" +SHA256SUM="sha256sum" +ASC="asc" + +function_check() { + local function=$1 + + declare -f -F "$function" > /dev/null +} + +variable_check() { + local variable=$1 + + test ! -z "${!variable}" +} + +arguments_list() { + local argument + + for argument in "$@" + do + echo "$argument" + done +} + +path_wildcard_expand() { + local path=$@ + + # Evaluation fails with unescaped whitespaces. + path=$( echo "$path" | sed "s/ /\\\ /g" ) + + eval "arguments_list "$path"" +} + +file_checksum_create() { + local path=$1 + + local checksum_path="$path.$SHA256SUM" + local name=$( basename "$path" ) + local directory_path=$( dirname "$path" ) + + ( + cd "$directory_path" + sha256sum "$name" > "$checksum_path" + ) +} + +file_checksum_check() { + local path=$1 + + local checksum_path="$path.$SHA256SUM" + local name=$( basename "$path" ) + local directory_path=$( dirname "$path" ) + + if ! [ -f "$checksum_path" ] + then + printf "Could not verify file checksum!\n" >&2 + return 1 + fi + + ( + cd "$directory_path" + sha256sum -c "$checksum_path" + ) +} + +file_signature_create() { + local path=$1 + + local signature_path="$path.$ASC" + + if [ -z "$RELEASE_KEY" ] + then + return 0 + fi + + gpg --default-key "$RELEASE_KEY" --armor --output "$signature_path" --detach-sign --yes "$path" +} + +file_signature_check() { + local path=$1 + + local signature_path="$path.$ASC" + + if ! [ -f "$signature_path" ] + then + printf "Could not verify file signature!\n" >&2 + return 1 + fi + + gpg --armor --verify "$signature_path" "$path" +} + +file_verification_create() { + local path=$1 + + file_checksum_create "$path" + file_signature_create "$path" +} + +file_verification_check() { + local path=$1 + + file_checksum_check "$path" + file_signature_check "$path" +} + +file_exists_check() { + local path=$1 + + test -f "$path" +} + +directory_filled_check() { + local path=$1 + + if [ -z "$( ls -A "$path" 2> /dev/null )" ] + then + return 1 + else + return 0 + fi +} + +archive_files_create() { + local source_path=$1 + + local directory=$( basename "$source_path" ) + local tarfiles_path="$source_path/$DOTTARFILES" + local revision_path="$source_path/$DOTREVISION" + local version_path="$source_path/$DOTVERSION" + + if git_check "$source_path" + then + git_files "$source_path" | tr -d '\0' > "$tarfiles_path" + echo "$DOTTARFILES" | tr -d '\0' >> "$tarfiles_path" + elif ! [ -f "$tarfiles_path" ] + then + touch "$tarfiles_path" + + ( + cd "$source_path" + find + ) | LC_ALL=C sort | sed "s,^./,," | grep -vP "^\.$" > "$tarfiles_path" + else + # Preserve tarfiles if not in git. + return 0 + fi + + if [ -f "$revision_path" ] + then + echo "$DOTREVISION" | tr -d '\0' >> "$tarfiles_path" + fi + + if [ -f "$version_path" ] + then + echo "$DOTVERSION" | tr -d '\0' >> "$tarfiles_path" + fi + + if [ -f "$epoch_path" ] + then + echo "$DOTEPOCH" | tr -d '\0' >> "$tarfiles_path" + fi +} + +archive_files_date() { + local source_path=$1 + + local epoch_path="$source_path/$DOTEPOCH" + + if ! [ -z "$SOURCE_DATE_EPOCH" ] + then + ( + cd "$source_path" + find -exec touch --no-dereference --date="@$SOURCE_DATE_EPOCH" {} \; + ) + fi +} + +archive_create() { + local archive_path=$1 + local source_path=$2 + local directory=$3 + + local tarfiles_path="$source_path/$DOTTARFILES" + local directory_path=$( dirname "$archive_path" ) + + mkdir -p "$directory_path" + + if [ -z "$directory" ] + then + directory=$( basename "$source_path" ) + fi + + archive_files_create "$source_path" + archive_files_date "$source_path" + + ( + cd "$source_path" + tar -cJf "$archive_path" --no-recursion -T "$tarfiles_path" --transform="s,^,$directory/,S" --owner=root --group=root --numeric-owner + ) +} + +archive_extract() { + local archive_path=$1 + local destination_path=$2 + + if [ -z "$destination_path" ] + then + destination_path=$( dirname "$archive_path" ) + fi + + tar -xf "$archive_path" -ps -C "$destination_path" +} + +rootfs_files_create() { + local source_path=$1 + + local directory=$( basename "$source_path" ) + local tarfiles_path="$source_path/$DOTTARFILES" + + touch "$tarfiles_path" + + ( + cd "$source_path" + execute_root find + ) | LC_ALL=C sort | sed "s,^./,," | grep -vP "^$DOTTARFILES|^\.$" > "$tarfiles_path" +} + +rootfs_files_date() { + local source_path=$1 + + local epoch_path="$source_path/$DOTEPOCH" + + if ! [ -z "$SOURCE_DATE_EPOCH" ] + then + ( + cd "$source_path" + execute_root find -exec touch --no-dereference --date="@$SOURCE_DATE_EPOCH" {} \; + ) + fi +} + +rootfs_create() { + local rootfs_path=$1 + local source_path=$2 + local directory=$3 + + local tarfiles_path="$source_path/$DOTTARFILES" + local directory_path=$( dirname "$rootfs_path" ) + + mkdir -p "$directory_path" + + if [ -z "$directory" ] + then + directory=$( basename "$source_path" ) + fi + + rootfs_files_create "$source_path" + rootfs_files_date "$source_path" + + ( + cd "$source_path" + execute_root tar -cJf "$rootfs_path" --no-recursion -T "$tarfiles_path" --numeric-owner + ) + + execute_root chmod 644 "$rootfs_path" + execute_root chown $USER:$USER "$rootfs_path" +} + +requirements() { + local requirement + local requirement_path + + for requirement in "$@" + do + requirement_path=$( which "$requirement" || true ) + + if [ -z "$requirement_path" ] + then + printf "Missing requirement: $requirement\n" >&2 + exit 1 + fi + done +} + +requirements_root() { + local requirement + local requirement_path + + for requirement in "$@" + do + # We need to keep stdout output to show the command. + requirement_path=$( execute_root which "$requirement" || true ) + + if [ -z "$requirement_path" ] + then + printf "Missing requirement: $requirement\n" >&2 + exit 1 + fi + done +} + +arguments_concat() { + local delimiter=$1 + shift + + local concat + + for argument in "$@" + do + if ! [ -z "$concat" ] + then + concat="$concat""$delimiter""$argument" + else + concat="$argument" + fi + done + + echo "$concat" +} + +execute_root() { + local sudo=$( which sudo 2> /dev/null || true ) + local arguments + + printf "Running command as root: " >&2 + echo "$@" >&2 + + if ! [ -z "$sudo" ] + then + sudo "$@" + else + # Quote arguments for eval through su. + arguments=$( printf "%q " "$@" ) + su -c "$arguments" + fi +} diff --git a/libs/git b/libs/git new file mode 100755 index 00000000..ead6651d --- /dev/null +++ b/libs/git @@ -0,0 +1,634 @@ +#!/bin/bash + +# Copyright (C) 2016 Paul Kocialkowski <contact@paulk.fr> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +DOTGIT=".git" +HEAD="HEAD" +ORIGIN_HEAD="origin/HEAD" +WILDDOTPATCH="*.patch" +GIT_NAME="Libreboot" +GIT_EMAIL="libreboot@libreboot.org" + +git_check() { + local repository_path=$1 + + directory_filled_check "$repository_path/$DOTGIT" +} + +git_clone() { + local repository_path=$1 + local url=$2 + + git clone "$url" "$repository_path" +} + +git_submodule_update() { + local repository_path=$1 + + ( + cd "$repository_path" + git submodule update --init + ) +} + +git_merge() { + local repository_path=$1 + local revision=$2 + + ( + cd "$repository_path" + git merge "$revision" + ) +} + +git_branch_create() { + local repository_path=$1 + local branch=$2 + local revision=$3 + + ( + cd "$repository_path" + git checkout -b "$branch" + + if ! [ -z "$revision" ] + then + git reset --hard "$revision" + fi + ) +} + +git_branch_delete() { + local repository_path=$1 + local branch=$2 + + ( + cd "$repository_path" + git branch -D "$branch" + ) +} + +git_branch_checkout() { + local repository_path=$1 + local branch=$2 + + ( + cd "$repository_path" + git checkout "$branch" > /dev/null + ) +} + +git_branch_check() { + local repository_path=$1 + local branch=$2 + + ( + cd "$repository_path" 2> /dev/null > /dev/null + if [ $? -ne 0 ] + then + return 1 + fi + + git rev-parse --verify "$branch" 2> /dev/null > /dev/null + if [ $? -ne 0 ] + then + return 1 + fi + ) +} + +git_fetch() { + local repository_path=$1 + + ( + cd "$repository_path" + git fetch origin + ) +} + +git_fetch_check() { + local repository_path=$1 + + ( + cd "$repository_path" 2> /dev/null > /dev/null + if [ $? -ne 0 ] + then + return 1 + fi + + local output=$( git fetch --dry-run origin 2>&1 ) + if ! [ -z "$output" ] + then + return 1 + fi + ) +} + +git_clean() { + local repository_path=$1 + + ( + cd "$repository_path" + git clean -df + ) +} + +git_remove() { + local repository_path=$1 + local path=$2 + + ( + cd "$repository_path" + git rm -rf "$path" + ) +} + +git_commit() { + local repository_path=$1 + local message=$2 + + ( + export GIT_COMMITTER_NAME=$GIT_NAME + export GIT_COMMITTER_EMAIL=$GIT_EMAIL + + cd "$repository_path" + git commit --author="$GIT_NAME <$GIT_EMAIL>" -m "$message" + ) +} + +git_patch() { + local repository_path=$1 + local branch=$2 + local patch=$3 + + ( + export GIT_COMMITTER_NAME=$GIT_NAME + export GIT_COMMITTER_EMAIL=$GIT_EMAIL + + cd "$repository_path" + git checkout "$branch" 2> /dev/null > /dev/null + git am "$patch" + ) +} + +git_revision() { + local repository_path=$1 + + ( + cd "$repository_path" + git rev-parse "$HEAD" + ) +} + +git_describe() { + local repository_path=$1 + + ( + cd "$repository_path" + git describe --tags + ) +} + +git_files() { + local repository_path=$1 + + ( + cd "$repository_path" + # Reproducible sorting. + git ls-files | LC_ALL=C sort -z + ) +} + +git_project_repository_path() { + local repository=$1 + + echo "$root/$SOURCES/$repository" +} + +git_project_check() { + local repository=$1 + + local repository_path=$( git_project_repository_path "$repository" ) + + git_check "$repository_path" +} + +git_project_patch_recursive() { + local project=$1 + local repository=$2 + local branch=$3 + local path=$4 + + local repository_path=$( git_project_repository_path "$repository" ) + local project_path=$( project_path "$project" ) + local patches_path="$project_path/$PATCHES/$path/$WILDDOTPATCH" + local patch_path + + if ! [ -z "$path" ] && [ "$path" != "." ] + then + git_project_patch_recursive "$project" "$repository" "$branch" "$( dirname "$path" )" + fi + + path_wildcard_expand "$patches_path" | while read patch_path + do + if ! [ -f "$patch_path" ] + then + continue + fi + + git_patch "$repository_path" "$branch" "$patch_path" + done +} + +git_project_clone() { + local repository=$1 + shift + local urls=$@ + + local repository_path=$( git_project_repository_path "$repository" ) + local directory_path=$( dirname "$repository_path" ) + local url + + mkdir -p "$directory_path" + + ( + set +e + + for url in $urls + do + git_clone "$repository_path" "$url" + + if [ $? -eq 0 ] + then + return 0 + fi + done + + return 1 + ) +} + +git_project_prepare() { + local project=$1 + shift + local repository=$1 + shift + + git_project_prepare_revision "$project" "$repository" "$@" + git_project_prepare_blobs "$project" "$repository" "$@" + git_project_prepare_patch "$project" "$repository" "$@" +} + +git_project_prepare_blobs() { + local project=$1 + shift + local repository=$1 + shift + + local repository_path=$( git_project_repository_path "$repository" ) + local blobs_path=$( project_blobs_path "$project" "$@" ) + local blob + + if [ -z "$blobs_path" ] + then + return + fi + + while read blob + do + git_remove "$repository_path" "$blob" + done < "$blobs_path" + + git_commit "$repository_path" "Removed blobs" +} + +git_project_prepare_patch() { + local project=$1 + shift + local repository=$1 + shift + + local project_path=$( project_path "$project" ) + local configs_path="$project_path/$CONFIGS" + local prepare_branch + local prepare_path + local branch=$project + local argument + local path + + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + if [ -z "$path" ] + then + path="$argument" + else + path="$path/$argument" + fi + + branch="$branch-$argument" + fi + + local revision_path="$configs_path/$path/$REVISION" + + if ! [ -f "$revision_path" ] + then + continue + fi + + prepare_branch=$branch + prepare_path=$path + done + + if ! [ -z "$prepare_branch" ] + then + git_project_patch_recursive "$project" "$repository" "$prepare_branch" "$prepare_path" + fi +} + +git_project_prepare_revision() { + local project=$1 + shift + local repository=$1 + shift + + local repository_path=$( git_project_repository_path "$repository" ) + local project_path=$( project_path "$project" ) + local configs_path="$project_path/$CONFIGS" + local prepare_branch + local prepare_revision + local branch=$project + local argument + local path + + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + if [ -z "$path" ] + then + path="$argument" + else + path="$path/$argument" + fi + + branch="$branch-$argument" + fi + + local revision_path="$configs_path/$path/$REVISION" + + if ! [ -f "$revision_path" ] + then + continue + fi + + prepare_branch=$branch + prepare_revision=$( cat "$revision_path" ) + done + + if ! [ -z "$prepare_branch" ] + then + git_branch_create "$repository_path" "$prepare_branch" "$prepare_revision" + fi +} + +git_project_prepare_check() { + local project=$1 + shift + local repository=$1 + shift + + local repository_path=$( git_project_repository_path "$repository" ) + local project_path=$( project_path "$project" ) + local configs_path="$project_path/$CONFIGS" + local prepare_branch + local branch=$project + local argument + local path + + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + if [ -z "$path" ] + then + path="$argument" + else + path="$path/$argument" + fi + + branch="$branch-$argument" + fi + + local revision_path="$configs_path/$path/$REVISION" + + if ! [ -f "$revision_path" ] + then + continue + fi + + prepare_branch=$branch + done + + if ! [ -z "$prepare_branch" ] + then + git_branch_check "$repository_path" "$prepare_branch" + fi +} + +git_project_prepare_clean() { + local project=$1 + shift + local repository=$1 + shift + + local repository_path=$( git_project_repository_path "$repository" ) + local prepare_branch + local branch=$project + local argument + + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + branch="$branch-$argument" + fi + + if ! git_branch_check "$repository_path" "$branch" + then + continue + fi + + prepare_branch=$branch + done + + if ! [ -z "$prepare_branch" ] + then + # Let's not worry about missing branches. + ( + set +e + + git_branch_delete "$repository_path" "$prepare_branch" + + if [ $? -ne 0 ] + then + return 0 + fi + ) + fi +} + +git_project_checkout() { + local project=$1 + shift + local repository=$1 + shift + + local repository_path=$( git_project_repository_path "$repository" ) + local checkout_branch + local branch=$project + local argument + + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + branch="$branch-$argument" + fi + + if ! git_branch_check "$repository_path" "$branch" + then + continue + fi + + checkout_branch=$branch + done + + if ! [ -z "$checkout_branch" ] + then + git_branch_checkout "$repository_path" "$checkout_branch" + git_submodule_update "$repository_path" + fi +} + +git_project_update() { + local project=$1 + shift + local repository=$1 + shift + + local repository_path=$( git_project_repository_path "$repository" ) + + git_fetch "$repository_path" + git_branch_checkout "$repository_path" "$ORIGIN_HEAD" + + git_project_prepare_clean "$project" "$repository" "$@" + git_project_prepare "$project" "$repository" "$@" +} + +git_project_update_check() { + local project=$1 + shift + local repository=$1 + shift + + git_project_prepare_check "$project" "$repository" "$@" + + git_fetch_check "$repository_path" +} + +git_project_release() { + local project=$1 + shift + local repository=$1 + shift + local arguments=$@ + + local repository_path=$( git_project_repository_path "$repository" ) + local release_branch + local branch=$project + local argument + + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + branch="$branch-$argument" + fi + + if ! git_branch_check "$repository_path" "$branch" + then + continue + fi + + release_branch=$branch + done + + if ! [ -z "$release_branch" ] + then + local archive_path="$root/$RELEASE/$SOURCES/$project/$release_branch.$TAR_XZ" + local sources_path="$root/$SOURCES/$repository" + + printf "Releasing sources archive for $project (with ${arguments:-no argument}) from "$repository" git\n" + + git_branch_checkout "$repository_path" "$release_branch" + git_submodule_update "$repository_path" + git_clean "$repository_path" + archive_create "$archive_path" "$sources_path" "$release_branch" + file_verification_create "$archive_path" + fi +} + +git_project_release_check() { + local project=$1 + shift + local repository=$1 + shift + + local repository_path=$( git_project_repository_path "$repository" ) + local release_branch + local branch=$project + local argument + + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + branch="$branch-$argument" + fi + + if ! git_branch_check "$repository_path" "$branch" + then + continue + fi + + release_branch=$branch + done + + if ! [ -z "$release_branch" ] + then + local archive_path="$root/$RELEASE/$SOURCES/$project/$release_branch.$TAR_XZ" + + file_exists_check "$archive_path" + + if [ $? -ne 0 ] + then + return 1 + else + return 0 + fi + fi +} diff --git a/libs/project b/libs/project new file mode 100755 index 00000000..44de3957 --- /dev/null +++ b/libs/project @@ -0,0 +1,1471 @@ +#!/bin/bash + +# Copyright (C) 2016 Paul Kocialkowski <contact@paulk.fr> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +PROJECT_ACTIONS_GENERIC="usage download extract update build install release clean" +PROJECT_ACTIONS_HELPERS="arguments" +PROJECT_FUNCTIONS=$( for action in $PROJECT_ACTIONS_GENERIC ; do echo "$action" "$action""_check" ; done ; echo "$PROJECT_ACTIONS_HELPERS" ) + +INSTALL_REGEX="\([^:]*\):\(.*\)" + +project_include() { + local project=$1 + + local project_path=$( project_path "$project" ) + + unset -f $PROJECT_FUNCTIONS + + . "$project_path/$project" + + project_helper_include "$project" +} + +project_helper_include() { + local project=$1 + + local project_path=$( project_path "$project" ) + local include="$project_path/$project-helper" + + if [ -f "$include" ] + then + . "$include" + fi +} + +project_check() { + local project=$1 + + local project_path=$( project_path "$project" ) + + if ! [ -f "$project_path/$project" ] + then + return 1 + fi + + return 0 +} + +project_function_check() { + local project=$1 + local function=$2 + + project_include "$project" + + if ! function_check "$function" + then + return 1 + fi + + return 0 +} + +project_action() { + local action=$1 + shift + local project=$1 + shift + local arguments=$@ + + ( + set +e + + if ! project_check "$project" + then + printf "Project $project check failed\n" >&2 + return 1 + fi + + project_action_check "$action" "$project" "$@" + + if [ $? -eq 0 ] + then + return 0 + fi + + project_include "$project" + + if ! function_check "$action" + then + return 0 + fi + + printf "Project $project $action (with ${arguments:-no argument})\n" >&2 + + ( + set -e + "$action" "$@" + ) + + if [ $? -ne 0 ] + then + printf "\nProject $project $action (with ${arguments:-no argument}) failed\n" >&2 + return 1 + else + printf "\nProject $project $action (with ${arguments:-no argument}) completed\n" >&2 + fi + ) +} + +project_action_check() { + local action=$1 + shift + local project=$1 + shift + + ( + set +e + + if ! project_check "$project" + then + printf "Project $project check failed\n" >&2 + return 1 + fi + + project_include "$project" + + if ! function_check "$action""_check" + then + return 1 + fi + + for project_force in $PROJECTS_FORCE + do + if [ "$project_force" = "$project" ] + then + return 1 + fi + done + + ( + set -e + "$action""_check" "$@" + ) + ) +} + +project_action_helper() { + local helper=$1 + shift + local project=$1 + shift + + ( + set +e + + if ! project_check "$project" + then + printf "Project $project check failed\n" >&2 + return 1 + fi + + project_include "$project" + + if ! function_check "$helper" + then + return 0 + fi + + ( + set -e + "$helper" "$@" + ) + ) +} + +project_action_arguments() { + local action=$1 + shift + local project=$1 + shift + + project_action_arguments_verify_recursive "$action" "$project" "$@" + project_action_arguments_recursive "$action" "$project" "$@" +} + +project_action_arguments_verify_recursive() { + local action=$1 + shift + local project=$1 + shift + + local action_helper_arguments + + # Store final argument. + local argument=${@:$#} + + local test + + if [ $# -gt 1 ] + then + # Set previous arguments. + set "${@:1:$#-1}" + elif [ $# -eq 1 ] + then + shift + else + return 0 + fi + + action_helper_arguments=$( project_action_helper "arguments" "$project" "$@" ) + + if ! [ -z "$action_helper_arguments" ] + then + test=$( echo "$action_helper_arguments" | grep -P "^$argument$" || true ) + + if [ -z "$test" ] + then + printf "Invalid argument $argument for project $project action $action\n" >&2 + return 1 + fi + fi + + project_action_arguments_verify_recursive "$action" "$project" "$@" +} + +project_action_arguments_recursive() { + local action=$1 + shift + local project=$1 + shift + + local action_helper_arguments + local argument + local ifs_save + + action_helper_arguments=$( project_action_helper "arguments" "$project" "$@" ) + + if [ $? -ne 0 ] || [ -z "$action_helper_arguments" ] + then + project_action "$action" "$project" "$@" + else + # This it to allow space characters in arguments. + ifs_save=$IFS + IFS=$'\n' + + for argument in $( echo "$action_helper_arguments" ) + do + ( + IFS=$ifs_save + + # Only a single argument at a time is returned by the helper. + project_action_arguments_recursive "$action" "$project" "$@" "$argument" + ) + done + + IFS=$ifs_save + fi +} + +project_action_projects() { + local action=$1 + shift + local project=$1 + shift + + local project_path=$( project_path "$project" ) + local project_projects_path="$project_path/$CONFIGS/$PROJECTS" + local project_projects_action_path="$project_path/$CONFIGS/$PROJECTS-$action" + local arguments + local path + + if [ -f "$project_projects_action_path" ] + then + path=$project_projects_action_path + else + path=$project_projects_path + fi + + # Multiple arguments can be read from the file. + while read arguments + do + eval "project_action_arguments "$action" $arguments" + done < "$path" +} + +project_path() { + local project=$1 + + local project_path="$root/$PROJECTS/$project" + + echo "$project_path" +} + +project_sources_path() { + local project=$1 + shift + local repository=$1 + shift + + local sources_path + local argument + local path + + # Check downloaded and extracted sources first, using "$project." + path="$root/$SOURCES/$project" + + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + path="$path-$argument" + fi + + if ! directory_filled_check "$path" + then + continue + fi + + sources_path=$path + done + + if ! [ -z "$sources_path" ] + then + echo "$sources_path" + return + fi + + # Check downloaded sources then, using "$repository." + path="$root/$SOURCES/$repository" + + if directory_filled_check "$path" + then + echo "$path" + return + fi + + # Check project sources finally, using "$project." + path="$root/$PROJECTS/$project/$SOURCES" + + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + path="$path/$argument" + fi + + if ! directory_filled_check "$path" + then + continue + fi + + sources_path=$path + done + + if ! [ -z "$sources_path" ] + then + echo "$sources_path" + return + fi +} + +project_sources_directory_filled_check() { + local project=$1 + shift + + local sources_path=$( project_sources_path "$project" "$@" ) + + test ! -z "$sources_path" +} + +project_sources_directory_filled_error() { + local project=$1 + shift + local arguments=$@ + + local sources_path=$( project_sources_path "$project" "$@" ) + + if ! [ -z "$sources_path" ] + then + printf "Sources directory for project $project (with ${arguments:-no argument}) already exists\n" >&2 + return 1 + else + return 0 + fi +} + +project_sources_directory_missing_empty_error() { + local project=$1 + shift + local arguments=$@ + + local sources_path=$( project_sources_path "$project" "$@" ) + + if [ -z "$sources_path" ] + then + printf "Sources directory for project $project (with ${arguments:-no argument}) missing or empty\n" >&2 + return 1 + else + return 0 + fi +} + +project_sources_archive() { + local project=$1 + shift + + local sources_archive + local argument + local path="$root/$SOURCES/$project" + + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + path="$path-$argument" + fi + + local archive="$path.$TAR_XZ" + + if ! [ -f "$archive" ] + then + continue + fi + + sources_archive=$archive + done + + if ! [ -z "$sources_archive" ] + then + echo "$sources_archive" + fi +} + +project_sources_archive_extract() { + local project=$1 + shift + local arguments=$@ + + local archive=$( project_sources_archive "$project" "$@" ) + local destination=$( dirname "$archive" ) + + printf "Extracting source archive for $project (with ${arguments:-no argument})\n" + + file_verification_check "$archive" + archive_extract "$archive" "$destination" +} + +project_sources_archive_update() { + local project=$1 + shift + local arguments=$@ + + local repository=$project + local sources_path=$( project_sources_path "$project" "$repository" "$@" ) + local archive=$( project_sources_archive "$project" "$@" ) + local destination=$( dirname "$archive" ) + + if [ -d "$sources_path" ] + then + rm -rf "$sources_path" + fi + + printf "Extracting source archive for $project (with ${arguments:-no argument})\n" + + file_verification_check "$archive" + archive_extract "$archive" "$destination" +} + +project_sources_archive_missing_error() { + local project=$1 + shift + local arguments=$@ + + local archive=$( project_sources_archive "$project" "$@" ) + if [ -z "$archive" ] || ! [ -f "$archive" ] + then + printf "Missing sources archive for $project (with ${arguments:-no argument})\n" >&2 + return 1 + else + return 0 + fi +} + +project_sources_archive_missing_check() { + local project=$1 + shift + + local archive=$( project_sources_archive "$project" "$@" ) + if [ -z "$archive" ] || ! [ -f "$archive" ] + then + return 0 + else + return 1 + fi +} + +project_blobs_path() { + local project=$1 + shift + + local project_path=$( project_path "$project" ) + local configs_path="$project_path/$CONFIGS" + local argument + local path + + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + if [ -z "$path" ] + then + path="$argument" + else + path="$path/$argument" + fi + fi + + local blobs_path="$configs_path/$path/$BLOBS" + + if [ -f "$blobs_path" ] + then + echo "$blobs_path" + return + fi + done +} + +project_blobs_ignore_path() { + local project=$1 + shift + + local project_path=$( project_path "$project" ) + local configs_path="$project_path/$CONFIGS" + local argument + local path + + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + if [ -z "$path" ] + then + path="$argument" + else + path="$path/$argument" + fi + fi + + blobs_ignore_path="$configs_path/$path/$BLOBS_IGNORE" + + if [ -f "$blobs_ignore_path" ] + then + echo "$blobs_ignore_path" + return + fi + done +} + +project_arguments_targets() { + local project=$1 + shift + + local project_path=$( project_path "$project" ) + local targets_path="$project_path/$CONFIGS" + local argument + + for argument in "$@" + do + targets_path="$targets_path/$argument" + done + + targets_path="$targets_path/$TARGETS" + + if [ -f "$targets_path" ] + then + cat "$targets_path" + fi +} + +project_usage_actions() { + local project=$1 + shift + + printf "\nGeneric actions:\n" + + for action in $PROJECT_ACTIONS_GENERIC + do + if function_check "$action" + then + printf " $action\n" + fi + done + + if [ $# -gt 0 ] + then + printf "\nSpecific actions:\n" + + for action in "$@" + do + printf " $action\n" + done + fi +} + +project_usage_arguments() { + local project=$1 + shift + + printf "\nArguments:\n" + + project_usage_arguments_recursive "$project" " " "$@" +} + +project_usage_arguments_recursive() { + local project=$1 + shift + local spacing=$1 + shift + + local action_helper_arguments + local argument + + action_helper_arguments=$( project_action_helper "arguments" "$project" "$@" ) + + if ! [ -z "$action_helper_arguments" ] + then + echo "$action_helper_arguments" | while read argument + do + printf "$spacing$argument\n" + project_usage_arguments_recursive "$project" " $spacing" "$@" "$argument" + done + fi +} + +project_download_git() { + local project=$1 + shift + local repository=$1 + shift + local urls=$1 + shift + + requirements "git" + + if ! git_project_check "$repository" + then + project_sources_directory_filled_error "$project" "$repository" "$@" + + git_project_clone "$repository" "$urls" + fi + + git_project_prepare "$project" "$repository" "$@" +} + +project_download_check_git() { + local project=$1 + shift + local repository=$1 + shift + + requirements "git" + + git_project_check "$repository" + git_project_prepare_check "$project" "$repository" "$@" +} + +project_extract() { + local project=$1 + shift + + local repository=$project + + if ! project_sources_directory_filled_check "$project" "$repository" "$@" + then + project_sources_archive_missing_error "$project" "$@" + project_sources_archive_extract "$project" "$@" + fi +} + +project_extract_check() { + local project=$1 + shift + + local repository=$project + + project_sources_directory_filled_check "$project" "$repository" "$@" +} + +project_update_git() { + local project=$1 + shift + local repository=$1 + shift + + requirements "git" + + project_sources_directory_missing_empty_error "$project" "$repository" "$@" + + if git_project_check "$repository" + then + git_project_update "$project" "$repository" "$@" + else + if ! project_sources_archive_missing_check "$project" "$@" + then + project_sources_archive_update "$project" "$@" + fi + fi +} + +project_update_check_git() { + local project=$1 + shift + local repository=$1 + shift + + requirements "git" + + if git_project_check "$repository" + then + # Git repository should always be updated (even if upstream didn't progress). + # For instance, this is useful for testing new versions of patches without changing revision. + return 1 + else + project_sources_archive_missing_check "$project" "$@" + fi +} + +project_build_check() { + local project=$1 + shift + + local project_path=$( project_path "$project" ) + local build_path=$( project_build_path "$project" "$@" ) + local source_file_path + local argument + local rule + local path + + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + if [ -z "$path" ] + then + path="$argument" + else + path="$path/$argument" + fi + fi + + configs_install_path="$project_path/$CONFIGS/$path/$INSTALL" + + if ! [ -f "$configs_install_path" ] + then + continue + fi + + while read rule + do + source=$( echo "$rule" | sed "s/$INSTALL_REGEX/\1/g" ) + source_path="$build_path/$source" + + # Source may contain a wildcard. + path_wildcard_expand "$source_path" | while read source_file_path + do + if ! [ -f "$source_file_path" ] && ! [ -d "$source_file_path" ] + then + false + fi + done + done < "$configs_install_path" + done +} + +project_build_path() { + local project=$1 + shift + + local build_path="$root/$BUILD/$project" + local argument + + for argument in "$@" + do + build_path="$build_path-$argument" + done + + echo "$build_path" +} + +project_build_directory_missing_empty_error() { + local project=$1 + shift + local arguments=$@ + + local build_path=$( project_build_path "$project" "$@" ) + + if ! directory_filled_check "$build_path" + then + printf "Build directory for project $project (with ${arguments:-no argument}) missing or empty\n" >&2 + return 1 + else + return 0 + fi +} + +project_install() { + local project=$1 + shift + + local project_path=$( project_path "$project" ) + local build_path=$( project_build_path "$project" "$@" ) + local install_path=$( project_install_path "$project" "$@" ) + local source_file_path + local argument + local rule + local path + + # Install built files first. + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + if [ -z "$path" ] + then + path="$argument" + else + path="$path/$argument" + fi + fi + + configs_install_path="$project_path/$CONFIGS/$path/$INSTALL" + + if ! [ -f "$configs_install_path" ] + then + continue + fi + + project_build_directory_missing_empty_error "$project" "$@" + + while read rule + do + source=$( echo "$rule" | sed "s/$INSTALL_REGEX/\1/g" ) + source_path="$build_path/$source" + + destination=$( echo "$rule" | sed "s/$INSTALL_REGEX/\2/g" ) + destination_path="$install_path/$destination" + destination_directory_path=$( dirname "$destination_path" ) + + mkdir -p "$destination_directory_path" + + # Source may contain a wildcard. + path_wildcard_expand "$source_path" | while read source_file_path + do + cp -rT "$source_file_path" "$destination_path" + done + done < "$configs_install_path" + done + + path="" + + # Install install files then. + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + if [ -z "$path" ] + then + path="$argument" + else + path="$path/$argument" + fi + fi + + install_install_path="$project_path/$INSTALL/$path/$INSTALL" + + if ! [ -f "$install_install_path" ] + then + continue + fi + + while read rule + do + source=$( echo "$rule" | sed "s/$INSTALL_REGEX/\1/g" ) + source_path="$project_path/$INSTALL/$path/$source" + + destination=$( echo "$rule" | sed "s/$INSTALL_REGEX/\2/g" ) + destination_path="$install_path/$destination" + destination_directory_path=$( dirname "$destination_path" ) + + mkdir -p "$destination_directory_path" + + # Source may contain a wildcard. + path_wildcard_expand "$source_path" | while read source_file_path + do + cp -rT "$source_file_path" "$destination_path" + done + done < "$install_install_path" + done +} + +project_install_check() { + local project=$1 + shift + + local project_path=$( project_path "$project" ) + local build_path=$( project_build_path "$project" "$@" ) + local install_path=$( project_install_path "$project" "$@" ) + local argument + local rule + local path + + # Install built files first. + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + if [ -z "$path" ] + then + path="$argument" + else + path="$path/$argument" + fi + fi + + configs_install_path="$project_path/$CONFIGS/$path/$INSTALL" + + if ! [ -f "$configs_install_path" ] + then + continue + fi + + project_build_directory_missing_empty_error "$project" "$@" + + while read rule + do + destination=$( echo "$rule" | sed "s/$INSTALL_REGEX/\2/g" ) + destination_path="$install_path/$destination" + + if ! [ -f "$destination_path" ] && ! [ -d "$destination_path" ] + then + false + fi + done < "$configs_install_path" + done + + path="" + + # Install install files then. + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + if [ -z "$path" ] + then + path="$argument" + else + path="$path/$argument" + fi + fi + + install_install_path="$project_path/$INSTALL/$path/$INSTALL" + + if ! [ -f "$install_install_path" ] + then + continue + fi + + while read rule + do + destination=$( echo "$rule" | sed "s/$INSTALL_REGEX/\2/g" ) + destination_path="$install_path/$destination" + + if ! [ -f "$destination_path" ] && ! [ -d "$destination_path" ] + then + false + fi + done < "$install_install_path" + done +} + +project_install_path() { + local project=$1 + shift + + local install_path="$root/$INSTALL/$project" + local argument + + for argument in "$@" + do + install_path="$install_path-$argument" + done + + echo "$install_path" +} + +project_install_directory_missing_empty_error() { + local project=$1 + shift + local arguments=$@ + + local install_path=$( project_install_path "$project" "$@" ) + + if ! directory_filled_check "$install_path" + then + printf "Install directory for project $project (with ${arguments:-no argument}) missing or empty\n" >&2 + return 1 + else + return 0 + fi +} + +project_release_path() { + local project=$1 + shift + local prefix=$1 + + local release_path="$root/$RELEASE/$prefix" + + # Special care for tools and systems, that depend on the host arch. + if [ "$prefix" = "$SYSTEMS" ] || [ "$prefix" = "$TOOLS" ] + then + local machine=$( uname -m ) + + release_path="$release_path/$machine/$project" + else + release_path="$release_path/$project" + fi + + echo "$release_path" +} + +project_release_archive_path() { + local project=$1 + shift + local prefix=$1 + shift + + local release_path=$( project_release_path "$project" "$prefix" ) + local argument + local path="$project" + + for argument in "$@" + do + path="$path-$argument" + done + + local archive_path="$release_path/$path.$TAR_XZ" + + echo "$archive_path" +} + +project_release_rootfs_path() { + local project=$1 + shift + local prefix=$1 + shift + + local release_path=$( project_release_path "$project" "$prefix" ) + local argument + local path="$project" + + for argument in "$@" + do + path="$path-$argument" + done + + local rootfs_path="$release_path/$path.$TAR_XZ" + + echo "$rootfs_path" +} + +project_release_sources_archive_path() { + local project=$1 + shift + + local sources_path="$root/$SOURCES/" + local release_path + local argument + local path="$project" + + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + path="$path-$argument" + fi + + local directory_path="$sources_path/$path" + + if ! directory_filled_check "$directory_path" + then + continue + fi + + release_path=$path + done + + if ! [ -z "$release_path" ] + then + local archive_path="$root/$RELEASE/$SOURCES/$project/$release_path.$TAR_XZ" + + echo "$archive_path" + fi +} + +project_release_sources_archive_create() { + local project=$1 + shift + local arguments=$@ + + local repository=$project + local archive_path=$( project_release_sources_archive_path "$project" "$@" ) + local sources_path=$( project_sources_path "$project" "$repository" "$@" ) + + printf "Releasing sources archive for $project (with ${arguments:-no argument})\n" + + archive_create "$archive_path" "$sources_path" + file_verification_create "$archive_path" +} + +project_release_sources_archive_exists_check() { + local project=$1 + shift + + local archive_path=$( project_release_sources_archive_path "$project" "$@" ) + if [ -z "$archive_path" ] || ! [ -f "$archive_path" ] + then + return 1 + else + return 0 + fi +} + +project_release_sources_git() { + local project=$1 + shift + local repository=$1 + shift + + requirements "git" + + project_sources_directory_missing_empty_error "$project" "$repository" "$@" + + if git_project_check "$repository" + then + if ! git_project_release_check "$project" "$repository" "$@" + then + git_project_release "$project" "$repository" "$@" + fi + else + if ! project_release_sources_archive_exists_check "$project" "$@" + then + project_release_sources_archive_create "$project" "$@" + fi + fi +} + +project_release_check_sources_git() { + local project=$1 + shift + local repository=$1 + shift + + requirements "git" + + if git_project_check "$repository" + then + git_project_release_check "$project" "$repository" "$@" + else + project_release_sources_archive_exists_check "$project" "$@" + fi +} + +project_release_install() { + local project=$1 + shift + local prefix=$1 + shift + + local install_path=$( project_install_path "$project" "$@" ) + local release_path=$( project_release_path "$project" "$prefix" ) + local directory_path + local path + + project_install_directory_missing_empty_error "$project" "$@" + + local files=$( cd "$install_path" && find -type f || true ) + local file + + echo "$files" | while read file + do + path="$release_path/$file" + directory_path=$( dirname "$path" ) + + mkdir -p "$directory_path" + + cp "$install_path/$file" "$path" + file_verification_create "$path" + done +} + +project_release_install_check() { + local project=$1 + shift + local prefix=$1 + shift + + local install_path=$( project_install_path "$project" "$@" ) + local release_path=$( project_release_path "$project" "$prefix" ) + local path + + project_install_directory_missing_empty_error "$project" "$@" + + local files=$( cd "$install_path" && find -type f || true ) + local file + + echo "$files" | while read file + do + path="$release_path/$file" + + file_exists_check "$path" + done +} + +project_release_install_archive() { + local project=$1 + shift + local prefix=$1 + shift + + project_install_directory_missing_empty_error "$project" "$@" + + if ! project_release_install_archive_exists_check "$project" "$prefix" "$@" + then + project_release_install_archive_create "$project" "$prefix" "$@" + fi +} + +project_release_install_archive_check() { + local project=$1 + shift + + project_release_install_archive_exists_check "$project" "$@" +} + +project_release_install_archive_create() { + local project=$1 + shift + local prefix=$1 + shift + local arguments=$@ + + local install_path=$( project_install_path "$project" "$@" ) + local archive_path=$( project_release_archive_path "$project" "$prefix" "$@" ) + + printf "Releasing $prefix archive for $project (with ${arguments:-no argument})\n" + + archive_create "$archive_path" "$install_path" + file_verification_create "$archive_path" +} + +project_release_install_archive_exists_check() { + local project=$1 + shift + local prefix=$1 + shift + + local archive_path=$( project_release_archive_path "$project" "$prefix" "$@" ) + + file_exists_check "$archive_path" +} + +project_release_install_rootfs() { + local project=$1 + shift + local prefix=$1 + shift + + project_install_directory_missing_empty_error "$project" "$@" + + if ! project_release_install_rootfs_exists_check "$project" "$prefix" "$@" + then + project_release_install_rootfs_create "$project" "$prefix" "$@" + fi +} + +project_release_install_rootfs_check() { + local project=$1 + shift + + project_release_install_rootfs_exists_check "$project" "$@" +} + +project_release_install_rootfs_create() { + local project=$1 + shift + local prefix=$1 + shift + local arguments=$@ + + local install_path=$( project_install_path "$project" "$@" ) + local rootfs_path=$( project_release_rootfs_path "$project" "$prefix" "$@" ) + + printf "Releasing $prefix rootfs for $project (with ${arguments:-no argument})\n" + + rootfs_create "$rootfs_path" "$install_path" + file_verification_create "$rootfs_path" +} + +project_release_install_rootfs_exists_check() { + local project=$1 + shift + local prefix=$1 + shift + + local rootfs_path=$( project_release_rootfs_path "$project" "$prefix" "$@" ) + + file_exists_check "$rootfs_path" +} + +project_clean() { + local project=$1 + shift + + project_clean_build "$project" "$@" + project_clean_install "$project" "$@" + project_clean_release "$project" "$@" +} + +project_clean_build() { + local project=$1 + shift + + local build_path=$( project_build_path "$project" "$@" ) + + rm -rf "$build_path" +} + +project_clean_install() { + local project=$1 + shift + + local install_path=$( project_install_path "$project" "$@" ) + + rm -rf "$install_path" +} + +project_clean_release() { + local project=$1 + shift + + local prefix + + for prefix in "$SOURCES" "$SYSTEMS" "$IMAGES" "$TOOLS" "$DOCS" + do + local release_path=$( project_release_path "$project" "$prefix" ) + + rm -rf "$release_path" + done +} + +project_clean_rootfs() { + local project=$1 + shift + + project_clean_build "$project" "$@" + project_clean_rootfs_install "$project" "$@" + project_clean_release "$project" "$@" + +} + +project_clean_rootfs_install() { + local project=$1 + shift + + local install_path=$( project_install_path "$project" "$@" ) + + execute_root rm -rf "$install_path" + +} + +project_file_path() { + local project=$1 + shift + local directory=$1 + shift + local file=$1 + shift + + local project_path=$( project_path "$project" ) + local path="$project_path/$directory" + local argument + local file_path + + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + path="$path/$argument" + fi + + if ! [ -f "$path/$file" ] + then + continue + fi + + file_path="$path/$file" + done + + if [ -z "$file_path" ] + then + return 1 + fi + + echo "$file_path" +} + +project_file_test() { + local file_path=$( project_file_path "$@" ) + + test -f "$file_path" +} + +project_file_contents() { + local file_path=$( project_file_path "$@" ) + + cat "$file_path" +} + +project_file_contents_herit() { + local project=$1 + shift + local directory=$1 + shift + local file=$1 + shift + + local project_path=$( project_path "$project" ) + local path="$project_path/$directory" + local argument + local file_path + + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + path="$path/$argument" + fi + + file_path="$path/$file" + + if ! [ -f "$file_path" ] + then + continue + fi + + cat "$file_path" + done +} diff --git a/libs/tool b/libs/tool new file mode 100755 index 00000000..889ee8d5 --- /dev/null +++ b/libs/tool @@ -0,0 +1,287 @@ +#!/bin/bash + +# Copyright (C) 2016 Paul Kocialkowski <contact@paulk.fr> +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see <http://www.gnu.org/licenses/>. + +TOOL_ACTIONS_GENERIC="usage update execute" +TOOL_ACTIONS_HELPERS="arguments" +TOOL_FUNCTIONS=$( for action in $TOOL_ACTIONS_GENERIC ; do echo "$action" "$action""_check" ; done ; echo "$TOOL_ACTIONS_HELPERS" ) + +tool_include() { + local tool=$1 + + local tool_path=$( tool_path "$tool" ) + + unset -f $TOOL_FUNCTIONS + + . "$tool_path/$tool" + + tool_helper_include "$tool" +} + +tool_helper_include() { + local tool=$1 + + local tool_path=$( tool_path "$tool" ) + local include="$tool_path/$tool-helper" + + if [ -f "$include" ] + then + . "$include" + fi +} + +tool_check() { + local tool=$1 + + local tool_path=$( tool_path "$tool" ) + + if ! [ -f "$tool_path/$tool" ] + then + return 1 + fi + + return 0 +} + +tool_function_check() { + local tool=$1 + local function=$2 + + tool_include "$tool" + + if ! function_check "$function" + then + return 1 + fi + + return 0 +} + +tool_action() { + local action=$1 + shift + local tool=$1 + shift + local arguments=$@ + + ( + set +e + + if ! tool_check "$tool" + then + printf "Tool $tool check failed\n" >&2 + return 1 + fi + + tool_action_check "$action" "$tool" "$@" + + if [ $? -eq 0 ] + then + return 0 + fi + + tool_include "$tool" + + if ! function_check "$action" + then + return 0 + fi + + printf "Tool $tool $action (with ${arguments:-no argument})\n" >&2 + + ( + set -e + "$action" "$@" + ) + + if [ $? -ne 0 ] + then + printf "\nTool $tool $action (with ${arguments:-no argument}) failed\n" >&2 + return 1 + else + printf "\nTool $tool $action (with ${arguments:-no argument}) completed\n" >&2 + fi + ) +} + +tool_action_check() { + local action=$1 + shift + local tool=$1 + shift + + ( + set +e + + if ! tool_check "$tool" + then + printf "Tool $tool check failed\n" >&2 + return 1 + fi + + tool_include "$tool" + + if ! function_check "$action""_check" + then + return 1 + fi + + for tool_force in $TOOLS_FORCE + do + if [ "$tool_force" = "$tool" ] + then + return 1 + fi + done + + ( + set -e + "$action""_check" "$@" + ) + ) +} + +tool_action_helper() { + local helper=$1 + shift + local tool=$1 + shift + + ( + set +e + + if ! tool_check "$tool" + then + printf "Tool $tool check failed\n" >&2 + return 1 + fi + + tool_include "$tool" + + if ! function_check "$helper" + then + return 0 + fi + + ( + set -e + "$helper" "$@" + ) + ) +} + +tool_action_arguments_recursive() { + local action=$1 + shift + local tool=$1 + shift + + local action_helper_arguments=$( tool_action_helper "arguments" "$tool" "$@" ) + local argument + + if [ $? -ne 0 ] || [ -z "$action_helper_arguments" ] + then + tool_action "$action" "$tool" "$@" + else + echo "$action_helper_arguments" | while read argument + do + tool_action_arguments_recursive "$action" "$tool" "$@" "$argument" + done + fi +} + +tool_path() { + local tool=$1 + + local tool_path="$root/$TOOLS/$tool" + + echo "$tool_path" +} + +tool_sources_path() { + local tool=$1 + shift + + local path="$root/$TOOLS/$tool/$SOURCES" + local argument + + for argument in "" "$@" + do + if ! [ -z "$argument" ] + then + path="$path/$argument" + fi + + if directory_filled_check "$path" + then + echo "$path" + return + fi + done +} + +tool_usage_actions() { + local tool=$1 + shift + + printf "\nGeneric actions:\n" + + for action in $TOOL_ACTIONS_GENERIC + do + if function_check "$action" + then + printf " $action\n" + fi + done + + if [ $# -gt 0 ] + then + printf "\nSpecific actions:\n" + + for action in "$@" + do + printf " $action\n" + done + fi +} + +tool_usage_arguments() { + local tool=$1 + shift + + printf "\nArguments:\n" + + tool_usage_arguments_recursive "$tool" " " "$@" +} + +tool_usage_arguments_recursive() { + local tool=$1 + shift + local spacing=$1 + shift + + local action_helper_arguments=$( tool_action_helper "arguments" "$tool" "$@" ) + local argument + + if ! [ -z "$action_helper_arguments" ] + then + echo "$action_helper_arguments" | while read argument + do + printf "$spacing$argument\n" + tool_usage_arguments_recursive "$tool" " $spacing" "$@" "$argument" + done + fi +} |