diff --git a/doc/src/history.adoc b/doc/src/history.adoc index 36cbbcff38..93562ac62b 100644 --- a/doc/src/history.adoc +++ b/doc/src/history.adoc @@ -8,6 +8,9 @@ * Make C/C++/ObjC include directive scanning pattern more strict to avoid trying to scan for empty file names. -- _Andrey Semashev_ +* Add listing of failed and skipped targets to end of build summary to make it + easier to find what fails. + -- _René Ferdinand Rivera Morell_ == Version 5.0.1 diff --git a/src/engine/build.bat b/src/engine/build.bat index ef7f249753..37ff324d3b 100644 --- a/src/engine/build.bat +++ b/src/engine/build.bat @@ -188,6 +188,7 @@ set B2_SOURCES=%B2_SOURCES% mod_regex.cpp set B2_SOURCES=%B2_SOURCES% mod_sequence.cpp set B2_SOURCES=%B2_SOURCES% mod_set.cpp set B2_SOURCES=%B2_SOURCES% mod_string.cpp +set B2_SOURCES=%B2_SOURCES% mod_summary.cpp set B2_SOURCES=%B2_SOURCES% mod_sysinfo.cpp set B2_SOURCES=%B2_SOURCES% mod_version.cpp diff --git a/src/engine/build.sh b/src/engine/build.sh index e88efad963..912c9478d5 100755 --- a/src/engine/build.sh +++ b/src/engine/build.sh @@ -498,6 +498,7 @@ mod_regex.cpp \ mod_sequence.cpp \ mod_set.cpp \ mod_string.cpp \ +mod_summary.cpp \ mod_sysinfo.cpp \ mod_version.cpp \ " diff --git a/src/engine/make1.cpp b/src/engine/make1.cpp index 0efd1f198c..c34aa14ee1 100644 --- a/src/engine/make1.cpp +++ b/src/engine/make1.cpp @@ -53,8 +53,11 @@ #include "output.h" #include "startup.h" +#include "mod_summary.h" + #include #include +#include #if !defined( NT ) || defined( __GNUC__ ) #include /* for unlink */ @@ -81,6 +84,10 @@ static struct int32_t made; } counts[ 1 ]; +static std::unique_ptr make_summary; +static const char * targets_failed = "targets failed"; +static const char * targets_skipped = "targets skipped"; + /* Target state. */ #define T_STATE_MAKE1A 0 /* make1a() should be called */ #define T_STATE_MAKE1B 1 /* make1b() should be called */ @@ -207,6 +214,9 @@ int32_t make1( LIST * targets ) int32_t status = 0; memset( (char *)counts, 0, sizeof( *counts ) ); + make_summary.reset(new b2::summary); + make_summary->group(targets_failed); + make_summary->group(targets_skipped); { LISTITER iter, end; @@ -247,15 +257,25 @@ int32_t make1( LIST * targets ) clear_state_freelist(); /* Talk about it. */ - if ( counts->failed ) - out_printf( "...failed updating %d target%s...\n", counts->failed, - counts->failed > 1 ? "s" : "" ); - if ( DEBUG_MAKE && counts->skipped ) - out_printf( "...skipped %d target%s...\n", counts->skipped, - counts->skipped > 1 ? "s" : "" ); if ( DEBUG_MAKE && counts->made ) - out_printf( "...updated %d target%s...\n", counts->made, + { + out_printf( "\n...updated %d target%s...\n", counts->made, counts->made > 1 ? "s" : "" ); + } + if ( DEBUG_MAKE && counts->skipped ) + { + out_printf( "\n...skipped %d target%s...\n", + make_summary->count(targets_skipped), + make_summary->count(targets_skipped) > 1 ? "s" : "" ); + make_summary->print(targets_skipped, " %s\n"); + } + if ( counts->failed ) + { + out_printf( "\n...failed updating %d target%s...\n", + make_summary->count(targets_failed), + make_summary->count(targets_failed) > 1 ? "s" : "" ); + make_summary->print(targets_failed, " %s\n"); + } /* If we were interrupted, exit now that all child processes have finished. */ @@ -425,6 +445,7 @@ static void make1b( state * const pState ) if ( ( t->status == EXEC_CMD_FAIL ) && t->actions ) { ++counts->skipped; + make_summary->message(targets_skipped, object_str( t->name )); if ( ( t->flags & ( T_FLAG_RMOLD | T_FLAG_NOTFILE ) ) == T_FLAG_RMOLD ) { if ( !unlink( object_str( t->boundname ) ) ) @@ -432,8 +453,10 @@ static void make1b( state * const pState ) ); } else + { out_printf( "...skipped %s for lack of %s...\n", object_str( t->name ), failed_name ); + } } if ( t->status == EXEC_CMD_OK ) @@ -941,6 +964,13 @@ static void make1c_closure out_printf( "...failed %s ", object_str( cmd->rule->name ) ); list_print( lol_get( (LOL *)&cmd->args, 0 ) ); out_printf( "...\n" ); + std::string m = object_str( cmd->rule->name ); + for (auto i: b2::list_cref(lol_get( (LOL *)&cmd->args, 0 ))) + { + m += " "; + m += i->str(); + } + make_summary->message(targets_failed, m.c_str()); } /* On interrupt, set quit so _everything_ fails. Do the same for failed diff --git a/src/engine/mod_summary.cpp b/src/engine/mod_summary.cpp new file mode 100644 index 0000000000..1e6eafbbf6 --- /dev/null +++ b/src/engine/mod_summary.cpp @@ -0,0 +1,46 @@ +/* +Copyright 2024 René Ferdinand Rivera Morell +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt) +*/ + +#include "mod_summary.h" + +#include "output.h" + +#include + +namespace b2 { + +void summary::group(value_ref group) +{ + group_order.push_back(group); + groups.emplace(group, new std::vector); +} + +void summary::message(value_ref group, value_ref message) +{ + groups[group]->push_back(message); +} + +int summary::count(value_ref group) +{ + return (int)(groups[group]->size()); +} + +void summary::print(value_ref group, value_ref format) +{ + std::string format_str = format; + auto & g = groups[group]; + std::sort(g->begin(), g->end(), [](value_ref a, value_ref b) -> bool + { + return std::strcmp(a->str(), b->str()) < 0; + }); + for (auto const & m : *g) + { + std::string m_str = m; + out_printf(format->str(), m_str.c_str()); + } +} + +} // namespace b2 diff --git a/src/engine/mod_summary.h b/src/engine/mod_summary.h new file mode 100644 index 0000000000..d6b43b4311 --- /dev/null +++ b/src/engine/mod_summary.h @@ -0,0 +1,59 @@ +/* +Copyright 2024 René Ferdinand Rivera Morell +Distributed under the Boost Software License, Version 1.0. +(See accompanying file LICENSE.txt or https://www.bfgroup.xyz/b2/LICENSE.txt) +*/ + +#ifndef B2_MOD_SUMMARY_H +#define B2_MOD_SUMMARY_H + +#include "config.h" + +#include "bind.h" +#include "value.h" + +#include +#include + +namespace b2 { + +class summary : public object +{ + public: + void group(value_ref group); + void message(value_ref group, value_ref message); + int count(value_ref group); + void print(value_ref group, value_ref format); + + private: + using group_t = std::unique_ptr>; + using groups_t = std::unordered_map; + + groups_t groups; + std::vector group_order; +}; + +struct summary_module : b2::bind::module_ +{ + const char * module_name = "summary"; + + template + void def(Binder & binder) + { + binder.def_class("summary", type_()) + .def(init_<>()) + .def(&summary::group, "group", "group" * _1) + .def(&summary::message, "message", "group" * _1, + "message" * _1n) + .def(&summary::count, "count", "group" * _1) + .def(&summary::print, "print", "group" * _1, + "format" * _1); + } +}; + +} // namespace b2 + +#endif diff --git a/test/core_actions_quietly.py b/test/core_actions_quietly.py index 6c0d75fe5e..87e1de6d84 100755 --- a/test/core_actions_quietly.py +++ b/test/core_actions_quietly.py @@ -49,12 +49,14 @@ [subtest_b] 0 [subtest_b] 1 [subtest_b] 2 + ...updated 2 targets... """) t.run_build_system(["-ffile.jam", "-d1"], stdout="""\ ...found 4 targets... ...updating 2 targets... + ...updated 2 targets... """) diff --git a/test/core_jamshell.py b/test/core_jamshell.py index 67292870b9..e1c0722802 100644 --- a/test/core_jamshell.py +++ b/test/core_jamshell.py @@ -49,7 +49,10 @@ t.expect_output_lines([ "...failed run test-raw-fail...", "0,1,2", + "", + "...updated 2 targets...", + "", "...failed updating 1 target...", - "...updated 2 targets..."]) + " run test-raw-fail"]) t.cleanup() diff --git a/test/core_multifile_actions.py b/test/core_multifile_actions.py index b6b78a5438..e322b2bac7 100755 --- a/test/core_multifile_actions.py +++ b/test/core_multifile_actions.py @@ -40,6 +40,7 @@ updating x1 x2 update x2 updating x2 x3 + ...updated 3 targets... """) @@ -59,6 +60,7 @@ updating x1 x2 update x2 updating x2 x3 + ...updated 3 targets... """) @@ -96,8 +98,11 @@ ...failed fail x1... update x2 updating x2 -...failed updating 2 targets... + ...updated 1 target... + +...failed updating 1 target... + fail x1 """) # Make sure that dependencies of targets that are @@ -124,6 +129,7 @@ updating x2 update x2 updating x2 x3 + ...updated 3 targets... """) @@ -147,6 +153,7 @@ ...updating 2 targets... update x1 updating x1 x2 + ...updated 2 targets... """) @@ -164,6 +171,7 @@ ...updating 1 target... update x1 updating x1 x1 + ...updated 1 target... """) @@ -194,6 +202,7 @@ updating x3 x4 : s4 update x4 updating x4 x3 : s5 + ...updated 4 targets... """) diff --git a/test/core_option_d2.py b/test/core_option_d2.py index 575923c7b6..62eecf3535 100755 --- a/test/core_option_d2.py +++ b/test/core_option_d2.py @@ -49,6 +49,7 @@ [subtest_b] 0 [subtest_b] 1 [subtest_b] 2 + ...updated 2 targets... """) diff --git a/test/core_option_n.py b/test/core_option_n.py index 3b90505a52..8e39e71ea0 100755 --- a/test/core_option_n.py +++ b/test/core_option_n.py @@ -44,6 +44,7 @@ echo [subtest_b] 1 echo [subtest_b] 2 + ...updated 2 targets... """) t.expect_nothing_more() diff --git a/test/core_parallel_actions.py b/test/core_parallel_actions.py index 6563c4fd97..a1a7ab6771 100755 --- a/test/core_parallel_actions.py +++ b/test/core_parallel_actions.py @@ -97,6 +97,7 @@ [.b] 0 [.b] 1 [.b] 2 + ...updated 8 targets... """) diff --git a/test/core_parallel_multifile_actions_1.py b/test/core_parallel_multifile_actions_1.py index 261bebe4c8..75ce4d65f3 100755 --- a/test/core_parallel_multifile_actions_1.py +++ b/test/core_parallel_multifile_actions_1.py @@ -72,6 +72,7 @@ 003 .use.2 u2.user 004 + ...updated 4 targets... """) diff --git a/test/core_parallel_multifile_actions_2.py b/test/core_parallel_multifile_actions_2.py index be66f731ef..419fbd314d 100755 --- a/test/core_parallel_multifile_actions_2.py +++ b/test/core_parallel_multifile_actions_2.py @@ -65,6 +65,7 @@ 001 - linked install installed_dll 002 - installed + ...updated 3 targets... """) diff --git a/test/core_update_now.py b/test/core_update_now.py index 5d7003010d..1d1f95da3f 100755 --- a/test/core_update_now.py +++ b/test/core_update_now.py @@ -31,6 +31,7 @@ def basic(): ...updating 1 target... do-print target1 updating target1 + ...updated 1 target... ...found 1 target... """) @@ -64,6 +65,7 @@ def ignore_minus_n(): echo updating target1 updating target1 + ...updated 1 target... ...found 1 target... """) @@ -106,13 +108,16 @@ def failed_target(): exit 1 ...failed fail target1... + ...failed updating 1 target... + fail target1 ...found 2 targets... ...updating 1 target... do-print target2 echo updating target2 + ...updated 1 target... """) @@ -181,12 +186,14 @@ def build_once(): echo updating target1 + ...updated 1 target... do-print target1 echo updating target1 updating target1 + ...updated 1 target... ...found 1 target... """) @@ -225,7 +232,9 @@ def return_status(): exit 1 ...failed fail target1... + ...failed updating 1 target... + fail target1 update1: update2: ...found 1 target... @@ -283,7 +292,10 @@ def save_restore(): exit 1 ...failed fail target2... + ...failed updating 2 targets... + fail target1 + fail target2 ...found 2 targets... ...updating 2 targets... fail target3 @@ -294,6 +306,7 @@ def save_restore(): exit 1 + ...updated 2 targets... ''') @@ -305,7 +318,9 @@ def save_restore(): exit 1 ...failed fail target1... + ...failed updating 1 target... + fail target1 ...found 2 targets... ...updating 2 targets... fail target3 @@ -313,7 +328,9 @@ def save_restore(): exit 1 ...failed fail target3... + ...failed updating 1 target... + fail target3 ''') t.run_build_system(['-n', '-sIGNORE_MINUS_Q=1', '-ffile.jam'], @@ -327,6 +344,7 @@ def save_restore(): exit 1 + ...updated 2 targets... ...found 2 targets... ...updating 2 targets... @@ -338,6 +356,7 @@ def save_restore(): exit 1 + ...updated 2 targets... ''') @@ -354,7 +373,10 @@ def save_restore(): exit 1 ...failed fail target2... + ...failed updating 2 targets... + fail target1 + fail target2 ...found 2 targets... ...updating 2 targets... fail target3 @@ -362,7 +384,9 @@ def save_restore(): exit 1 ...failed fail target3... + ...failed updating 1 target... + fail target3 ''') t.cleanup() diff --git a/test/timedata.py b/test/timedata.py index 3a4e25a787..08bd5c218d 100644 --- a/test/timedata.py +++ b/test/timedata.py @@ -82,6 +82,7 @@ def basic_jam_action_test(): make bar time foo bar +user: [0-9.]+ +system: +[0-9.]+ +clock: +[0-9.]+ * + \\.\\.\\.updated 2 targets\\.\\.\\.$ """