使用 cmake 中的自动工具构建库

发布于 2024-11-06 19:35:37 字数 1187 浏览 1 评论 0原文

这是我第一次尝试使用 cmake,如果可能的话,我希望得到一些关于我所做的事情的反馈,因为仍然存在一些问题。

在库文件夹的 CMakeLists.txt 中,我创建了两个 makefile 目标:configure-antlr3cantlr3c。第一个目标运行 autotools 配置 shell 脚本,第二个目标运行 make 可执行文件来构建库:

# CMakeLists.txt in libantlr3c-3.1.3 
add_custom_target(
  configure-antlr3c
  ${SHELL_EXECUTABLE} configure
  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)

add_custom_target(
  antlr3c
   ${MAKE}
   DEPENDS configure-antlr3c
   WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)

主要问题是 configure-antlr3c 目标始终“过时”,因此它始终会即使没有发生任何变化也会被执行。此外,我必须在单独的目录(而不是在我的项目的根目录中)生成我的cmake makefile,以避免覆盖库的autotools Makefile...

有人遇到过这个问题吗(使用cmake构建autotools项目)?如果是这样,您的解决方案是什么?

谢谢。

编辑:解决方案 在根 CMakeLists.txt 中:

include(ExternalProject)
ExternalProject_Add(
  libantlr3c
  SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/libantlr3c-3.1.3
  CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/lib/libantlr3c-3.1.3/configure --prefix=${CMAKE_CURRENT_SOURCE_DIR}/lib/libantlr3c-3.1.3
  PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/lib/libantlr3c-3.1.3
  BUILD_COMMAND make
  BUILD_IN_SOURCE 1
)

This is my first try with cmake and I would like to have, if possible, some feedbacks about what I did since some problems remain.

In the CMakeLists.txt of the library folder, I created two makefile targets: configure-antlr3c and antlr3c. The first target runs the autotools configuration shell script, the second one runs the make executable to build the library:

# CMakeLists.txt in libantlr3c-3.1.3 
add_custom_target(
  configure-antlr3c
  ${SHELL_EXECUTABLE} configure
  WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)

add_custom_target(
  antlr3c
   ${MAKE}
   DEPENDS configure-antlr3c
   WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
)

The main problem is thatconfigure-antlr3c target is always "out of date", so it will always be executed even if no changes happened. Moreover, I necessarily need to generate my cmake makefiles in a separate directory (not in the root directory of my project) to avoid overriding the autotools Makefile of the library...

Has anyone had this problem (building autotools projects with cmake) ? And if so, what have been your solutions ?

Thank you.

EDIT : Solution
In the root CMakeLists.txt:

include(ExternalProject)
ExternalProject_Add(
  libantlr3c
  SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/lib/libantlr3c-3.1.3
  CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/lib/libantlr3c-3.1.3/configure --prefix=${CMAKE_CURRENT_SOURCE_DIR}/lib/libantlr3c-3.1.3
  PREFIX ${CMAKE_CURRENT_SOURCE_DIR}/lib/libantlr3c-3.1.3
  BUILD_COMMAND make
  BUILD_IN_SOURCE 1
)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

記柔刀 2024-11-13 19:35:37

我认为你最好使用 ExternalProject< /a> cmake 的功能。我猜你有你的项目并且在子目录中有 libantrl ?

project
      +- libantlr
      +- mysrc
  ---- etc ----

如果是这种情况,您可以在顶层 CMakeLists.txt 中执行类似的操作:

cmake_minimum_required(VERSION 2.8)
project(test)
include(ExternalProject)
ExternalProject_Add(libantlr
    SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libantlr
    CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/libantlr/configure --prefix=<INSTALL_DIR>
    BUILD_COMMAND ${MAKE})

扩展为类似 libantlr-prefix 的内容,因此内容会安装在您的构建树中,而不是在 /usr/local 中,这就是 autotools 在没有前缀的情况下所做的事情。

