abseil-cpp
abseil-cpp copied to clipboard
Resolving linking order is a nightmare on alpine
Describe the issue
Multiple reports already exist explaining how struggling resolving library linking order can be in general. Though it's become more of a nightmare lately with more absl dependencies needed for the very same code base, and handling different library link orders for my two primaryr platforms (Ubuntu 22.04 and Alpine 3.19) is really insane. After spending several hours on it (counting) I want to add my +1 for an at least somewhat better maintainable packaging.
Steps to reproduce the problem
Use a random app which depends on boringssl and abseil, and have fun sorting the libraries.
What version of Abseil are you using?
20240116.1
What operating system and version are you using?
Alpine 3.19
What compiler and version are you using?
musl
What build system are you using?
cmake
Additional context
No response
Use a random app which depends on boringssl and abseil, and have fun sorting the libraries.
I don't know what you are talking about. Please explain.
Here is a concrete example what I ended up with as libraries for one of our applications. The sort order matters, the required libraries and dependencies of dependencies differ between versions ~6 months apart (we use abseil-cpp which comes with alpine 3.19 due required patches included with it). I have a much large example, but in this app alone had to figure out the directly required libs and here also the dependencies between the flags* libs by trial and error. Both platforms use the same code base and I do not see a reason at all why here for example the flags library is required on alpine at all.
I also refer to this discussion: https://github.com/abseil/abseil-cpp/issues/367
find_package(absl REQUIRED) SET(ABSEIL_LIBS absl::base absl::btree absl::config absl::core_headers absl::dynamic_annotations absl::endian absl::fixed_array absl::flat_hash_map absl::flat_hash_set absl::hash absl::inlined_vector absl::int128 absl::log_severity absl::memory absl::span absl::str_format absl::strings absl::type_traits absl::utility absl::synchronization absl::str_format_internal ) if(IS_ALPINE) list(APPEND ABSEIL_LIBS absl::log_internal_check_op absl::status absl::flags absl::flags_parse absl::flags_usage absl::flags_internal absl::flags_commandlineflag absl::flags_commandlineflag_internal absl::flags_config absl::flags_marshalling absl::flags_program_name absl::flags_reflection absl::flags_usage_internal) endif()
This is not a reproduction, it is a list of libraries. Please provide a reproduction of the problem so I can see what is going on.
If I'm understanding your problem is similar to mine, I found the easiest way to tackle this was to:
- Create a graphviz target graph with cmake
- Find all targets that are not depended upon by other targets (graphviz files that do not have a corresponding .dependers file)
- Create a CMake executable that depends on these top level targets (I removed
test_allocator
from the list, since it depends on GTest/GMock) -
export VERBOSE=1
and build to inspect the linker command line to determine target order, which gave me the following:
[233/233] : && /usr/bin/c++ CMakeFiles/main.dir/main.cpp.o -o main _deps/abseil-cpp-build/absl/strings/libabsl_cordz_sample_token.a _deps/abseil-cpp-build/absl/log/libabsl_die_if_null.a _deps/abseil-cpp-build/absl/debugging/libabsl_failure_signal_handler.a _deps/abseil-cpp-build/absl/flags/libabsl_flags_parse.a _deps/abseil-cpp-build/absl/log/libabsl_log_flags.a _deps/abseil-cpp-build/absl/log/libabsl_log_initialize.a _deps/abseil-cpp-build/absl/profiling/libabsl_periodic_sampler.a _deps/abseil-cpp-build/absl/random/libabsl_random_internal_distribution_test_util.a _deps/abseil-cpp-build/absl/base/libabsl_scoped_set_env.a _deps/abseil-cpp-build/absl/status/libabsl_statusor.a _deps/abseil-cpp-build/absl/types/libabsl_bad_any_cast_impl.a _deps/abseil-cpp-build/absl/log/libabsl_log_internal_check_op.a _deps/abseil-cpp-build/absl/debugging/libabsl_leak_check.a _deps/abseil-cpp-build/absl/flags/libabsl_flags_usage.a _deps/abseil-cpp-build/absl/flags/libabsl_flags_usage_internal.a _deps/abseil-cpp-build/absl/flags/libabsl_flags_internal.a _deps/abseil-cpp-build/absl/flags/libabsl_flags_reflection.a _deps/abseil-cpp-build/absl/flags/libabsl_flags_config.a _deps/abseil-cpp-build/absl/flags/libabsl_flags_program_name.a _deps/abseil-cpp-build/absl/flags/libabsl_flags_private_handle_accessor.a _deps/abseil-cpp-build/absl/flags/libabsl_flags_commandlineflag.a _deps/abseil-cpp-build/absl/flags/libabsl_flags_commandlineflag_internal.a _deps/abseil-cpp-build/absl/flags/libabsl_flags_marshalling.a _deps/abseil-cpp-build/absl/log/libabsl_log_internal_conditions.a _deps/abseil-cpp-build/absl/log/libabsl_log_internal_message.a _deps/abseil-cpp-build/absl/debugging/libabsl_examine_stack.a _deps/abseil-cpp-build/absl/log/libabsl_log_internal_nullguard.a _deps/abseil-cpp-build/absl/log/libabsl_log_internal_format.a _deps/abseil-cpp-build/absl/log/libabsl_log_internal_proto.a _deps/abseil-cpp-build/absl/log/libabsl_log_internal_log_sink_set.a _deps/abseil-cpp-build/absl/log/libabsl_log_globals.a _deps/abseil-cpp-build/absl/log/libabsl_vlog_config_internal.a _deps/abseil-cpp-build/absl/log/libabsl_log_internal_fnmatch.a _deps/abseil-cpp-build/absl/log/libabsl_log_internal_globals.a _deps/abseil-cpp-build/absl/log/libabsl_log_sink.a _deps/abseil-cpp-build/absl/log/libabsl_log_entry.a _deps/abseil-cpp-build/absl/container/libabsl_raw_hash_set.a _deps/abseil-cpp-build/absl/hash/libabsl_hash.a _deps/abseil-cpp-build/absl/hash/libabsl_city.a _deps/abseil-cpp-build/absl/hash/libabsl_low_level_hash.a _deps/abseil-cpp-build/absl/container/libabsl_hashtablez_sampler.a _deps/abseil-cpp-build/absl/random/libabsl_random_distributions.a _deps/abseil-cpp-build/absl/random/libabsl_random_seed_sequences.a _deps/abseil-cpp-build/absl/random/libabsl_random_internal_pool_urbg.a _deps/abseil-cpp-build/absl/random/libabsl_random_internal_randen.a _deps/abseil-cpp-build/absl/random/libabsl_random_internal_randen_hwaes.a _deps/abseil-cpp-build/absl/random/libabsl_random_internal_randen_hwaes_impl.a _deps/abseil-cpp-build/absl/random/libabsl_random_internal_randen_slow.a _deps/abseil-cpp-build/absl/random/libabsl_random_internal_platform.a _deps/abseil-cpp-build/absl/random/libabsl_random_internal_seed_material.a _deps/abseil-cpp-build/absl/random/libabsl_random_seed_gen_exception.a _deps/abseil-cpp-build/absl/status/libabsl_status.a _deps/abseil-cpp-build/absl/strings/libabsl_cord.a _deps/abseil-cpp-build/absl/strings/libabsl_cordz_info.a _deps/abseil-cpp-build/absl/strings/libabsl_cordz_handle.a _deps/abseil-cpp-build/absl/strings/libabsl_cordz_functions.a _deps/abseil-cpp-build/absl/profiling/libabsl_exponential_biased.a _deps/abseil-cpp-build/absl/synchronization/libabsl_synchronization.a _deps/abseil-cpp-build/absl/synchronization/libabsl_graphcycles_internal.a _deps/abseil-cpp-build/absl/synchronization/libabsl_kernel_timeout_internal.a _deps/abseil-cpp-build/absl/time/libabsl_time.a _deps/abseil-cpp-build/absl/time/libabsl_civil_time.a _deps/abseil-cpp-build/absl/time/libabsl_time_zone.a _deps/abseil-cpp-build/absl/strings/libabsl_cord_internal.a _deps/abseil-cpp-build/absl/crc/libabsl_crc_cord_state.a _deps/abseil-cpp-build/absl/crc/libabsl_crc32c.a _deps/abseil-cpp-build/absl/crc/libabsl_crc_internal.a _deps/abseil-cpp-build/absl/crc/libabsl_crc_cpu_detect.a _deps/abseil-cpp-build/absl/debugging/libabsl_stacktrace.a _deps/abseil-cpp-build/absl/types/libabsl_bad_optional_access.a _deps/abseil-cpp-build/absl/strings/libabsl_str_format_internal.a _deps/abseil-cpp-build/absl/base/libabsl_strerror.a _deps/abseil-cpp-build/absl/debugging/libabsl_symbolize.a _deps/abseil-cpp-build/absl/strings/libabsl_strings.a _deps/abseil-cpp-build/absl/strings/libabsl_strings_internal.a _deps/abseil-cpp-build/absl/numeric/libabsl_int128.a _deps/abseil-cpp-build/absl/strings/libabsl_string_view.a _deps/abseil-cpp-build/absl/base/libabsl_throw_delegate.a _deps/abseil-cpp-build/absl/debugging/libabsl_debugging_internal.a _deps/abseil-cpp-build/absl/base/libabsl_malloc_internal.a _deps/abseil-cpp-build/absl/debugging/libabsl_demangle_internal.a _deps/abseil-cpp-build/absl/base/libabsl_base.a _deps/abseil-cpp-build/absl/base/libabsl_spinlock_wait.a -lrt _deps/abseil-cpp-build/absl/types/libabsl_bad_variant_access.a _deps/abseil-cpp-build/absl/base/libabsl_raw_logging_internal.a _deps/abseil-cpp-build/absl/base/libabsl_log_severity.a && :
I've attached an example CMake script to reproduce the first 2 steps below:
# cmake -S. -Bbuild && cmake --build build
cmake_minimum_required(VERSION 3.22...3.28)
if (CMAKE_SCRIPT_MODE_FILE)
file(GLOB graphviz_FILES "targets/*")
foreach(graphviz_FILE IN LISTS graphviz_FILES)
if ((NOT EXISTS "${graphviz_FILE}.dependers") AND (NOT "${graphviz_FILE}" MATCHES "\\.dependers$"))
string(REGEX REPLACE ".*graph\.dot\." "" graphviz_FILE "${graphviz_FILE}")
message(${graphviz_FILE})
endif()
endforeach()
else()
project(abseil-target-graph)
# Download abseil - could be add subdirectory if already submoduled / cloned
include(FetchContent)
set(absl_URL https://github.com/abseil/abseil-cpp.git)
set(absl_TAG 20240116.0)
FetchContent_Declare(
abseil-cpp
GIT_REPOSITORY ${absl_URL}
GIT_TAG ${absl_TAG}
GIT_SHALLOW 1
EXCLUDE_FROM_ALL
)
FetchContent_MakeAvailable(abseil-cpp)
# Generate target graph & list
add_custom_target(graph ALL
COMMAND "${CMAKE_COMMAND}" "${PROJECT_SOURCE_DIR}" --graphviz=targets/graph.dot
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}"
VERBATIM
)
add_custom_command(
TARGET graph POST_BUILD
COMMAND "${CMAKE_COMMAND}" -P "${CMAKE_CURRENT_LIST_FILE}"
WORKING_DIRECTORY "${PROJECT_BINARY_DIR}"
VERBATIM
)
endif()
Hope that helps!
I wrote a python script to extract the information out and do a topological sort on the targets. Python has a builtin library for doing topological sort.