abseil/abseil-cpp

Resolving linking order is a nightmare on alpine

Opened this issue · 4 comments

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: #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:

  1. Create a graphviz target graph with cmake
  2. Find all targets that are not depended upon by other targets (graphviz files that do not have a corresponding .dependers file)
  3. Create a CMake executable that depends on these top level targets (I removed test_allocator from the list, since it depends on GTest/GMock)
  4. 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!