I think that you'd be better off using the ExternalProject feature of cmake. I guess you have your project and have libantrl in a sub directory?

project
      +- libantlr
      +- mysrc
  ---- etc ----

If that's the case, you can do something like this in the top level CMakeLists.txt:

cmake_minimum_required(VERSION 2.8)
project(test)
include(ExternalProject)
ExternalProject_Add(libantlr
    SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/libantlr
    CONFIGURE_COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/libantlr/configure --prefix=<INSTALL_DIR>
    BUILD_COMMAND ${MAKE})

The <INSTALL_DIR> is expanded to something like libantlr-prefix, so things are installed in your build tree rather than in /usr/local, which is what autotools would do without a prefix.

樱花落人离去 2024-11-13 19:35:37

我在大约四年前发布了下面的答案,似乎仍然偶尔会被点赞。答案“有效”,但在几个方面存在问题。此外,与此同时,CMake 现在拥有 FetchContent,它应该在大多数(如果不是全部)用例中取代 ExternalProject。事实上,任何阅读本文的人都应该考虑使用 CMake Package Manager (CPM),它包装了FetchContent 并提供了一些非常好的功能,同时与 FetchContent 相比,复杂性相对较小。下面的新示例使用 FetchContent,但可以轻松修改为使用 CPM

此代码安装 libgpiod,但只需稍加更改即可用于安装使用 autotools 构建的其他软件包。


include(FetchContent)

# This integrates libgpiod into the CMake build process.
# libgpiod uses `autotools`, sometimes referred to as (autohell)[https://www.shlomifish.org/open-source/anti/autohell/].
# While the cmake code below is fairly clean, it was not so easy to discover.
# Here are some notes for things I figured out that may be useful to anyone
# trying to do something similar.
#
# 1. `autotools` typically is used to configure and build in the source directory.
#    This is not ideal for CMake, which prefers to build in a separate directory.
#    The pattern used below does the trick: set the WORKING_DIRECTORY to the
#    binary/build directory, but reference autgen.sh with the source directory path.
# 1b. `autotools` requires packages that we do not typically install in our build
#    images. I had to modify our docker image to include pkg-config & autoconf-archive.
#    The error messages before doing this were not very helpful.
# 2. `FetchContent` is nicer to use than `ExternalProject`. The structure below
#    is similar to how we use `FetchContent` elsewhere.
# 3. `autotools` uses the `CC` and `CXX` environment variables to determine the
#    compiler to use. We want to ensure that when cross-compiling these variables
#    are set to the correct compiler as defined by the toolchain. The code below
#    sets these variables in the environment for the `autogen.sh` script.
# 4. `autotools` uses the `--host` flag to determine the target architecture.
# 5. The INSTALL directory is simply the root of the build directory, e.g.
#    `builds/min-x86` in our build system. In that directory all of the built
#     library and header artifacts are placed into the `lib` and `include`
#     directories, respectively. This is the expected struture for CMake,
#     and we largely get it for free by using `FetchContent`.
# 6. We use `pkg-config` to find the installed library. This is nice but requires
#    that CMAKE_PREFIX_PATH include the INSTALL directory, which bizarrely is
#    not the default.
# 7. `pkg_check_modules` with the `IMPORTED_TARGET` option is able to create
#    a modern cmake target for the library. Such targets are always scoped with
#    `PkgConfig::`. In this case, the target is `PkgConfig::gpiod`.

set(INSTALL ${PROJECT_BINARY_DIR})

list(APPEND CMAKE_PREFIX_PATH ${INSTALL})
find_package(PkgConfig REQUIRED)

FetchContent_Declare(libgpiod
    GIT_REPOSITORY "https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git"
    GIT_TAG v2.0.1
    UPDATE_DISCONNECTED ON
)

# If libgpiod is already installed, we can use pkg-config to find it.
pkg_check_modules(gpiod IMPORTED_TARGET libgpiod)

