Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
377 changes: 292 additions & 85 deletions compile.sh
Original file line number Diff line number Diff line change
@@ -1,100 +1,307 @@
#!/bin/bash -x
# Use to print each step: #!/bin/bash -x
#
# Example script -- boilerplate to automate compilation (and, possibly, execution) the lab project.
#!/bin/bash
# The Tea-Ware License (Version 2.0)
# Copyright (c) 2024 Radomyr Husiev <h.radomyr@proton.me>
#
# This script is part of a project licensed under the Tea-Ware License.
# Feel free to use, modify, and distribute this script, keeping this notice intact.
# If we meet and you find this useful, a cup of tea would be appreciated!
#
# Full license: https://github.com/rhusiev/Tea-Ware-License-v2

set -o errexit
set -o nounset
set -o pipefail

debug_build=true
optimize_build=false
remove_dirs=false
install_prefix=".."

while [[ $# -gt 0 ]]; do
case $1 in
-I | --install_prefix)
if [ "$2" -eq "$2" ] 2>/dev/null; then
install_prefix=$2
shift 2
else
echo "Option --install_prefix requires an numerical argument." >&2
exit 1
fi
;;
-D | --debug-build)
debug_build=true
shift
;;
-d | --no-debug-build)
debug_build=false
shift
;;
-O | --optimize-build)
optimize_build=true
shift
;;
-o | --no-optimize-build)
optimize_build=false
shift
;;
-R | --remove-build-dirs)
remove_dirs=true
shift
;;
-h | --help)
echo "Usage: ./compile.sh [options]
log=1
help_message="Usage: ./compile.sh [options]
Options:
-h --help Show help message.
-O --optimize-build Compile with optimization before executing.
-o --no-optimize-build Compile without optimization before executing.
-D --debug-build Compile with debug options.
-d --no-debug-build Compile without debug options.
-I --install_prefix Installation path.
-R --remove-build-dirs Remove build dirs after the install."
-h --help Show help message
-o --optimize-build Compile with optimization before executing
-d --debug-build Compile with debug options
-i --relwithdebinfo-build Compile with release debug info
-I --install_prefix Installation path
-p --pipeline Enable pipeline build using different compilers and sanitizers and analyzers
-c --clean Clean cmake-build-* directories and compile.log
-n --nolog Don't log to compile.log
--s='<args>' Arguments for program when run under valgrind and sanitizers. If '--s' not present, valgrind and sanitizers will not be executed"

if [ $# -eq 0 ]; then
echo "$help_message"
exit 0
;;
\?)
echo "Invalid option: -$OPTARG" >&2
exit 1
;;
:)
echo "Option -$OPTARG requires an numerical argument." >&2
exit 1
;;
*)
break
;;
esac
fi

for arg in "$@"; do
case $arg in
-n|--nolog)
log=0
;;
-I | --install_prefix)
if [ "$2" -eq "$2" ] 2>/dev/null; then
install_prefix=$2
shift 2
else
echo "Option --install_prefix requires a numerical argument." 2>&1
exit 1
fi
;;
esac
done


if [[ "$debug_build" == true ]]; then
mkdir -p ./cmake-build-debug
(
pushd ./cmake-build-debug > /dev/null || exit 1
echo Compiling...
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="${install_prefix}" .. || exit 1
cmake --build . || exit 1
cmake --install . || exit 1
popd
)
call_location=$(pwd)
if [ $log -eq 1 ]; then
echo "" > "$call_location/compile.log"
handle_output() {
while IFS= read -r line; do
echo "$line" | tee -a $call_location/compile.log | grep --invert-match ".*\(Consider enabling PVS-Studio\|Sanitizers enabled\|[Ee]nabled in CMakeLists.txt\).*"
done
}
else
handle_output() {
while IFS= read -r line; do
echo "$line" | grep --invert-match ".*\(Consider enabling PVS-Studio\|Sanitizers enabled\|[Ee]nabled in CMakeLists.txt\).*"
done
}
fi

if [[ "$optimize_build" == true ]]; then
mkdir -p ./cmake-build-release
(
pushd ./cmake-build-release >/dev/null || exit 1
echo Compiling...
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="${install_prefix}" .. || exit 1
cmake --build . || exit 1
cmake --install . || exit 1
popd
)
fi
echo "===Starting===" 2>&1 | handle_output

