abseil-cpp icon indicating copy to clipboard operation
abseil-cpp copied to clipboard

Resolving linking order is a nightmare on alpine

Open benstadin opened this issue 11 months ago • 5 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

benstadin avatar Mar 23 '24 17:03 benstadin

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.

derekmauro avatar Mar 25 '24 15:03 derekmauro

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()

benstadin avatar Mar 25 '24 16:03 benstadin

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.

derekmauro avatar Mar 26 '24 13:03 derekmauro

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!

JonTheBurger avatar Apr 12 '24 04:04 JonTheBurger

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.

snnn avatar Jul 27 '24 05:07 snnn