From c5498aa11ba6476b80b9bee3946fac131663cc93 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Thu, 19 Jun 2025 22:17:12 -0400 Subject: [PATCH 01/16] tidy: copy clang-tidy file from bitcoin core --- .clang-tidy | 65 +++++++++++++++++++++++++---------------------------- 1 file changed, 31 insertions(+), 34 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 2d29f120..544bd5ac 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,41 +1,38 @@ Checks: ' -*, -bugprone-*, --bugprone-easily-swappable-parameters, --bugprone-exception-escape, --bugprone-move-forwarding-reference, --bugprone-narrowing-conversions, --bugprone-reserved-identifier, -misc-*, --misc-non-private-member-variables-in-classes, --misc-no-recursion, --misc-unconventional-assign-operator, --misc-unused-parameters, --misc-use-anonymous-namespace, -modernize-*, --modernize-avoid-c-arrays, --modernize-concat-nested-namespaces, --modernize-deprecated-headers, --modernize-use-nodiscard, --modernize-use-trailing-return-type, --modernize-use-using, +bitcoin-*, +bugprone-argument-comment, +bugprone-move-forwarding-reference, +bugprone-string-constructor, +bugprone-use-after-move, +bugprone-lambda-function-name, +bugprone-unhandled-self-assignment, +misc-unused-using-decls, +misc-no-recursion, +modernize-deprecated-headers, +modernize-use-default-member-init, +modernize-use-emplace, +modernize-use-equals-default, +modernize-use-noexcept, +modernize-use-nullptr, +modernize-use-starts-ends-with, performance-*, -performance-avoid-endl, +-performance-enum-size, +-performance-inefficient-string-concatenation, +-performance-no-int-to-ptr, -performance-noexcept-move-constructor, -readability-*, --readability-braces-around-statements, --readability-convert-member-functions-to-static, --readability-else-after-return, --readability-function-cognitive-complexity, --readability-identifier-length, --readability-implicit-bool-conversion, --readability-inconsistent-declaration-parameter-name, --readability-magic-numbers, --readability-named-parameter, --readability-uppercase-literal-suffix, --readability-use-anyofallof, +-performance-unnecessary-value-param, +readability-const-return-type, +readability-redundant-declaration, +readability-redundant-string-init, ' +HeaderFilterRegex: '.' +WarningsAsErrors: '*' CheckOptions: - - key: modernize-use-override.IgnoreDestructors - value: true -HeaderFilterRegex: 'example/calculator.h|example/init.h|example/printer.h|include/mp/proxy-io.h|include/mp/proxy-types.h|include/mp/proxy.h|include/mp/util.h|test/mp/test/foo-types.h|test/mp/test/foo.h' + - key: modernize-deprecated-headers.CheckHeaderFile + value: false + - key: performance-move-const-arg.CheckTriviallyCopyableMove + value: false + - key: bugprone-unhandled-self-assignment.WarnOnlyIfThisHasSuspiciousField + value: false From c6784c6adefada2daa67ebcbae82791dfd90f1c6 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Thu, 19 Jun 2025 22:18:24 -0400 Subject: [PATCH 02/16] mpgen: disable clang-tidy misc-no-recursion error src/mp/gen.cpp:82:13: error: function 'ForEachMethod' is within a recursive call chain [misc-no-recursion,-warnings-as-errors] 82 | static void ForEachMethod(const capnp::InterfaceSchema& interface, const std::function& callback) --- src/mp/gen.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mp/gen.cpp b/src/mp/gen.cpp index 22001eba..8e29c0c5 100644 --- a/src/mp/gen.cpp +++ b/src/mp/gen.cpp @@ -76,7 +76,7 @@ static bool GetAnnotationInt32(const Reader& reader, uint64_t id, int32_t* resul return false; } -static void ForEachMethod(const capnp::InterfaceSchema& interface, const std::function& callback) +static void ForEachMethod(const capnp::InterfaceSchema& interface, const std::function& callback) // NOLINT(misc-no-recursion) { for (const auto super : interface.getSuperclasses()) { ForEachMethod(super, callback); From 422923f384857fe53fe4664e7a6d23d77840a375 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Thu, 19 Jun 2025 22:21:02 -0400 Subject: [PATCH 03/16] proxy-types: fix clang-tidy bugprone-use-after-move error include/mp/proxy-types.h:203:98: error: 'values' used after it was forwarded [bugprone-use-after-move,-warnings-as-errors] 203 | CustomBuildField(TypeList(), Priority<3>(), context, std::forward(values)..., | ^ include/mp/proxy-types.h:202:9: note: forward occurred here 202 | if (CustomHasValue(context, std::forward(values)...)) { | ^ --- include/mp/proxy-types.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/mp/proxy-types.h b/include/mp/proxy-types.h index c2f09865..952353ed 100644 --- a/include/mp/proxy-types.h +++ b/include/mp/proxy-types.h @@ -191,7 +191,7 @@ void ThrowField(TypeList, InvokeContext& invoke_context, Input&& } template -bool CustomHasValue(InvokeContext& invoke_context, Values&&... value) +bool CustomHasValue(InvokeContext& invoke_context, const Values&... value) { return true; } @@ -199,7 +199,7 @@ bool CustomHasValue(InvokeContext& invoke_context, Values&&... value) template void BuildField(TypeList, Context& context, Output&& output, Values&&... values) { - if (CustomHasValue(context, std::forward(values)...)) { + if (CustomHasValue(context, values...)) { CustomBuildField(TypeList(), Priority<3>(), context, std::forward(values)..., std::forward(output)); } From 3673114bc9d97ffc3e6aa57f3c41dc85b8c7a4df Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Thu, 19 Jun 2025 22:21:02 -0400 Subject: [PATCH 04/16] proxy-types: fix clang-tidy bugprone-use-after-move error include/mp/proxy-types.h:333:74: error: 'arg2' used after it was forwarded [bugprone-use-after-move,-warnings-as-errors] 333 | next_fn.handleChain(std::forward(arg1), std::forward(arg2), typename S::Second(), | ^ include/mp/proxy-types.h:332:9: note: forward occurred here 332 | handleChain(std::forward(arg1), std::forward(arg2), typename S::First()); --- include/mp/proxy-types.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/mp/proxy-types.h b/include/mp/proxy-types.h index 952353ed..5c002faa 100644 --- a/include/mp/proxy-types.h +++ b/include/mp/proxy-types.h @@ -326,18 +326,18 @@ template struct IterateFieldsHelper { template - void handleChain(Arg1&& arg1, Arg2&& arg2, ParamList, NextFn&& next_fn, NextFnArgs&&... next_fn_args) + void handleChain(Arg1& arg1, Arg2& arg2, ParamList, NextFn&& next_fn, NextFnArgs&&... next_fn_args) { using S = Split; - handleChain(std::forward(arg1), std::forward(arg2), typename S::First()); - next_fn.handleChain(std::forward(arg1), std::forward(arg2), typename S::Second(), + handleChain(arg1, arg2, typename S::First()); + next_fn.handleChain(arg1, arg2, typename S::Second(), std::forward(next_fn_args)...); } template - void handleChain(Arg1&& arg1, Arg2&& arg2, ParamList) + void handleChain(Arg1& arg1, Arg2& arg2, ParamList) { - static_cast(this)->handleField(std::forward(arg1), std::forward(arg2), ParamList()); + static_cast(this)->handleField(arg1, arg2, ParamList()); } private: IterateFieldsHelper() = default; From 07a741bf69464f5a03749c1c4650e31e5e1084e0 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Thu, 19 Jun 2025 22:21:02 -0400 Subject: [PATCH 05/16] proxy-types: fix clang-tidy bugprone-use-after-move error include/mp/proxy-types.h:399:70: error: 'values' used after it was forwarded [bugprone-use-after-move,-warnings-as-errors] 399 | ParamList(), Priority<1>(), std::forward(values)..., Make(params)); | ^ include/mp/proxy-types.h:396:17: note: forward occurred here 396 | MaybeBuildField(std::integral_constant(), ParamList(), invoke_context, --- include/mp/proxy-types.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/include/mp/proxy-types.h b/include/mp/proxy-types.h index 5c002faa..77f90e6c 100644 --- a/include/mp/proxy-types.h +++ b/include/mp/proxy-types.h @@ -274,7 +274,7 @@ void MaybeReadField(std::false_type, Args&&...) } template -void MaybeSetWant(TypeList, Priority<1>, Value&& value, Output&& output) +void MaybeSetWant(TypeList, Priority<1>, const Value& value, Output&& output) { if (value) { output.setWant(); @@ -282,7 +282,7 @@ void MaybeSetWant(TypeList, Priority<1>, Value&& value, Output&& out } template -void MaybeSetWant(LocalTypes, Priority<0>, Args&&...) +void MaybeSetWant(LocalTypes, Priority<0>, const Args&...) { } @@ -393,10 +393,10 @@ struct ClientParam void handleField(ClientInvokeContext& invoke_context, Params& params, ParamList) { auto const fun = [&](Values&&... values) { + MaybeSetWant( + ParamList(), Priority<1>(), values..., Make(params)); MaybeBuildField(std::integral_constant(), ParamList(), invoke_context, Make(params), std::forward(values)...); - MaybeSetWant( - ParamList(), Priority<1>(), std::forward(values)..., Make(params)); }; // Note: The m_values tuple just consists of lvalue and rvalue From ae4f1dc2bb1a742c7308d0964e23b50986e4e965 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Thu, 19 Jun 2025 22:26:31 -0400 Subject: [PATCH 06/16] type-number: fix clang-tidy modernize-use-nullptr error include/mp/type-number.h:53:77: error: use nullptr [modernize-use-nullptr,-warnings-as-errors] 53 | typename std::enable_if::value>::type* enable = 0) | ^ | nullptr --- include/mp/type-number.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mp/type-number.h b/include/mp/type-number.h index 4d172a01..75301df8 100644 --- a/include/mp/type-number.h +++ b/include/mp/type-number.h @@ -50,7 +50,7 @@ decltype(auto) CustomReadField(TypeList, InvokeContext& invoke_context, Input&& input, ReadDest&& read_dest, - typename std::enable_if::value>::type* enable = 0) + typename std::enable_if::value>::type* enable = nullptr) { return read_dest.construct(static_cast(input.get())); } From 753d2b10cc27152fa3c075cf9d507d38b60a95ff Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Thu, 19 Jun 2025 22:30:41 -0400 Subject: [PATCH 07/16] util: fix clang-tidy modernize-use-equals-default error mp/include/mp/util.h:173:5: error: use '= default' to define a trivial destructor [modernize-use-equals-default,-warnings-as-errors] 173 | ~Lock() MP_RELEASE() {} | ^ ~~ | = default; --- include/mp/util.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mp/util.h b/include/mp/util.h index 22e8188e..ce4a4ec2 100644 --- a/include/mp/util.h +++ b/include/mp/util.h @@ -174,7 +174,7 @@ class MP_CAPABILITY("mutex") Mutex { class MP_SCOPED_CAPABILITY Lock { public: explicit Lock(Mutex& m) MP_ACQUIRE(m) : m_lock(m.m_mutex) {} - ~Lock() MP_RELEASE() {} + ~Lock() MP_RELEASE() = default; void unlock() MP_RELEASE() { m_lock.unlock(); } void lock() MP_ACQUIRE() { m_lock.lock(); } void assert_locked(Mutex& mutex) MP_ASSERT_CAPABILITY() MP_ASSERT_CAPABILITY(mutex) From 0d5f1faae5da26205ef9563cb9cc157725ab9479 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Thu, 19 Jun 2025 22:29:13 -0400 Subject: [PATCH 08/16] iwyu: fix add/remove include errors --- example/calculator.cpp | 12 ++++++---- example/example.cpp | 9 ++++++-- example/init.capnp | 3 +-- example/printer.cpp | 14 ++++++++---- example/types.h | 9 ++++++++ include/mp/proxy-io.h | 2 ++ include/mp/proxy.h | 18 ++++++--------- include/mp/util.h | 6 +---- src/mp/gen.cpp | 47 +++++++++++++++++++++++++++++++++------- src/mp/proxy.cpp | 12 ++++------ src/mp/util.cpp | 4 ++-- test/mp/test/foo-types.h | 15 +++++++++++++ test/mp/test/test.cpp | 19 +++++++++++----- 13 files changed, 119 insertions(+), 51 deletions(-) diff --git a/example/calculator.cpp b/example/calculator.cpp index e94be4b8..016a0486 100644 --- a/example/calculator.cpp +++ b/example/calculator.cpp @@ -3,17 +3,21 @@ // file COPYING or http://www.opensource.org/licenses/mit-license.php. #include +#include +#include // NOLINT(misc-include-cleaner) // IWYU pragma: keep + #include +#include #include -#include -#include // NOLINT(misc-include-cleaner) -#include #include +#include +#include +#include #include #include -#include #include #include +#include #include class CalculatorImpl : public Calculator diff --git a/example/example.cpp b/example/example.cpp index ce1a9ba7..5088f796 100644 --- a/example/example.cpp +++ b/example/example.cpp @@ -2,12 +2,17 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include +#include + +#include // IWYU pragma: keep #include #include #include -#include -#include #include +#include +#include +#include #include #include #include diff --git a/example/init.capnp b/example/init.capnp index 03c88991..01897f13 100644 --- a/example/init.capnp +++ b/example/init.capnp @@ -12,8 +12,7 @@ using Printer = import "printer.capnp"; $Proxy.include("calculator.h"); $Proxy.include("init.h"); $Proxy.include("printer.h"); -$Proxy.includeTypes("calculator.capnp.proxy-types.h"); -$Proxy.includeTypes("printer.capnp.proxy-types.h"); +$Proxy.includeTypes("types.h"); interface InitInterface $Proxy.wrap("Init") { construct @0 (threadMap: Proxy.ThreadMap) -> (threadMap :Proxy.ThreadMap); diff --git a/example/printer.cpp b/example/printer.cpp index 931eab46..eb384018 100644 --- a/example/printer.cpp +++ b/example/printer.cpp @@ -2,17 +2,23 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. +#include + +#include +#include // NOLINT(misc-include-cleaner) // IWYU pragma: keep + #include +#include #include -#include -#include // NOLINT(misc-include-cleaner) -#include #include +#include +#include +#include #include #include -#include #include #include +#include class PrinterImpl : public Printer { diff --git a/example/types.h b/example/types.h index 6b6b579d..c926a00b 100644 --- a/example/types.h +++ b/example/types.h @@ -5,10 +5,19 @@ #ifndef EXAMPLE_TYPES_H #define EXAMPLE_TYPES_H +#include +#include + +// IWYU pragma: begin_exports #include #include #include #include #include +// IWYU pragma: end_exports + +struct InitInterface; // IWYU pragma: export +struct CalculatorInterface; // IWYU pragma: export +struct PrinterInterface; // IWYU pragma: export #endif // EXAMPLE_TYPES_H diff --git a/include/mp/proxy-io.h b/include/mp/proxy-io.h index 9f3eed62..3ed7bad7 100644 --- a/include/mp/proxy-io.h +++ b/include/mp/proxy-io.h @@ -13,6 +13,7 @@ #include #include +#include #include #include #include @@ -20,6 +21,7 @@ #include #include #include +#include namespace mp { struct ThreadContext; diff --git a/include/mp/proxy.h b/include/mp/proxy.h index 94c65820..fff511fd 100644 --- a/include/mp/proxy.h +++ b/include/mp/proxy.h @@ -7,35 +7,31 @@ #include -#include #include #include #include +#include #include #include #include #include +#include // IWYU pragma: keep namespace mp { class Connection; class EventLoop; //! Mapping from capnp interface type to proxy client implementation (specializations are generated by //! proxy-codegen.cpp). -template -struct ProxyClient; +template struct ProxyClient; // IWYU pragma: export //! Mapping from capnp interface type to proxy server implementation (specializations are generated by //! proxy-codegen.cpp). -template -struct ProxyServer; +template struct ProxyServer; // IWYU pragma: export //! Mapping from capnp method params type to method traits (specializations are generated by proxy-codegen.cpp). -template -struct ProxyMethod; +template struct ProxyMethod; // IWYU pragma: export //! Mapping from capnp struct type to struct traits (specializations are generated by proxy-codegen.cpp). -template -struct ProxyStruct; +template struct ProxyStruct; // IWYU pragma: export //! Mapping from local c++ type to capnp type and traits (specializations are generated by proxy-codegen.cpp). -template -struct ProxyType; +template struct ProxyType; // IWYU pragma: export using CleanupList = std::list>; using CleanupIt = typename CleanupList::iterator; diff --git a/include/mp/util.h b/include/mp/util.h index ce4a4ec2..d9f3ca3e 100644 --- a/include/mp/util.h +++ b/include/mp/util.h @@ -8,14 +8,10 @@ #include #include #include +#include #include -#include -#include -#include #include -#include #include -#include #include #include #include diff --git a/src/mp/gen.cpp b/src/mp/gen.cpp index 8e29c0c5..21a4d931 100644 --- a/src/mp/gen.cpp +++ b/src/mp/gen.cpp @@ -6,13 +6,15 @@ #include #include +#include #include +#include #include #include #include -#include #include #include +#include #include #include #include @@ -26,6 +28,7 @@ #include #include #include +#include #include #define PROXY_BIN "mpgen" @@ -198,19 +201,45 @@ static void Generate(kj::StringPtr src_prefix, std::ofstream cpp_server(output_path + ".proxy-server.c++"); cpp_server << "// Generated by " PROXY_BIN " from " << src_file << "\n\n"; + cpp_server << "// IWYU pragma: no_include \n"; + cpp_server << "// IWYU pragma: no_include \n"; + cpp_server << "// IWYU pragma: begin_keep\n"; + cpp_server << "#include <" << include_path << ".proxy.h>\n"; cpp_server << "#include <" << include_path << ".proxy-types.h>\n"; - cpp_server << "#include <" << PROXY_TYPES << ">\n\n"; + cpp_server << "#include \n"; + cpp_server << "#include \n"; + cpp_server << "#include \n"; + cpp_server << "#include \n"; + cpp_server << "#include \n"; + cpp_server << "#include \n"; + cpp_server << "#include \n"; + cpp_server << "#include <" << PROXY_TYPES << ">\n"; + cpp_server << "// IWYU pragma: end_keep\n\n"; cpp_server << "namespace mp {\n"; std::ofstream cpp_client(output_path + ".proxy-client.c++"); cpp_client << "// Generated by " PROXY_BIN " from " << src_file << "\n\n"; + cpp_client << "// IWYU pragma: no_include \n"; + cpp_client << "// IWYU pragma: no_include \n"; + cpp_client << "// IWYU pragma: begin_keep\n"; + cpp_client << "#include <" << include_path << ".h>\n"; + cpp_client << "#include <" << include_path << ".proxy.h>\n"; cpp_client << "#include <" << include_path << ".proxy-types.h>\n"; - cpp_client << "#include <" << PROXY_TYPES << ">\n\n"; + cpp_client << "#include \n"; + cpp_client << "#include \n"; + cpp_client << "#include \n"; + cpp_client << "#include \n"; + cpp_client << "#include \n"; + cpp_client << "#include <" << PROXY_TYPES << ">\n"; + cpp_client << "// IWYU pragma: end_keep\n\n"; cpp_client << "namespace mp {\n"; std::ofstream cpp_types(output_path + ".proxy-types.c++"); cpp_types << "// Generated by " PROXY_BIN " from " << src_file << "\n\n"; - cpp_types << "#include <" << include_path << ".proxy-types.h>\n"; + cpp_types << "// IWYU pragma: no_include \"mp/proxy.h\"\n"; + cpp_types << "// IWYU pragma: no_include \"mp/proxy-io.h\"\n"; + cpp_types << "#include <" << include_path << ".proxy.h>\n"; + cpp_types << "#include <" << include_path << ".proxy-types.h> // IWYU pragma: keep\n"; cpp_types << "#include <" << PROXY_TYPES << ">\n\n"; cpp_types << "namespace mp {\n"; @@ -226,10 +255,12 @@ static void Generate(kj::StringPtr src_prefix, inl << "// Generated by " PROXY_BIN " from " << src_file << "\n\n"; inl << "#ifndef " << guard << "_PROXY_TYPES_H\n"; inl << "#define " << guard << "_PROXY_TYPES_H\n\n"; - inl << "#include <" << include_path << ".proxy.h>\n"; + inl << "// IWYU pragma: no_include \"mp/proxy.h\"\n"; + inl << "#include // IWYU pragma: keep\n"; + inl << "#include <" << include_path << ".proxy.h> // IWYU pragma: keep\n"; for (const auto annotation : file_schema.getProto().getAnnotations()) { if (annotation.getId() == INCLUDE_TYPES_ANNOTATION_ID) { - inl << "#include <" << annotation.getValue().getText() << ">\n"; + inl << "#include \"" << annotation.getValue().getText() << "\" // IWYU pragma: export\n"; } } inl << "namespace mp {\n"; @@ -238,10 +269,10 @@ static void Generate(kj::StringPtr src_prefix, h << "// Generated by " PROXY_BIN " from " << src_file << "\n\n"; h << "#ifndef " << guard << "_PROXY_H\n"; h << "#define " << guard << "_PROXY_H\n\n"; - h << "#include <" << include_path << ".h>\n"; + h << "#include <" << include_path << ".h> // IWYU pragma: keep\n"; for (const auto annotation : file_schema.getProto().getAnnotations()) { if (annotation.getId() == INCLUDE_ANNOTATION_ID) { - h << "#include <" << annotation.getValue().getText() << ">\n"; + h << "#include \"" << annotation.getValue().getText() << "\" // IWYU pragma: export\n"; } } h << "#include <" << PROXY_DECL << ">\n\n"; diff --git a/src/mp/proxy.cpp b/src/mp/proxy.cpp index 0f5e566a..579f7995 100644 --- a/src/mp/proxy.cpp +++ b/src/mp/proxy.cpp @@ -10,24 +10,23 @@ #include #include -#include #include -#include #include +#include #include #include #include -#include #include +#include +#include #include #include -#include #include #include #include #include #include -#include +#include #include #include #include @@ -38,9 +37,6 @@ namespace mp { -template -struct ProxyServer; - thread_local ThreadContext g_thread_context; void LoggingErrorHandler::taskFailed(kj::Exception&& exception) diff --git a/src/mp/util.cpp b/src/mp/util.cpp index cbe8b9fb..a9485399 100644 --- a/src/mp/util.cpp +++ b/src/mp/util.cpp @@ -5,12 +5,12 @@ #include #include -#include +#include +#include #include #include #include #include -#include #include #include #include diff --git a/test/mp/test/foo-types.h b/test/mp/test/foo-types.h index bb7f3041..e70bc4c1 100644 --- a/test/mp/test/foo-types.h +++ b/test/mp/test/foo-types.h @@ -5,7 +5,13 @@ #ifndef MP_TEST_FOO_TYPES_H #define MP_TEST_FOO_TYPES_H +#include #include + +// IWYU pragma: begin_exports +#include +#include +#include #include #include #include @@ -18,9 +24,18 @@ #include #include #include +#include +#include +// IWYU pragma: end_exports namespace mp { namespace test { +namespace messages { +struct ExtendedCallback; // IWYU pragma: export +struct FooCallback; // IWYU pragma: export +struct FooFn; // IWYU pragma: export +struct FooInterface; // IWYU pragma: export +} // namespace messages template void CustomBuildField(TypeList, Priority<1>, InvokeContext& invoke_context, const FooCustom& value, Output&& output) diff --git a/test/mp/test/test.cpp b/test/mp/test/test.cpp index 37201a9a..fa5a6f4c 100644 --- a/test/mp/test/test.cpp +++ b/test/mp/test/test.cpp @@ -2,23 +2,32 @@ // Distributed under the MIT software license, see the accompanying // file COPYING or http://www.opensource.org/licenses/mit-license.php. -#include #include #include -#include #include -#include -#include +#include +#include #include +#include #include -#include +#include +#include #include +#include #include #include +#include +#include +#include +#include +#include +#include #include +#include #include #include +#include namespace mp { namespace test { From 977d721020f624d13a3ad686b901f3c5216cf6c1 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Thu, 19 Jun 2025 22:36:11 -0400 Subject: [PATCH 09/16] ci: add github actions jobs testing gcc, clang-20, clang-tidy, and iwyu --- .github/workflows/ci.yml | 29 +++++++++++++++++++++++++++++ CMakeLists.txt | 25 +++++++++++++++++++++---- ci/README.md | 23 +++++++++++++++++++++++ ci/configs/default.sh | 4 ++++ ci/configs/llvm.sh | 10 ++++++++++ ci/scripts/ci.sh | 12 ++++++++++++ ci/scripts/run.sh | 4 ++++ cmake/TargetCapnpSources.cmake | 2 ++ shell.nix | 14 ++++++++++++++ 9 files changed, 119 insertions(+), 4 deletions(-) create mode 100644 .github/workflows/ci.yml create mode 100644 ci/README.md create mode 100644 ci/configs/default.sh create mode 100644 ci/configs/llvm.sh create mode 100755 ci/scripts/ci.sh create mode 100755 ci/scripts/run.sh create mode 100644 shell.nix diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 00000000..720a9abf --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,29 @@ +name: CI + +on: + push: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + strategy: + fail-fast: false + matrix: + config: [default, llvm] + + name: build • ${{ matrix.config }} + + steps: + - uses: actions/checkout@v4 + + - name: Install Nix + uses: cachix/install-nix-action@v31 # 2025-05-27, from https://github.com/cachix/install-nix-action/tags + with: + nix_path: nixpkgs=channel:nixos-25.05 # latest release + + - name: Run CI script + env: + CI_CONFIG: ci/configs/${{ matrix.config }}.sh + run: ci/scripts/run.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index f94086f5..fd23904e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,16 +15,32 @@ include("cmake/compat_find.cmake") find_package(CapnProto REQUIRED) find_package(Threads REQUIRED) -option(Libmultiprocess_ENABLE_CLANG_TIDY "Run clang-tidy with the compiler." OFF) -if(Libmultiprocess_ENABLE_CLANG_TIDY) +set(MPGEN_EXECUTABLE "" CACHE FILEPATH "If specified, should be full path to an external mpgen binary to use rather than the one built internally.") + +option(MP_ENABLE_CLANG_TIDY "Run clang-tidy with the compiler." OFF) +if(MP_ENABLE_CLANG_TIDY) find_program(CLANG_TIDY_EXECUTABLE NAMES clang-tidy) if(NOT CLANG_TIDY_EXECUTABLE) - message(FATAL_ERROR "Libmultiprocess_ENABLE_CLANG_TIDY is ON but clang-tidy is not found.") + message(FATAL_ERROR "MP_ENABLE_CLANG_TIDY is ON but clang-tidy is not found.") endif() set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXECUTABLE}") + + # Workaround for nix from https://gitlab.kitware.com/cmake/cmake/-/issues/20912#note_793338 + # Nix injects header paths via $NIX_CFLAGS_COMPILE; CMake tags these as + # CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES and omits them from the compile + # database, so clang-tidy, which ignores $NIX_CFLAGS_COMPILE, can't find capnp + # headers. Setting them as standard passes them to clang-tidy. + set(CMAKE_CXX_STANDARD_INCLUDE_DIRECTORIES ${CMAKE_CXX_IMPLICIT_INCLUDE_DIRECTORIES}) endif() -set(MPGEN_EXECUTABLE "" CACHE FILEPATH "If specified, should be full path to an external mpgen binary to use rather than the one built internally.") +option(MP_ENABLE_IWYU "Run include-what-you-use with the compiler." OFF) +if(MP_ENABLE_IWYU) + find_program(IWYU_EXECUTABLE NAMES include-what-you-use iwyu) + if(NOT IWYU_EXECUTABLE) + message(FATAL_ERROR "MP_ENABLE_IWYU is ON but include-what-you-use was not found.") + endif() + set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "${IWYU_EXECUTABLE};-Xiwyu;--error") +endif() include("cmake/compat_config.cmake") include("cmake/pthread_checks.cmake") @@ -51,6 +67,7 @@ configure_file(include/mp/config.h.in "${CMAKE_CURRENT_BINARY_DIR}/include/mp/co # Generated C++ Capn'Proto schema files capnp_generate_cpp(MP_PROXY_SRCS MP_PROXY_HDRS include/mp/proxy.capnp) +set_source_files_properties("${MP_PROXY_SRCS}" PROPERTIES SKIP_LINTING TRUE) # Ignored before cmake 3.27 # util library add_library(mputil OBJECT src/mp/util.cpp) diff --git a/ci/README.md b/ci/README.md new file mode 100644 index 00000000..047d3115 --- /dev/null +++ b/ci/README.md @@ -0,0 +1,23 @@ +### CI quick-reference + +All CI is just bash and nix. + +* **Workflow**: + - `.github/workflows/ci.yml` – lists the jobs (`default`, `llvm`, …). +* **Scripts**: + - `ci/scripts/run.sh` – spins up the Nix shell then calls… + - `ci/scripts/ci.sh` – …to configure, build, and test. +* **Configuration**: + - `ci/configs/*.sh` – defines flags for each job. + - `shell.nix` – defines build environment (compilers, tools, libraries). +* **Build directories**: + - `build-*/` – separate build directories (like `build-default`, `build-llvm`) will be created for each job. + +To run jobs locally: + +```bash +CI_CONFIG=ci/configs/default.sh ci/scripts/run.sh +CI_CONFIG=ci/configs/llvm.sh ci/scripts/run.sh +``` + +By default CI jobs will reuse their build directories. `CI_CLEAN=1` can be specified to delete them before running instead. diff --git a/ci/configs/default.sh b/ci/configs/default.sh new file mode 100644 index 00000000..c7f21f1a --- /dev/null +++ b/ci/configs/default.sh @@ -0,0 +1,4 @@ +CI_DESC="CI job using default libraries and tools" +CI_DIR=build-default +export CXXFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-unused-parameter" +BUILD_ARGS=(-k) diff --git a/ci/configs/llvm.sh b/ci/configs/llvm.sh new file mode 100644 index 00000000..b9f90936 --- /dev/null +++ b/ci/configs/llvm.sh @@ -0,0 +1,10 @@ +CI_DESC="CI job using LLVM-based tools (clang, clang-tidy, iwyu) and testing Ninja" +CI_DIR=build-llvm +export CXX=clang++ +export CXXFLAGS="-Werror -Wall -Wextra -Wpedantic -Wthread-safety-analysis -Wno-unused-parameter" +CMAKE_ARGS=( + -G Ninja + -DMP_ENABLE_CLANG_TIDY=ON + -DMP_ENABLE_IWYU=ON +) +BUILD_ARGS=(-k 0) diff --git a/ci/scripts/ci.sh b/ci/scripts/ci.sh new file mode 100755 index 00000000..d636af2c --- /dev/null +++ b/ci/scripts/ci.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash +set -o errexit -o nounset -o pipefail -o xtrace + +[ "${CI_CONFIG+x}" ] && source "$CI_CONFIG" + +: "${CI_DIR:=build}" + +[ -n "${CI_CLEAN-}" ] && rm -rf "${CI_DIR}" + +cmake -B "$CI_DIR" "${CMAKE_ARGS[@]+"${CMAKE_ARGS[@]}"}" +cmake --build "$CI_DIR" -t all tests mpexamples -- "${BUILD_ARGS[@]+"${BUILD_ARGS[@]}"}" +ctest --test-dir "$CI_DIR" --output-on-failure diff --git a/ci/scripts/run.sh b/ci/scripts/run.sh new file mode 100755 index 00000000..746f333f --- /dev/null +++ b/ci/scripts/run.sh @@ -0,0 +1,4 @@ +#!/usr/bin/env bash +set -o errexit -o nounset -o pipefail -o xtrace + +nix-shell --pure --keep CI_CONFIG --keep CI_CLEAN --run ci/scripts/ci.sh shell.nix diff --git a/cmake/TargetCapnpSources.cmake b/cmake/TargetCapnpSources.cmake index cf7d20fe..347ef4a0 100644 --- a/cmake/TargetCapnpSources.cmake +++ b/cmake/TargetCapnpSources.cmake @@ -81,6 +81,8 @@ function(target_capnp_sources target include_prefix) DEPENDS ${capnp_file} VERBATIM ) + # Skip linting for capnp-generated files but keep it for mpgen-generated ones + set_source_files_properties(${capnp_file}.c++ PROPERTIES SKIP_LINTING TRUE) # Ignored before cmake 3.27 target_sources(${target} PRIVATE ${CMAKE_CURRENT_BINARY_DIR}/${capnp_file}.c++ ${CMAKE_CURRENT_BINARY_DIR}/${capnp_file}.proxy-client.c++ diff --git a/shell.nix b/shell.nix new file mode 100644 index 00000000..0bfd93f4 --- /dev/null +++ b/shell.nix @@ -0,0 +1,14 @@ +{ pkgs ? import {} }: + +pkgs.mkShell { + buildInputs = with pkgs; [ + capnproto + ]; + nativeBuildInputs = with pkgs; [ + cmake + include-what-you-use + llvmPackages_20.clang + llvmPackages_20.clang-tools + ninja + ]; +} From 060a739269565e28cd4cc81e97e356407c514966 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Fri, 20 Jun 2025 06:57:41 -0400 Subject: [PATCH 10/16] proxy-types: fix clang-tidy StackAddressEscape error This error should not be a real problem because code is taking an invalid reference to an empty object that has no state and could never be used. But taking the reference could technically be undefined behavior. Reported by fanquake https://github.com/bitcoin/bitcoin/pull/31802#issuecomment-2988011933 https://cirrus-ci.com/task/6552721135763456 https://api.cirrus-ci.com/v1/task/6552721135763456/logs/ci.log include/mp/proxy-types.h:134:5: error: Address of stack memory associated with temporary object of type '(lambda at include/mp/proxy-types.h:134:51)' is still referred to by a temporary object on the stack upon returning to the caller. This will be a dangling reference [clang-analyzer-core.StackAddressEscape,-warnings-as-errors] 134 | return ReadDestEmplace{TypeList(), [&](auto&&... args) -> decltype(auto) { | ^ build/test/mp/test/foo.capnp.proxy-server.c++:51:12: note: Calling 'serverInvoke, capnp::CallContext, mp::ServerField<1, mp::Accessor, mp::ServerRet, mp::ServerCall>>>' 51 | return serverInvoke(*this, call_context, MakeServerField<1, Accessor>(Make>(ServerCall()))); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:739:16: note: Calling 'ReplaceVoid<(lambda at include/mp/proxy-types.h:739:28), (lambda at include/mp/proxy-types.h:740:13)>' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 740 | [&]() { return kj::Promise(kj::mv(call_context)); }) | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:19: note: 'is_same_v' is true 700 | if constexpr (std::is_same_v) { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:5: note: Taking true branch 700 | if constexpr (std::is_same_v) { | ^ include/mp/proxy-types.h:701:9: note: Calling 'operator()' 701 | fn(); | ^~~~ include/mp/proxy-types.h:739:43: note: Calling 'ServerField::invoke' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:563:16: note: Calling 'PassField, mp::test::FooCustom, mp::ServerInvokeContext, capnp::CallContext>, const mp::ServerRet, mp::ServerCall> &, mp::TypeList<>>' 563 | return PassField(Priority<2>(), | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 564 | typename Split::First(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 565 | server_context, | ~~~~~~~~~~~~~~~ 566 | this->parent(), | ~~~~~~~~~~~~~~~ 567 | typename Split::Second(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 568 | std::forward(args)...); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:304:5: note: Calling 'MaybeReadField, mp::InvokeContext &, mp::StructField, const mp::test::messages::FooInterface::PassCustomParams::Reader>, mp::ReadDestEmplace>' 304 | MaybeReadField(std::integral_constant(), TypeList(), invoke_context, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 305 | Make(params), ReadDestEmplace(TypeList(), [&](auto&&... args) -> auto& { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 306 | param.emplace(std::forward(args)...); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 307 | return *param; | ~~~~~~~~~~~~~~ 308 | })); | ~~~ include/mp/proxy-types.h:276:5: note: Calling 'ReadField, const mp::test::messages::FooInterface::PassCustomParams::Reader>, mp::ReadDestEmplace>' 276 | ReadField(std::forward(args)...); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:175:12: note: Calling 'CustomReadField, const mp::test::messages::FooInterface::PassCustomParams::Reader>, mp::ReadDestEmplace>' 175 | return CustomReadField(TypeList...>(), Priority<2>(), std::forward(args)...); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ test/mp/test/foo-types.h:51:12: note: Calling 'ReadDestEmplace::update' 51 | return read_dest.update([&](FooCustom& value) { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 52 | value.v1 = ReadField(TypeList(), invoke_context, mp::Make(custom.getV1()), ReadDestTemp()); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 53 | value.v2 = custom.getV2(); | ~~~~~~~~~~~~~~~~~~~~~~~~~~ 54 | }); | ~~ include/mp/proxy-types.h:112:23: note: 'is_const_v' is false 112 | if constexpr (std::is_const_v>>) { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:112:9: note: Taking false branch 112 | if constexpr (std::is_const_v>>) { | ^ include/mp/proxy-types.h:122:13: note: Calling 'operator()' 122 | update_fn(temp); | ^~~~~~~~~~~~~~~ test/mp/test/foo-types.h:52:113: note: Calling 'ReadDestTemp>' 52 | value.v1 = ReadField(TypeList(), invoke_context, mp::Make(custom.getV1()), ReadDestTemp()); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:134:5: note: Address of stack memory associated with temporary object of type '(lambda at include/mp/proxy-types.h:134:51)' is still referred to by a temporary object on the stack upon returning to the caller. This will be a dangling reference 134 | return ReadDestEmplace{TypeList(), [&](auto&&... args) -> decltype(auto) { | ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 135 | return LocalType{std::forward(args)...}; | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 136 | }}; | ~ --- .clang-tidy | 1 + include/mp/proxy-types.h | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 544bd5ac..1e073fca 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -26,6 +26,7 @@ performance-*, readability-const-return-type, readability-redundant-declaration, readability-redundant-string-init, +clang-analyzer-core.StackAddressEscape, ' HeaderFilterRegex: '.' WarningsAsErrors: '*' diff --git a/include/mp/proxy-types.h b/include/mp/proxy-types.h index 77f90e6c..19c98063 100644 --- a/include/mp/proxy-types.h +++ b/include/mp/proxy-types.h @@ -92,7 +92,7 @@ struct StructField template struct ReadDestEmplace { - ReadDestEmplace(TypeList, EmplaceFn&& emplace_fn) : m_emplace_fn(emplace_fn) {} + ReadDestEmplace(TypeList, EmplaceFn emplace_fn) : m_emplace_fn(std::move(emplace_fn)) {} //! Simple case. If ReadField impementation calls this construct() method //! with constructor arguments, just pass them on to the emplace function. @@ -123,7 +123,7 @@ struct ReadDestEmplace return temp; } } - EmplaceFn& m_emplace_fn; + EmplaceFn m_emplace_fn; }; //! Helper function to create a ReadDestEmplace object that constructs a @@ -131,7 +131,7 @@ struct ReadDestEmplace template auto ReadDestTemp() { - return ReadDestEmplace{TypeList(), [&](auto&&... args) -> decltype(auto) { + return ReadDestEmplace{TypeList(), [](auto&&... args) -> decltype(auto) { return LocalType{std::forward(args)...}; }}; } From 4896e7fe51ba677d8bf041395dc7adb88b898359 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Fri, 20 Jun 2025 06:57:41 -0400 Subject: [PATCH 11/16] proxy-types: fix clang-tidy EnumCastOutOfRange error Reported by fanquake https://github.com/bitcoin/bitcoin/pull/31802#issuecomment-2988011933 https://cirrus-ci.com/task/6552721135763456 https://api.cirrus-ci.com/v1/task/6552721135763456/logs/ci.log include/mp/type-number.h:55:32: error: The value '0' provided to the cast expression is not in the valid range of values for 'FooEnum' [clang-analyzer-optin.core.EnumCastOutOfRange,-warnings-as-errors] 55 | return read_dest.construct(static_cast(input.get())); | ^ test/mp/test/foo.h:26:12: note: enum declared here 26 | enum class FooEnum : uint8_t { ONE = 1, TWO = 2, }; | ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ build/test/mp/test/foo.capnp.proxy-server.c++:63:12: note: Calling 'serverInvoke, capnp::CallContext, mp::ServerField<1, mp::Accessor, mp::ServerRet, mp::ServerCall>>>' 63 | return serverInvoke(*this, call_context, MakeServerField<1, Accessor>(Make>(ServerCall()))); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:739:16: note: Calling 'ReplaceVoid<(lambda at include/mp/proxy-types.h:739:28), (lambda at include/mp/proxy-types.h:740:13)>' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 740 | [&]() { return kj::Promise(kj::mv(call_context)); }) | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:19: note: 'is_same_v' is true 700 | if constexpr (std::is_same_v) { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:5: note: Taking true branch 700 | if constexpr (std::is_same_v) { | ^ include/mp/proxy-types.h:701:9: note: Calling 'operator()' 701 | fn(); | ^~~~ include/mp/proxy-types.h:739:43: note: Calling 'ServerField::invoke' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:563:16: note: Calling 'PassField, mp::test::FooEnum, mp::ServerInvokeContext, capnp::CallContext>, const mp::ServerRet, mp::ServerCall> &, mp::TypeList<>>' 563 | return PassField(Priority<2>(), | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 564 | typename Split::First(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 565 | server_context, | ~~~~~~~~~~~~~~~ 566 | this->parent(), | ~~~~~~~~~~~~~~~ 567 | typename Split::Second(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 568 | std::forward(args)...); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:304:5: note: Calling 'MaybeReadField, mp::InvokeContext &, mp::StructField, const mp::test::messages::FooInterface::PassEnumParams::Reader>, mp::ReadDestEmplace>' 304 | MaybeReadField(std::integral_constant(), TypeList(), invoke_context, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 305 | Make(params), ReadDestEmplace(TypeList(), [&](auto&&... args) -> auto& { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 306 | param.emplace(std::forward(args)...); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 307 | return *param; | ~~~~~~~~~~~~~~ 308 | })); | ~~~ include/mp/proxy-types.h:276:5: note: Calling 'ReadField, const mp::test::messages::FooInterface::PassEnumParams::Reader>, mp::ReadDestEmplace>' 276 | ReadField(std::forward(args)...); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:175:12: note: Calling 'CustomReadField, const mp::test::messages::FooInterface::PassEnumParams::Reader>, mp::ReadDestEmplace>' 175 | return CustomReadField(TypeList...>(), Priority<2>(), std::forward(args)...); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/type-number.h:55:55: note: Calling 'StructField::get' 55 | return read_dest.construct(static_cast(input.get())); | ^~~~~~~~~~~ include/mp/proxy-types.h:40:41: note: Calling 'Arg::get' 40 | decltype(auto) get() const { return Accessor::get(this->m_struct); } | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ build/test/mp/test/foo.capnp.proxy.h:198:82: note: Calling 'Reader::getArg' 198 | template static auto get(S&& s) -> decltype(s.getArg()) { return s.getArg(); } | ^~~~~~~~~~ build/test/mp/test/foo.capnp.h:7042:10: note: Calling 'StructReader::getDataField' 7042 | return _reader.getDataField< ::int32_t>( | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 7043 | ::capnp::bounded<0>() * ::capnp::ELEMENTS); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/capnp/layout.h:1099:7: note: Assuming the condition is false 1099 | if ((offset + ONE * ELEMENTS) * capnp::bitsPerElement() <= dataSize) { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/capnp/layout.h:1099:3: note: Taking false branch 1099 | if ((offset + ONE * ELEMENTS) * capnp::bitsPerElement() <= dataSize) { | ^ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/capnp/layout.h:1102:5: note: Returning zero 1102 | return static_cast(0); | ^~~~~~~~~~~~~~~~~~~~~~~~ build/test/mp/test/foo.capnp.h:7042:10: note: Returning from 'StructReader::getDataField' 7042 | return _reader.getDataField< ::int32_t>( | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 7043 | ::capnp::bounded<0>() * ::capnp::ELEMENTS); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ build/test/mp/test/foo.capnp.h:7042:3: note: Returning zero 7042 | return _reader.getDataField< ::int32_t>( | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 7043 | ::capnp::bounded<0>() * ::capnp::ELEMENTS); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ build/test/mp/test/foo.capnp.proxy.h:198:82: note: Returning from 'Reader::getArg' 198 | template static auto get(S&& s) -> decltype(s.getArg()) { return s.getArg(); } | ^~~~~~~~~~ build/test/mp/test/foo.capnp.proxy.h:198:75: note: Returning zero 198 | template static auto get(S&& s) -> decltype(s.getArg()) { return s.getArg(); } | ^~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:40:41: note: Returning from 'Arg::get' 40 | decltype(auto) get() const { return Accessor::get(this->m_struct); } | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:40:34: note: Returning zero 40 | decltype(auto) get() const { return Accessor::get(this->m_struct); } | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/type-number.h:55:55: note: Returning from 'StructField::get' 55 | return read_dest.construct(static_cast(input.get())); | ^~~~~~~~~~~ include/mp/type-number.h:55:32: note: The value '0' provided to the cast expression is not in the valid range of values for 'FooEnum' 55 | return read_dest.construct(static_cast(input.get())); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ --- .clang-tidy | 1 + include/mp/type-number.h | 6 ++++++ 2 files changed, 7 insertions(+) diff --git a/.clang-tidy b/.clang-tidy index 1e073fca..adfc3a2c 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -27,6 +27,7 @@ readability-const-return-type, readability-redundant-declaration, readability-redundant-string-init, clang-analyzer-core.StackAddressEscape, +clang-analyzer-optin.core.EnumCastOutOfRange, ' HeaderFilterRegex: '.' WarningsAsErrors: '*' diff --git a/include/mp/type-number.h b/include/mp/type-number.h index 75301df8..5c997f54 100644 --- a/include/mp/type-number.h +++ b/include/mp/type-number.h @@ -52,6 +52,12 @@ decltype(auto) CustomReadField(TypeList, ReadDest&& read_dest, typename std::enable_if::value>::type* enable = nullptr) { + // Disable clang-tidy out-of-range enum value check which triggers when + // using an enum type that does not have a 0 value. The check correctly + // triggers when it detects that Cap'n Proto returns 0 when reading an + // integer field that is unset. But the warning is spurious because the + // corresponding BuildField call should never leave the field unset. + // NOLINTNEXTLINE(clang-analyzer-optin.core.EnumCastOutOfRange) return read_dest.construct(static_cast(input.get())); } From 76313450c2c4aa70cf5c443dc2b33c621a565fb5 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Fri, 20 Jun 2025 08:25:43 -0400 Subject: [PATCH 12/16] type-context: disable clang-tidy UndefinedBinaryOperatorResult error Reported by fanquake https://github.com/bitcoin/bitcoin/pull/31802#issuecomment-2988011933 https://cirrus-ci.com/task/6552721135763456 https://api.cirrus-ci.com/v1/task/6552721135763456/logs/ci.log Error is spurious and comes from kj/async-inl.h and should be suppressed in the next version of capnproto https://github.com/capnproto/capnproto/pull/2334 The error is a clang-analyzer false positive that comes from ABI-specific code in Cap'n Proto that gets the starting function address (that can be passed to addr2line) from a lambda or function object. This code calls a helper to get the starting function address from a pointer-to-member-function, which in this case is the the operator() member function. That code handles pointers to virtual member functions, so it checks if the pointer is virtual by testing its low-order bit, and if set, assumes the first bytes of the object are a vtable pointer, and does pointer arithmetic with the vtable address. Clang-tidy complains about this because it does not know the vtable address is valid, assuming incorrectly it is a "garbage value". This change turns off the UndefinedBinaryOperatorResult altogether instead of suppressing this one instance because clang-tidy incorrectly considers this error to come from "main file" of the translation unit (see https://clang.llvm.org/extra/clang-tidy/, https://stackoverflow.com/a/47611238, https://reviews.llvm.org/D26418). So it is not suppressed even though the header is included via -isystem and clang-tidy --dump-config shows "SystemHeaders: false". It is also not suppressed when exclude patterns are added to the .clang-tidy configuration like: HeaderFilterRegex: '.*' ExcludeHeaderFilterRegex: '.*/include/kj/async-inl\.h$' This has no effect because ExcludeHeaderFilterRegex does not override the isInMainFile condition (https://github.com/llvm/llvm-project/pull/91400/files). Adding NOLINT to the getLocalServer() line in types-context.h at the boundary between libmultiprocess and Cap'n Proto code also does not suppress the error. It does suppress clang-tidy "note:" lines below the NOLINT point in the call stack, making the error messages shorter, but the only way of suppressing the error completely seems to be either adding NOLINT inside the Cap'n Proto header, which requires a patch, or adding it to all top-level callers of the getLocalServer() function in .cpp files, which seems impractical and overbroad, and I didn't attempt. Complete error output is: /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: error: The left operand of '+' is a garbage value [clang-analyzer-core.UndefinedBinaryOperatorResult,-warnings-as-errors] 609 | return *(void**)(*(char**)obj + voff); | ^ build/test/mp/test/foo.capnp.proxy-server.c++:93:12: note: Calling 'serverInvoke, capnp::CallContext, mp::ServerField<0, mp::Accessor, mp::ServerRet, mp::ServerCall>>>' 93 | return serverInvoke(*this, call_context, MakeServerField<0, Accessor>(Make>(ServerCall()))); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:739:16: note: Calling 'ReplaceVoid<(lambda at include/mp/proxy-types.h:739:28), (lambda at include/mp/proxy-types.h:740:13)>' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 740 | [&]() { return kj::Promise(kj::mv(call_context)); }) | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:19: note: 'is_same_v' is false 700 | if constexpr (std::is_same_v) { | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:700:5: note: Taking false branch 700 | if constexpr (std::is_same_v) { | ^ include/mp/proxy-types.h:704:16: note: Calling 'operator()' 704 | return fn(); | ^~~~ include/mp/proxy-types.h:739:43: note: Calling 'ServerField::invoke' 739 | return ReplaceVoid([&]() { return fn.invoke(server_context, ArgList()); }, | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/proxy-types.h:563:16: note: Calling 'PassField, mp::ServerInvokeContext, capnp::CallContext>, mp::ServerRet, mp::ServerCall>, mp::TypeList<>>' 563 | return PassField(Priority<2>(), | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 564 | typename Split::First(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 565 | server_context, | ~~~~~~~~~~~~~~~ 566 | this->parent(), | ~~~~~~~~~~~~~~~ 567 | typename Split::Second(), | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 568 | std::forward(args)...); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ include/mp/type-context.h:151:12: note: Calling 'CapabilityServerSet::getLocalServer' 151 | return server.m_context.connection->m_threads.getLocalServer(thread_client) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/capnp/capability.h:1274:10: note: Calling 'Promise::then' 1274 | return getLocalServerInternal(client) | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1275 | .then([](void* server) -> kj::Maybe { | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1276 | if (server == nullptr) { | ~~~~~~~~~~~~~~~~~~~~~~~~ 1277 | return nullptr; | ~~~~~~~~~~~~~~~ 1278 | } else { | ~~~~~~~~ 1279 | return *reinterpret_cast(server); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1280 | } | ~ 1281 | }); | ~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:1295:32: note: Calling 'GetFunctorStartAddress::apply' 1295 | void* continuationTracePtr = _::GetFunctorStartAddress<_::FixVoid&&>::apply(func); | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:677:12: note: Calling 'PtmfHelper::apply' 677 | return PtmfHelper::from, ParamTypes...>( | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 678 | &Decay::operator()).apply(&func); | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:9: note: Assuming the condition is true 606 | if (voff & 1) { | ^~~~~~~~ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:606:5: note: Taking true branch 606 | if (voff & 1) { | ^ /nix/store/46kiq9naswgbqfc14kc9nxcbgd0rv0m2-capnproto-1.1.0/include/kj/async-inl.h:609:37: note: The left operand of '+' is a garbage value 609 | return *(void**)(*(char**)obj + voff); | ~~~~~~~~~~~~ ^ --- .clang-tidy | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index adfc3a2c..a8155ef6 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -26,8 +26,9 @@ performance-*, readability-const-return-type, readability-redundant-declaration, readability-redundant-string-init, -clang-analyzer-core.StackAddressEscape, -clang-analyzer-optin.core.EnumCastOutOfRange, +clang-analyzer-core.*, +-clang-analyzer-core.UndefinedBinaryOperatorResult, +clang-analyzer-optin.core.*, ' HeaderFilterRegex: '.' WarningsAsErrors: '*' From cbb1e43fdc6e229affacfa79c8eed037e962b115 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Mon, 23 Jun 2025 13:43:52 -0400 Subject: [PATCH 13/16] ci: test libc++ instead of libstdc++ in one job Also add IWYU to the default job, since using libc++ instead of libstdc++ causes IWYU to give slightly different recommendations. Better to comply with both sets of recommendations so IWYU is usable on more platforms. --- CMakeLists.txt | 3 +++ ci/configs/default.sh | 3 ++- ci/configs/llvm.sh | 3 ++- ci/scripts/run.sh | 4 +++- shell.nix | 22 +++++++++++++++++----- 5 files changed, 27 insertions(+), 8 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index fd23904e..d29eb490 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -40,6 +40,9 @@ if(MP_ENABLE_IWYU) message(FATAL_ERROR "MP_ENABLE_IWYU is ON but include-what-you-use was not found.") endif() set(CMAKE_CXX_INCLUDE_WHAT_YOU_USE "${IWYU_EXECUTABLE};-Xiwyu;--error") + if(DEFINED ENV{IWYU_MAPPING_FILE}) + list(APPEND CMAKE_CXX_INCLUDE_WHAT_YOU_USE "-Xiwyu" "--mapping_file=$ENV{IWYU_MAPPING_FILE}") + endif() endif() include("cmake/compat_config.cmake") diff --git a/ci/configs/default.sh b/ci/configs/default.sh index c7f21f1a..56231228 100644 --- a/ci/configs/default.sh +++ b/ci/configs/default.sh @@ -1,4 +1,5 @@ -CI_DESC="CI job using default libraries and tools" +CI_DESC="CI job using default libraries and tools, and running IWYU" CI_DIR=build-default export CXXFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-unused-parameter" +CMAKE_ARGS=(-DMP_ENABLE_IWYU=ON) BUILD_ARGS=(-k) diff --git a/ci/configs/llvm.sh b/ci/configs/llvm.sh index b9f90936..afa957ed 100644 --- a/ci/configs/llvm.sh +++ b/ci/configs/llvm.sh @@ -1,5 +1,6 @@ -CI_DESC="CI job using LLVM-based tools (clang, clang-tidy, iwyu) and testing Ninja" +CI_DESC="CI job using LLVM-based libraries and tools (clang, libc++, clang-tidy, iwyu) and testing Ninja" CI_DIR=build-llvm +NIX_ARGS=(--arg enableLibcxx true) export CXX=clang++ export CXXFLAGS="-Werror -Wall -Wextra -Wpedantic -Wthread-safety-analysis -Wno-unused-parameter" CMAKE_ARGS=( diff --git a/ci/scripts/run.sh b/ci/scripts/run.sh index 746f333f..5f5e30d1 100755 --- a/ci/scripts/run.sh +++ b/ci/scripts/run.sh @@ -1,4 +1,6 @@ #!/usr/bin/env bash set -o errexit -o nounset -o pipefail -o xtrace -nix-shell --pure --keep CI_CONFIG --keep CI_CLEAN --run ci/scripts/ci.sh shell.nix +[ "${CI_CONFIG+x}" ] && source "$CI_CONFIG" + +nix-shell --pure --keep CI_CONFIG --keep CI_CLEAN "${NIX_ARGS[@]+"${NIX_ARGS[@]}"}" --run ci/scripts/ci.sh shell.nix diff --git a/shell.nix b/shell.nix index 0bfd93f4..0775a95c 100644 --- a/shell.nix +++ b/shell.nix @@ -1,14 +1,26 @@ -{ pkgs ? import {} }: +{ pkgs ? import {} +, enableLibcxx ? false # Whether to use libc++ toolchain and libraries instead of libstdc++ +}: -pkgs.mkShell { - buildInputs = with pkgs; [ +let + lib = pkgs.lib; + llvm = pkgs.llvmPackages_20; + capnproto = pkgs.capnproto.override (lib.optionalAttrs enableLibcxx { clangStdenv = llvm.libcxxStdenv; }); + clang = if enableLibcxx then llvm.libcxxClang else llvm.clang; + clang-tools = llvm.clang-tools.override { inherit enableLibcxx; }; +in pkgs.mkShell { + buildInputs = [ capnproto + llvm.libcxx ]; nativeBuildInputs = with pkgs; [ + clang + clang-tools cmake include-what-you-use - llvmPackages_20.clang - llvmPackages_20.clang-tools ninja ]; + + # Tell IWYU where its libc++ mapping lives + IWYU_MAPPING_FILE = if enableLibcxx then "${llvm.libcxx.dev}/include/c++/v1/libcxx.imp" else null; } From 1a598d5905f7fabfcda5a41d10792c63657ab774 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Mon, 23 Jun 2025 14:02:19 -0400 Subject: [PATCH 14/16] clang-tidy: drop 'bitcoin-*' check Bitcoin tidy plugin isn't currently enabled here so they don't do anything. Suggested by maflcko <6399679+maflcko@users.noreply.github.com> https://github.com/bitcoin-core/libmultiprocess/pull/184#discussion_r2161151944 --- .clang-tidy | 1 - 1 file changed, 1 deletion(-) diff --git a/.clang-tidy b/.clang-tidy index a8155ef6..9a2afcc5 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,6 +1,5 @@ Checks: ' -*, -bitcoin-*, bugprone-argument-comment, bugprone-move-forwarding-reference, bugprone-string-constructor, From 15bf349000ebefe9b7c6004688e67b7e54b924a7 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Tue, 24 Jun 2025 08:57:52 -0400 Subject: [PATCH 15/16] doc: fix typo found by DrahtBot Suggested by maflcko <6399679+maflcko@users.noreply.github.com> https://github.com/bitcoin-core/libmultiprocess/pull/184#discussion_r2163106467 --- include/mp/proxy-types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/mp/proxy-types.h b/include/mp/proxy-types.h index 19c98063..597bdfda 100644 --- a/include/mp/proxy-types.h +++ b/include/mp/proxy-types.h @@ -94,7 +94,7 @@ struct ReadDestEmplace { ReadDestEmplace(TypeList, EmplaceFn emplace_fn) : m_emplace_fn(std::move(emplace_fn)) {} - //! Simple case. If ReadField impementation calls this construct() method + //! Simple case. If ReadField implementation calls this construct() method //! with constructor arguments, just pass them on to the emplace function. template decltype(auto) construct(Args&&... args) From 757e13a7554644d7ee1e3e85a9c56beeaffd5249 Mon Sep 17 00:00:00 2001 From: Ryan Ofsky Date: Wed, 25 Jun 2025 17:14:56 -0400 Subject: [PATCH 16/16] ci: add gnu32 cross-compiled 32-bit build --- .github/workflows/ci.yml | 2 +- ci/README.md | 1 + ci/configs/gnu32.sh | 9 +++++++++ shell.nix | 14 ++++++++------ 4 files changed, 19 insertions(+), 7 deletions(-) create mode 100644 ci/configs/gnu32.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 720a9abf..a92e59a2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -11,7 +11,7 @@ jobs: strategy: fail-fast: false matrix: - config: [default, llvm] + config: [default, llvm, gnu32] name: build • ${{ matrix.config }} diff --git a/ci/README.md b/ci/README.md index 047d3115..95599128 100644 --- a/ci/README.md +++ b/ci/README.md @@ -18,6 +18,7 @@ To run jobs locally: ```bash CI_CONFIG=ci/configs/default.sh ci/scripts/run.sh CI_CONFIG=ci/configs/llvm.sh ci/scripts/run.sh +CI_CONFIG=ci/configs/gnu32.sh ci/scripts/run.sh ``` By default CI jobs will reuse their build directories. `CI_CLEAN=1` can be specified to delete them before running instead. diff --git a/ci/configs/gnu32.sh b/ci/configs/gnu32.sh new file mode 100644 index 00000000..961821ce --- /dev/null +++ b/ci/configs/gnu32.sh @@ -0,0 +1,9 @@ +CI_DESC="CI job cross-compiling to 32-bit" +CI_DIR=build-gnu32 +NIX_ARGS=( + --arg minimal true + --arg crossPkgs 'import { crossSystem = { config = "i686-unknown-linux-gnu"; }; }' +) +export CXXFLAGS="-Werror -Wall -Wextra -Wpedantic -Wno-unused-parameter" +CMAKE_ARGS=(-G Ninja) +BUILD_ARGS=(-k 0) diff --git a/shell.nix b/shell.nix index 0775a95c..eacfdc2a 100644 --- a/shell.nix +++ b/shell.nix @@ -1,24 +1,26 @@ { pkgs ? import {} +, crossPkgs ? import {} , enableLibcxx ? false # Whether to use libc++ toolchain and libraries instead of libstdc++ +, minimal ? false # Whether to create minimal shell without extra tools (faster when cross compiling) }: let lib = pkgs.lib; - llvm = pkgs.llvmPackages_20; - capnproto = pkgs.capnproto.override (lib.optionalAttrs enableLibcxx { clangStdenv = llvm.libcxxStdenv; }); + llvm = crossPkgs.llvmPackages_20; + capnproto = crossPkgs.capnproto.override (lib.optionalAttrs enableLibcxx { clangStdenv = llvm.libcxxStdenv; }); clang = if enableLibcxx then llvm.libcxxClang else llvm.clang; clang-tools = llvm.clang-tools.override { inherit enableLibcxx; }; -in pkgs.mkShell { +in crossPkgs.mkShell { buildInputs = [ capnproto - llvm.libcxx ]; nativeBuildInputs = with pkgs; [ - clang - clang-tools cmake include-what-you-use ninja + ] ++ lib.optionals (!minimal) [ + clang + clang-tools ]; # Tell IWYU where its libc++ mapping lives