if [[ "$remove_dirs" == true ]]; then
rm -rf ./cmake-build-debug ./cmake-build-release
fi
handle_error() {
echo "Something went wrong, restoring the original CMakeLists.txt"
sed -i 's/set(ENABLE_UBSan ON)/set(ENABLE_UBSan OFF)/g' ../CMakeLists.txt
sed -i 's/set(ENABLE_ASAN ON)/set(ENABLE_ASAN OFF)/g' ../CMakeLists.txt
sed -i 's/set(ENABLE_TSan ON)/set(ENABLE_TSan OFF)/g' ../CMakeLists.txt
sed -i 's/set(ENABLE_MSan ON)/set(ENABLE_MSan OFF)/g' ../CMakeLists.txt
sed -i "s/clang-ubsan)/${project_name})/g" ../CMakeLists.txt
sed -i "s/clang-asan)/${project_name})/g" ../CMakeLists.txt
sed -i "s/clang-tsan)/${project_name})/g" ../CMakeLists.txt
sed -i "s/clang-msan)/${project_name})/g" ../CMakeLists.txt
sed -i "s/ENABLE_PVS_STUDIO ON)/ENABLE_PVS_STUDIO OFF)/g" ../CMakeLists.txt
sed -i 's/#set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-checks=\*")/set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-checks=\*")/g' ../CMakeLists.txt
cd ..
remove_pvs_headers
cd -
exit 1
}

add_pvs_headers() {
cd ..
find . -type f -name "*.cpp" -o -name "*.c" -o -name "*.hpp" -o -name "*.h" | grep -P '.*\/[^.]*\.(cpp|c|hpp|h)$' >files.txt
echo "**Adding PVS headers to the files:**" 2>&1 | handle_output
# Remove files whose names start with "cmake", "CMake" or "./cmake" or "./CMake"
sed -i '/^\.\/cmake/d' files.txt
sed -i '/^\.\/CMake/d' files.txt
sed -i '/^cmake/d' files.txt
sed -i '/^CMake/d' files.txt
cat files.txt 2>&1 | handle_output
while IFS= read -r file; do
if [[ $(head -n 1 "$file") != "// This is a personal academic project. Dear PVS-Studio, please check it." ]]; then
sed -i '1s/^/\/\/ This is a personal academic project. Dear PVS-Studio, please check it.\n\/\/ PVS-Studio Static Code Analyzer for C, C++, C#, and Java: http:\/\/www.viva64.com\n/' "$file"
fi
done <files.txt
rm files.txt
cd -
}

remove_pvs_headers() {
find . -type f -name "*.cpp" -o -name "*.c" -o -name "*.hpp" -o -name "*.h" | grep -P '.*\/[^.]*\.(cpp|c|hpp|h)$' >files.txt
echo "**Removing PVS headers from files:**" 2>&1 | handle_output
# Remove files whose names start with "cmake", "CMake" or "./cmake" or "./CMake"
sed -i '/^\.\/cmake/d' files.txt
sed -i '/^\.\/CMake/d' files.txt
sed -i '/^cmake/d' files.txt
sed -i '/^CMake/d' files.txt
# cat files.txt | sed 's/^/ /' 2>&1 | handle_output
cat files.txt 2>&1 | handle_output
while IFS= read -r file; do
if [[ $(head -n 1 "$file") == "// This is a personal academic project. Dear PVS-Studio, please check it." ]]; then
sed -i '1,2d' "$file"
fi
done <files.txt
rm files.txt
}

