From 29a74e754713e0bc19f4accba20f562907c95806 Mon Sep 17 00:00:00 2001 From: Corentin Le Molgat Date: Mon, 22 Sep 2025 17:41:54 +0200 Subject: [PATCH] base: backport rework from main --- cmake/dependencies/CMakeLists.txt | 5 +- cmake/glop.cmake | 4 - ortools/base/BUILD.bazel | 43 +-- ortools/base/CMakeLists.txt | 3 +- ortools/base/array.h | 27 +- ortools/base/array_internal.h | 40 --- ortools/base/basictypes.h | 24 -- ortools/base/encodingutils.h | 37 --- ortools/base/gmock.h | 29 +- ortools/base/gzipfile.h | 6 +- ortools/base/logging.h | 7 +- ortools/base/macros.h | 27 -- ortools/base/status-matchers.h | 294 ------------------ ortools/base/typeid.h | 25 -- ortools/constraint_solver/routing_search.cc | 4 +- ortools/graph/BUILD.bazel | 4 +- ortools/graph/christofides.h | 74 +++-- ortools/graph/christofides_test.cc | 107 ++++--- ortools/graph/graph.h | 6 + ortools/graph/linear_assignment.h | 2 +- ortools/graph/one_tree_lower_bound.h | 2 +- ortools/gurobi/isv_public/BUILD.bazel | 2 + ortools/linear_solver/BUILD.bazel | 1 + ortools/linear_solver/model_validator.cc | 4 +- ortools/lp_data/BUILD.bazel | 161 ++++++---- ortools/port/BUILD.bazel | 6 - ortools/port/utf8.h | 39 --- ortools/service/v1/mathopt/result.proto | 1 + ortools/service/v1/mathopt/solution.proto | 1 + .../service/v1/mathopt/solver_resources.proto | 1 + .../v1/mathopt/sparse_containers.proto | 1 + ortools/third_party_solvers/BUILD.bazel | 19 +- ortools/util/BUILD.bazel | 3 - ortools/util/proto_tools.h | 34 +- ortools/util/stats.cc | 30 +- ortools/util/stats.h | 2 - patches/abseil-cpp-20250814.0.patch | 36 ++- 37 files changed, 351 insertions(+), 760 deletions(-) delete mode 100644 ortools/base/array_internal.h delete mode 100644 ortools/base/basictypes.h delete mode 100644 ortools/base/encodingutils.h delete mode 100644 ortools/base/macros.h delete mode 100644 ortools/base/status-matchers.h delete mode 100644 ortools/base/typeid.h delete mode 100644 ortools/port/utf8.h diff --git a/cmake/dependencies/CMakeLists.txt b/cmake/dependencies/CMakeLists.txt index 43739ba387..0e474017e4 100644 --- a/cmake/dependencies/CMakeLists.txt +++ b/cmake/dependencies/CMakeLists.txt @@ -110,6 +110,9 @@ if(BUILD_absl) set(ABSL_USE_SYSTEM_INCLUDES ON) # We want Abseil to declare what C++ standard it was compiled with. set(ABSL_PROPAGATE_CXX_STD ON) + set(ABSL_BUILD_TEST_HELPERS ON) + set(ABSL_USE_EXTERNAL_GOOGLETEST ON) + set(ABSL_FIND_GOOGLETEST OFF) # We want Abseil to keep the INSTALL rules enabled, even though it is a # subproject. Otherwise the install rules in this project break. set(ABSL_ENABLE_INSTALL ON) @@ -527,8 +530,8 @@ if(BUILD_googletest) "${CMAKE_CURRENT_LIST_DIR}/../../patches/googletest-v1.17.0.patch" ) set(gtest_force_shared_crt ON CACHE BOOL "" FORCE) - set(INSTALL_GTEST OFF) set(GTEST_HAS_ABSL ON) + set(INSTALL_GTEST ON) FetchContent_MakeAvailable(googletest) list(POP_BACK CMAKE_MESSAGE_INDENT) message(CHECK_PASS "fetched") diff --git a/cmake/glop.cmake b/cmake/glop.cmake index 03bb2a088d..610137ec0f 100644 --- a/cmake/glop.cmake +++ b/cmake/glop.cmake @@ -81,7 +81,6 @@ add_library(glop) target_sources(glop PRIVATE ortools/base/accurate_sum.h ortools/base/base_export.h - ortools/base/basictypes.h ortools/base/commandlineflags.h ortools/base/file.cc ortools/base/file.h @@ -89,7 +88,6 @@ target_sources(glop PRIVATE ortools/base/hash.h ortools/base/int_type.h ortools/base/logging.h - ortools/base/macros.h ortools/base/sysinfo.cc ortools/base/sysinfo.h ortools/base/timer.h @@ -302,14 +300,12 @@ install(DIRECTORY ortools/glop install(FILES ortools/base/accurate_sum.h ortools/base/base_export.h - ortools/base/basictypes.h ortools/base/commandlineflags.h ortools/base/file.h ortools/base/gzipstring.h ortools/base/hash.h ortools/base/int_type.h ortools/base/logging.h - ortools/base/macros.h ortools/base/recordio.h ortools/base/strong_int.h ortools/base/strong_vector.h diff --git a/ortools/base/BUILD.bazel b/ortools/base/BUILD.bazel index a6d2784f9f..dab2753916 100644 --- a/ortools/base/BUILD.bazel +++ b/ortools/base/BUILD.bazel @@ -38,7 +38,6 @@ cc_library( cc_library( name = "array", - srcs = ["array_internal.h"], hdrs = ["array.h"], deps = ["@abseil-cpp//absl/utility"], ) @@ -88,11 +87,6 @@ cc_library( hdrs = ["base_export.h"], ) -cc_library( - name = "basictypes", - hdrs = ["basictypes.h"], -) - cc_library( name = "bitmap", srcs = ["bitmap.cc"], @@ -172,12 +166,6 @@ cc_test( ], ) -cc_library( - name = "encodingutils", - hdrs = ["encodingutils.h"], - deps = [":base"], -) - cc_library( name = "flags", hdrs = ["flags.h"], @@ -214,7 +202,7 @@ cc_library( hdrs = ["gmock.h"], deps = [ ":protocol-buffer-matchers", - ":status-matchers", + "@abseil-cpp//absl/status:status_matchers", "@googletest//:gtest", ], ) @@ -234,7 +222,6 @@ cc_library( hdrs = ["gzipfile.h"], deps = [ ":base", - ":basictypes", ":file", ":path", "@abseil-cpp//absl/strings", @@ -298,7 +285,7 @@ cc_library( srcs = ["logging.cc"], hdrs = ["logging.h"], deps = [ - ":macros", + ":base_export", "@abseil-cpp//absl/base:log_severity", "@abseil-cpp//absl/flags:flag", "@abseil-cpp//absl/flags:usage", @@ -314,14 +301,6 @@ cc_library( ], ) -cc_library( - name = "macros", - hdrs = ["macros.h"], - deps = [ - ":base_export", - ], -) - cc_library( name = "map_util", hdrs = ["map_util.h"], @@ -470,18 +449,6 @@ cc_library( ], ) -cc_library( - name = "status-matchers", - hdrs = ["status-matchers.h"], - deps = [ - ":base", - "@abseil-cpp//absl/status", - "@abseil-cpp//absl/status:statusor", - "@abseil-cpp//absl/strings", - "@googletest//:gtest", - ], -) - cc_library( name = "status_macros", hdrs = ["status_macros.h"], @@ -613,11 +580,6 @@ cc_library( hdrs = ["top_n.h"], ) -cc_library( - name = "typeid", - hdrs = ["typeid.h"], -) - cc_library( name = "types", hdrs = ["types.h"], @@ -628,7 +590,6 @@ cc_library( srcs = ["zipfile.cc"], hdrs = ["zipfile.h"], deps = [ - ":basictypes", ":file", ":path", ":stl_util", diff --git a/ortools/base/CMakeLists.txt b/ortools/base/CMakeLists.txt index 3fb969f8b3..c58c6abfff 100644 --- a/ortools/base/CMakeLists.txt +++ b/ortools/base/CMakeLists.txt @@ -14,7 +14,6 @@ file(GLOB _SRCS "*.h" "*.cc") list(FILTER _SRCS EXCLUDE REGEX "/.*_test.cc") list(FILTER _SRCS EXCLUDE REGEX "/gmock\.h") -list(FILTER _SRCS EXCLUDE REGEX "/status-matchers\.h") list(FILTER _SRCS EXCLUDE REGEX "/protocol-buffer-matchers\..*") set(NAME ${PROJECT_NAME}_base) @@ -54,12 +53,12 @@ ortools_cxx_library( "gmock.h" "protocol-buffer-matchers.cc" "protocol-buffer-matchers.h" - "status-matchers.h" TYPE STATIC LINK_LIBRARIES absl::log absl::strings + absl::status_matchers GTest::gtest GTest::gmock protobuf::libprotobuf diff --git a/ortools/base/array.h b/ortools/base/array.h index 0a118f67f0..b073da0e1a 100644 --- a/ortools/base/array.h +++ b/ortools/base/array.h @@ -21,20 +21,25 @@ #include #include "absl/utility/utility.h" -#include "ortools/base/array_internal.h" namespace gtl { -/// A utility function to build `std::array` objects from built-in arrays -/// without specifying their size. -/// -/// Example: -/// @code{.cpp} -/// auto b = gtl::to_array>({ -/// {1, 2}, -/// {3, 4}, -/// }); -/// @endcode +namespace internal_array { + +template +constexpr std::array, N> to_array_internal( + T (&ts)[N], absl::index_sequence) { + return {{ts[Idx]...}}; +} + +template +constexpr std::array, N> to_array_internal( + T (&&ts)[N], absl::index_sequence) { + return {{std::move(ts[Idx])...}}; +} + +} // namespace internal_array + template constexpr std::array, N> to_array(T (&ts)[N]) { return internal_array::to_array_internal(ts, absl::make_index_sequence{}); diff --git a/ortools/base/array_internal.h b/ortools/base/array_internal.h deleted file mode 100644 index 2fd3ed4b81..0000000000 --- a/ortools/base/array_internal.h +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright 2010-2025 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_ARRAY_INTERNAL_H_ -#define OR_TOOLS_BASE_ARRAY_INTERNAL_H_ - -#include -#include -#include -#include - -#include "absl/utility/utility.h" - -namespace gtl::internal_array { - -template -constexpr std::array, N> to_array_internal( - T (&ts)[N], absl::index_sequence) { - return {{ts[Idx]...}}; -} - -template -constexpr std::array, N> to_array_internal( - T (&&ts)[N], absl::index_sequence) { - return {{std::move(ts[Idx])...}}; -} - -} // namespace gtl::internal_array - -#endif // OR_TOOLS_BASE_ARRAY_INTERNAL_H_ diff --git a/ortools/base/basictypes.h b/ortools/base/basictypes.h deleted file mode 100644 index e469e3bb4f..0000000000 --- a/ortools/base/basictypes.h +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2010-2025 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// Basic integer type definitions for various platforms -// -#ifndef OR_TOOLS_BASE_BASICTYPES_H_ -#define OR_TOOLS_BASE_BASICTYPES_H_ - -// Argument type used in interfaces that can optionally take ownership -// of a passed in argument. If TAKE_OWNERSHIP is passed, the called -// object takes ownership of the argument. Otherwise it does not. -enum Ownership { DO_NOT_TAKE_OWNERSHIP, TAKE_OWNERSHIP }; - -#endif // OR_TOOLS_BASE_BASICTYPES_H_ diff --git a/ortools/base/encodingutils.h b/ortools/base/encodingutils.h deleted file mode 100644 index ecded04a47..0000000000 --- a/ortools/base/encodingutils.h +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2010-2025 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_ENCODINGUTILS_H_ -#define OR_TOOLS_BASE_ENCODINGUTILS_H_ - -#include - -namespace EncodingUtils { - -// Returns the number of characters of a UTF8-encoded string. -inline int UTF8StrLen(const std::string& utf8_str) { - if (utf8_str.empty()) return 0; - const char* c = utf8_str.c_str(); - int count = 0; - while (*c != '\0') { - ++count; - // See http://en.wikipedia.org/wiki/UTF-8#Description . - const unsigned char x = *c; - c += x < 0xC0 ? 1 : x < 0xE0 ? 2 : x < 0xF0 ? 3 : 4; - } - return count; -} - -} // namespace EncodingUtils - -#endif // OR_TOOLS_BASE_ENCODINGUTILS_H_ diff --git a/ortools/base/gmock.h b/ortools/base/gmock.h index b941d1b83f..0720afc6df 100644 --- a/ortools/base/gmock.h +++ b/ortools/base/gmock.h @@ -14,8 +14,33 @@ #ifndef OR_TOOLS_BASE_GMOCK_H_ #define OR_TOOLS_BASE_GMOCK_H_ -#include "gmock/gmock.h" +#include "absl/status/status_matchers.h" +#include "ortools/base/gmock.h" #include "ortools/base/protocol-buffer-matchers.h" // IWYU pragma: export -#include "ortools/base/status-matchers.h" // IWYU pragma: export + +namespace testing::status { +using ::absl_testing::IsOk; +using ::absl_testing::IsOkAndHolds; +using ::absl_testing::StatusIs; +} // namespace testing::status + +// Macros for testing the results of functions that return absl::Status or +// absl::StatusOr (for any type T). +#define EXPECT_OK(expression) EXPECT_THAT(expression, ::testing::status::IsOk()) +#define ASSERT_OK(expression) ASSERT_THAT(expression, ::testing::status::IsOk()) + +#define STATUS_MATCHERS_IMPL_CONCAT_INNER_(x, y) x##y +#define STATUS_MATCHERS_IMPL_CONCAT_(x, y) \ + STATUS_MATCHERS_IMPL_CONCAT_INNER_(x, y) + +#undef ASSERT_OK_AND_ASSIGN +#define ASSERT_OK_AND_ASSIGN(lhs, rexpr) \ + ASSERT_OK_AND_ASSIGN_IMPL_( \ + STATUS_MATCHERS_IMPL_CONCAT_(_status_or_value, __COUNTER__), lhs, rexpr) + +#define ASSERT_OK_AND_ASSIGN_IMPL_(statusor, lhs, rexpr) \ + auto statusor = (rexpr); \ + ASSERT_TRUE(statusor.ok()) << statusor.status(); \ + lhs = std::move(statusor.value()) #endif // OR_TOOLS_BASE_GMOCK_H_ diff --git a/ortools/base/gzipfile.h b/ortools/base/gzipfile.h index 13b2a998a2..7cf891f974 100644 --- a/ortools/base/gzipfile.h +++ b/ortools/base/gzipfile.h @@ -17,10 +17,14 @@ #include // for Z_DEFAULT_COMPRESSION #include "absl/strings/string_view.h" -#include "ortools/base/basictypes.h" // for Ownership enum class File; +// Argument type used in interfaces that can optionally take ownership +// of a passed in argument. If TAKE_OWNERSHIP is passed, the called +// object takes ownership of the argument. Otherwise it does not. +enum Ownership { DO_NOT_TAKE_OWNERSHIP, TAKE_OWNERSHIP }; + // Argument type used in interfaces that can optionally accept appended // compressed streams. If kConcatenateStreams is passed, the output will // include all streams. Otherwise only the first stream is output. diff --git a/ortools/base/logging.h b/ortools/base/logging.h index 489162df16..6209c440a4 100644 --- a/ortools/base/logging.h +++ b/ortools/base/logging.h @@ -28,7 +28,12 @@ #include "absl/strings/str_cat.h" // IWYU pragma: export #include "absl/strings/string_view.h" // IWYU pragma: export #include "ortools/base/base_export.h" // IWYU pragma: export -#include "ortools/base/macros.h" // IWYU pragma: export + +#ifdef NDEBUG +const bool DEBUG_MODE = false; +#else // NDEBUG +const bool DEBUG_MODE = true; +#endif // NDEBUG namespace operations_research { diff --git a/ortools/base/macros.h b/ortools/base/macros.h deleted file mode 100644 index 98ef0f7118..0000000000 --- a/ortools/base/macros.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2010-2025 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_MACROS_H_ -#define OR_TOOLS_BASE_MACROS_H_ - -#include "ortools/base/base_export.h" // IWYU pragma: export - -#define COMPILE_ASSERT(x, msg) - -#ifdef NDEBUG -const bool DEBUG_MODE = false; -#else // NDEBUG -const bool DEBUG_MODE = true; -#endif // NDEBUG - -#endif // OR_TOOLS_BASE_MACROS_H_ diff --git a/ortools/base/status-matchers.h b/ortools/base/status-matchers.h deleted file mode 100644 index f1524d014a..0000000000 --- a/ortools/base/status-matchers.h +++ /dev/null @@ -1,294 +0,0 @@ -// Copyright 2010-2025 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -// emulates g3/testing/base/public/gmock_utils/status-matchers.h -#ifndef ORTOOLS_BASE_STATUS_MATCHERS_H_ -#define ORTOOLS_BASE_STATUS_MATCHERS_H_ - -#include -#include -#include -#include - -#include "absl/status/status.h" -#include "absl/status/statusor.h" -#include "absl/strings/string_view.h" -#include "gmock/gmock-matchers.h" -#include "gtest/gtest.h" - -namespace testing::status { - -inline const ::absl::Status& GetStatus(const ::absl::Status& status) { - return status; -} - -template -inline const ::absl::Status& GetStatus(const ::absl::StatusOr& status) { - return status.status(); -} - -namespace internal { - -// Monomorphic implementation of matcher IsOkAndHolds(m). -// StatusOrType is a reference to StatusOr. -template -class IsOkAndHoldsMatcherImpl - : public ::testing::MatcherInterface { - public: - using value_type = - typename std::remove_reference::type::value_type; - - template - explicit IsOkAndHoldsMatcherImpl(InnerMatcher&& inner_matcher) - : inner_matcher_(::testing::SafeMatcherCast( - std::forward(inner_matcher))) {} - - void DescribeTo(std::ostream* os) const override { - *os << "is OK and has a value that "; - inner_matcher_.DescribeTo(os); - } - - void DescribeNegationTo(std::ostream* os) const override { - *os << "is not OK or has a value that "; - inner_matcher_.DescribeNegationTo(os); - } - - bool MatchAndExplain( - StatusOrType actual_value, - ::testing::MatchResultListener* result_listener) const override { - if (!actual_value.ok()) { - *result_listener << "which has status " << actual_value.status(); - return false; - } - - ::testing::StringMatchResultListener inner_listener; - const bool matches = - inner_matcher_.MatchAndExplain(*actual_value, &inner_listener); - const std::string inner_explanation = inner_listener.str(); - if (!inner_explanation.empty()) { - *result_listener << "which contains value " - << ::testing::PrintToString(*actual_value) << ", " - << inner_explanation; - } - return matches; - } - - private: - const ::testing::Matcher inner_matcher_; -}; - -// Implements IsOkAndHolds(m) as a polymorphic matcher. -template -class IsOkAndHoldsMatcher { - public: - explicit IsOkAndHoldsMatcher(InnerMatcher inner_matcher) - : inner_matcher_(std::move(inner_matcher)) {} - - // Converts this polymorphic matcher to a monomorphic matcher of the - // given type. StatusOrType can be either StatusOr or a - // reference to StatusOr. - template - operator ::testing::Matcher() const { // NOLINT - return ::testing::Matcher( - new IsOkAndHoldsMatcherImpl(inner_matcher_)); - } - - private: - const InnerMatcher inner_matcher_; -}; - -} // namespace internal - -// Returns a gMock matcher that matches a StatusOr<> whose status is -// OK and whose value matches the inner matcher. -template -internal::IsOkAndHoldsMatcher::type> -IsOkAndHolds(InnerMatcher&& inner_matcher) { - return internal::IsOkAndHoldsMatcher::type>( - std::forward(inner_matcher)); -} - -//////////////////////////////////////////////////////////// -// Implementation of IsOk(). -namespace internal { - -// Monomorphic implementation of matcher IsOk() for a given type T. -// T can be Status, StatusOr<>, or a reference to either of them. -template -class MonoIsOkMatcherImpl : public ::testing::MatcherInterface { - public: - void DescribeTo(std::ostream* os) const override { *os << "is OK"; } - void DescribeNegationTo(std::ostream* os) const override { - *os << "is not OK"; - } - bool MatchAndExplain(T actual_value, - ::testing::MatchResultListener*) const override { - return GetStatus(actual_value).ok(); - } -}; - -// Implements IsOk() as a polymorphic matcher. -class IsOkMatcher { - public: - template - operator ::testing::Matcher() const { // NOLINT - return ::testing::Matcher(new MonoIsOkMatcherImpl()); - } -}; -} // namespace internal - -// Returns a gMock matcher that matches a Status or StatusOr<> which is OK. -inline internal::IsOkMatcher IsOk() { return internal::IsOkMatcher(); } - -//////////////////////////////////////////////////////////// -// Implementation of StatusIs(). -// -// StatusIs() is a polymorphic matcher. This class is the common -// implementation of it shared by all types T where StatusIs() can be used as -// a Matcher. -namespace internal { - -class StatusIsMatcherCommonImpl { - public: - StatusIsMatcherCommonImpl( - ::testing::Matcher code_matcher, - ::testing::Matcher message_matcher) - : code_matcher_(std::move(code_matcher)), - message_matcher_(std::move(message_matcher)) {} - - void DescribeTo(std::ostream* os) const { - *os << "has a status code that "; - code_matcher_.DescribeTo(os); - *os << ", and has an error message that "; - message_matcher_.DescribeTo(os); - } - - void DescribeNegationTo(std::ostream* os) const { - *os << "has a status code that "; - code_matcher_.DescribeNegationTo(os); - *os << ", or has an error message that "; - message_matcher_.DescribeNegationTo(os); - } - - bool MatchAndExplain(const absl::Status& status, - ::testing::MatchResultListener* result_listener) const { - ::testing::StringMatchResultListener inner_listener; - - inner_listener.Clear(); - if (!code_matcher_.MatchAndExplain(status.code(), &inner_listener)) { - *result_listener << (inner_listener.str().empty() - ? "whose status code is wrong" - : "which has a status code " + - inner_listener.str()); - return false; - } - - if (!message_matcher_.Matches(std::string(status.message()))) { - *result_listener << "whose error message is wrong"; - return false; - } - - return true; - } - - private: - const ::testing::Matcher code_matcher_; - const ::testing::Matcher message_matcher_; -}; - -// Monomorphic implementation of matcher StatusIs() for a given type T. T can -// be Status, StatusOr<>, or a reference to either of them. -template -class MonoStatusIsMatcherImpl : public ::testing::MatcherInterface { - public: - explicit MonoStatusIsMatcherImpl(StatusIsMatcherCommonImpl common_impl) - : common_impl_(std::move(common_impl)) {} - - void DescribeTo(std::ostream* os) const override { - common_impl_.DescribeTo(os); - } - - void DescribeNegationTo(std::ostream* os) const override { - common_impl_.DescribeNegationTo(os); - } - - bool MatchAndExplain( - T actual_value, - ::testing::MatchResultListener* result_listener) const override { - return common_impl_.MatchAndExplain(GetStatus(actual_value), - result_listener); - } - - private: - StatusIsMatcherCommonImpl common_impl_; -}; - -// Implements StatusIs() as a polymorphic matcher. -class StatusIsMatcher { - public: - StatusIsMatcher(::testing::Matcher code_matcher, - ::testing::Matcher message_matcher) - : common_impl_( - ::testing::MatcherCast(code_matcher), - ::testing::MatcherCast(message_matcher)) {} - - // Converts this polymorphic matcher to a monomorphic matcher of the given - // type. T can be StatusOr<>, Status, or a reference to either of them. - template - operator ::testing::Matcher() const { // NOLINT - return ::testing::MakeMatcher(new MonoStatusIsMatcherImpl(common_impl_)); - } - - private: - const StatusIsMatcherCommonImpl common_impl_; -}; -} // namespace internal - -// Returns a matcher that matches a Status or StatusOr<> whose status code -// matches code_matcher, and whose error message matches message_matcher. -template -internal::StatusIsMatcher StatusIs(CodeMatcher code_matcher, - MessageMatcher message_matcher) { - return internal::StatusIsMatcher(std::move(code_matcher), - std::move(message_matcher)); -} - -// Returns a matcher that matches a Status or StatusOr<> whose status code -// matches code_matcher. -template -internal::StatusIsMatcher StatusIs(CodeMatcher code_matcher) { - return StatusIs(std::move(code_matcher), ::testing::_); -} - -} // namespace testing::status - -// Macros for testing the results of functions that return absl::Status or -// absl::StatusOr (for any type T). -#define EXPECT_OK(expression) EXPECT_THAT(expression, ::testing::status::IsOk()) -#define ASSERT_OK(expression) ASSERT_THAT(expression, ::testing::status::IsOk()) - -#define STATUS_MATCHERS_IMPL_CONCAT_INNER_(x, y) x##y -#define STATUS_MATCHERS_IMPL_CONCAT_(x, y) \ - STATUS_MATCHERS_IMPL_CONCAT_INNER_(x, y) - -#undef ASSERT_OK_AND_ASSIGN -#define ASSERT_OK_AND_ASSIGN(lhs, rexpr) \ - ASSERT_OK_AND_ASSIGN_IMPL_( \ - STATUS_MATCHERS_IMPL_CONCAT_(_status_or_value, __COUNTER__), lhs, rexpr) - -#define ASSERT_OK_AND_ASSIGN_IMPL_(statusor, lhs, rexpr) \ - auto statusor = (rexpr); \ - ASSERT_TRUE(statusor.ok()) << statusor.status(); \ - lhs = std::move(statusor.value()) - -#endif // ORTOOLS_BASE_STATUS_MATCHERS_H_ diff --git a/ortools/base/typeid.h b/ortools/base/typeid.h deleted file mode 100644 index 8a4b314f76..0000000000 --- a/ortools/base/typeid.h +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2010-2025 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_BASE_TYPEID_H_ -#define OR_TOOLS_BASE_TYPEID_H_ - -#include -namespace gtl { -template -inline size_t FastTypeId() { - static char d; - return reinterpret_cast(&d); -} -} // namespace gtl -#endif // OR_TOOLS_BASE_TYPEID_H_ diff --git a/ortools/constraint_solver/routing_search.cc b/ortools/constraint_solver/routing_search.cc index 2c71108836..26f0976472 100644 --- a/ortools/constraint_solver/routing_search.cc +++ b/ortools/constraint_solver/routing_search.cc @@ -4828,9 +4828,9 @@ bool ChristofidesFilteredHeuristic::BuildSolutionInternal() { ChristofidesPathSolver:: MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING); } - if (christofides_solver.Solve()) { + if (christofides_solver.Solve().ok()) { path_per_cost_class[cost_class] = - christofides_solver.TravelingSalesmanPath(); + christofides_solver.TravelingSalesmanPath().value(); } } } diff --git a/ortools/graph/BUILD.bazel b/ortools/graph/BUILD.bazel index 5fb8114224..44857577ae 100644 --- a/ortools/graph/BUILD.bazel +++ b/ortools/graph/BUILD.bazel @@ -272,13 +272,13 @@ cc_library( ":minimum_spanning_tree", ":perfect_matching", "//ortools/base", - "//ortools/base:types", "//ortools/graph", "//ortools/linear_solver", "//ortools/linear_solver:linear_solver_cc_proto", "//ortools/util:saturated_arithmetic", "@abseil-cpp//absl/status", "@abseil-cpp//absl/status:statusor", + "@abseil-cpp//absl/strings", ], ) @@ -289,7 +289,7 @@ cc_test( ":christofides", "//ortools/base", "//ortools/base:gmock_main", - "@abseil-cpp//absl/strings", + "@abseil-cpp//absl/status", "@abseil-cpp//absl/strings:str_format", "@abseil-cpp//absl/types:span", "@google_benchmark//:benchmark", diff --git a/ortools/graph/christofides.h b/ortools/graph/christofides.h index 72565d6bc3..f2f2818a90 100644 --- a/ortools/graph/christofides.h +++ b/ortools/graph/christofides.h @@ -35,6 +35,7 @@ #include "absl/status/status.h" #include "absl/status/statusor.h" +#include "absl/strings/str_cat.h" #include "ortools/base/logging.h" #include "ortools/graph/eulerian_path.h" #include "ortools/graph/graph.h" @@ -48,6 +49,7 @@ namespace operations_research { using ::util::CompleteGraph; +// TODO(user): Only support integer weights/costs. template > @@ -66,23 +68,22 @@ class ChristofidesPathSolver { // (MINIMUM_WEIGHT_MATCHING) guarantees the 3/2 upper bound to the optimal // solution. A minimal weight perfect matching (MINIMAL_WEIGHT_MATCHING) // finds a locally minimal weight matching which does not offer any bound - // guarantee but, as of 1/2017, is orders of magnitude faster than the - // minimum matching. - // By default, MINIMAL_WEIGHT_MATCHING is selected. - // TODO(user): Change the default when minimum matching gets faster. + // guarantee but, as of 09/2025, can sometimes be faster on larger problems. + // By default, MINIMUM_WEIGHT_MATCHING is selected. void SetMatchingAlgorithm(MatchingAlgorithm matching) { matching_ = matching; } - // Returns the cost of the approximate TSP tour. - CostType TravelingSalesmanCost(); + // If the problem was not solved, solves it with the Christofides algorithm, + // and returns the cost of the approximate TSP tour. + absl::StatusOr TravelingSalesmanCost(); - // Returns the approximate TSP tour. - std::vector TravelingSalesmanPath(); + // If the problem was not solved, solves it with the Christofides algorithm, + // and returns the approximate TSP tour. + absl::StatusOr&> TravelingSalesmanPath(); - // Runs the Christofides algorithm. Returns true if a solution was found, - // false otherwise. - bool Solve(); + // If the problem was not solved, solves it with the Christofides algorithm. + absl::Status Solve(); private: // Safe addition operator to avoid overflows when possible. @@ -117,13 +118,17 @@ class ChristofidesPathSolver { }; // Computes a minimum weight perfect matching on an undirected graph. -template +template absl::StatusOr>> ComputeMinimumWeightMatching(const GraphType& graph, const WeightFunctionType& weight) { using ArcIndex = typename GraphType::ArcIndex; using NodeIndex = typename GraphType::NodeIndex; + if constexpr (!std::is_integral_v) { + DLOG(WARNING) << "Weights are being cast to int64_t. This might result " + "in loss of precision or overflows."; + } MinCostPerfectMatching matching(graph.num_nodes()); for (NodeIndex tail : graph.AllNodes()) { for (const ArcIndex arc : graph.OutgoingArcs(tail)) { @@ -135,8 +140,10 @@ ComputeMinimumWeightMatching(const GraphType& graph, } } MinCostPerfectMatching::Status status = matching.Solve(); - if (status != MinCostPerfectMatching::OPTIMAL) { - return absl::InvalidArgumentError("Perfect matching failed"); + if (status != MinCostPerfectMatching::OPTIMAL && + status != MinCostPerfectMatching::COST_OVERFLOW) { + return absl::InvalidArgumentError( + absl::StrCat("Perfect matching failed: ", status)); } std::vector> match; for (NodeIndex tail : graph.AllNodes()) { @@ -232,7 +239,7 @@ template ChristofidesPathSolver:: ChristofidesPathSolver(NodeIndex num_nodes, CostFunction costs) - : matching_(MatchingAlgorithm::MINIMAL_WEIGHT_MATCHING), + : matching_(MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING), graph_(num_nodes), costs_(std::move(costs)), tsp_cost_(0), @@ -240,30 +247,30 @@ ChristofidesPathSolver:: template -CostType ChristofidesPathSolver::TravelingSalesmanCost() { +absl::StatusOr ChristofidesPathSolver< + CostType, ArcIndex, NodeIndex, CostFunction>::TravelingSalesmanCost() { if (!solved_) { - bool const ok = Solve(); - DCHECK(ok); + const absl::Status status = Solve(); + if (!status.ok()) return status; } return tsp_cost_; } template -std::vector ChristofidesPathSolver< +absl::StatusOr&> ChristofidesPathSolver< CostType, ArcIndex, NodeIndex, CostFunction>::TravelingSalesmanPath() { if (!solved_) { - const bool ok = Solve(); - DCHECK(ok); + const absl::Status status = Solve(); + if (!status.ok()) return status; } return tsp_path_; } template -bool ChristofidesPathSolver::Solve() { +absl::Status +ChristofidesPathSolver::Solve() { const NodeIndex num_nodes = graph_.num_nodes(); tsp_path_.clear(); tsp_cost_ = 0; @@ -271,7 +278,7 @@ bool ChristofidesPathSolver mst = @@ -291,21 +298,20 @@ bool ChristofidesPathSolver reduced_graph(reduced_size); std::vector> closure_arcs; switch (matching_) { case MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING: { - auto result = ComputeMinimumWeightMatching( + auto result = ComputeMinimumWeightMatching( reduced_graph, [this, &reduced_graph, &odd_degree_nodes](CompleteGraph<>::ArcIndex arc) { return costs_(odd_degree_nodes[reduced_graph.Tail(arc)], odd_degree_nodes[reduced_graph.Head(arc)]); }); if (!result.ok()) { - return false; + return result.status(); } result->swap(closure_arcs); break; @@ -319,7 +325,7 @@ bool ChristofidesPathSolverswap(closure_arcs); break; @@ -336,10 +342,10 @@ bool ChristofidesPathSolver touched_nodes(reduced_size, false); for (ArcIndex arc_index = 0; closure_arcs.size() * 2 < reduced_size; ++arc_index) { @@ -379,7 +385,7 @@ bool ChristofidesPathSolver #include +#include "absl/status/status.h" #include "absl/strings/str_format.h" -#include "absl/strings/string_view.h" #include "absl/types/span.h" #include "benchmark/benchmark.h" #include "gtest/gtest.h" +#include "ortools/base/gmock.h" #include "ortools/base/logging.h" namespace operations_research { +using ::testing::ElementsAre; +using ::testing::IsEmpty; +using ::testing::status::IsOkAndHolds; +using ::testing::status::StatusIs; + // Displays the path. std::string PathToString(absl::Span path) { std::string path_string; @@ -44,16 +50,17 @@ std::string PathToString(absl::Span path) { template void ComputeAndShow(const std::string& name, ChristofidesPathSolver* chris_solver) { - LOG(INFO) << name << " TSP cost = " << chris_solver->TravelingSalesmanCost(); + LOG(INFO) << name + << " TSP cost = " << chris_solver->TravelingSalesmanCost().value(); LOG(INFO) << name << " TSP path = " - << PathToString(chris_solver->TravelingSalesmanPath()); + << PathToString(chris_solver->TravelingSalesmanPath().value()); } void TestChristofides(const std::string& name, const int size, absl::Span cost_data, bool use_minimal_matching, bool use_mip, const int expected_cost, - absl::string_view expected_solution) { + const std::vector& expected_solution) { using MatchingAlgorithm = ChristofidesPathSolver::MatchingAlgorithm; std::vector> cost_mat(size); for (int i = 0; i < size; ++i) { @@ -89,9 +96,10 @@ void TestChristofides(const std::string& name, const int size, MatchingAlgorithm::MINIMAL_WEIGHT_MATCHING); } ComputeAndShow(name, &chris_solver); - EXPECT_EQ(expected_cost, chris_solver.TravelingSalesmanCost()); - EXPECT_EQ(expected_solution, - PathToString(chris_solver.TravelingSalesmanPath())); + EXPECT_THAT(chris_solver.TravelingSalesmanCost(), + IsOkAndHolds(expected_cost)); + EXPECT_THAT(chris_solver.TravelingSalesmanPath(), + IsOkAndHolds(expected_solution)); } // Gr17 as taken from TSPLIB: @@ -113,12 +121,15 @@ TEST(HamiltonianPathTest, Gr17) { 165, 383, 240, 140, 448, 202, 57, 0, 246, 745, 472, 237, 528, 364, 332, 349, 202, 685, 542, 157, 289, 426, 483, 0, 121, 518, 142, 84, 297, 35, 29, 36, 236, 390, 238, 301, 55, 96, 153, 336, 0}; - TestChristofides("Gr17", kGr17Size, gr17_data, false, /*use_mip=*/true, 2190, - "0 12 6 7 5 10 4 1 9 2 14 13 16 3 8 11 15 0 "); - TestChristofides("Gr17", kGr17Size, gr17_data, false, /*use_mip=*/false, 2190, - "0 12 6 7 5 10 4 1 9 2 14 13 16 3 8 11 15 0 "); - TestChristofides("Gr17", kGr17Size, gr17_data, true, /*use_mip=*/false, 2421, - "0 12 3 8 11 15 1 4 10 9 2 14 13 16 6 7 5 0 "); + TestChristofides( + "Gr17", kGr17Size, gr17_data, false, /*use_mip=*/true, 2190, + {0, 12, 6, 7, 5, 10, 4, 1, 9, 2, 14, 13, 16, 3, 8, 11, 15, 0}); + TestChristofides( + "Gr17", kGr17Size, gr17_data, false, /*use_mip=*/false, 2190, + {0, 12, 6, 7, 5, 10, 4, 1, 9, 2, 14, 13, 16, 3, 8, 11, 15, 0}); + TestChristofides( + "Gr17", kGr17Size, gr17_data, true, /*use_mip=*/false, 2421, + {0, 12, 3, 8, 11, 15, 1, 4, 10, 9, 2, 14, 13, 16, 6, 7, 5, 0}); } // Gr24 as taken from TSPLIB: @@ -149,15 +160,15 @@ TEST(HamiltonianPathTest, Gr24) { 235, 108, 119, 165, 178, 154, 71, 136, 262, 110, 74, 96, 264, 187, 182, 261, 239, 165, 151, 221, 0, 121, 142, 99, 84, 35, 29, 42, 36, 220, 70, 126, 55, 249, 104, 178, 60, 96, 175, 153, 146, 47, 135, 169, 0}; - TestChristofides( - "Gr24", kGr24Size, gr24_data, false, /*use_mip=*/true, 1407, - "0 15 5 6 2 10 7 20 4 9 16 21 17 18 1 14 19 12 8 22 13 23 11 3 0 "); - TestChristofides( - "Gr24", kGr24Size, gr24_data, false, /*use_mip=*/false, 1407, - "0 15 5 6 2 10 7 20 4 9 16 21 17 18 1 14 19 12 8 22 13 23 11 3 0 "); - TestChristofides( - "Gr24", kGr24Size, gr24_data, true, /*use_mip=*/false, 1607, - "0 15 5 6 7 20 4 9 16 21 17 18 1 19 14 13 22 8 12 10 2 23 11 3 0 "); + TestChristofides("Gr24", kGr24Size, gr24_data, false, /*use_mip=*/true, 1407, + {0, 15, 5, 6, 2, 10, 7, 20, 4, 9, 16, 21, 17, + 18, 1, 14, 19, 12, 8, 22, 13, 23, 11, 3, 0}); + TestChristofides("Gr24", kGr24Size, gr24_data, false, /*use_mip=*/false, 1407, + {0, 15, 5, 6, 2, 10, 7, 20, 4, 9, 16, 21, 17, + 18, 1, 14, 19, 12, 8, 22, 13, 23, 11, 3, 0}); + TestChristofides("Gr24", kGr24Size, gr24_data, true, /*use_mip=*/false, 1607, + {0, 15, 5, 6, 7, 20, 4, 9, 16, 21, 17, 18, 1, + 19, 14, 13, 22, 8, 12, 10, 2, 23, 11, 3, 0}); } // This is the geographic distance as defined in TSPLIB. It is used here to @@ -208,35 +219,53 @@ TEST(HamiltonianPathTest, Ulysses) { ChristofidesPathSolver< double>::MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING); ComputeAndShow("Ulysses22", &chris_solver); - EXPECT_EQ(7448, chris_solver.TravelingSalesmanCost()); - EXPECT_EQ("0 7 21 16 1 2 3 17 12 13 14 4 10 8 9 18 19 20 11 6 5 15 0 ", - PathToString(chris_solver.TravelingSalesmanPath())); + EXPECT_THAT(chris_solver.TravelingSalesmanCost(), IsOkAndHolds(7448)); + EXPECT_THAT(chris_solver.TravelingSalesmanPath(), + IsOkAndHolds(ElementsAre(0, 7, 21, 16, 1, 2, 3, 17, 12, 13, 14, 4, + 10, 8, 9, 18, 19, 20, 11, 6, 5, 15, 0))); } TEST(ChristofidesTest, EmptyModel) { ChristofidesPathSolver chris_solver(0, [](int, int) { return 0; }); - EXPECT_EQ(0, chris_solver.TravelingSalesmanCost()); - EXPECT_TRUE(chris_solver.TravelingSalesmanPath().empty()); + EXPECT_THAT(chris_solver.TravelingSalesmanCost(), IsOkAndHolds(0)); + EXPECT_THAT(chris_solver.TravelingSalesmanPath(), IsOkAndHolds(IsEmpty())); } TEST(ChristofidesTest, SingleNodeModel) { ChristofidesPathSolver chris_solver(1, [](int, int) { return 0; }); - EXPECT_EQ(0, chris_solver.TravelingSalesmanCost()); - EXPECT_EQ("0 0 ", PathToString(chris_solver.TravelingSalesmanPath())); + EXPECT_THAT(chris_solver.TravelingSalesmanCost(), IsOkAndHolds(0)); + EXPECT_THAT(chris_solver.TravelingSalesmanPath(), + IsOkAndHolds(ElementsAre(0, 0))); } -TEST(ChristofidesTest, Int64Overflow) { +TEST(ChristofidesTest, Int64OverflowMinimalMatching) { ChristofidesPathSolver chris_solver( 10, [](int, int) { return std::numeric_limits::max() / 2; }); - EXPECT_EQ(std::numeric_limits::max(), - chris_solver.TravelingSalesmanCost()); + chris_solver.SetMatchingAlgorithm( + ChristofidesPathSolver< + int64_t>::MatchingAlgorithm::MINIMAL_WEIGHT_MATCHING); + EXPECT_THAT(chris_solver.TravelingSalesmanCost(), + IsOkAndHolds(std::numeric_limits::max())); } -TEST(ChristofidesTest, SaturatedDouble) { +TEST(ChristofidesTest, Int64OverflowMinimumMatching) { + ChristofidesPathSolver chris_solver( + 10, [](int, int) { return std::numeric_limits::max() / 2; }); + chris_solver.SetMatchingAlgorithm( + ChristofidesPathSolver< + int64_t>::MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING); + EXPECT_THAT(chris_solver.Solve(), + StatusIs(absl::StatusCode::kInvalidArgument)); +} + +TEST(ChristofidesTest, SaturatedDoubleWithMinimalMatching) { ChristofidesPathSolver chris_solver( 10, [](int, int) { return std::numeric_limits::max() / 2.0; }); - EXPECT_EQ(std::numeric_limits::infinity(), - chris_solver.TravelingSalesmanCost()); + chris_solver.SetMatchingAlgorithm( + ChristofidesPathSolver< + double>::MatchingAlgorithm::MINIMAL_WEIGHT_MATCHING); + EXPECT_THAT(chris_solver.TravelingSalesmanCost(), + IsOkAndHolds(std::numeric_limits::infinity())); } TEST(ChristofidesTest, NoPerfectMatching) { @@ -253,7 +282,8 @@ TEST(ChristofidesTest, NoPerfectMatching) { chris_solver.SetMatchingAlgorithm( ChristofidesPathSolver< int64_t>::MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING); - EXPECT_FALSE(chris_solver.Solve()); + EXPECT_THAT(chris_solver.Solve(), + StatusIs(absl::StatusCode::kInvalidArgument)); } // Benchmark for the Christofides algorithm on a 'size' by 'size' grid of nodes. @@ -290,11 +320,12 @@ void BM_ChristofidesPathSolver(benchmark::State& state) { chris_solver.SetMatchingAlgorithm( MatchingAlgorithm::MINIMUM_WEIGHT_MATCHING); } - EXPECT_NE(0, chris_solver.TravelingSalesmanCost()); + EXPECT_THAT(chris_solver.TravelingSalesmanCost(), + IsOkAndHolds(testing::Gt(0))); } } BENCHMARK_TEMPLATE(BM_ChristofidesPathSolver, true)->Range(2, 1 << 5); -BENCHMARK_TEMPLATE(BM_ChristofidesPathSolver, false)->Range(2, 1 << 4); +BENCHMARK_TEMPLATE(BM_ChristofidesPathSolver, false)->Range(2, 1 << 5); } // namespace operations_research diff --git a/ortools/graph/graph.h b/ortools/graph/graph.h index caaed8a38b..c92b83749c 100644 --- a/ortools/graph/graph.h +++ b/ortools/graph/graph.h @@ -636,6 +636,12 @@ struct GraphTraits { // + (ArcIndexType + NodeIndexType) * arc_capacity(). // - Has an efficient Tail() but need an extra NodeIndexType/arc memory for it. // - Never changes the initial arc index returned by AddArc(). +// +// OutgoingArcs(), OutgoingArcsStartingFrom(), and the [] operator are all +// deterministic. Specifically, for two graphs constructed from the same +// sequence of node and arc creation calls, these iterators will return the +// result in the same order). +// template class ListGraph : public BaseGraph { typedef BaseGraph Base; diff --git a/ortools/graph/linear_assignment.h b/ortools/graph/linear_assignment.h index c77ad79b1f..bed3dce33e 100644 --- a/ortools/graph/linear_assignment.h +++ b/ortools/graph/linear_assignment.h @@ -160,7 +160,7 @@ // possible. // // We don't use the interface from -// operations_research/algorithms/hungarian.h because we want to be +// cs/ortools/algorithms/hungarian.h because we want to be // able to express sparse problems efficiently. // // When asked to solve the given assignment problem we return a diff --git a/ortools/graph/one_tree_lower_bound.h b/ortools/graph/one_tree_lower_bound.h index dd53cb0889..6b7c002937 100644 --- a/ortools/graph/one_tree_lower_bound.h +++ b/ortools/graph/one_tree_lower_bound.h @@ -219,7 +219,7 @@ class HeldWolfeCrowderEvaluator { // bounds lead to faster convergence. ChristofidesPathSolver solver( number_of_nodes, cost); - upper_bound_ = solver.TravelingSalesmanCost(); + upper_bound_ = solver.TravelingSalesmanCost().value(); } bool Next() { diff --git a/ortools/gurobi/isv_public/BUILD.bazel b/ortools/gurobi/isv_public/BUILD.bazel index 1604eb599a..1950a31c67 100644 --- a/ortools/gurobi/isv_public/BUILD.bazel +++ b/ortools/gurobi/isv_public/BUILD.bazel @@ -20,6 +20,8 @@ cc_library( srcs = ["gurobi_isv.cc"], hdrs = ["gurobi_isv.h"], deps = [ + "//ortools/base:status_builder", + "//ortools/base:status_macros", "//ortools/math_opt/solvers:gurobi_cc_proto", "//ortools/third_party_solvers:gurobi_environment", "@abseil-cpp//absl/log:check", diff --git a/ortools/linear_solver/BUILD.bazel b/ortools/linear_solver/BUILD.bazel index a65cf12dd1..d6edf109bf 100644 --- a/ortools/linear_solver/BUILD.bazel +++ b/ortools/linear_solver/BUILD.bazel @@ -352,6 +352,7 @@ cc_library( srcs = ["gurobi_util.cc"], hdrs = ["gurobi_util.h"], deps = [ + "//ortools/base:status_macros", "//ortools/third_party_solvers:gurobi_environment", "@abseil-cpp//absl/flags:flag", "@abseil-cpp//absl/log", diff --git a/ortools/linear_solver/model_validator.cc b/ortools/linear_solver/model_validator.cc index 6f85b3df63..bdb6ed7b1d 100644 --- a/ortools/linear_solver/model_validator.cc +++ b/ortools/linear_solver/model_validator.cc @@ -16,7 +16,6 @@ #include #include #include -#include #include #include #include @@ -24,15 +23,14 @@ #include "absl/container/flat_hash_map.h" #include "absl/container/flat_hash_set.h" +#include "absl/flags/flag.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/match.h" #include "absl/strings/str_cat.h" #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" -#include "absl/types/optional.h" #include "ortools/base/accurate_sum.h" -#include "ortools/base/commandlineflags.h" #include "ortools/base/map_util.h" #include "ortools/linear_solver/linear_solver.pb.h" #include "ortools/port/file.h" diff --git a/ortools/lp_data/BUILD.bazel b/ortools/lp_data/BUILD.bazel index 391e373773..407dfbb34b 100644 --- a/ortools/lp_data/BUILD.bazel +++ b/ortools/lp_data/BUILD.bazel @@ -35,21 +35,25 @@ cc_library( hdrs = ["lp_types.h"], deps = [ "//ortools/base", - "//ortools/base:hash", "//ortools/base:strong_vector", "//ortools/util:bitset", "//ortools/util:strong_integers", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:check", ], ) +# Handling of permutations. + cc_library( name = "permutation", hdrs = ["permutation.h"], copts = SAFE_FP_CODE, deps = [ ":base", - "//ortools/base", + "//ortools/base:strong_vector", "//ortools/util:return_macros", + "@abseil-cpp//absl/log:check", "@abseil-cpp//absl/random", ], ) @@ -60,13 +64,13 @@ cc_library( hdrs = ["scattered_vector.h"], deps = [ ":base", - "//ortools/base", - "//ortools/base:strong_vector", "//ortools/util:bitset", - "//ortools/util:strong_integers", + "@abseil-cpp//absl/log:check", ], ) +# Compressed sparse columns. + cc_library( name = "sparse_vector", hdrs = ["sparse_vector.h"], @@ -75,9 +79,9 @@ cc_library( ":base", ":permutation", "//ortools/base", + "//ortools/base:types", "//ortools/graph:iterators", "//ortools/util:return_macros", - "@abseil-cpp//absl/strings", "@abseil-cpp//absl/strings:str_format", ], ) @@ -89,8 +93,10 @@ cc_library( copts = SAFE_FP_CODE, deps = [ ":base", + ":permutation", ":sparse_vector", - "//ortools/base", + "//ortools/util:return_macros", + "@abseil-cpp//absl/log:check", ], ) @@ -99,9 +105,8 @@ cc_library( hdrs = ["sparse_row.h"], copts = SAFE_FP_CODE, deps = [ - ":base", ":sparse_vector", - "//ortools/base", + "//ortools/base:strong_vector", ], ) @@ -114,20 +119,21 @@ cc_library( copts = SAFE_FP_CODE, deps = [ ":base", - ":matrix_scaler_hdr", ":permutation", ":scattered_vector", ":sparse_column", - "//ortools/base", - "//ortools/base:hash", - "//ortools/base:strong_vector", - "//ortools/util:fp_utils", + "//ortools/base:types", + "//ortools/graph:iterators", + "//ortools/util:bitset", "//ortools/util:return_macros", - "//ortools/util:strong_integers", - "@abseil-cpp//absl/strings", + "@abseil-cpp//absl/log:check", + "@abseil-cpp//absl/strings:str_format", + "@abseil-cpp//absl/types:span", ], ) +# Matrix scaler. + cc_library( name = "matrix_scaler", srcs = ["matrix_scaler.cc"], @@ -135,27 +141,24 @@ cc_library( copts = SAFE_FP_CODE, deps = [ ":base", + ":lp_data", ":lp_utils", ":sparse", + ":sparse_column", "//ortools/base", - "//ortools/base:hash", "//ortools/base:strong_vector", + "//ortools/base:types", "//ortools/glop:parameters_cc_proto", "//ortools/glop:revised_simplex", "//ortools/glop:status", - "//ortools/util:fp_utils", + "//ortools/util:return_macros", + "//ortools/util:time_limit", + "@abseil-cpp//absl/log:check", + "@abseil-cpp//absl/strings:str_format", ], ) -cc_library( - name = "matrix_scaler_hdr", - hdrs = ["matrix_scaler.h"], - deps = [ - ":base", - "//ortools/base", - "//ortools/base:strong_vector", - ], -) +# Linear Programming data storage. cc_library( name = "lp_data", @@ -169,6 +172,7 @@ cc_library( ":matrix_utils", ":permutation", ":sparse", + ":sparse_column", "//ortools/base", "//ortools/base:hash", "//ortools/base:strong_vector", @@ -177,7 +181,9 @@ cc_library( "//ortools/util:strong_integers", "@abseil-cpp//absl/container:flat_hash_map", "@abseil-cpp//absl/container:flat_hash_set", + "@abseil-cpp//absl/log:check", "@abseil-cpp//absl/strings", + "@abseil-cpp//absl/strings:str_format", ], ) @@ -189,10 +195,16 @@ cc_library( ":base", ":lp_data", ":matrix_scaler", + ":scattered_vector", + ":sparse_column", "//ortools/glop:parameters_cc_proto", + "@abseil-cpp//absl/log:check", + "@abseil-cpp//absl/types:span", ], ) +# Lp utilities. + cc_library( name = "lp_utils", srcs = ["lp_utils.cc"], @@ -200,10 +212,12 @@ cc_library( copts = SAFE_FP_CODE, deps = [ ":base", + ":permutation", ":scattered_vector", ":sparse_column", - "//ortools/base", "//ortools/base:accurate_sum", + "@abseil-cpp//absl/log:check", + "@abseil-cpp//absl/types:span", ], ) @@ -215,8 +229,9 @@ cc_library( deps = [ ":base", ":sparse", - "//ortools/base", + ":sparse_column", "//ortools/base:hash", + "@abseil-cpp//absl/log:check", ], ) @@ -229,15 +244,20 @@ cc_library( ":base", ":lp_data", ":proto_utils", - "//ortools/base", - "//ortools/base:map_util", "//ortools/linear_solver:linear_solver_cc_proto", + "@abseil-cpp//absl/base:core_headers", + "@abseil-cpp//absl/container:flat_hash_set", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:check", + "@abseil-cpp//absl/status", "@abseil-cpp//absl/status:statusor", "@abseil-cpp//absl/strings", "@re2", ], ) +# Linear Programming printing utilities. + cc_library( name = "lp_print_utils", srcs = ["lp_print_utils.cc"], @@ -245,39 +265,47 @@ cc_library( copts = SAFE_FP_CODE, deps = [ ":base", - "//ortools/base", + "//ortools/base:types", "//ortools/util:rational_approximation", "@abseil-cpp//absl/strings", "@abseil-cpp//absl/strings:str_format", ], ) +# Proto conversion + cc_library( name = "proto_utils", srcs = ["proto_utils.cc"], hdrs = ["proto_utils.h"], copts = SAFE_FP_CODE, + visibility = ["//visibility:public"], deps = [ ":base", ":lp_data", - "//ortools/base", + ":sparse", + ":sparse_column", "//ortools/linear_solver:linear_solver_cc_proto", + "@abseil-cpp//absl/log:check", ], ) +# Test utilities. + +# MPS reader. cc_library( name = "mps_reader_template", srcs = ["mps_reader_template.cc"], hdrs = ["mps_reader_template.h"], deps = [ - "//ortools/base", - "//ortools/base:map_util", + "//ortools/base:file", "//ortools/base:status_macros", "//ortools/util:filelineiter", "@abseil-cpp//absl/container:flat_hash_map", "@abseil-cpp//absl/container:flat_hash_set", "@abseil-cpp//absl/container:inlined_vector", "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:check", "@abseil-cpp//absl/status", "@abseil-cpp//absl/status:statusor", "@abseil-cpp//absl/strings", @@ -290,21 +318,47 @@ cc_library( hdrs = ["mps_reader.h"], copts = SAFE_FP_CODE, deps = [ + ":base", ":lp_data", - ":lp_print_utils", ":mps_reader_template", - "//ortools/base", "//ortools/base:protobuf_util", - "//ortools/base:status_builder", "//ortools/base:status_macros", - "//ortools/base:strong_vector", "//ortools/linear_solver:linear_solver_cc_proto", + "@abseil-cpp//absl/base:core_headers", "@abseil-cpp//absl/container:btree", + "@abseil-cpp//absl/container:flat_hash_map", + "@abseil-cpp//absl/log", + "@abseil-cpp//absl/log:check", "@abseil-cpp//absl/status", + "@abseil-cpp//absl/status:statusor", "@abseil-cpp//absl/strings", ], ) +# Utility to represent the MPS layout as a PNG. + +cc_library( + name = "sol_reader", + srcs = ["sol_reader.cc"], + hdrs = ["sol_reader.h"], + deps = [ + ":base", + ":lp_data", + "//ortools/base:numbers", + "//ortools/base:status_macros", + "//ortools/linear_solver:linear_solver_cc_proto", + "//ortools/util:file_util", + "@abseil-cpp//absl/container:flat_hash_map", + "@abseil-cpp//absl/status", + "@abseil-cpp//absl/status:statusor", + "@abseil-cpp//absl/strings", + ], +) + +# Library to show matrix fill-in as a PNG bitmap. + +# Decompose a LinearProgram into several independent LinearPrograms. + cc_library( name = "lp_decomposer", srcs = ["lp_decomposer.cc"], @@ -313,30 +367,13 @@ cc_library( deps = [ ":base", ":lp_data", - ":lp_utils", + ":sparse", + ":sparse_column", "//ortools/algorithms:dynamic_partition", - "//ortools/base", - "//ortools/base:hash", - "//ortools/glop:parameters_cc_proto", + "//ortools/util:bitset", + "@abseil-cpp//absl/base:core_headers", + "@abseil-cpp//absl/log:check", "@abseil-cpp//absl/synchronization", - ], -) - -cc_library( - name = "sol_reader", - srcs = ["sol_reader.cc"], - hdrs = ["sol_reader.h"], - deps = [ - ":base", - ":lp_data", - ":proto_utils", - "//ortools/base", - "//ortools/base:numbers", - "//ortools/linear_solver:linear_solver_cc_proto", - "//ortools/util:file_util", - "@abseil-cpp//absl/container:flat_hash_map", - "@abseil-cpp//absl/status", - "@abseil-cpp//absl/status:statusor", - "@abseil-cpp//absl/strings", + "@abseil-cpp//absl/types:span", ], ) diff --git a/ortools/port/BUILD.bazel b/ortools/port/BUILD.bazel index 48ced71a74..52bf732957 100644 --- a/ortools/port/BUILD.bazel +++ b/ortools/port/BUILD.bazel @@ -43,12 +43,6 @@ cc_library( ], ) -cc_library( - name = "utf8", - hdrs = ["utf8.h"], - deps = ["//ortools/base:encodingutils"], -) - cc_library( name = "file", srcs = ["file.cc"], diff --git a/ortools/port/utf8.h b/ortools/port/utf8.h deleted file mode 100644 index 359f2c49b8..0000000000 --- a/ortools/port/utf8.h +++ /dev/null @@ -1,39 +0,0 @@ -// Copyright 2010-2025 Google LLC -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -#ifndef OR_TOOLS_PORT_UTF8_H_ -#define OR_TOOLS_PORT_UTF8_H_ - -#include - -#ifndef __PORTABLE_PLATFORM__ -#include "ortools/base/encodingutils.h" -#endif - -namespace operations_research { -namespace utf8 { - -// str_type should be string/StringPiece/Cord -template -int UTF8StrLen(StrType str_type) { -#if defined(__PORTABLE_PLATFORM__) - return str_type.size(); -#else - return ::EncodingUtils::UTF8StrLen(str_type); -#endif -} - -} // namespace utf8 -} // namespace operations_research - -#endif // OR_TOOLS_PORT_UTF8_H_ diff --git a/ortools/service/v1/mathopt/result.proto b/ortools/service/v1/mathopt/result.proto index 16e944d281..656921adc8 100644 --- a/ortools/service/v1/mathopt/result.proto +++ b/ortools/service/v1/mathopt/result.proto @@ -22,6 +22,7 @@ import "ortools/service/v1/mathopt/solution.proto"; option java_multiple_files = true; option java_package = "com.google.ortools.service.v1.mathopt"; + option csharp_namespace = "Google.OrTools.Service"; // Problem feasibility status as claimed by the solver (solver is not required diff --git a/ortools/service/v1/mathopt/solution.proto b/ortools/service/v1/mathopt/solution.proto index 4145bd5e58..60224437e8 100644 --- a/ortools/service/v1/mathopt/solution.proto +++ b/ortools/service/v1/mathopt/solution.proto @@ -21,6 +21,7 @@ import "ortools/service/v1/mathopt/sparse_containers.proto"; option java_multiple_files = true; option java_package = "com.google.ortools.service.v1.mathopt"; + option csharp_namespace = "Google.OrTools.Service"; // Feasibility of a primal or dual solution as claimed by the solver. diff --git a/ortools/service/v1/mathopt/solver_resources.proto b/ortools/service/v1/mathopt/solver_resources.proto index c8fa07d042..9aff438ea5 100644 --- a/ortools/service/v1/mathopt/solver_resources.proto +++ b/ortools/service/v1/mathopt/solver_resources.proto @@ -19,6 +19,7 @@ package operations_research.service.v1.mathopt; option java_multiple_files = true; option java_package = "com.google.ortools.service.v1.mathopt"; + option csharp_namespace = "Google.OrTools.Service"; // This message is used to specify some hints on the resources a remote solve is diff --git a/ortools/service/v1/mathopt/sparse_containers.proto b/ortools/service/v1/mathopt/sparse_containers.proto index ad965714fe..174d4ad97a 100644 --- a/ortools/service/v1/mathopt/sparse_containers.proto +++ b/ortools/service/v1/mathopt/sparse_containers.proto @@ -19,6 +19,7 @@ package operations_research.service.v1.mathopt; option java_multiple_files = true; option java_package = "com.google.ortools.service.v1.mathopt"; + option csharp_namespace = "Google.OrTools.Service"; // A sparse representation of a vector of doubles. diff --git a/ortools/third_party_solvers/BUILD.bazel b/ortools/third_party_solvers/BUILD.bazel index 5f07e608ae..cab59ea2d3 100644 --- a/ortools/third_party_solvers/BUILD.bazel +++ b/ortools/third_party_solvers/BUILD.bazel @@ -23,9 +23,8 @@ cc_library( "//conditions:default": [], }), deps = [ - "//ortools/base", "//ortools/base:logging", - "@abseil-cpp//absl/strings", + "@abseil-cpp//absl/strings:string_view", ], ) @@ -36,14 +35,10 @@ cc_library( deps = [ ":dynamic_library", "//ortools/base", - "//ortools/base:file", - "//ortools/base:status_macros", - "@abseil-cpp//absl/base:core_headers", + "@abseil-cpp//absl/base:no_destructor", "@abseil-cpp//absl/status", - "@abseil-cpp//absl/status:statusor", "@abseil-cpp//absl/strings", - "@abseil-cpp//absl/synchronization", - "@abseil-cpp//absl/types:optional", + "@abseil-cpp//absl/strings:string_view", ], ) @@ -54,13 +49,11 @@ cc_library( deps = [ ":dynamic_library", "//ortools/base", - "//ortools/base:file", - "//ortools/base:status_macros", - "@abseil-cpp//absl/flags:flag", + "//ortools/base:base_export", + "@abseil-cpp//absl/base", + "@abseil-cpp//absl/base:core_headers", "@abseil-cpp//absl/status", - "@abseil-cpp//absl/status:statusor", "@abseil-cpp//absl/strings", "@abseil-cpp//absl/synchronization", - "@abseil-cpp//absl/types:optional", ], ) diff --git a/ortools/util/BUILD.bazel b/ortools/util/BUILD.bazel index 4145dd7ad6..5cda482d37 100644 --- a/ortools/util/BUILD.bazel +++ b/ortools/util/BUILD.bazel @@ -167,11 +167,8 @@ cc_library( hdrs = ["stats.h"], deps = [ "//ortools/base", - "//ortools/base:stl_util", "//ortools/base:timer", - "//ortools/base:types", "//ortools/port:sysinfo", - "//ortools/port:utf8", "@abseil-cpp//absl/container:flat_hash_map", "@abseil-cpp//absl/log:check", "@abseil-cpp//absl/strings", diff --git a/ortools/util/proto_tools.h b/ortools/util/proto_tools.h index 0a7ea33d4c..8d18bdf87a 100644 --- a/ortools/util/proto_tools.h +++ b/ortools/util/proto_tools.h @@ -16,9 +16,6 @@ #include -#include "absl/base/casts.h" -#include "absl/container/flat_hash_map.h" -#include "absl/container/flat_hash_set.h" #include "absl/status/status.h" #include "absl/status/statusor.h" #include "absl/strings/str_format.h" @@ -65,34 +62,6 @@ void ExploreAndCountAllProtoPathsInInstance( // several proto instances to build a global count of proto paths. absl::flat_hash_map* proto_path_counts); -// This recursive function lists all the fields of a given proto *type* -// (not a proto instance), up to the given depth of nested sub-messages, and -// inserts their proto paths into `proto_paths`. -// -// SIMPLE EXAMPLE: with this .proto message definition: -// message YY { repeated int z; } -// message XX { int a; int b; repeated int c; repeated YY d; }, -// ExploreAndInsertAllProtoPathsInType( -// XX().GetDescriptor(), {}, {}, /*max_depth=*/3) -// Returns: {"a", "b", "c", "d", "d.z"}. -// See the unit test for more complex examples. -absl::flat_hash_set ExploreAndInsertAllProtoPathsInType( - const google::protobuf::Descriptor* descriptor, - // ADVANCED USAGE: Pruning the proto tree exploration with two possible - // mechanisms: 1) based on proto path, or 2) based on proto type. - // 1) List of proto paths to skip: those fields will not be output nor - // explored, meaning that their descendants is also skipped. - const absl::flat_hash_set& skip_these_proto_paths, - // 2) Maps a field's full *type* name (as in Descriptor::full_name(), e.g., - // "operations_research.MyProto") to the subset of its child field names - // that may be explored. - const absl::flat_hash_map>& - proto_type_names_to_field_name_allowlist, - // The maximum depth, when diving into sub-messages. Note that some protos - // may have infinite potential depth, e.g. message X { X x; }, so we must - // limit the recursion depth. - int max_depth); - // ============================================================================= // Implementation of function templates. @@ -102,7 +71,8 @@ absl::StatusOr SafeProtoDownCast(google::protobuf::Message* proto) { Proto::default_instance().GetDescriptor(); const google::protobuf::Descriptor* actual_descriptor = proto->GetDescriptor(); - if (actual_descriptor == expected_descriptor) return reinterpret_cast(proto); + if (actual_descriptor == expected_descriptor) + return reinterpret_cast(proto); return absl::InvalidArgumentError(absl::StrFormat( "Expected message type '%s', but got type '%s'", expected_descriptor->full_name(), actual_descriptor->full_name())); diff --git a/ortools/util/stats.cc b/ortools/util/stats.cc index a8e8073c34..6b2fb773b7 100644 --- a/ortools/util/stats.cc +++ b/ortools/util/stats.cc @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -23,10 +24,8 @@ #include "absl/strings/str_format.h" #include "absl/strings/string_view.h" #include "ortools/base/logging.h" -#include "ortools/base/stl_util.h" -#include "ortools/base/types.h" +#include "ortools/base/timer.h" #include "ortools/port/sysinfo.h" -#include "ortools/port/utf8.h" namespace operations_research { @@ -72,6 +71,25 @@ bool CompareStatPointers(const Stat* s1, const Stat* s2) { } } +// This function counts the number of codepoints in the string and assumes well +// formed UTF-8 strings. Codepoints can take up to 4 bytes. +// * 1-byte codepoint : 0yyyzzzz +// * 2-byte codepoint : 110xxxyy 10yyzzzz +// * 3-byte codepoint : 1110wwww 10xxxxyy 10yyzzzz +// * 4-byte codepoint : 11110uvv 10vvwwww 10xxxxyy 10yyzzzz +// We count one codepoint for bytes where the two most significant bits are +// different of 0b10, effectively discarding all trailing bytes in multibyte +// codepoints. +int UTF8StrLen(absl::string_view str) { + int len = 0; + for (const char c : str) { + if ((c & 0b11'00'0000) != 0b10'00'0000) { + ++len; + } + } + return len; +} + } // namespace std::string StatsGroup::StatString() const { @@ -82,7 +100,7 @@ std::string StatsGroup::StatString() const { for (int i = 0; i < stats_.size(); ++i) { if (!stats_[i]->WorthPrinting()) continue; // We support UTF8 characters in the stat names. - const int size = operations_research::utf8::UTF8StrLen(stats_[i]->Name()); + const int size = UTF8StrLen(stats_[i]->Name()); longest_name_size = std::max(longest_name_size, size); sorted_stats.push_back(stats_[i]); } @@ -108,9 +126,7 @@ std::string StatsGroup::StatString() const { for (int i = 0; i < sorted_stats.size(); ++i) { result += " "; result += sorted_stats[i]->Name(); - result.append(longest_name_size - operations_research::utf8::UTF8StrLen( - sorted_stats[i]->Name()), - ' '); + result.append(longest_name_size - UTF8StrLen(sorted_stats[i]->Name()), ' '); result += " : " + sorted_stats[i]->ValueAsString(); } result += "}\n"; diff --git a/ortools/util/stats.h b/ortools/util/stats.h index dc123d60a5..d8455b7178 100644 --- a/ortools/util/stats.h +++ b/ortools/util/stats.h @@ -69,8 +69,6 @@ #define OR_TOOLS_UTIL_STATS_H_ #include -#include -#include #include #include #include diff --git a/patches/abseil-cpp-20250814.0.patch b/patches/abseil-cpp-20250814.0.patch index ad52cb5629..15b025d8f0 100644 --- a/patches/abseil-cpp-20250814.0.patch +++ b/patches/abseil-cpp-20250814.0.patch @@ -1,5 +1,31 @@ +diff --git a/CMake/AbseilHelpers.cmake b/CMake/AbseilHelpers.cmake +index 624a3c7..8d0493d 100644 +--- a/CMake/AbseilHelpers.cmake ++++ b/CMake/AbseilHelpers.cmake +@@ -345,7 +345,7 @@ Cflags: -I\${includedir}${PC_CFLAGS}\n") + endif() + endif() + +- if(ABSL_ENABLE_INSTALL) ++ if(ABSL_ENABLE_INSTALL AND NOT ABSL_CC_LIB_TESTONLY) + install(TARGETS ${_NAME} EXPORT ${PROJECT_NAME}Targets + RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR} + LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} +diff --git a/CMakeLists.txt b/CMakeLists.txt +index 1e7c856..a3c3dae 100644 +--- a/CMakeLists.txt ++++ b/CMakeLists.txt +@@ -145,7 +145,7 @@ if((BUILD_TESTING AND ABSL_BUILD_TESTING) OR ABSL_BUILD_TEST_HELPERS) + add_library(GTest::gmock ALIAS gmock) + add_library(GTest::gmock_main ALIAS gmock_main) + else() +- message(FATAL_ERROR "ABSL_USE_EXTERNAL_GOOGLETEST is ON and ABSL_FIND_GOOGLETEST is OFF, which means that the top-level project must build the Google Test project. However, the target gtest was not found.") ++ message(WARNING "ABSL_USE_EXTERNAL_GOOGLETEST is ON and ABSL_FIND_GOOGLETEST is OFF, which means that the top-level project must build the Google Test project. However, the target gtest was not found.") + endif() + endif() + else() diff --git a/absl/flags/declare.h b/absl/flags/declare.h -index 8d2a856e..a1540467 100644 +index 8d2a856..a154046 100644 --- a/absl/flags/declare.h +++ b/absl/flags/declare.h @@ -59,10 +59,15 @@ ABSL_NAMESPACE_END @@ -19,7 +45,7 @@ index 8d2a856e..a1540467 100644 #endif // ABSL_FLAGS_DECLARE_H_ diff --git a/absl/log/CMakeLists.txt b/absl/log/CMakeLists.txt -index eb19bec0..13b2d240 100644 +index eb19bec..13b2d24 100644 --- a/absl/log/CMakeLists.txt +++ b/absl/log/CMakeLists.txt @@ -47,6 +47,7 @@ absl_cc_library( @@ -31,7 +57,7 @@ index eb19bec0..13b2d240 100644 absl::log_internal_nullguard absl::log_internal_nullstream diff --git a/absl/log/check_test_impl.inc b/absl/log/check_test_impl.inc -index 5a7caf47..7bcedd40 100644 +index 5a7caf4..7bcedd4 100644 --- a/absl/log/check_test_impl.inc +++ b/absl/log/check_test_impl.inc @@ -13,6 +13,8 @@ @@ -63,7 +89,7 @@ index 5a7caf47..7bcedd40 100644 enum { CASE_A, CASE_B }; diff --git a/absl/log/internal/BUILD.bazel b/absl/log/internal/BUILD.bazel -index 1ba9d766..005861f9 100644 +index 1ba9d76..005861f 100644 --- a/absl/log/internal/BUILD.bazel +++ b/absl/log/internal/BUILD.bazel @@ -82,6 +82,7 @@ cc_library( @@ -75,7 +101,7 @@ index 1ba9d766..005861f9 100644 ) diff --git a/absl/log/internal/check_op.h b/absl/log/internal/check_op.h -index 4554475d..c6078640 100644 +index 4554475..c607864 100644 --- a/absl/log/internal/check_op.h +++ b/absl/log/internal/check_op.h @@ -40,6 +40,7 @@