if(NOT libgpiod_POPULATED AND NOT gpiod_FOUND)
    FetchContent_Populate(libgpiod)

    set(ENV${CC} ${CMAKE_C_COMPILER})
    set(ENV{CXX} ${CMAKE_CXX_COMPILER})

    # Configure
    execute_process(
        OUTPUT_QUIET
        COMMAND ${libgpiod_SOURCE_DIR}/autogen.sh --host=${CMAKE_SYSTEM_PROCESSOR} -enable-tools=yes --enable-bindings-cxx --prefix=${INSTALL}
        WORKING_DIRECTORY ${libgpiod_BINARY_DIR}
    )

    # Build
    execute_process(
        OUTPUT_QUIET
        COMMAND make
        WORKING_DIRECTORY ${libgpiod_BINARY_DIR}
    )

    # Install
    execute_process(
        OUTPUT_QUIET
        COMMAND make install
        WORKING_DIRECTORY ${libgpiod_BINARY_DIR}
    )

    pkg_check_modules(gpiod REQUIRED IMPORTED_TARGET libgpiod)

endif()

下面是 4 年前的答案,可能作为参考很有用。

我需要做类似的事情,但发现很难找到可行的解决方案,尽管这里提供了示例并提供了已接受的答案,以及其他几篇博客文章中提供的代码片段、CMake 支持电子邮件 listserv 档案等。为了让遇到此问题的其他人受益,这里是我的解决方案。

我们想要使用的外部项目是 libmodbus,尽管我相信我的解决方案足够通用,可以与使用 ./autoconf 的标准 autoconf 配方配置的任何项目一起使用.sh &&配置.sh &&制作&&进行安装

我们想要添加 libmodbus 作为 git 存储库的子模块。我们将其添加到存储库的路径 /opt/libmodbus 中。配置它的 CMake 代码位于 /cmake/modbus.cmake 中,它包含在我们的根 CMakeLists.txt 中使用

# libmodbus
include(cmake/modbus.cmake)

cmake/modbus.cmake 的内容是:

include(ExternalProject)

set(MODBUS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/opt/libmodbus)
set(MODBUS_BIN ${CMAKE_CURRENT_BINARY_DIR}/libmodbus)
set(MODBUS_STATIC_LIB ${MODBUS_BIN}/lib/libmodbus.a)
set(MODBUS_INCLUDES ${MODBUS_BIN}/include)

file(MAKE_DIRECTORY ${MODBUS_INCLUDES})

ExternalProject_Add(
    libmodbus
    PREFIX ${MODBUS_BIN}
    SOURCE_DIR ${MODBUS_DIR}
    DOWNLOAD_COMMAND cd ${MODBUS_DIR} && git clean -dfX && ${MODBUS_DIR}/autogen.sh
    CONFIGURE_COMMAND ${MODBUS_DIR}/configure --srcdir=${MODBUS_DIR} --prefix=${MODBUS_BIN} --enable-static=yes --disable-shared
    BUILD_COMMAND make
    INSTALL_COMMAND make install
    BUILD_BYPRODUCTS ${MODBUS_STATIC_LIB}
)

add_library(modbus STATIC IMPORTED GLOBAL)

add_dependencies(modbus libmodbus)

set_target_properties(modbus PROPERTIES IMPORTED_LOCATION ${MODBUS_STATIC_LIB})
set_target_properties(modbus PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${MODBUS_INCLUDES})

使用 libmodbus 的组件可以照常声明其依赖项:

    add_executable(hello_modbus main.cpp)
    target_link_libraries(hello_modbus modbus)

