From b7109ef9fa8775dea351d276437669e38bfdb595 Mon Sep 17 00:00:00 2001 From: Timerix Date: Mon, 15 Jul 2024 23:01:38 +0300 Subject: [PATCH] dependency compilation --- CHANGELOG.md | 9 +- cbuild.sh | 23 +-- config.sh | 55 +++--- default.config | 23 +-- default_tasks/clean.sh | 21 +- example_dependency_configs/libexample1.config | 10 + example_dependency_configs/libexample2.config | 19 ++ functions.sh | 181 +++++++++++------- myprint.sh | 2 +- rebuild_dep.sh | 11 -- setup.sh | 2 +- 11 files changed, 198 insertions(+), 158 deletions(-) create mode 100644 example_dependency_configs/libexample1.config create mode 100644 example_dependency_configs/libexample2.config delete mode 100644 rebuild_dep.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 3041a7a..e4eab53 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,8 +1,11 @@ +# v2.0.2 ++ new dependency resolution system (see **config** and `example_dependency_configs`) ++ **config**: changed description of `OBJDIR` + # v2.0.1 -+ fixed bugs in `myprint.sh`, `--new-project`, task execution + updated `.gitignore` -+ added `pwd` call to `valgrind` task in config -+ added `""` empty task check in config ++ **config**: added `pwd` call to `valgrind` task ++ **config**: added `""` empty task check # 2.0.0 + updated setup.sh to do system-wide installation diff --git a/cbuild.sh b/cbuild.sh index dfe50d8..8f260e6 100644 --- a/cbuild.sh +++ b/cbuild.sh @@ -96,9 +96,8 @@ function call_task { local default_config_path="$2" local task="$3" - print_header "${CYAN}" "─" "$task" - load_config "$current_config_path" "$default_config_path" "$task" - resolve_dependencies "$DEPS_BASEDIR" "$DEPS" + print_header "${CYAN}" "─" "$PROJECT/$task" + load_config "$current_config_path" "$default_config_path" "$task" true if [ ! -z "$PRE_TASK_SCRIPT" ]; then myprint "${BLUE}executing ${WHITE}$TASK_SCRIPT" @@ -116,27 +115,15 @@ function call_task { function call_tasks { local tasks="$@" - load_config "$current_config_path" "$default_config_path" + load_config "$current_config_path" "$default_config_path" "" false print_header "${WHITE}" "═" "$PROJECT" for task in $tasks ; do call_task "$current_config_path" "$default_config_path" "$task" done - print_hline "${WHITE}" "═" } +print_hline "${WHITE}" "═" selected_tasks_count=${#selected_tasks_array[@]} if [ $selected_tasks_count -gt 0 ]; then - time call_tasks "${selected_tasks_array[@]}" #2>&1 | tee cbuild.log + call_tasks "${selected_tasks_array[@]}" fi - -if [ -f cbuild.log ]; then - # remove terminal escape codes - sed -e 's/[^[:blank:][:print:]]//g' \ - -e 's/\[0;[0-9][0-9]m//g' \ - -e 's/\[0;[0-9]m//g' \ - -e 's/\[[0-9][0-9]m//g' \ - -e 's/\[[0-9]m//g' \ - -e 's/ H //g' \ - -e 's/\[3gH //g' \ - -i cbuild.log -fi \ No newline at end of file diff --git a/config.sh b/config.sh index 258d7e9..db6600e 100644 --- a/config.sh +++ b/config.sh @@ -4,10 +4,29 @@ include cbuild/myprint.sh include cbuild/functions.sh include cbuild/detect_os.sh +function myprint_quiet { + local quiet=$1 + local text="$2" + if [ "$quiet" != true ]; then + myprint "$text" + fi +} + +function exec_script_line { + local script="$1" + local line_num="$2" + local quiet=$3 + myprint_quiet $quiet "${BLUE}reading line $line_num from $script" + local line_str="$(sed $line_num'!d' $script)" + myprint_quiet $quiet "$line_str" + eval "$line_str" +} + function load_config { local current_config_path="$1" local default_config_path="$2" TASK="$3" + local quiet=$4 if [ -z "$current_config_path" ]; then error "current_config_path is null" fi @@ -16,16 +35,7 @@ function load_config { fi OS=$(detect_os) - myprint "${GREEN}detected OS: $OS" - - function exec_script_line { - local script="$1" - local line_num="$2" - myprint "${BLUE}reading line $line_num from $script" - local line_str="$(sed $line_num'!d' $script)" - myprint "$line_str" - eval "$line_str" - } + myprint_quiet $quiet "${GREEN}detected OS: $OS" # getting version of cbuild installation if [ -z "$INSTALLED_CBUILD_VERSION" ]; then @@ -33,7 +43,7 @@ function load_config { fi # getting version of default config - exec_script_line "$default_config_path" 3 + exec_script_line "$default_config_path" 3 $quiet DEFAULT_CONFIG_VERSION="$CONFIG_VERSION" unset CONFIG_VERSION @@ -50,31 +60,32 @@ function load_config { myprint "${YELLOW}Created copy (${current_config_path}) of default config (${default_config_path})" fi - myprint "${BLUE}reading $current_config_path" + myprint_quiet $quiet "${BLUE}reading $current_config_path" include "$current_config_path" - myprint "${WHITE}project: ${CYAN}$PROJECT" + myprint_quiet $quiet "${WHITE}project: ${CYAN}$PROJECT" - myprint "${WHITE}${current_config_path} cbuild version: ${CYAN}$CBUILD_VERSION" - myprint "${WHITE}installed cbuild version: ${CYAN}$INSTALLED_CBUILD_VERSION" - myprint "${WHITE}${current_config_path} version: ${CYAN}$CONFIG_VERSION" - myprint "${WHITE}${default_config_path} version: ${CYAN}$DEFAULT_CONFIG_VERSION" + myprint_quiet $quiet "${WHITE}${current_config_path} cbuild version: ${CYAN}$CBUILD_VERSION" + myprint_quiet $quiet "${WHITE}installed cbuild version: ${CYAN}$INSTALLED_CBUILD_VERSION" + myprint_quiet $quiet "${WHITE}${current_config_path} version: ${CYAN}$CONFIG_VERSION" + myprint_quiet $quiet "${WHITE}${default_config_path} version: ${CYAN}$DEFAULT_CONFIG_VERSION" # checking versions - if [ ! "$CBUILD_VERSION" -eq "$INSTALLED_CBUILD_VERSION" ]; then + if [ "$CBUILD_VERSION" != "$INSTALLED_CBUILD_VERSION" ]; then error "config was created for outdated cbuild version" fi - if [ ! "$CONFIG_VERSION" -eq "$DEFAULT_CONFIG_VERSION" ]; then - error "config version isn't correct" + if [ "$CONFIG_VERSION" != "$DEFAULT_CONFIG_VERSION" ]; then + error "current config version doesn't match default config version" fi mkdir -p "$OUTDIR" - mkdir -p "$OBJDIR/libs" mkdir -p "$OBJDIR/objects" + mkdir -p "$OBJDIR/static_libs" + mkdir -p "$OBJDIR/dynamic_libs" mkdir -p "$OBJDIR/profile" # dont thorw error on undefined variable set +u - myprint "${GREEN}cbuild initialized!" + myprint_quiet $quiet "${GREEN}loaded cbuild config" } \ No newline at end of file diff --git a/default.config b/default.config index a20f685..a5992bf 100644 --- a/default.config +++ b/default.config @@ -1,5 +1,5 @@ #!/usr/bin/env bash -CBUILD_VERSION=2.0.1 +CBUILD_VERSION=2.0.2 CONFIG_VERSION=1 PROJECT="%PROJECT_NAME%" @@ -14,20 +14,17 @@ SRC_CPP="$( find src -name '*.cpp')" TESTS_C="$( find tests -name '*.c')" TESTS_CPP="$(find tests -name '*.cpp')" -# dir with dependeicy dirs -DEPS_BASEDIR="." -# EXAMPLE: "dependency_dir='build_task out_dir lib_file' -# other_depndency_dir=..." -# Dependencies must be declared on separate lines -# Values can be override by resetting one of dependencies: -# DEPS="$DEPS -# dependency_dir='...'" -DEPS="" +# Directory with dependency configs. +# See cbuild/example_dependency_configs +DEPENDENCY_CONFIGS_DIR='.' +# List of dependency config files in DEPENDENCY_CONFIGS_DIR separated by space. +ENABLED_DEPENDENCIES='' # OBJDIR structure: -# ├── objects/ - dir where compiled *.o files are stored. cleans every call of build task -# ├── profile/ - dir where gcc *.gcda profiling info files stored -# └── libs/ - there you can put static libs and linker will find them +# ├── objects/ - Compiled object files. Cleans on each call of build task +# ├── static_libs/ - Symbolic links to static libraries used by linker. Cleans on each call of build task. +# ├── static_libs/ - Symbolic links to dynamic libraries used by linker. Cleans on each call of build task. +# └── profile/ - gcc *.gcda profiling info files OBJDIR="obj" OUTDIR="bin" STATIC_LIB_FILE="lib$PROJECT.a" diff --git a/default_tasks/clean.sh b/default_tasks/clean.sh index 336af1f..49366b4 100644 --- a/default_tasks/clean.sh +++ b/default_tasks/clean.sh @@ -5,21 +5,8 @@ try_delete_dir_or_file "$OUTDIR" myprint "${WHITE}deleting build logs" rm -rf *.log -for tmpfile in $(ls -a | grep -e '\.rebuild.*\.tmp'); do - try_delete_dir_or_file "$tmpfile" +for dep in $ENABLED_DEPENDENCIES; do + load_dependency_config "$DEPENDENCY_CONFIGS_DIR/$dep.config" + cd "$DEP_WORKING_DIR" + exec_command "$DEP_CLEAN_COMMAND" done - -set +e -OLDIFS="$IFS" -IFS=$'\n' -cd "$DEPS_BASEDIR" -for dep in $DEPS; do - dep_dir=$(safeprint ${dep/=*/} | tr -d '[:blank:]') - print_header "${CYAN}" "─" "$dep_dir" - cd "$dep_dir" - make clean - cd .. -done -IFS="$OLDIFS" -cd .. -set -e \ No newline at end of file diff --git a/example_dependency_configs/libexample1.config b/example_dependency_configs/libexample1.config new file mode 100644 index 0000000..6a5d812 --- /dev/null +++ b/example_dependency_configs/libexample1.config @@ -0,0 +1,10 @@ +#!/usr/bin/env bash +DEP_WORKING_DIR='depencencies/libexample1' +DEP_PRE_BUILD_COMMAND='' +DEP_BUILD_COMMAND='make libexample1.a' +DEP_POST_BUILD_COMMAND='' +DEP_CLEAN_COMMAND='make clean' +# won't be copied to project $OUTDIR +DEP_STATIC_OUT_FILES='libexample1.a libexample1_addon.a' +# will be copied tp project $OUTDIR +DEP_DYNAMIC_OUT_FILES='libexample1.config.json' diff --git a/example_dependency_configs/libexample2.config b/example_dependency_configs/libexample2.config new file mode 100644 index 0000000..0a491e5 --- /dev/null +++ b/example_dependency_configs/libexample2.config @@ -0,0 +1,19 @@ +#!/usr/bin/env bash +DEP_WORKING_DIR='depencencies/libexample2' +DEP_PRE_BUILD_COMMAND='' +DEP_POST_BUILD_COMMAND='' +DEP_CLEAN_COMMAND='make clean' +DEP_STATIC_OUT_FILES='' +case $OS in + WINDOWS) + DEP_BUILD_COMMAND='make libexample2.dll' + DEP_DYNAMIC_OUT_FILES='libexample2.dll' + ;; + LINUX) + DEP_BUILD_COMMAND='make libexample2.so' + DEP_DYNAMIC_OUT_FILES='libexample2.so' + ;; + *) + error "operating system $OS has no configuration variants" + ;; +esac diff --git a/functions.sh b/functions.sh index 17d4f73..3eddef6 100755 --- a/functions.sh +++ b/functions.sh @@ -23,7 +23,85 @@ function try_delete_dir_or_file { fi } +function exec_command { + local command="$@" + if [ ! -z "$command" ]; then + myprint "${GRAY}$command" + $command || error "command returned eror" + fi +} + +function load_dependency_config { + local dependency_config_file="$1" + myprint "${BLUE}loading dependency config ${WHITE}${dependency_config_file}${BLUE}" + include "$dependency_config_file" +} + +# builds a dependency when $dep_out_files dont exist or rebuild task is executed +function build_dependency { + # path to *.config file + local dependency_config_file="$1" + # true or false + local force_build="$2" + load_dependency_config "$dependency_config_file" + + local proj_root_dir="$(pwd)" + myprint "${BLUE}entering dependency directory '${DEP_WORKING_DIR}'" + cd "$DEP_WORKING_DIR" + + local build_needed="$force_build" + if [ "$build_needed" != true ]; then + for file in $DEP_STATIC_OUT_FILES $DEP_DYNAMIC_OUT_FILES; do + if [ ! -f "$file" ]; then + myprint "${GRAY}missing file '$file'" + local build_needed=true + fi + done + fi + + if [ "$build_needed" = true ]; then + exec_command "$DEP_PRE_BUILD_COMMAND" + exec_command "$DEP_BUILD_COMMAND" + exec_command "$DEP_POST_BUILD_COMMAND" + myprint "${GRAY}dependency build finished" + else + myprint "${GRAY}dependency was built already" + fi + + if [ ! -z "$DEP_DYNAMIC_OUT_FILES" ]; then + # copy each file to $OUTDIR + cp -rv $DEP_DYNAMIC_OUT_FILES "$proj_root_dir/$OUTDIR" + # symlink each file to $OBJDIR/dynamic_libs + for file in $DEP_DYNAMIC_OUT_FILES; do + ln -sfv $(realpath $file) "$proj_root_dir/$OBJDIR/dynamic_libs" + done + fi + if [ ! -z "$DEP_STATIC_OUT_FILES" ]; then + # symlink each file to $OBJDIR/static_libs + for file in $DEP_STATIC_OUT_FILES; do + ln -sfv $(realpath $file) "$proj_root_dir/$OBJDIR/static_libs" + done + fi + + cd "$proj_root_dir" +} + +function build_dependencies { + local dependencies="$1" + # true or false + local force_build="$2" + myprint "${BLUE}resolving dependencies" + clean_dir "$OBJDIR/static_libs" + clean_dir "$OBJDIR/dynamic_libs" + for dep in $dependencies; do + build_dependency "$DEPENDENCY_CONFIGS_DIR/$dep.config" "$force_build" + done +} + function compile { + build_dependencies "$ENABLED_DEPENDENCIES" + print_hline "${BLUE}" "─" + local cmp="$1" myprint "${BLUE}compiler: ${GRAY}$cmp" local std="$2" @@ -50,91 +128,50 @@ function compile { # (args, sources) function compile_c { - print_header "${CYAN}" "─" "compile_c" + print_header "${CYAN}" "─" "$PROJECT/$TASK/compile_c" compile "$CMP_C" "$STD_C" "$WARN_C" "$1" "$INCLUDE" "$2" } # (args, sources) function compile_cpp { - print_header "${CYAN}" "─" "compile_cpp" + print_header "${CYAN}" "─" "$PROJECT/$TASK/compile_cpp" compile "$CMP_CPP" "$STD_CPP" "$WARN_CPP" "$1" "$INCLUDE" "$2" } # (outfile) function pack_static_lib { - print_header "${CYAN}" "─" "pack_static_lib" + print_header "${CYAN}" "─" "$PROJECT/$TASK/pack_static_lib" local outfile="$1" myprint "${BLUE}outfile: ${GRAY}$outfile" - local objects="$(find $OBJDIR/objects -name *.o) -$(find $OBJDIR/libs -name '*.a')" + local objects="$(find $OBJDIR/objects -type f,l | tr '\n' ' ')" myprint "${BLUE}objects: ${GRAY}$objects" - if ar rcs "$OUTDIR/$outfile" $(safeprint "$objects" | tr '\n' ' ') - then - myprint "${GREEN}file $CYAN$outfile ${GREEN}created" - else - error "some error happened" - fi -} - - -# if $lib_file doesn't exist or rebuild_* task was executed, builds static lib -function handle_static_dependency { - local deps_basedir="${1%/}" - local lib_project_dir="$2" - local lib_build_task="$3" - local lib_build_dir="$4" - local lib_file="$5" - if [ ! -f "$OBJDIR/libs/$lib_file" ] || [ -f .rebuild_$lib_file.tmp ]; then - [[ -z "$lib_build_task" ]] && error "lib_build_task is empty" - myprint "${BLUE}making $lib_file by task $lib_build_task" - - local proj_root_dir="$(pwd)" - cd "$deps_basedir/$lib_project_dir" - if ! make "$lib_build_task"; then - exit 1 - fi - cd "$proj_root_dir" - - cp "$deps_basedir/$lib_project_dir/$lib_build_dir/$lib_file" "$OBJDIR/libs/" - myprint "${GREEN}copied ${CYAN}$lib_file to $OBJDIR/libs/" - rm -f .rebuild_$lib_file.tmp - fi -} - -function resolve_dependencies { - deps_basedir=$1 - deps=$2 - [[ -z "$deps_basedir" ]] && deps_basedir="." - OLDIFS="$IFS" - IFS=$'\n' - # Evalueting dependency expressions. - # Necessary for overriding dependency configurations. - for dep in $deps; do - eval $dep - done - # handling dependencies - for dep in $deps; do - IFS="$OLDIFS" - dep_dir=$(safeprint ${dep/=*/} | tr -d '[:blank:]') - eval 'dep_params=$'$dep_dir - f_args="$deps_basedir $dep_dir $dep_params" - myprint "${BLUE}resolving dependency ${WHITE}$dep_dir${BLUE}: ${GRAY}$f_args" - handle_static_dependency $f_args - IFS=$'\n' - done - IFS="$OLDIFS" -} - -function link { - print_header "${CYAN}" "─" "link" - local args="$1" - local outfile="$2" - myprint "${BLUE}args: ${GRAY}$args" - myprint "${BLUE}outfile: ${GRAY}$outfile" - local objects="$(find $OBJDIR/objects -name '*.o') -$(find $OBJDIR/libs -name '*.a')" - myprint "${BLUE}objects: ${GRAY}$objects" - local command="$CMP_CPP $(safeprint "$objects" | tr '\n' ' ') $args -o $OUTDIR/$outfile" + local command="ar rcs $OUTDIR/$outfile $objects" + myprint "$command" + if $command + then + myprint "${GREEN}file $CYAN$outfile ${GREEN}created" + else + error "some error happened" + fi +} + +function link { + print_header "${CYAN}" "─" "$PROJECT/$TASK/link" + local args="$1" + local outfile="$2" + myprint "${BLUE}args: ${GRAY}$args" + myprint "${BLUE}outfile: ${GRAY}$outfile" + local objects="$(find $OBJDIR/objects -type f,l | tr '\n' ' ')" + myprint "${BLUE}objects: ${GRAY}$objects" + local static_libs="$(find $OBJDIR/static_libs -type f,l | tr '\n' ' ')" + myprint "${BLUE}static libraries: ${GRAY}$static_libs" + local dynamic_libs="$(find $OBJDIR/dynamic_libs -type f,l | tr '\n' ' ')" + myprint "${BLUE}dynamic libraries: ${GRAY}$dynamic_libs" + local dynamic_libs_args="-L $OBJDIR/dynamic_libs" + for lib in $dynamic_libs; do + dynamic_libs_args="$dynamic_libs_args -l:$lib" + done + local command="$CMP_CPP $objects $static_libs $args $dynamic_libs_args -o $OUTDIR/$outfile" myprint "$command" if $command then diff --git a/myprint.sh b/myprint.sh index b829ccb..2ba9d8a 100644 --- a/myprint.sh +++ b/myprint.sh @@ -23,7 +23,7 @@ function myprint { # print message and exit function error { - myprint "$@" + myprint "${RED}$@" exit 1 } diff --git a/rebuild_dep.sh b/rebuild_dep.sh deleted file mode 100644 index 12a97b0..0000000 --- a/rebuild_dep.sh +++ /dev/null @@ -1,11 +0,0 @@ -#!/usr/bin/env bash -echo "ERROR: rebuild_dep.sh IS OBSOLETE" -exit 1 - -include "cbuild/config.sh" - -load_config -target_file="$1" -touch ".rebuild_$target_file.tmp" -rm -fv "$OBJDIR/libs/$target_file" -myprint "${YELLOW}dependency ${WHITE}$target_file ${YELLOW}will be rebuilt during the next build task" diff --git a/setup.sh b/setup.sh index 902759c..bbaabc7 100644 --- a/setup.sh +++ b/setup.sh @@ -12,4 +12,4 @@ if [ "$CBUILD_INSTALL_DIR" != "." ]; then cp -r ./ "$CBUILD_INSTALL_DIR" rm -rf "$CBUILD_INSTALL_DIR/.git" fi -ln -sfv "$(realpath $CBUILD_INSTALL_DIR/cbuild.sh)" -T "/usr/local/bin/cbuild" +ln -sf "$(realpath $CBUILD_INSTALL_DIR/cbuild.sh)" -T "/usr/local/bin/cbuild"