From ef6a3f82c429ec232e1b910eecbdece76de933b3 Mon Sep 17 00:00:00 2001 From: Timerix22 Date: Wed, 24 May 2023 22:19:33 +0600 Subject: [PATCH] v7 --- CHANGELOG.md | 9 ++++ default.Makefile | 18 ++++++- default.config | 32 +++++++++++- default_tasks/callgrind.sh | 19 +++++++ default_tasks/clean.sh | 17 +++++++ functions.sh | 102 ++++++++++++++++++++++++++----------- init.sh | 2 + rebuild_dep.sh | 7 +++ 8 files changed, 171 insertions(+), 35 deletions(-) create mode 100644 default_tasks/callgrind.sh create mode 100644 rebuild_dep.sh diff --git a/CHANGELOG.md b/CHANGELOG.md index 4d69efd..9f04fe4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,12 @@ +# v7 ++ added function `resolve_dependencies` to `link` ++ added variables `DEPS_BASEDIR` and `DEPS` to config ++ added script `rebuild_dep.sh` which can be called through `Makefile` ++ added dependency cleaning in `default_tasks/clean.sh` ++ added task `callgrind` ++ added task `no_task` which is been set in `init.sh` when `TASK` is empty ++ now `STATIC_LIB_FILE` starts with "lib" + # v6 + `build_profile` task was split to `profile` and `gprof` + added task `sanitize` diff --git a/default.Makefile b/default.Makefile index 0bd17cd..508581c 100644 --- a/default.Makefile +++ b/default.Makefile @@ -28,6 +28,12 @@ build_static_lib: build_static_lib_dbg: @cbuild/call_task.sh build_static_lib_dbg 2>&1 | tee make_raw.log +# recompile libsome_dep.a in the next build task +#rebuild_some_dep: +# @cbuild/rebuild_dep.sh libsome_dep.a 2>&1 | tee make_raw.log + +#rebuild_all: rebuild_some_dep + ###################################### ###### Launch tasks ####### ###################################### @@ -49,10 +55,18 @@ profile: @cbuild/call_task.sh profile 2>&1 | tee make_raw.log # compiles program with -pg and runs it with gprof -# uses gprof2dot python script to generate function call tree +# uses gprof2dot python script to generate function call tree (pip install gprof2dot) +# requires graphviz (https://www.graphviz.org/download/source/) gprof: @cbuild/call_task.sh gprof 2>&1 | tee make_raw.log - + +# compiles program and runs it with callgrind (part of valgrind) +# uses gprof2dot python script to generate function call tree (pip install gprof2dot) +# requires graphviz (https://www.graphviz.org/download/source/) +# P.S. detailed rezults can be viewed in KCacheGrind +callgrind: + @cbuild/call_task.sh callgrind 2>&1 | tee make_raw.log + # compiles executable with sanitizers and executes it to find errors and warnings sanitize: @cbuild/call_task.sh sanitize 2>&1 | tee make_raw.log diff --git a/default.config b/default.config index 2ed0a53..0274190 100644 --- a/default.config +++ b/default.config @@ -1,5 +1,5 @@ #!/bin/bash -CBUILD_VERSION=6 +CBUILD_VERSION=7 CONFIG_VERSION=1 PROJECT="NULL" @@ -14,13 +14,24 @@ 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="" + + # 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 OBJDIR="obj" OUTDIR="bin" -STATIC_LIB_FILE="$PROJECT.a" +STATIC_LIB_FILE="lib$PROJECT.a" # OS-specific options case "$OS" in @@ -138,6 +149,20 @@ case "$TASK" in TASK_SCRIPT=cbuild/default_tasks/gprof.sh POST_TASK_SCRIPT= ;; + # compiles program and runs it with callgrind (part of valgrind) + # uses gprof2dot python script to generate function call tree (pip install gprof2dot) + # requires graphviz (https://www.graphviz.org/download/source/) + # P.S. detailed rezults can be viewed in KCacheGrind + callgrind) + OUTDIR="$OUTDIR/callgrind" + # -pg adds code to executable, that generates file containing function call info (gmon.out) + C_ARGS="-O2 -flto=auto -fuse-linker-plugin" + CPP_ARGS="$C_ARGS" + LINKER_ARGS="$CPP_ARGS" + PRE_TASK_SCRIPT=tasks/pre_build.sh + TASK_SCRIPT=cbuild/default_tasks/build_exec.sh + POST_TASK_SCRIPT=cbuild/default_tasks/callgrind.sh + ;; # compiles executable with sanitizers and executes it to find errors and warnings sanitize) OUTDIR="$OUTDIR/sanitize" @@ -152,6 +177,9 @@ case "$TASK" in clean) TASK_SCRIPT=cbuild/default_tasks/clean.sh ;; + # nothing to do + no_task) + ;; # unknown task *) error "task <$TASK> not found" diff --git a/default_tasks/callgrind.sh b/default_tasks/callgrind.sh new file mode 100644 index 0000000..9e9d070 --- /dev/null +++ b/default_tasks/callgrind.sh @@ -0,0 +1,19 @@ +#!/bin/bash + +cd "$OUTDIR" + +# deleting all files except excutable +echo "$(find . ! -name $EXEC_FILE -type f -delete)" + +# executing file with callgrind +myprint "${BLUE}executing $OUTDIR/$EXEC_FILE" +valgrind --tool=callgrind --callgrind-out-file=callgrind.out ./$EXEC_FILE > exec.log +myprint "${GREEN}execution log saved to ${CYAN}$OUTDIR/exec.log" +# exit 0 +# generating function call graph +myprint "${BLUE}generating function call graph..." +gprof2dot -f callgrind callgrind.out > gprof2dot.graph +dot gprof2dot.graph -Tpng -Gdpi=300 -o gprof2dot.png +myprint "${GREEN}function call graph saved to ${CYAN}$OUTDIR/gprof2dot.png" + +cd ../.. diff --git a/default_tasks/clean.sh b/default_tasks/clean.sh index 4e40e05..60f8d1f 100644 --- a/default_tasks/clean.sh +++ b/default_tasks/clean.sh @@ -4,3 +4,20 @@ try_delete_dir_or_file "$OBJDIR" 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" +done + +set +e +OLDIFS="$IFS" +IFS=$'\n' +for dep in $DEPS; do + dep_dir=$(echo ${dep/=*/} | tr -d '[:blank:]') + myprint "${CYAN}--------------[$dep_dir]--------------" + cd "$DEPS_BASEDIR/$dep_dir" + make clean + cd .. +done +IFS="$OLDIFS" +set -e diff --git a/functions.sh b/functions.sh index bba1d3f..77b7689 100755 --- a/functions.sh +++ b/functions.sh @@ -1,7 +1,8 @@ #!/bin/bash function myprint { - printf "$@${GRAY}\n" + # first gray is needed to print string trarting with - + printf "${GRAY}$@${GRAY}\n" } function error { @@ -44,25 +45,16 @@ function compile { myprint "${BLUE}include dirs: ${GRAY}$include" local sources="$6" myprint "${BLUE}sources: ${GRAY}$sources" - local compilation_error=0 - for srcfile in $sources do ( local object="$OBJDIR/objects/$(basename $srcfile).o" if ! $($cmp -std=$std $warn $args $include -c -o $object $srcfile) then error "some error happened" - #TODO parallel variable assignement doesnt work in bash - compilation_error=1 + # TODO stop all threads fi ) & done wait - - #TODO doesnt work with multithreading - if [ $compilation_error != 0 ] - then - exit -50 - fi } # (args, sources) @@ -77,26 +69,6 @@ function compile_cpp { compile "$CMP_CPP" "$STD_CPP" "$WARN_CPP" "$1" "$INCLUDE" "$2" } -# (args, outfile) -function link { - myprint "${CYAN}----------------[link]----------------" - local args="$1" - myprint "${BLUE}args: ${GRAY}$args" - local outfile="$2" - 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 $(echo "$objects" | tr '\n' ' ') $args -o $OUTDIR/$outfile" - myprint "$command" - if $command - then - myprint "${GREEN}file $CYAN$outfile ${GREEN}created" - else - error "some error happened" - fi -} - # (outfile) function pack_static_lib { myprint "${CYAN}----------[pack_static_lib]-----------" @@ -112,3 +84,71 @@ $(find $OBJDIR/libs -name '*.a')" 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" + + cd "$deps_basedir/$lib_project_dir" + if ! make "$lib_build_task"; then + exit 1 + fi + cd .. + + 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=$(echo ${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 { + myprint "${CYAN}----------------[link]----------------" + local args="$1" + local outfile="$2" + resolve_dependencies "$DEPS_BASEDIR" "$DEPS" + 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 $(echo "$objects" | tr '\n' ' ') $args -o $OUTDIR/$outfile" + myprint "$command" + if $command + then + myprint "${GREEN}file $CYAN$outfile ${GREEN}created" + else + error "some error happened" + fi +} diff --git a/init.sh b/init.sh index 0b5bf8d..ce9c336 100755 --- a/init.sh +++ b/init.sh @@ -35,6 +35,8 @@ exec_script_line default.config 3 DEFAULT_CONFIG_VERSION="$CONFIG_VERSION" unset CONFIG_VERSION +# undefined task +[ -z "$TASK" ] && TASK="no_task" # error on undefined set -u diff --git a/rebuild_dep.sh b/rebuild_dep.sh new file mode 100644 index 0000000..5af8ee3 --- /dev/null +++ b/rebuild_dep.sh @@ -0,0 +1,7 @@ +#!/bin/bash +source "cbuild/init.sh" + +target_file="$1" +touch ".rebuild_$target_file.tmp" +rm -fv "$OBJDIR/libs/$target_file.a" +myprint "${YELLOW}dependency ${WHITE}$target_file ${YELLOW}will be rebuilt with the next build task"