一些注意事项:

  1. 这滥用了 DOWNLOAD_COMMAND代码>执行 autogen.sh 步骤。 git clean -dfX 可能不是必需的(它是使用 BUILD_IN_SOURCE 选项的早期版本的剩余部分。如果您确实想下载代码而不是使用git 子模块,您需要适当地修改此行,
  2. ,我们会不厌其烦地强制构建静态库。
  3. 如果您想要共享库 如果没有 BUILD_BYPRODUCTS ${MODBUS_STATIC_LIB} 声明,用于设置 IMPORTED_LOCATIONset_target_properties 命令将会失败。
  4. 同样,set_target_properties 命令 也会失败。如果没有 file(MAKE_DIRECTORY) 设置 INTERFACE_INCLUDE_DIRECTORIES 将失败${MODBUS_INCLUDES})。

I posted the answer below almost four years ago and it seems that it is still occasionally being upvoted. The answer "works" but it problematic in several ways. Also, in the meantime CMake now has FetchContent which should replace ExternalProject in most if not all use cases. In fact, anyone reading this should consider using the CMake Package Manager (CPM) which wraps FetchContent and provides some very nice features while being a relatively minimal step up in complexity over FetchContent. The new example below uses FetchContent but could easily be modified to use CPM.

This code installs libgpiod but should be usable with only minor changes to for installing other packages built with autotools.


include(FetchContent)

# This integrates libgpiod into the CMake build process.
# libgpiod uses `autotools`, sometimes referred to as (autohell)[https://www.shlomifish.org/open-source/anti/autohell/].
# While the cmake code below is fairly clean, it was not so easy to discover.
# Here are some notes for things I figured out that may be useful to anyone
# trying to do something similar.
#
# 1. `autotools` typically is used to configure and build in the source directory.
#    This is not ideal for CMake, which prefers to build in a separate directory.
#    The pattern used below does the trick: set the WORKING_DIRECTORY to the
#    binary/build directory, but reference autgen.sh with the source directory path.
# 1b. `autotools` requires packages that we do not typically install in our build
#    images. I had to modify our docker image to include pkg-config & autoconf-archive.
#    The error messages before doing this were not very helpful.
# 2. `FetchContent` is nicer to use than `ExternalProject`. The structure below
#    is similar to how we use `FetchContent` elsewhere.
# 3. `autotools` uses the `CC` and `CXX` environment variables to determine the
#    compiler to use. We want to ensure that when cross-compiling these variables
#    are set to the correct compiler as defined by the toolchain. The code below
#    sets these variables in the environment for the `autogen.sh` script.
# 4. `autotools` uses the `--host` flag to determine the target architecture.
# 5. The INSTALL directory is simply the root of the build directory, e.g.
#    `builds/min-x86` in our build system. In that directory all of the built
#     library and header artifacts are placed into the `lib` and `include`
#     directories, respectively. This is the expected struture for CMake,
#     and we largely get it for free by using `FetchContent`.
# 6. We use `pkg-config` to find the installed library. This is nice but requires
#    that CMAKE_PREFIX_PATH include the INSTALL directory, which bizarrely is
#    not the default.
# 7. `pkg_check_modules` with the `IMPORTED_TARGET` option is able to create
#    a modern cmake target for the library. Such targets are always scoped with
#    `PkgConfig::`. In this case, the target is `PkgConfig::gpiod`.

set(INSTALL ${PROJECT_BINARY_DIR})

list(APPEND CMAKE_PREFIX_PATH ${INSTALL})
find_package(PkgConfig REQUIRED)

FetchContent_Declare(libgpiod
    GIT_REPOSITORY "https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git"
    GIT_TAG v2.0.1
    UPDATE_DISCONNECTED ON
)

# If libgpiod is already installed, we can use pkg-config to find it.
pkg_check_modules(gpiod IMPORTED_TARGET libgpiod)

