From bf6d40b18c7c61af4e880e43d4695d5880276640 Mon Sep 17 00:00:00 2001 From: Marcelo Mendes Spessoto Junior Date: Wed, 20 Mar 2024 06:19:53 -0300 Subject: [PATCH] src: build: Add --from-sha option Add --from-sha option that builds every commit from to actual commit Closes #666 Signed-off-by: Marcelo Mendes Spessoto Junior --- documentation/man/features/kw-build.rst | 13 +++ documentation/tutorials/buildlinux.rst | 9 ++ src/_kw | 24 ++--- src/bash_autocomplete.sh | 2 +- src/build.sh | 73 ++++++++++++++- tests/unit/build_test.sh | 116 ++++++++++++++++++++++++ tests/unit/lib/kw_include_test.sh | 8 +- tests/unit/utils.sh | 15 +++ 8 files changed, 241 insertions(+), 19 deletions(-) diff --git a/documentation/man/features/kw-build.rst b/documentation/man/features/kw-build.rst index 3acd3136c..f63a6558e 100644 --- a/documentation/man/features/kw-build.rst +++ b/documentation/man/features/kw-build.rst @@ -17,6 +17,7 @@ SYNOPSIS | *kw* (*b* | *build*) [(-c | \--clean)] [\--alert=(s | v | (sv | vs) | n)] | *kw* (*b* | *build*) [(-f | \--full-cleanup)] [\--alert=(s | v | (sv | vs) | n)] | *kw* (*b* | *build*) [\--cflags] +| *kw* (*b* | *build*) [\--from-sha ] | *kw* (*b* | *build*) [\--verbose] DESCRIPTION @@ -102,6 +103,10 @@ OPTIONS | **sv** or **vs** enables both. | **n** (or any other option) disables notifications (this is the default). +\--from-sha: + Build every commit after to branch head. Useful for testing if all patches in + patchset compile. + EXAMPLES ======== For these examples, we assume that the relevant fields in your configuration @@ -159,3 +164,11 @@ If you want to reset the kernel tree to its default, `all config and script outp If you want to use cflags:: kw b --cflags "-O3 -pipe -march=native" + +If you want to build every commit after HEAD~2 to HEAD:: + + kw b --from-sha HEAD~2 + +If you want to build every commit after ee3b5 to HEAD:: + + kw b --from-sha ee3b5 diff --git a/documentation/tutorials/buildlinux.rst b/documentation/tutorials/buildlinux.rst index d2932b802..57dbc13ef 100644 --- a/documentation/tutorials/buildlinux.rst +++ b/documentation/tutorials/buildlinux.rst @@ -80,3 +80,12 @@ Well, that's it. kw will automatically infer the number of job slots for compiling based on the number of cores of your machine (i.e. when running make ``-j``, ** is an integer that specifies the number of processes that will run at the same time), and compilation will begin! + +Compiling patchsets +------------------- +You may want to try compiling every patch in your patchset to test if everything is alright. +You can do this by using the "from-sha" flag:: + + kw build --from-sha SHA + +This will compile every patch after the commit with given SHA to the branch HEAD. diff --git a/src/_kw b/src/_kw index c6caf4420..94dddac0b 100644 --- a/src/_kw +++ b/src/_kw @@ -92,18 +92,18 @@ _kw_build() { _arguments : \ '(--verbose)--verbose[enable verbose mode]' \ - '(-i --info -c --clean -f --full-cleanup -n --menu -d --doc --ccache -w --warnings -S --cpu-scaling -s --save-log-to --llvm --cflags)'{-i,--info}'[display kernel release name, version and number of modules to compile]' \ - '(-c --clean -f --full-cleanup -i --info -n --menu -d --doc --ccache -w --warnings -S --cpu-scaling -s --save-log-to --llvm --cflags)'{-c,--clean}'[remove files generated by the kernel build system]' \ - '(-f --full-cleanup -c --clean -i --info -n --menu -d --doc --ccache -w --warnings -S --cpu-scaling -s --save-log-to --llvm --cflags)'{-f,--full-cleanup}'[reset the kernel tree to its default option integrated into env]' \ - '(-n --menu -i --info -c --clean -f --full-cleanup -d --doc --cflags)'{-n,--menu}'[invoke kernel menuconfig]' \ - '(-d --doc -i --info -c --clean -f --full-cleanup -n --menu --cflags)'{-d,--doc}'[build the kernel-doc]' \ - '(-i --info -c --clean -f --full-cleanup --cflags)--ccache[enable ccache during compilation tasks]' \ - '(-w --warnings -i --info -c --clean -f --full-cleanup --cflags)'{-w,--warnings}'[enable compilation warnings]:log level:(1 2 3 12 13 23 123)' \ - '(-S --cpu-scaling -i --info -c --clean -f --full-cleanup --cflags)'{-S,--cpu-scaling}'[set CPU usage]:scaling percentage: ' \ - '(-s --save-log-to -i --info -c --clean -f --full-cleanup --cflags)'{-s,--save-log-to}'[save full compilation log with the enabled warnings to the specified path]:log path:_files' \ - '(-i --info -c --clean -f --full-cleanup --cflags)--llvm[enable the usage of the LLVM toolchain]' \ - '(-i --info -c --clean -f --full-cleanup -n --menu -d --doc --ccache -w --warnings -S --cpu-scaling -s --save-log-to --llvm --cflags)--cflags[customize kernel compilation with specific flags]' - + '(-i --info -c --clean -f --full-cleanup -n --menu -d --doc --ccache -w --warnings -S --cpu-scaling -s --save-log-to --llvm --cflags --from-sha)'{-i,--info}'[display kernel release name, version and number of modules to compile]' \ + '(-c --clean -f --full-cleanup -i --info -n --menu -d --doc --ccache -w --warnings -S --cpu-scaling -s --save-log-to --llvm --cflags --from-sha)'{-c,--clean}'[remove files generated by the kernel build system]' \ + '(-f --full-cleanup -c --clean -i --info -n --menu -d --doc --ccache -w --warnings -S --cpu-scaling -s --save-log-to --llvm --cflags --from-sha)'{-f,--full-cleanup}'[reset the kernel tree to its default option integrated into env]' \ + '(-n --menu -i --info -c --clean -f --full-cleanup -d --doc --cflags --from-sha)'{-n,--menu}'[invoke kernel menuconfig]' \ + '(-d --doc -i --info -c --clean -f --full-cleanup -n --menu --cflags --from-sha)'{-d,--doc}'[build the kernel-doc]' \ + '(-i --info -c --clean -f --full-cleanup --cflags --from-sha)--ccache[enable ccache during compilation tasks]' \ + '(-w --warnings -i --info -c --clean -f --full-cleanup --cflags --from-sha)'{-w,--warnings}'[enable compilation warnings]:log level:(1 2 3 12 13 23 123)' \ + '(-S --cpu-scaling -i --info -c --clean -f --full-cleanup --cflags --from-sha)'{-S,--cpu-scaling}'[set CPU usage]:scaling percentage: ' \ + '(-s --save-log-to -i --info -c --clean -f --full-cleanup --cflags --from-sha)'{-s,--save-log-to}'[save full compilation log with the enabled warnings to the specified path]:log path:_files' \ + '(-i --info -c --clean -f --full-cleanup --cflags --from-sha)--llvm[enable the usage of the LLVM toolchain]' \ + '(-i --info -c --clean -f --full-cleanup -n --menu -d --doc --ccache -w --warnings -S --cpu-scaling -s --save-log-to --llvm --cflags --from-sha)--cflags[customize kernel compilation with specific flags]' \ + '(-i --info -c --clean -f --full-cleanup -n --menu -d --doc --ccache -w --warnings -S --cpu-scaling -s --save-log-to --llvm --cflags --from-sha)--from-sha[Compile all commits from given sha value to branch head]: :' } _kw_clear-cache() diff --git a/src/bash_autocomplete.sh b/src/bash_autocomplete.sh index 1159b8ccd..6de0327e6 100644 --- a/src/bash_autocomplete.sh +++ b/src/bash_autocomplete.sh @@ -21,7 +21,7 @@ function _kw_autocomplete() kw_options['init']='--arch --remote --target --force --template --verbose' kw_options['build']='--help --info --menu --cpu-scaling --ccache --llvm --clean - --full-cleanup --verbose --doc --warnings --save-log-to --cflags' + --full-cleanup --verbose --doc --warnings --save-log-to --cflags --from-sha' kw_options['b']="${kw_options['build']}" diff --git a/src/build.sh b/src/build.sh index c8221f69b..99759e06f 100644 --- a/src/build.sh +++ b/src/build.sh @@ -33,6 +33,9 @@ function build_kernel_main() local clean local output_kbuild_flag='' local cflags + local from_sha_arg + local sha_base + local merge_base parse_build_options "$@" @@ -59,6 +62,7 @@ function build_kernel_main() clean=${options_values['CLEAN']} full_cleanup=${options_values['FULL_CLEANUP']} cflags=${options_values['CFLAGS']} + from_sha_arg=${options_values['FROM_SHA_ARG']} [[ -n "${options_values['VERBOSE']}" ]] && flag='VERBOSE' flag=${flag:-'SILENT'} @@ -124,6 +128,41 @@ function build_kernel_main() return "$?" fi + if [[ -n "$from_sha_arg" ]]; then + # Check if there is a rebase in process. + if [[ -d .git/rebase-merge ]]; then + warning 'ERROR: Abort the repository rebase before continuing with build from sha (use "git rebase --abort")!' + return 125 # ECANCELED + elif [[ -f .git/MERGE_HEAD ]]; then + warning 'ERROR: Abort the repository merge before continuing with build from sha (use "git rebase --abort")!' + return 125 # ECANCELED + elif [[ -f .git/BISECT_LOG ]]; then + warning 'ERROR: Stop the repository bisect before continuing with build from sha (use "git bisect reset")!' + return 125 # ECANCELED + elif [[ -d .git/rebase-apply ]]; then + printf 'ERROR: Abort the repository patch apply before continuing with build from sha (use "git am --abort")!' + return 125 # ECANCELED + fi + + # Check if given SHA represents real commit + cmd_manager 'SILENT' "git cat-file -e ${from_sha_arg}^{commit} 2> /dev/null" + if [[ "$?" != 0 ]]; then + complain "ERROR: The given SHA (${from_sha_arg}) does not represent a valid commit sha." + return 22 # EINVAL + fi + + # Check if given SHA is in working tree. + sha_base=$(git rev-parse --verify "$from_sha_arg") + merge_base=$(git merge-base "$from_sha_arg" HEAD) + if [[ "$sha_base" != "$merge_base" ]]; then + complain "ERROR: Given SHA (${from_sha_arg}) is invalid. Check if it is an ancestor of the branch head." + return 22 # EINVAL + fi + + build_from_sha "$flag" "$from_sha_arg" + return "$?" + fi + command="make ${optimizations} ${llvm}ARCH=${platform_ops}${warnings}" if [[ -n "$cflags" ]]; then @@ -247,9 +286,33 @@ function full_cleanup() cmd_manager "$flag" "$cmd" } +# This functions uses iteractive 'git rebase' with '--exec' flag under the hood +# to apply a 'kw build' over each commit from SHA to branch head. +# +# @flag How to display a command, see `src/lib/kwlib.sh` function `cmd_manager`. +# @sha The SHA from the first commit to be compiled until the branch head. +# +# Return: +# 0 if successfully compiled patchset, 125 (ECANCELED) otherwise. +function build_from_sha() +{ + local flag="$1" + local sha="$2" + local cmd + + flag=${flag:-'SILENT'} + cmd="git rebase ${sha} --exec 'kw build'" + cmd_manager "$flag" "$cmd" + + if [[ "$?" != 0 ]]; then + complain "kw build failed during the compilation of a patch! Check the rebase in progress for more information." + return 125 #ECANCELED + fi +} + function parse_build_options() { - local long_options='help,info,menu,doc,ccache,cpu-scaling:,warnings::,save-log-to:,llvm,clean,full-cleanup,verbose,cflags:' + local long_options='help,info,menu,doc,ccache,cpu-scaling:,warnings::,save-log-to:,llvm,clean,full-cleanup,verbose,cflags:,from-sha:' local short_options='h,i,n,d,S:,w::,s:,c,f' local doc_type local file_name_size @@ -279,6 +342,7 @@ function parse_build_options() options_values['FULL_CLEANUP']='' options_values['VERBOSE']='' options_values['CFLAGS']="${build_config[cflags]}" + options_values['FROM_SHA_ARG']='' # Check llvm option if [[ ${options_values['USE_LLVM_TOOLCHAIN']} =~ 'yes' ]]; then @@ -362,6 +426,10 @@ function parse_build_options() options_values['LOG_PATH']="$2" shift 2 ;; + --from-sha) + options_values['FROM_SHA_ARG']="$2" + shift 2 + ;; --) shift ;; @@ -394,7 +462,8 @@ function build_help() ' build (-c | --clean) - Clean option integrated into env' \ ' build (-f | --full-cleanup) - Reset the kernel tree to its default option integrated into env' \ ' build (--cflags) - Customize kernel compilation with specific flags' \ - ' build (--verbose) - Show a detailed output' + ' build (--verbose) - Show a detailed output' \ + ' build (--from-sha ) - Build all commits from to actual commit' } # Every time build.sh is loaded its proper configuration has to be loaded as well diff --git a/tests/unit/build_test.sh b/tests/unit/build_test.sh index bde08f68e..d4fe23fe0 100755 --- a/tests/unit/build_test.sh +++ b/tests/unit/build_test.sh @@ -861,6 +861,122 @@ function test_kernel_build_cpu_scaling_llvm_warning_sava_log_to() compare_command_sequence '' "($LINENO)" 'expected_result' "$output" } +function test_kernel_build_from_sha() +{ + local expected_result + local output + local sha='HEAD^' + + mk_fake_git + + output=$(build_kernel_main 'TEST_MODE' --from-sha ${sha}) + declare -a expected_result=( + "git rebase HEAD^ --exec 'kw build'" + ) + + compare_command_sequence '' "($LINENO)" 'expected_result' "$output" +} + +function test_kernel_build_from_sha_nonexisting_sha() +{ + local expected_result + local output + local sha='fakesha' + + mk_fake_git + + output=$(build_kernel_main 'TEST_MODE' --from-sha ${sha}) + declare -a expected_result=( + "ERROR: The given SHA (${sha}) does not represent a valid commit sha." + ) + + compare_command_sequence '' "($LINENO)" 'expected_result' "$output" +} + +function test_kernel_build_from_sha_sha_not_ancestor() +{ + local expected_result + local output + + mk_fake_git + mk_git_branch 'branch' + + output=$(build_kernel_main 'TEST_MODE' --from-sha branch) + declare -a expected_result=( + 'ERROR: Given SHA (branch) is invalid. Check if it is an ancestor of the branch head.' + ) + + compare_command_sequence '' "($LINENO)" 'expected_result' "$output" +} + +function test_kernel_build_from_sha_pending_rebase() +{ + local expected_result + local output + local sha='HEAD^' + + mk_fake_git + mkdir '.git/rebase-merge' + + output=$(build_kernel_main 'TEST_MODE' --from-sha ${sha}) + declare -a expected_result=( + 'ERROR: Abort the repository rebase before continuing with build from sha (use "git rebase --abort")!' + ) + + compare_command_sequence '' "($LINENO)" 'expected_result' "$output" +} + +function test_kernel_build_from_sha_pending_merge() +{ + local expected_result + local output + local sha='HEAD^' + + mk_fake_git + touch '.git/MERGE_HEAD' + + output=$(build_kernel_main 'TEST_MODE' --from-sha ${sha}) + declare -a expected_result=( + 'ERROR: Abort the repository merge before continuing with build from sha (use "git rebase --abort")!' + ) + + compare_command_sequence '' "($LINENO)" 'expected_result' "$output" +} + +function test_kernel_build_from_sha_pending_bisect() +{ + local expected_result + local output + local sha='HEAD^' + + mk_fake_git + touch '.git/BISECT_LOG' + + output=$(build_kernel_main 'TEST_MODE' --from-sha ${sha}) + declare -a expected_result=( + 'ERROR: Stop the repository bisect before continuing with build from sha (use "git bisect reset")!' + ) + + compare_command_sequence '' "($LINENO)" 'expected_result' "$output" +} + +function test_kernel_build_from_sha_pending_apply() +{ + local expected_result + local output + local sha='HEAD^' + + mk_fake_git + mkdir '.git/rebase-apply' + + output=$(build_kernel_main 'TEST_MODE' --from-sha ${sha}) + declare -a expected_result=( + 'ERROR: Abort the repository patch apply before continuing with build from sha (use "git am --abort")!' + ) + + compare_command_sequence '' "($LINENO)" 'expected_result' "$output" +} + function test_kernel_build_inside_an_env() { local output diff --git a/tests/unit/lib/kw_include_test.sh b/tests/unit/lib/kw_include_test.sh index 78244c714..c191db80e 100755 --- a/tests/unit/lib/kw_include_test.sh +++ b/tests/unit/lib/kw_include_test.sh @@ -16,13 +16,13 @@ function oneTimeSetUp() touch "${test_files[@]}" # the next files will be checked for name collisions - printf "%s\n" "function test1(){ printf '%s\n' 'output of test1';}" > \ - "$SHUNIT_TMPDIR/include_test_similar_path.sh" + printf "%s\n" "function test1(){ printf '%s\n' 'output of test1';}" > "\ +$SHUNIT_TMPDIR/include_test_similar_path.sh" mkdir "$SHUNIT_TMPDIR/include_test" - printf "%s\n" "function test2(){ printf '%s\n' 'output of test2';}" > \ - "$SHUNIT_TMPDIR/include_test/similar_path.sh" + printf "%s\n" "function test2(){ printf '%s\n' 'output of test2';}" > "\ +$SHUNIT_TMPDIR/include_test/similar_path.sh" } function oneTimeTearDown() diff --git a/tests/unit/utils.sh b/tests/unit/utils.sh index 19bc52671..4697f8845 100755 --- a/tests/unit/utils.sh +++ b/tests/unit/utils.sh @@ -265,6 +265,21 @@ function mk_fake_git() git commit --allow-empty -q -m 'Third commit' } +# Create a new git branch for current local repository and return to master branch afterwards. +# +# @branch_name The name of the new branch to be created. +function mk_git_branch() +{ + local branch_name="$1" + + git checkout --quiet HEAD^ + git checkout --quiet -b "$branch_name" + touch branch_file + git add branch_file + git commit --message "create_branch" --quiet + git checkout --quiet master +} + function mk_fake_kw_folder() { local target_folder="$1"