diff --git a/.github/workflows/test-lang-php.yml b/.github/workflows/test-lang-php.yml index 7082b9838bf..9c430e1f24a 100644 --- a/.github/workflows/test-lang-php.yml +++ b/.github/workflows/test-lang-php.yml @@ -146,4 +146,4 @@ jobs: run: ./build.sh interop-data-generate - name: Run Interop Tests - run: ./build.sh test-interop + run: ./build.sh interop-data-test diff --git a/build.sh b/build.sh index 09b02f8da06..57675d41e6c 100755 --- a/build.sh +++ b/build.sh @@ -126,7 +126,7 @@ do (cd lang/csharp; ./build.sh interop-data-test) (cd lang/js; ./build.sh interop-data-test) (cd lang/ruby; ./build.sh interop-data-test) - (cd lang/php; ./build.sh test-interop) + (cd lang/php; ./build.sh interop-data-test) (cd lang/perl; ./build.sh interop-data-test) # java needs to package the jars for the interop rpc tests diff --git a/lang/c/build.sh b/lang/c/build.sh index 6753e778dc2..6757b68a83b 100755 --- a/lang/c/build.sh +++ b/lang/c/build.sh @@ -20,84 +20,82 @@ set -e # exit on error +cd "$(dirname "$0")" # If being called from another folder, cd into the directory containing this script. + +# shellcheck disable=SC1091 +source ../../share/build-helper.sh "C" + root_dir=$(pwd) -build_dir="../../build/c" -dist_dir="../../dist/c" +build_dir="$BUILD_ROOT/build/c" +dist_dir="$BUILD_ROOT/dist/c" version=$(./version.sh project) tarball="avro-c-$version.tar.gz" -doc_dir="../../build/avro-doc-$version/api/c" +doc_dir="$BUILD_ROOT/build/avro-doc-$version/api/c" -function prepare_build { - clean - mkdir -p $build_dir - (cd $build_dir && cmake $root_dir -DCMAKE_BUILD_TYPE=RelWithDebInfo) +function prepare_build() +{ + command_clean + execute mkdir -p "$build_dir" + execute pushd "$build_dir" + execute cmake "$root_dir" -DCMAKE_BUILD_TYPE=RelWithDebInfo + execute popd } -function clean { - if [ -d $build_dir ]; then - find $build_dir | xargs chmod 755 - rm -rf $build_dir +function command_clean() +{ + if [ -d "$build_dir" ]; then + execute find "$build_dir" -exec chmod 755 {} + + execute rm -rf "$build_dir" fi - rm -f VERSION.txt - rm -f examples/quickstop.db + execute rm -f VERSION.txt + execute rm -f examples/quickstop.db } -for target in "$@" -do - - case "$target" in - - interop-data-generate) - prepare_build - make -C $build_dir - $build_dir/tests/generate_interop_data "../../share/test/schemas/interop.avsc" "../../build/interop/data" - ;; - - interop-data-test) - prepare_build - make -C $build_dir - $build_dir/tests/test_interop_data "../../build/interop/data" - ;; - - lint) - echo 'This is a stub where someone can provide linting.' - ;; - - test) - prepare_build - make -C $build_dir - make -C $build_dir test - ;; +function command_interop-data-generate() +{ + prepare_build + execute make -C "$build_dir" + execute "$build_dir/tests/generate_interop_data" "$BUILD_ROOT/share/test/schemas/interop.avsc" "$BUILD_ROOT/build/interop/data" +} - dist) - prepare_build - cp ../../share/VERSION.txt $root_dir - make -C $build_dir docs - # This is a hack to force the built documentation to be included - # in the source package. - cp $build_dir/docs/*.html $root_dir/docs - make -C $build_dir package_source - rm $root_dir/docs/*.html - if [ ! -d $dist_dir ]; then - mkdir -p $dist_dir - fi - if [ ! -d $doc_dir ]; then - mkdir -p $doc_dir - fi - mv $build_dir/$tarball $dist_dir - cp $build_dir/docs/*.html $doc_dir - clean - ;; +function command_interop-data-test() +{ + prepare_build + execute make -C "$build_dir" + execute "$build_dir/tests/test_interop_data" "$BUILD_ROOT/build/interop/data" +} - clean) - clean - ;; +function command_lint() +{ + echo 'This is a stub where someone can provide linting.' +} - *) - echo "Usage: $0 {interop-data-generate|interop-data-test|lint|test|dist|clean}" - exit 1 - esac +function command_test() +{ + prepare_build + execute make -C "$build_dir" + execute make -C "$build_dir" test +} -done +function command_dist() +{ + prepare_build + execute cp "$BUILD_ROOT/share/VERSION.txt" "$root_dir" + execute make -C "$build_dir" docs + # This is a hack to force the built documentation to be included + # in the source package. + execute cp "$build_dir"/docs/*.html "$root_dir/docs" + execute make -C "$build_dir" package_source + execute rm "$root_dir"/docs/*.html + if [ ! -d "$dist_dir" ]; then + execute mkdir -p "$dist_dir" + fi + if [ ! -d "$doc_dir" ]; then + execute mkdir -p "$doc_dir" + fi + execute mv "$build_dir/$tarball" "$dist_dir" + execute cp "$build_dir"/docs/*.html "$doc_dir" + command_clean +} -exit 0 +build-run "$@" \ No newline at end of file diff --git a/lang/csharp/build.sh b/lang/csharp/build.sh index 623ef03a353..e8e9ac6a125 100755 --- a/lang/csharp/build.sh +++ b/lang/csharp/build.sh @@ -15,85 +15,132 @@ # See the License for the specific language governing permissions and # limitations under the License. -set -e # exit on error -set -x - -cd `dirname "$0"` # connect to root - -ROOT=../.. -VERSION=`cat $ROOT/share/VERSION.txt` - -for target in "$@" -do - - case "$target" in - - lint) - echo 'This is a stub where someone can provide linting.' - ;; - - test) - dotnet build --configuration Release Avro.sln - - # AVRO-2442: Explicitly set LANG to work around ICU bug in `dotnet test` - LANG=en_US.UTF-8 dotnet test --configuration Release --no-build \ - --filter "TestCategory!=Interop" Avro.sln - ;; - - perf) - pushd ./src/apache/perf/ - dotnet run --configuration Release --framework net7.0 - ;; - - dist) - # pack NuGet packages - dotnet pack --configuration Release Avro.sln - - # add the binary LICENSE and NOTICE to the tarball - mkdir -p build/ - cp LICENSE NOTICE build/ - - # add binaries to the tarball - mkdir -p build/main/ - cp -R src/apache/main/bin/Release/* build/main/ - # add codec binaries to the tarball - for codec in Avro.File.Snappy Avro.File.BZip2 Avro.File.XZ Avro.File.Zstandard - do - mkdir -p build/codec/$codec/ - cp -R src/apache/codec/$codec/bin/Release/* build/codec/$codec/ - done - # add codegen binaries to the tarball - mkdir -p build/codegen/ - cp -R src/apache/codegen/bin/Release/* build/codegen/ - - # build the tarball - mkdir -p ${ROOT}/dist/csharp - (cd build; tar czf ${ROOT}/../dist/csharp/avro-csharp-${VERSION}.tar.gz main codegen codec LICENSE NOTICE) - - # build documentation - doxygen Avro.dox - mkdir -p ${ROOT}/build/avro-doc-${VERSION}/api/csharp - cp -pr build/doc/* ${ROOT}/build/avro-doc-${VERSION}/api/csharp - ;; - - interop-data-generate) - dotnet run --project src/apache/test/Avro.test.csproj --framework net7.0 ../../share/test/schemas/interop.avsc ../../build/interop/data - ;; - - interop-data-test) - LANG=en_US.UTF-8 dotnet test --filter "TestCategory=Interop" --logger "console;verbosity=normal;noprogress=true" src/apache/test/Avro.test.csproj - ;; - - clean) - rm -rf src/apache/{main,test,codegen,ipc,msbuild,perf}/{obj,bin} - rm -rf build - rm -f TestResult.xml - ;; - - *) - echo "Usage: $0 {lint|test|clean|dist|perf|interop-data-generate|interop-data-test}" - exit 1 - - esac - -done +set -e + +shopt -s globstar # enable **/* globbing +shopt -s nullglob # return nothing if no glob match + +cd "$(dirname "$0")" # If being called from another folder, cd into the directory containing this script. + +# shellcheck disable=SC1091 +source ../../share/build-helper.sh "C#" + +CSHARP_CODEC_LIBS="Avro.File.Snappy Avro.File.BZip2 Avro.File.XZ Avro.File.Zstandard" +SUPPORTED_SDKS="3.1 5.0 6.0 7.0" +DEFAULT_FRAMEWORK="net7.0" +CONFIGURATION="Release" + +function command_lint() +{ + echo "This is a stub where someone can provide linting." +} + +function command_test() +{ + execute dotnet build --configuration $CONFIGURATION Avro.sln + + # AVRO-2442: Explicitly set LANG to work around ICU bug in `dotnet test` + execute LANG=en_US.UTF-8 dotnet test --configuration "$CONFIGURATION" --no-build --filter "TestCategory!=Interop" Avro.sln +} + +function command_perf() +{ + execute pushd ./src/apache/perf/ + execute dotnet run --configuration "$CONFIGURATION" --framework "$FRAMEWORK" +} + +function command_dist() +{ + # pack NuGet packages + execute dotnet pack --configuration "$CONFIGURATION" Avro.sln + + # add the binary LICENSE and NOTICE to the tarball + execute mkdir -p build/ + execute cp LICENSE NOTICE build/ + + # add binaries to the tarball + execute mkdir -p build/main/ + execute cp -R src/apache/main/bin/$CONFIGURATION/* build/main/ + # add codec binaries to the tarball + for codec in $CSHARP_CODEC_LIBS + do + execute mkdir -p build/codec/"$codec"/ + execute cp -R src/apache/codec/"$codec"/bin/$CONFIGURATION/* build/codec/"$codec"/ + done + # add codegen binaries to the tarball + execute mkdir -p build/codegen/ + execute cp -R src/apache/codegen/bin/$CONFIGURATION/* build/codegen/ + + # build the tarball + execute mkdir -p "${BUILD_ROOT}/dist/csharp" + execute pushd build + execute tar czf "${BUILD_ROOT}/../dist/csharp/avro-csharp-${BUILD_VERSION}.tar.gz" main codegen codec LICENSE NOTICE + execute popd + + # build documentation + command_doc +} + +function command_doc() +{ + # build documentation + execute doxygen Avro.dox + execute mkdir -p "${BUILD_ROOT}/build/avro-doc-${BUILD_VERSION}/api/csharp" + execute cp -pr build/doc/* "${BUILD_ROOT}/build/avro-doc-${BUILD_VERSION}/api/csharp" +} + +function command_release() +{ + [ "$NUGET_SOURCE" ] || NUGET_SOURCE="https://api.nuget.org/v3/index.json" + [ "$NUGET_KEY" ] || fatal "NUGET_KEY not set" + + command_dist + + # Push packages to nuget.org + for package in ./build/**/*.nupkg + do + ask "Push $package to nuget.org" && execute dotnet nuget push "$package" -k "$NUGET_KEY" -s "$NUGET_SOURCE" + done +} + +function command_verify-release() +{ + for sdk_ver in $SUPPORTED_SDKS + do + execute docker run -it --rm mcr.microsoft.com/dotnet/sdk:"$sdk_ver" /bin/bash -ce "\ + mkdir test-project && \ + cd test-project && \ + dotnet new console && \ + dotnet add package Apache.Avro --version $BUILD_VERSION && \ + for codec in $CSHARP_CODEC_LIBS; do \ + dotnet add package Apache.\$codec --version $BUILD_VERSION; \ + done && \ + dotnet build && \ + dotnet tool install --global Apache.Avro.Tools --version $BUILD_VERSION && \ + export PATH=\$PATH:/root/.dotnet/tools && \ + avrogen --help" + done + echo "Verified" +} + +function command_interop-data-generate() +{ + execute dotnet run --project src/apache/test/Avro.test.csproj --framework $DEFAULT_FRAMEWORK ../../share/test/schemas/interop.avsc ../../build/interop/data +} + +function command_interop-data-test() +{ + execute LANG=en_US.UTF-8 dotnet test --filter 'TestCategory=Interop' --logger 'console\;verbosity=normal\;noprogress=true' src/apache/test/Avro.test.csproj +} + +function command_clean() +{ + # Alternatively all the steps below can be replaced with `git clean -dXf` + + execute rm -rf src/apache/{main,test,codegen,ipc,msbuild,perf,benchmark}/{obj,bin} + execute rm -rf src/apache/codec/Avro.File.*{,.Test}/{obj,bin} + execute rm -rf build + execute rm -f TestResult.xml +} + +build-run "$@" diff --git a/lang/java/build.sh b/lang/java/build.sh index 96fdb3489cb..77df38f5ce6 100755 --- a/lang/java/build.sh +++ b/lang/java/build.sh @@ -17,35 +17,32 @@ set -e -usage() { - echo "Usage: $0 {lint|test|dist|clean}" - exit 1 +cd "$(dirname "$0")" # If being called from another folder, cd into the directory containing this script. + +# shellcheck disable=SC1091 +source ../../share/build-helper.sh "Java" + +function command_lint() +{ + execute mvn -B spotless:apply +} + + +function command_test() +{ + execute mvn -B test + # Test the modules that depend on hadoop using Hadoop 2 + execute mvn -B test -Phadoop2 +} + +function command_dist() +{ + execute mvn -P dist package -DskipTests javadoc:aggregate } -main() { - local target - (( $# )) || usage - for target; do - case "$target" in - lint) - mvn -B spotless:apply - ;; - test) - mvn -B test - # Test the modules that depend on hadoop using Hadoop 2 - mvn -B test -Phadoop2 - ;; - dist) - mvn -P dist package -DskipTests javadoc:aggregate - ;; - clean) - mvn clean - ;; - *) - usage - ;; - esac - done +function command_clean() +{ + execute mvn clean } -main "$@" +build-run "$@" diff --git a/lang/js/build.sh b/lang/js/build.sh index cc9b69ccabc..6bd2b7d0db7 100755 --- a/lang/js/build.sh +++ b/lang/js/build.sh @@ -17,35 +17,43 @@ set -e -cd `dirname "$0"` - -for target in "$@" -do - case "$target" in - lint) - npm install - npm run lint - ;; - test) - npm install - npm run cover - ;; - dist) - npm pack - mkdir -p ../../dist/js - mv avro-js-*.tgz ../../dist/js - ;; - clean) - rm -rf coverage - ;; - interop-data-generate) - npm run interop-data-generate - ;; - interop-data-test) - npm run interop-data-test - ;; - *) - echo "Usage: $0 {lint|test|dist|clean}" >&2 - exit 1 - esac -done +cd "$(dirname "$0")" # If being called from another folder, cd into the directory containing this script. + +# shellcheck disable=SC1091 +source ../../share/build-helper.sh "Javascript" + +function command_lint() +{ + execute npm install + execute npm run lint +} + +function command_test() +{ + execute npm install + execute npm run cover +} + +function command_dist() +{ + execute npm pack + execute mkdir -p "$BUILD_ROOT/dist/js" + execute mv avro-js-*.tgz "$BUILD_ROOT/dist/js" +} + +function command_clean() +{ + execute rm -rf coverage +} + +function command_interop-data-generate() +{ + execute npm run interop-data-generate +} + +function command_interop-data-test() +{ + execute npm run interop-data-test +} + +build-run "$@" \ No newline at end of file diff --git a/lang/perl/build.sh b/lang/perl/build.sh index 2b689276b82..bf81d18c637 100755 --- a/lang/perl/build.sh +++ b/lang/perl/build.sh @@ -17,29 +17,26 @@ set -e # exit on error -function usage { - echo "Usage: $0 {lint|test|dist|clean|interop-data-generate|interop-data-test}" - exit 1 -} +shopt -s globstar # enable **/* globbing +shopt -s nullglob # return nothing if no glob match -if [ $# -eq 0 ] -then - usage -fi +cd "$(dirname "$0")" # If being called from another folder, cd into the directory containing this script. -for target in "$@" -do +# shellcheck disable=SC1091 +source ../../share/build-helper.sh "Perl" -function do_clean(){ - [ ! -f Makefile ] || make clean - rm -f Avro-*.tar.gz META.yml Makefile.old - rm -rf lang/perl/inc/ +function command_clean() +{ + [ ! -f Makefile ] || execute make clean + execute rm -f Avro-*.tar.gz META.yml Makefile.old + execute rm -rf lang/perl/inc/ } -function do_lint(){ +function command_lint() +{ local failures=0 - for i in $(find lib t xt -name '*.p[lm]' -or -name '*.t'); do - if ! perlcritic --verbose 1 ${i}; then + for i in {lib,t,xt}/**/*.{p[lm],t}; do + if ! execute perlcritic --verbose 1 "${i}"; then ((failures=failures+1)) fi done @@ -48,36 +45,27 @@ function do_lint(){ fi } -case "$target" in - lint) - do_lint - ;; - - test) - perl ./Makefile.PL && make test - ;; - - dist) - cp ../../share/VERSION.txt . - perl ./Makefile.PL && make dist - ;; - - clean) - do_clean - ;; - - interop-data-generate) - perl -Ilib share/interop-data-generate - ;; +function command_test() +{ + execute perl ./Makefile.PL + execute make test +} - interop-data-test) - prove -Ilib xt/interop.t - ;; +function command_dist() +{ + execute cp "$BUILD_ROOT/share/VERSION.txt" . + execute perl ./Makefile.PL + execute make dist +} - *) - usage -esac +function command_interop-data-generate() +{ + execute perl -Ilib share/interop-data-generate +} -done +function command_interop-data-test() +{ + execute prove -Ilib xt/interop.t +} -exit 0 +build-run "$@" \ No newline at end of file diff --git a/lang/php/build.sh b/lang/php/build.sh index d470e8b9140..d92bab63de1 100755 --- a/lang/php/build.sh +++ b/lang/php/build.sh @@ -17,68 +17,62 @@ set -e -cd `dirname "$0"` +cd "$(dirname "$0")" # If being called from another folder, cd into the directory containing this script. -dist_dir="../../dist/php" +# shellcheck disable=SC1091 +source ../../share/build-helper.sh "PHP" + +dist_dir="$BUILD_ROOT/dist/php" build_dir="pkg" -version=$(cat ../../share/VERSION.txt) -libname="avro-php-$version" +libname="avro-php-$BUILD_VERSION" lib_dir="$build_dir/$libname" tarball="$libname.tar.bz2" test_tmp_dir="test/tmp" -function clean { - rm -rf "$test_tmp_dir" - rm -rf "$build_dir" +function command_clean() +{ + execute rm -rf "$test_tmp_dir" + execute rm -rf "$build_dir" } -function dist { +function command_dist() +{ mkdir -p "$build_dir/$libname" "$lib_dir/examples" cp -pr lib "$lib_dir" cp -pr examples/*.php "$lib_dir/examples" cp README.md LICENSE NOTICE "$lib_dir" - cd "$build_dir" + pushd "$build_dir" tar -cjf "$tarball" "$libname" mkdir -p "../$dist_dir" cp "$tarball" "../$dist_dir" + popd } -for target in "$@" -do - case "$target" in - interop-data-generate) - composer install -d "../.." - php test/generate_interop_data.php - ;; - - test-interop) - composer install -d "../.." - vendor/bin/phpunit test/InterOpTest.php - ;; - lint) - composer install -d "../.." - find . -name "*.php" -print0 | xargs -0 -n1 -P8 php -l - vendor/bin/phpcs --standard=PSR12 lib - ;; - - test) - composer install -d "../.." - vendor/bin/phpunit -v - ;; +function command_interop-data-generate() +{ + execute composer install -d "$BUILD_ROOT" + execute php test/generate_interop_data.php +} - dist) - dist - ;; +function command_interop-data-test() +{ + execute composer install -d "$BUILD_ROOT" + execute vendor/bin/phpunit test/InterOpTest.php +} - clean) - clean - ;; +function command_lint() +{ + execute composer install -d "$BUILD_ROOT" + find . -name "*.php" -print0 | xargs -0 -n1 -P8 php -l + execute vendor/bin/phpcs --standard=PSR12 lib +} - *) - echo "Usage: $0 {interop-data-generate|test-interop|lint|test|dist|clean}" - esac -done +function command_test() +{ + execute composer install -d "$BUILD_ROOT" + execute vendor/bin/phpunit -v +} -exit 0 +build-run "$@" diff --git a/lang/py/build.sh b/lang/py/build.sh index 83eafe16cfa..80a5517a501 100755 --- a/lang/py/build.sh +++ b/lang/py/build.sh @@ -17,13 +17,14 @@ set -e -usage() { - echo "Usage: $0 {clean|dist|doc|interop-data-generate|interop-data-test|lint|test}" - exit 1 -} +cd "$(dirname "$0")" # If being called from another folder, cd into the directory containing this script. + +# shellcheck disable=SC1091 +source ../../share/build-helper.sh "Python" -clean() { - git clean -xdf '*.avpr' \ +function command_clean() +{ + execute git clean -xdf '*.avpr' \ '*.avsc' \ '*.egg-info' \ '*.py[co]' \ @@ -35,7 +36,8 @@ clean() { 'userlogs' } -dist() ( +function command_dist() +{ ## # Use https://pypa-build.readthedocs.io to create the build artifacts. local destination virtualenv @@ -46,53 +48,41 @@ dist() ( pwd ) virtualenv="$(mktemp -d)" - python3 -m venv "$virtualenv" - "$virtualenv/bin/python3" -m pip install build - "$virtualenv/bin/python3" -m build --outdir "$destination" -) - -doc() { - local doc_dir - [[ -s VERSION.txt ]] || cp ../../share/VERSION.txt . - doc_dir="../../build/avro-doc-$(&2 - exit 1 - esac -done +build-run "$@" diff --git a/share/build-helper.sh b/share/build-helper.sh new file mode 100644 index 00000000000..c87c2120999 --- /dev/null +++ b/share/build-helper.sh @@ -0,0 +1,233 @@ +#!/bin/bash + +# Licensed to the Apache Software Foundation (ASF) under one or more +# contributor license agreements. See the NOTICE file distributed with +# this work for additional information regarding copyright ownership. +# The ASF licenses this file to You under the Apache License, Version 2.0 +# (the "License"); you may not use this file except in compliance with +# the License. You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +BUILD_HELPER_DIR="$(dirname "${BASH_SOURCE[0]}")" +BUILD_ROOT="$(realpath --relative-to="$(pwd)" "$BUILD_HELPER_DIR/..")" + +# Read version +BUILD_VERSION="$(cat "$BUILD_ROOT/share/VERSION.txt")" + +OPTION_YES="" +OPTION_DRY_RUN="" + +[ "$BUILD_LANGUAGE" ] || BUILD_LANGUAGE="$1" + +# Colors +COLOR_RED="\033[0;31m" +COLOR_GREEN="\033[0;32m" +COLOR_YELLOW="\033[0;33m" +#COLOR_BLUE="\033[0;34m" +#COLOR_PURPLE="\033[0;35m" +COLOR_CYAN="\033[0;36m" +COLOR_NONE="\033[0m" + +# Commands +declare -A BUILD_COMMANDS + +BUILD_COMMANDS["lint"]="Lint the code" +BUILD_COMMANDS["test"]="Run unit tests" +BUILD_COMMANDS["clean"]="Clean the build files" +BUILD_COMMANDS["dist"]="Create a distribution tarball" +BUILD_COMMANDS["release"]="Release project" +BUILD_COMMANDS["verify-release"]="Verify release" +BUILD_COMMANDS["perf"]="Run performance tests" +BUILD_COMMANDS["interop-data-generate"]="Generate interop data" +BUILD_COMMANDS["interop-data-test"]="Test interop data" + +# Example to define additional command. Put the following into the language specific build.sh +# +#function command_new-command() { +# echo "This is a new command" +#} +#build-add-command "new-command" "Description of new command" + +BUILD_START_TIME="$(date +%s)" + +function usage() +{ + echo "Usage: $(basename "$0") [OPTION]... [COMMAND]..." + echo "Build script for Apache Avro $BUILD_LANGUAGE" + echo "" + echo "Options:" + printf " %-40s%s\n" "-y, --yes" "Answer yes to all question" + printf " %-40s%s\n" " --dry-run" "Do not execute commands, just echo them" + printf " %-40s%s\n" " --no-colors" "No colors" + printf " %-40s%s\n" "-v, --verbose" "Verbose output" + printf " %-40s%s\n" "-V, --version" "Shows version" + printf " %-40s%s\n" "-h, --help" "Shows help" + echo "" + echo "Commands:" + for cmd in "${!BUILD_COMMANDS[@]}" + do + printf " %-40s%s\n" "$cmd" "${BUILD_COMMANDS[$cmd]}" + done | sort +} + +function cleanup() +{ + local RC="$?" + + trap "" INT TERM EXIT + set +e + + BUILD_END_TIME="$(date +%s)" + + if [ "$RC" == "0" ] + then + ok "Done in $(( BUILD_END_TIME - BUILD_START_TIME ))s." + else + error "FAILED!" + fi + + exit "$RC" +} + +function ok() +{ + echo -e "$COLOR_GREEN$1$COLOR_NONE" +} + +function warn() +{ + echo -e "$COLOR_YELLOW$1$COLOR_NONE" +} + +function error() +{ + echo -e "$COLOR_RED$1$COLOR_NONE" +} + +function fatal() +{ + error "$1" + [ "$2" ] && exit "$2" || exit 1 +} + +function disable-colors() +{ + COLOR_RED="" + COLOR_GREEN="" + COLOR_YELLOW="" + #COLOR_BLUE="" + #COLOR_PURPLE="" + COLOR_CYAN="" + COLOR_NONE="" +} + +function ask() +{ + local prompt="$COLOR_YELLOW$1 ([y]es/[n]o/([a]bort)? $COLOR_NONE" + [ "$OPTION_YES" == "1" ] && echo -e "$prompt y" && return 0 + [ "$OPTION_DRY_RUN" == "1" ] && return 0 + while true; do + answer="$(read -r -n 1 -p "$(echo -e "$prompt")" answer && echo "$answer")" + echo + case ${answer} in + [Yy]) + return 0 + ;; + + [Nn]) + echo "Skip..." + return 1 + ;; + + [Aa]) + fatal "ABORT" + ;; + esac + done +} + +function execute() +{ + echo -e "$COLOR_CYAN$*$COLOR_NONE" + [ "$OPTION_DRY_RUN" == "1" ] && return 0 + # shellcheck disable=2294 + eval "$@" +} + +function build-add-command() +{ + BUILD_COMMANDS["$1"]="$2" +} + +function build-run() +{ + [ "$#" == "0" ] && fatal "Nothing to do" + + # Iterate through arguments + while [ $# -gt 0 ] + do + case "$1" in + -h|--help) + usage + # Do not show done message + trap "" INT TERM EXIT + exit 0 + ;; + + -v|--verbose) + set -x + ;; + + -y|--yes) + OPTION_YES="1" + ;; + + --dry-run) + OPTION_DRY_RUN="1" + ;; + + --no-colors) + disable-colors + ;; + + -V|--version) + echo "$BUILD_VERSION" + ;; + + -*) + fatal "Unknown option: $1" + ;; + + *) + # Check if command + if [ "${BUILD_COMMANDS[$1]}" ] + then + # Check if command function is available + if [ "$(type -t "command_$1")" == "function" ] + then + # Execute command + "command_$1" + else + fatal "Command $1 is not implemented" + fi + else + fatal "Unknown command: $1" + fi + ;; + esac + + shift + done +} + +# Turn off colors automatically if not terminal +[ -t 1 ] || disable-colors + +trap "cleanup" EXIT INT TERM \ No newline at end of file