pipeline() {
echo "===Running with Pipeline===" 2>&1 | handle_output
mkdir -p ./cmake-build-pipeline
(
pushd ./cmake-build-pipeline >/dev/null || exit 1
# Find the project_name in `set(PROJECT_NAME project_name)`
project_name=$(grep -oP '(?<=set\(PROJECT_NAME ).*(?=\))' ../CMakeLists.txt)
# CLANG
sed -i "s/ENABLE_PVS_STUDIO ON)/ENABLE_PVS_STUDIO OFF)/g" ../CMakeLists.txt
sed -i 's/set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-checks=\*")/#set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-checks=\*")/g' ../CMakeLists.txt

sed -i 's/set(ENABLE_UBSan OFF)/set(ENABLE_UBSan ON)/g' ../CMakeLists.txt
sed -i "s/${project_name})/clang-ubsan)/g" ../CMakeLists.txt
echo "====Compiling with Clang UBSan====" 2>&1 | handle_output
rm -rf ../cmake-build-pipeline/* # instead of just rm -rf * - in case something fails, it won't delete everything
CC=clang CXX=clang++ cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="${install_prefix}" .. 2>&1 | handle_output || handle_error
cmake --build . 2>&1 | handle_output || handle_error
cmake --install . 2>&1 | handle_output || handle_error

echo "====Compiling with Clang ASan====" 2>&1 | handle_output
sed -i 's/set(ENABLE_UBSan ON)/set(ENABLE_UBSan OFF)/g' ../CMakeLists.txt
sed -i 's/set(ENABLE_ASAN OFF)/set(ENABLE_ASAN ON)/g' ../CMakeLists.txt
sed -i 's/clang-ubsan)/clang-asan)/g' ../CMakeLists.txt
rm -rf ../cmake-build-pipeline/*
CC=clang CXX=clang++ cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="${install_prefix}" .. 2>&1 | handle_output || handle_error
cmake --build . 2>&1 | handle_output || handle_error
cmake --install . 2>&1 | handle_output || handle_error

echo "====Compiling with Clang TSan====" 2>&1 | handle_output
sed -i 's/set(ENABLE_ASAN ON)/set(ENABLE_ASAN OFF)/g' ../CMakeLists.txt
sed -i 's/set(ENABLE_TSan OFF)/set(ENABLE_TSan ON)/g' ../CMakeLists.txt
sed -i 's/clang-asan)/clang-tsan)/g' ../CMakeLists.txt
rm -rf ../cmake-build-pipeline/*
CC=clang CXX=clang++ cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="${install_prefix}" .. 2>&1 | handle_output || handle_error
cmake --build . 2>&1 | handle_output || handle_error
cmake --install . 2>&1 | handle_output || handle_error

echo "====Compiling with Clang MSan====" 2>&1 | handle_output
sed -i 's/set(ENABLE_TSan ON)/set(ENABLE_TSan OFF)/g' ../CMakeLists.txt
sed -i 's/set(ENABLE_MSan OFF)/set(ENABLE_MSan ON)/g' ../CMakeLists.txt
sed -i 's/clang-tsan)/clang-msan)/g' ../CMakeLists.txt
rm -rf ../cmake-build-pipeline/*
CC=clang CXX=clang++ cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="${install_prefix}" .. 2>&1 | handle_output || handle_error
cmake --build . 2>&1 | handle_output || handle_error
cmake --install . 2>&1 | handle_output || handle_error
sed -i 's/set(ENABLE_MSan ON)/set(ENABLE_MSan OFF)/g' ../CMakeLists.txt
sed -i "s/clang-msan)/${project_name})/g" ../CMakeLists.txt

# GCC
echo "====Compiling with GCC + PVS + clang-tidy====" 2>&1 | handle_output
add_pvs_headers
if [ -f /app/project/cmake/extra/PVS-Studio.cmake ]; then sed -i "s/cmake_minimum_required(VERSION 2.8.12)/cmake_minimum_required(VERSION 3.5)/g" /app/project/cmake/extra/PVS-Studio.cmake; fi
sed -i "s/ENABLE_PVS_STUDIO OFF)/ENABLE_PVS_STUDIO ON)/g" ../CMakeLists.txt
sed -i 's/#set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-checks=\*")/set(CMAKE_CXX_CLANG_TIDY "clang-tidy;-checks=\*")/g' ../CMakeLists.txt
rm -rf ../cmake-build-pipeline/*
CC=gcc CXX=g++ cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="${install_prefix}" .. 2>&1 | handle_output || handle_error
cmake --build . 2>&1 | handle_output || handle_error
cmake --install . 2>&1 | handle_output || handle_error
sed -i "s/ENABLE_PVS_STUDIO ON)/ENABLE_PVS_STUDIO OFF)/g" ../CMakeLists.txt
popd
)
# Remove PVS things
remove_pvs_headers
exit 0
}

sanitizers() {
sanitizers_args=$1
echo "===Running with Valgrind and Sanitizers===" 2>&1 | handle_output
echo "Sanitizers args: $sanitizers_args" 2>&1 | handle_output
project_name=$(grep -oP '(?<=set\(PROJECT_NAME ).*(?=\))' ./CMakeLists.txt)
echo "====Valgrind====" 2>&1 | handle_output
valgrind --leak-check=full --show-leak-kinds=all --track-origins=yes ./bin/$project_name $sanitizers_args 2>&1 | handle_output || handle_error
echo "====Valgrind Helgrind====" 2>&1 | handle_output
valgrind --tool=helgrind ./bin/$project_name $sanitizers_args 2>&1 | handle_output || handle_error
echo "====Valgrind DRD====" 2>&1 | handle_output
valgrind --tool=drd ./bin/$project_name $sanitizers_args 2>&1 | handle_output || handle_error
echo "====UBSan====" 2>&1 | handle_output
./bin/clang-ubsan $sanitizers_args 2>&1 | handle_output || handle_error
echo "====ASan====" 2>&1 | handle_output
./bin/clang-asan $sanitizers_args 2>&1 | handle_output || handle_error
echo "====TSan====" 2>&1 | handle_output
./bin/clang-tsan $sanitizers_args 2>&1 | handle_output || handle_error
echo "====MSan====" 2>&1 | handle_output
./bin/clang-msan $sanitizers_args 2>&1 | handle_output || handle_error
}

debug() {
echo "===Running with Debug Build===" 2>&1 | handle_output
mkdir -p ./cmake-build-debug
(
pushd ./cmake-build-debug >/dev/null || exit 1
cmake -DCMAKE_BUILD_TYPE=Debug -DCMAKE_INSTALL_PREFIX="${install_prefix}" .. 2>&1 | handle_output || exit 1
cmake --build . 2>&1 | handle_output || exit 1
cmake --install . 2>&1 | handle_output || exit 1
popd
)
}

optimize() {
echo "===Running with Optimize Build===" 2>&1 | handle_output
mkdir -p ./cmake-build-release
(
pushd ./cmake-build-release >/dev/null || exit 1
cmake -DCMAKE_BUILD_TYPE=Release -DCMAKE_INSTALL_PREFIX="${install_prefix}" .. 2>&1 | handle_output || exit 1
cmake --build . 2>&1 | handle_output || exit 1
cmake --install . 2>&1 | handle_output || exit 1
popd
)
}

relwithdebinfo() {
echo "===Running with Release Debug Info===" 2>&1 | handle_output
mkdir -p ./cmake-build-relwithdebinfo
(
pushd ./cmake-build-relwithdebinfo >/dev/null || exit 1
cmake -DCMAKE_BUILD_TYPE=RelWithDebInfo -DCMAKE_INSTALL_PREFIX="${install_prefix}" .. 2>&1 | handle_output || exit 1
cmake --build . 2>&1 | handle_output || exit 1
cmake --install . 2>&1 | handle_output || exit 1
popd
)
}

clean() {
echo "===Cleaning===" 2>&1 | handle_output
rm -rf cmake-build-* compile.log
}

run() {
echo "===Running custom command===" 2>&1 | handle_output
run_args=$1
echo "Running command: '$run_args'" 2>&1 | handle_output
eval $run_args 2>&1 | handle_output
}

while [[ $# -gt 0 ]]; do
case $1 in
-n | --nolog | -I | --install_prefix)
shift
;;
-d | --debug-build)
debug
shift
;;
-o | --optimize-build)
optimize
shift
;;
-i | --relwithdebinfo-build)
relwithdebinfo
shift
;;
-p | --pipeline)
pipeline
shift
;;
-c | --clean)
clean
shift
;;
--s=*)
sanitizers_args="${1#*=}"
sanitizers "$sanitizers_args"
shift
;;
-h | --help)
echo "$help_message"
exit 0
;;
\?)
echo "Invalid option: -$OPTARG" 2>&1 | handle_output
exit 1
;;
:)
echo "Option -$OPTARG requires an numerical argument." 2>&1 | handle_output
exit 1
;;
*)
echo "Unknown argument: $1"
break
;;
esac
done
Loading
Loading