if(NOT libgpiod_POPULATED AND NOT gpiod_FOUND)
    FetchContent_Populate(libgpiod)

    set(ENV${CC} ${CMAKE_C_COMPILER})
    set(ENV{CXX} ${CMAKE_CXX_COMPILER})

    # Configure
    execute_process(
        OUTPUT_QUIET
        COMMAND ${libgpiod_SOURCE_DIR}/autogen.sh --host=${CMAKE_SYSTEM_PROCESSOR} -enable-tools=yes --enable-bindings-cxx --prefix=${INSTALL}
        WORKING_DIRECTORY ${libgpiod_BINARY_DIR}
    )

    # Build
    execute_process(
        OUTPUT_QUIET
        COMMAND make
        WORKING_DIRECTORY ${libgpiod_BINARY_DIR}
    )

    # Install
    execute_process(
        OUTPUT_QUIET
        COMMAND make install
        WORKING_DIRECTORY ${libgpiod_BINARY_DIR}
    )

    pkg_check_modules(gpiod REQUIRED IMPORTED_TARGET libgpiod)

endif()

The 4-year old answer is below, which might be useful as reference.

I needed to do something similar but found it surprisingly difficult to get a working solution, despite the example provided here with the accepted answer, and code snippets provided in several other blog posts, the CMake support email listserv archives, etc. For the benefit of others who come across this question, here is my solution.

The external project we wanted to use is libmodbus, though I believe my solution is general enough to work with any project configured with the standard autoconf recipe of ./autoconf.sh && configure.sh && make && make install.

We wanted to add libmodbus as a submodule of our git repository. We added to our repository at the path <root>/opt/libmodbus. The CMake code to configure it is located in <root>/cmake/modbus.cmake, which is included from our root CMakeLists.txt using

# libmodbus
include(cmake/modbus.cmake)

The content of cmake/modbus.cmake is:

include(ExternalProject)

set(MODBUS_DIR ${CMAKE_CURRENT_SOURCE_DIR}/opt/libmodbus)
set(MODBUS_BIN ${CMAKE_CURRENT_BINARY_DIR}/libmodbus)
set(MODBUS_STATIC_LIB ${MODBUS_BIN}/lib/libmodbus.a)
set(MODBUS_INCLUDES ${MODBUS_BIN}/include)

file(MAKE_DIRECTORY ${MODBUS_INCLUDES})

ExternalProject_Add(
    libmodbus
    PREFIX ${MODBUS_BIN}
    SOURCE_DIR ${MODBUS_DIR}
    DOWNLOAD_COMMAND cd ${MODBUS_DIR} && git clean -dfX && ${MODBUS_DIR}/autogen.sh
    CONFIGURE_COMMAND ${MODBUS_DIR}/configure --srcdir=${MODBUS_DIR} --prefix=${MODBUS_BIN} --enable-static=yes --disable-shared
    BUILD_COMMAND make
    INSTALL_COMMAND make install
    BUILD_BYPRODUCTS ${MODBUS_STATIC_LIB}
)

add_library(modbus STATIC IMPORTED GLOBAL)

add_dependencies(modbus libmodbus)

set_target_properties(modbus PROPERTIES IMPORTED_LOCATION ${MODBUS_STATIC_LIB})
set_target_properties(modbus PROPERTIES INTERFACE_INCLUDE_DIRECTORIES ${MODBUS_INCLUDES})

A component that uses libmodbus can declare its dependency as usual:

    add_executable(hello_modbus main.cpp)
    target_link_libraries(hello_modbus modbus)

A few notes:

  1. This abuses the DOWNLOAD_COMMAND to perform the autogen.sh step. The git clean -dfX is probably not necessary (it is a leftover from an earlier version that used the BUILD_IN_SOURCE option. If you really want to download the code instead of using a git submodule, you'll need to modify this line appropriately.
  2. We go to the trouble to force a static-only build of the library. Adjust your configure command line if you want shared libraries.
  3. The set_target_properties command to set the IMPORTED_LOCATION will fail without the BUILD_BYPRODUCTS ${MODBUS_STATIC_LIB} declaration.
  4. Likewise, the set_target_properties command to set the INTERFACE_INCLUDE_DIRECTORIES will fail without the file(MAKE_DIRECTORY ${MODBUS_INCLUDES}).
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文