From 14f93aee86b81d21d207f669e29983f181a8c179 Mon Sep 17 00:00:00 2001 From: Nikolay Shirokovskiy <nshirokovskiy@tarantool.org> Date: Wed, 7 Dec 2022 11:49:41 +0300 Subject: [PATCH] libunwind: improve incremental build/rebuild Currently we use ExternalProject_Add to build libunwind subproject. The usecases it supports are not quite aligned with our. Configuration and build steps are rerun in case download step sees updated sources. But we use git submodules to fetch third-party projects and thus subproject is not rebuild when it's files are changed on disk. Either because of we doing some sort of experiments locally or because the new sources are brought by git pull. Actually libunwind has a CMakeList.txt so that we could try to build it just as project subdirectory. But it requires quite a fresh CMake version, currently supports only build on Visual Studio and probably is not up to date given it's package version variables. Keeping CMakeLists.txt in sync with main autotools build is additional maintenance burden. So I'd like to use main autotools build. So ext_project_autotools function is added to provide nicer build integration with third-party projects which are build using autotools. Actually incremental rebuild works only if CMake is at least 3.12 but I think this should be true for developer installations. Hopefully we can reuse the function in case we need to bundle more subprojects with autotools builds. Follow-up #5665 NO_DOC=build improvement NO_TEST=build improvement NO_CHANGELOG=build improvement --- CMakeLists.txt | 1 - cmake/BuildLibUnwind.cmake | 149 ++++++++++++------------------ cmake/ext_project_autotools.cmake | 119 ++++++++++++++++++++++++ cmake/utils.cmake | 13 +++ 4 files changed, 193 insertions(+), 89 deletions(-) create mode 100644 cmake/ext_project_autotools.cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 25059b62ff..71e69c941d 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -543,7 +543,6 @@ if(ENABLE_BACKTRACE) endif() include(BuildLibUnwind) - libunwind_build() add_dependencies(build_bundled_libs bundled-libunwind bundled-libunwind-platform) diff --git a/cmake/BuildLibUnwind.cmake b/cmake/BuildLibUnwind.cmake index 4890e72716..52fa32c901 100644 --- a/cmake/BuildLibUnwind.cmake +++ b/cmake/BuildLibUnwind.cmake @@ -9,100 +9,73 @@ Result Variables Include directory needed to use libunwind. ``LIBUNWIND_LIBRARIES`` Libraries needed to link to libunwind. - -Cache Variables -^^^^^^^^^^^^^^^ -``LIBUNWIND_INCLUDE_DIR`` - The directory containing ``libunwind.h``. -``LIBUNWIND_LIBRARIES`` - The paths to the libunwind libraries. #]========================================================================] -macro(libunwind_build) - set(LIBUNWIND_SOURCE_DIR ${PROJECT_SOURCE_DIR}/third_party/libunwind) - set(LIBUNWIND_BUILD_DIR ${PROJECT_BINARY_DIR}/build/libunwind) - set(LIBUNWIND_BINARY_DIR ${LIBUNWIND_BUILD_DIR}/work) - set(LIBUNWIND_INSTALL_DIR ${LIBUNWIND_BUILD_DIR}/dest) - set(LIBUNWIND_CFLAGS "${DEPENDENCY_CFLAGS} -g -O2") - set(LIBUNWIND_CXXFLAGS "-g -O2") - - include(ExternalProject) - ExternalProject_Add(bundled-libunwind-project - TMP_DIR ${LIBUNWIND_BUILD_DIR}/tmp - STAMP_DIR ${LIBUNWIND_BUILD_DIR}/stamp - SOURCE_DIR ${LIBUNWIND_SOURCE_DIR} - BINARY_DIR ${LIBUNWIND_BINARY_DIR} - INSTALL_DIR ${LIBUNWIND_INSTALL_DIR} - - DOWNLOAD_COMMAND "" - - CONFIGURE_COMMAND - autoreconf -i <SOURCE_DIR> && - <SOURCE_DIR>/configure - AR=${CMAKE_AR} - CC=${CMAKE_C_COMPILER} - CXX=${CMAKE_CXX_COMPILER} - CFLAGS=${LIBUNWIND_CFLAGS} - CXXFLAGS=${LIBUNWIND_CXXFLAGS} - --prefix=<INSTALL_DIR> - # Bundled libraries are linked statically. - --disable-shared - # Ditto. - --enable-static - # See https://github.com/libunwind/libunwind/blob/e07b43c02d5cf1ea060c018fdf2e2ad34b7c7d80/configure.ac#L122-L125. - --disable-coredump - # See https://github.com/libunwind/libunwind/blob/e07b43c02d5cf1ea060c018fdf2e2ad34b7c7d80/configure.ac#L130-L133. - --disable-ptrace - # See https://github.com/libunwind/libunwind/blob/e07b43c02d5cf1ea060c018fdf2e2ad34b7c7d80/configure.ac#L138-L141. - --disable-setjmp - # See https://github.com/libunwind/libunwind/blob/e07b43c02d5cf1ea060c018fdf2e2ad34b7c7d80/configure.ac#L143-L145 - --disable-documentation - # See https://github.com/libunwind/libunwind/blob/e07b43c02d5cf1ea060c018fdf2e2ad34b7c7d80/configure.ac#L147-L149 - --disable-tests - # By default libunwind provides a weak alias to - # `backtrace` function: this can lead to a conflict with - # glibc's `backtrace`, see https://github.com/libunwind/libunwind/blob/e07b43c02d5cf1ea060c018fdf2e2ad34b7c7d80/configure.ac#L151-L153 - --disable-weak-backtrace - # See https://github.com/libunwind/libunwind/blob/e07b43c02d5cf1ea060c018fdf2e2ad34b7c7d80/configure.ac#L155-L157 - --disable-unwind-header - # See https://github.com/libunwind/libunwind/blob/e07b43c02d5cf1ea060c018fdf2e2ad34b7c7d80/configure.ac#L302-L317 - --disable-minidebuginfo - # See https://github.com/libunwind/libunwind/blob/e07b43c02d5cf1ea060c018fdf2e2ad34b7c7d80/configure.ac#L319-L334 - --disable-zlibdebuginfo - - LOG_BUILD TRUE - LOG_INSTALL TRUE - LOG_MERGED_STDOUTERR TRUE - LOG_OUTPUT_ON_FAILURE TRUE +include(ext_project_autotools) - EXCLUDE_FROM_ALL +set(LIBUNWIND_CFLAGS "${DEPENDENCY_CFLAGS} -g -O2") +set(LIBUNWIND_CXXFLAGS "-g -O2") - BUILD_BYPRODUCTS ${LIBUNWIND_INSTALL_DIR}/lib/libunwind-x86_64.a - BUILD_BYPRODUCTS ${LIBUNWIND_INSTALL_DIR}/lib/libunwind.a) - unset(LIBUNWIND_CFLAGS) - unset(LIBUNWIND_CXXFLAGS) +ext_project_autotools(libunwind-build + DIR + third_party/libunwind + CONFIGURE + AR=${CMAKE_AR} + CC=${CMAKE_C_COMPILER} + CXX=${CMAKE_CXX_COMPILER} + CFLAGS=${LIBUNWIND_CFLAGS} + CXXFLAGS=${LIBUNWIND_CXXFLAGS} + # Bundled libraries are linked statically. + --disable-shared + # Ditto. + --enable-static + # See https://github.com/libunwind/libunwind/blob/e07b43c02d5cf1ea060c018fdf2e2ad34b7c7d80/configure.ac#L122-L125. + --disable-coredump + # See https://github.com/libunwind/libunwind/blob/e07b43c02d5cf1ea060c018fdf2e2ad34b7c7d80/configure.ac#L130-L133. + --disable-ptrace + # See https://github.com/libunwind/libunwind/blob/e07b43c02d5cf1ea060c018fdf2e2ad34b7c7d80/configure.ac#L138-L141. + --disable-setjmp + # See https://github.com/libunwind/libunwind/blob/e07b43c02d5cf1ea060c018fdf2e2ad34b7c7d80/configure.ac#L143-L145 + --disable-documentation + # See https://github.com/libunwind/libunwind/blob/e07b43c02d5cf1ea060c018fdf2e2ad34b7c7d80/configure.ac#L147-L149 + --disable-tests + # By default libunwind provides a weak alias to + # `backtrace` function: this can lead to a conflict with + # glibc's `backtrace`, see https://github.com/libunwind/libunwind/blob/e07b43c02d5cf1ea060c018fdf2e2ad34b7c7d80/configure.ac#L151-L153 + --disable-weak-backtrace + # See https://github.com/libunwind/libunwind/blob/e07b43c02d5cf1ea060c018fdf2e2ad34b7c7d80/configure.ac#L155-L157 + --disable-unwind-header + # See https://github.com/libunwind/libunwind/blob/e07b43c02d5cf1ea060c018fdf2e2ad34b7c7d80/configure.ac#L302-L317 + --disable-minidebuginfo + # See https://github.com/libunwind/libunwind/blob/e07b43c02d5cf1ea060c018fdf2e2ad34b7c7d80/configure.ac#L319-L334 + --disable-zlibdebuginfo + BYPRODUCTS + src/.libs/libunwind.a + src/.libs/libunwind-${CMAKE_SYSTEM_PROCESSOR}.a +) +unset(LIBUNWIND_CFLAGS) +unset(LIBUNWIND_CXXFLAGS) - add_library(bundled-libunwind STATIC IMPORTED GLOBAL) - set_target_properties(bundled-libunwind PROPERTIES - IMPORTED_LOCATION - ${LIBUNWIND_INSTALL_DIR}/lib/libunwind.a) - add_dependencies(bundled-libunwind bundled-libunwind-project) +set(LIBUNWIND_BUILD_DIR ${CMAKE_BINARY_DIR}/third_party/libunwind) - add_library(bundled-libunwind-platform STATIC IMPORTED GLOBAL) - set_target_properties(bundled-libunwind-platform PROPERTIES - IMPORTED_LOCATION - ${LIBUNWIND_INSTALL_DIR}/lib/libunwind-${CMAKE_SYSTEM_PROCESSOR}.a) - add_dependencies(bundled-libunwind-platform bundled-libunwind-project) +add_library(bundled-libunwind STATIC IMPORTED GLOBAL) +set_target_properties(bundled-libunwind PROPERTIES + IMPORTED_LOCATION + ${LIBUNWIND_BUILD_DIR}/src/.lib/libunwind.a) +add_dependencies(bundled-libunwind libunwind-build) - set(LIBUNWIND_INCLUDE_DIR ${LIBUNWIND_INSTALL_DIR}/include) - set(LIBUNWIND_LIBRARIES - ${LIBUNWIND_INSTALL_DIR}/lib/libunwind-${CMAKE_SYSTEM_PROCESSOR}.a - ${LIBUNWIND_INSTALL_DIR}/lib/libunwind.a) +add_library(bundled-libunwind-platform STATIC IMPORTED GLOBAL) +set_target_properties(bundled-libunwind-platform PROPERTIES + IMPORTED_LOCATION + ${LIBUNWIND_BUILD_DIR}/src/.lib/libunwind-${CMAKE_SYSTEM_PROCESSOR}.a) +add_dependencies(bundled-libunwind-platform libunwind-build) - message(STATUS "Using bundled libunwind") +set(LIBUNWIND_INCLUDE_DIR + ${LIBUNWIND_BUILD_DIR}/include + ${CMAKE_SOURCE_DIR}/third_party/libunwind/include) +set(LIBUNWIND_LIBRARIES + ${LIBUNWIND_BUILD_DIR}/src/.libs/libunwind-${CMAKE_SYSTEM_PROCESSOR}.a + ${LIBUNWIND_BUILD_DIR}/src/.libs/libunwind.a) - unset(LIBUNWIND_SOURCE_DIR) - unset(LIBUNWIND_BUILD_DIR) - unset(LIBUNWIND_BINARY_DIR) - unset(LIBUNWIND_INSTALL_DIR) -endmacro() +unset(LIBUNWIND_BUILD_DIR) +message(STATUS "Using bundled libunwind") diff --git a/cmake/ext_project_autotools.cmake b/cmake/ext_project_autotools.cmake new file mode 100644 index 0000000000..a213992686 --- /dev/null +++ b/cmake/ext_project_autotools.cmake @@ -0,0 +1,119 @@ +include(utils) +include(ProcessorCount) + +# Build a third-party project that uses autotools to generate build system. +# Basically it generates configure script, run configure and then make and +# rerun them according to their dependants in case of incremental build. +# +# DIR - relative path to project directory in main project tree. +# CONFIGURE - options for configure script. +# BYPRODUCTS - products caller targets depend upon. +# +# Note that incremental rebuild in case autotools files or config files are +# changed is available only if CMake version is at least 3.12. Otherwise +# rebuild will not be triggered. Rebuild is only required for developers +# which presumably has newer CMake versions. +function(ext_project_autotools name) + set(sv_keywords + DIR + ) + set(mv_keywords + CONFIGURE + BYPRODUCTS + ) + set(options + "" + ) + cmake_parse_arguments( + ARGS "${options}" "${sv_keywords}" "${mv_keywords}" ${ARGN} + ) + if (NOT DEFINED ARGS_DIR) + message(FATAL_ERROR "DIR argument is mandatory") + endif() + + # CONFIGURE_DEPENDS available since 3.12 + # VERSION_GREATER_EQUAL is only since 3.7 + if (${CMAKE_VERSION} VERSION_GREATER "3.12" OR + ${CMAKE_VERSION} VERSION_EQUAL "3.12") + set(autotool_files_standard + configure.ac + acinclude.m4 + ) + list_add_prefix( + autotool_files_standard "${ARGS_DIR}/" autotool_files_standard + ) + + file( + GLOB_RECURSE autotool_files_am + CONFIGURE_DEPENDS + RELATIVE "${CMAKE_SOURCE_DIR}" + "${ARGS_DIR}/*.am" + ) + + set(autotool_files ${autotool_files_am} ${autotool_files_standard}) + + file( + GLOB_RECURSE config_files_in + CONFIGURE_DEPENDS + RELATIVE "${CMAKE_SOURCE_DIR}" + "${ARGS_DIR}/*.in" + ) + endif() + + list_add_prefix(ARGS_BYPRODUCTS "${ARGS_DIR}/" byproducts) + + file(MAKE_DIRECTORY ${CMAKE_BINARY_DIR}/${ARGS_DIR}) + + add_custom_command( + OUTPUT ${CMAKE_SOURCE_DIR}/${ARGS_DIR}/configure + WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/${ARGS_DIR} + COMMAND + autoreconf -i + COMMAND + # autoreconf do not touch configure on all paths. For example if + # only *.am are changed. We don't aim to follow precise dependency + # path of autotools thus let's just update configure in this case. + touch configure + DEPENDS ${autotool_files} + ) + + add_custom_command( + OUTPUT ${ARGS_DIR}/Makefile + WORKING_DIRECTORY ${ARGS_DIR} + COMMAND ${CMAKE_SOURCE_DIR}/${ARGS_DIR}/configure ${ARGS_CONFIGURE} + DEPENDS ${ARGS_DIR}/configure + ) + + # Some *.in files are products of *.am files and some are not. For the + # latter we don't need to run autoreconf && configure as it will be + # overkill. We only need to run config.status. + add_custom_command( + OUTPUT + ${ARGS_DIR}/config.log + WORKING_DIRECTORY ${ARGS_DIR} + COMMAND ./config.status + DEPENDS + ${config_files_in} + # This dependency reflects dependency on config.status. + # config.status and Makefile are both updated on configure run. + ${ARGS_DIR}/Makefile + ) + + ProcessorCount(nproc) + if (nproc) + set(make make -j${nproc}) + else() + set(make make) + endif() + + add_custom_target(${name} + WORKING_DIRECTORY ${ARGS_DIR} + COMMAND + ${make} + DEPENDS + ${ARGS_DIR}/Makefile + ${ARGS_DIR}/config.log + BYPRODUCTS + ${byproducts} + ) +endfunction() diff --git a/cmake/utils.cmake b/cmake/utils.cmake index 9d23d6f1a3..a0c305fa59 100644 --- a/cmake/utils.cmake +++ b/cmake/utils.cmake @@ -114,3 +114,16 @@ function(file_is_in_directory varname file dir) set(${varname} TRUE PARENT_SCOPE) endif() endfunction() + +# list() has TRANSFORM option but only since 3.12. +function(list_add_prefix + list_in + prefix + list_out +) + set(result "") + foreach(i ${${list_in}}) + list(APPEND result "${prefix}${i}") + endforeach() + set(${list_out} ${result} PARENT_SCOPE) +endfunction() -- GitLab