我如何使用CMake按需构建wxwidgets并与之链接

时间:2022-01-02 22:57:08

I have the following situation:

我有以下情况:

  • I'm working on an application that depends on a number of third party libs, among them wxwidgets
  • 我正在开发一个依赖于许多第三方库的应用程序,其中包括wxwidgets
  • I build the application for multiple target configurations (x86, arm, Linux, Windows) using Linux as my build host system
  • 我使用Linux作为构建主机系统,为多个目标配置(x86,arm,Linux,Windows)构建应用程序
  • Due to the above mentioned multiple target configurations, I have chosen to build those third-party libs from source, using CMake's ExternalProject_Add function.
  • 由于上面提到的多目标配置,我选择使用CMake的ExternalProject_Add函数从源构建那些第三方库。
  • The third-party libs are built 'on-demand' at a location separate from my application's CMAKE_BINARY_DIR so that I can wipe the build tree for my application without having to rebuild the third-party libs (takes a looooong time).
  • 第三方库是在与我的应用程序的CMAKE_BINARY_DIR分开的位置“按需”构建的,这样我就可以擦除应用程序的构建树而无需重建第三方库(需要花费很长时间)。
  • The location of the third-party libs is different depending on what target configuration I build them for (obviously).
  • 第三方库的位置根据我为其构建的目标配置而有所不同(显然)。

I'm quite new to CMake and the problem I currently face is this: The source files in my application can't find the wx include files and I need to set the correct linker flags to be able to link my application against wxwidgets.

我对CMake很新,我目前面临的问题是:我的应用程序中的源文件找不到wx包含文件,我需要设置正确的链接器标志才能将我的应用程序链接到wxwidgets。

This seems to be handled by a utility 'wx-config' that provides exactly that info as output when run with either the --cppflags or --libs flag. I can not however, figure out how to catch that output and append it to the include dirs and linked libraries I setup from my CMakeLists.txt files.

这似乎是由一个实用程序'wx-config'处理的,它在使用--cppflags或--libs标志运行时提供了完全相同的信息作为输出。但是,我无法弄清楚如何捕获该输出并将其附加到我从CMakeLists.txt文件设置的include dirs和链接库。

So basically what I want is.

基本上我想要的是。

  1. Build wxwidgets (if it doesn't exist) for the current target configuration
  2. 为当前目标配置构建wxwidgets(如果它不存在)
  3. Run wx-config --cppflags and --libs to find out the correct include dirs and linker flags for the current target configuration
  4. 运行wx-config --cppflags和--libs以找出当前目标配置的正确include dirs和链接器标志
  5. Use the info from step 2 when building targets that are my own application
  6. 构建作为我自己的应用程序的目标时,请使用步骤2中的信息

So far I've tried something like this:

到目前为止,我尝试过这样的事情:

# Set a target-configuration-specific location 
set(wxwidgetsTop ${MYPROJECT_EXTERNAL_DIR}/wxwidgets/wxwidgets_${MYPROJECT_CURRENT_TARGET_CFG})

# Build the project
ExternalProject_Add( wxWidgetsExternal
  PREFIX ${wxwidgetsTop}
  URL ${MYPROJECT_EXTERNAL_DIR}/tarballs/wxWidgets-3.0.2.tar.bz2
  SOURCE_DIR ${wxwidgetsTop}/src/wxwidgets
  CONFIGURE_COMMAND ${configure_cmdline}
  BUILD_COMMAND make -j${MYPROJECT_NCPU}
  INSTALL_COMMAND make install
  )

# Create a wxwidgets target to be used as a dependency from other code
add_library(wxWidgets IMPORTED STATIC GLOBAL)
add_dependencies(wxWidgets wxWidgetsExternal)

# (non-working) attempt to get the correct include dirs and linker
# flags for wxwidgets 
add_custom_command(TARGET wxWidgetsExternal
  POST_BUILD
  COMMAND ${INSTALL_DIR}/bin/wx-config ARGS --cppflags
  COMMENT "Running wx-config"
)

but the above does not provide a way to actually use the result from the custom command to append the cppflags and linker options when building the targets that make up my application.

但是上面没有提供一种方法来实际使用自定义命令的结果来在构建构成我的应用程序的目标时附加cppflags和链接器选项。

What is a good way to achieve what I want?

什么是实现我想要的好方法?

2 个解决方案

#1


2  

I see three different ways of doing this:

我看到三种不同的方式:

Method 1: use find_package

Use wxWidgets as a standalone requirement for your project, and expect the devs to install it before building your project. In your CMakeLists.txt you will need to call find_package(wxWidgets), like this:

使用wxWidgets作为项目的独立需求,并期望开发人员在构建项目之前安装它。在你的CMakeLists.txt中,你需要调用find_package(wxWidgets),如下所示:

    find_package(wxWidgets COMPONENTS net gl core base)
    if(wxWidgets_FOUND)
        include(${wxWidgets_USE_FILE})
        # and for each of your dependent executable/library targets:
        target_link_libraries(<YourTarget> ${wxWidgets_LIBRARIES})
    endif()

This has the advantage of not rebuilding the lib if you rebuild your project, however it requires some work for your user (they need to handle the installation of wxWidgets by hand) and for you (you need to setup include paths / compile definitions / ... by hand).

这还没有重建,如果您重建项目的lib的优势,但是它需要用户的一些工作(他们需要处理手动安装的wxWidgets)和你(你需要设置包含路径/编译定义/。 .. 用手)。

Method 2: embed wxWidgets

The second option is to bundle wxWidgets in your repo (svn external or git submodule) and usually (re)write the CMakeLists.txt of this lib to be target-oriented. Then, in your top-most CMakeLists.txt, you can do the following:

第二个选项是在你的repo(svn external或git子模块)中捆绑wxWidgets,并且通常(重新)写这个lib的CMakeLists.txt是面向目标的。然后,在最顶层的CMakeLists.txt中,您可以执行以下操作:

    # for example, if you just need core and net:
    target_link_librairies(my_app PUBLIC wxWidgetsCore wxWidgetsNet)
    # No need to manually setup include dirs, etc...

To make a CMakeLists.txt target-oriented, you define include directories and other compilation properties for a target, not a directory. Example:

要使面向目标的CMakeLists.txt,您可以为目标而不是目录定义包含目录和其他编译属性。例:

    # When defining wxWidgetsCore, for example
    add_library(wxWidgetsCore ...)
    target_include_directories(wxWidgetsCore PUBLIC someDir)
    target_compile_definitions(wxWidgetsCore PUBLIC -pedantic)
    target_link_libraries(wxWidgetsCore PUBLIC someLib)

The drawback of this approach is that rebuilding your project will trigger a rebuild of wxWidgets. However, it is possible to trick this by not using "rebuild" but "clean just my app, then build". Here is some insight on how to achieve this.

这种方法的缺点是重建项目将触发wxWidgets的重建。但是,可以通过不使用“rebuild”但“仅清理我的应用程序,然后构建”来欺骗这一点。以下是如何实现这一目标的一些见解。

Method 3: some sort of hybrid

The big drawback of method 2 leads to the third approach: don't put wxWidgets in your project, but create a CMakeLists.txt that will "import" the lib. The idea: you ask your user for the directory where wxWidgets is installed, then this script will setup everything for your project. First, put the CMakeLists.txt here:

方法2的最大缺点导致第三种方法:不要在项目中放置wxWidgets,而是创建一个“导入”lib的CMakeLists.txt。想法:您向用户询问安装wxWidgets的目录,然后此脚本将为您的项目设置所有内容。首先,将CMakeLists.txt放在这里:

    /your-project-root
        /thirdparty
            /wxWidgets
                CMakeLists.txt
    /dir-where-wxwidgets-is-installed
        ...

Now, you define an imported target:

现在,您定义导入的目标:

    # When defining wxWidgetsCore, for example
    set(WX_INCLUDE_DIR ${USER_SPECIFIED_WX_ROOT}/include)
    add_library(wxWidgetsCore IMPORTED GLOBAL)
    set_property(TARGET wxWidgetsCore APPEND PROPERTY
        INTERFACE_INCLUDE_DIRECTORIES ${WX_INCLUDE_DIR})

See INTERFACE_INCLUDE_DIRECTORIES and INTERFACE_LINK_LIBRARIES. You need your user to have build wxWidgets somewhere in his system, but from your point of view you just do target_link_libraries(your_app PUBLIC wxWidgets...), as in method 2. The advantage is that this approach is interchangeable with method 2 transparently, and you don't put the whole dependency in your project.

请参阅INTERFACE_INCLUDE_DIRECTORIES和INTERFACE_LINK_LIBRARIES。您需要您的用户在他的系统中的某个位置构建wxWidgets,但是从您的角度来看,您只需执行target_link_libraries(your_app PUBLIC wxWidgets ...),如方法2所示。优点是此方法可以透明地与方法2互换,而且你没有把整个依赖项放在你的项目中。

#2


1  

Setting cppflags and linker flags has to be done at CMake time, but you are trying to run wx-config at build time and you are not capturing its output anyway, so your add_custom_command() isn't doing anything useful other than printing things to the build tool's output.

设置cppflags和链接器标志必须在CMake时完成,但是你试图在构建时运行wx-config并且你还没有捕获它的输出,所以你的add_custom_command()除了打印东西之外没有做任何有用的事情。构建工具的输出。

Ideally, you would use the FindwxWidgets module CMake already provides. It requires wxWidgets to already be built (but see further below). Have a look at the CMake documentation for it and see if that at least sounds like what you are trying to achieve manually by using wx-config. If you can get FindwxWidgets to do the job for you, that would be a much cleaner approach.

理想情况下,您将使用CMake已提供的FindwxWidgets模块。它需要构建wxWidgets(但请参见下文)。看看它的CMake文档,看看它是否至少听起来像你试图通过使用wx-config手动实现的。如果你能让FindwxWidgets为你完成这项工作,那将是一个更清洁的方法。

Getting something to build at configure time so you can use it later on in your CMakeLists.txt file is a bit more tricky. ExternalProject_Add() downloads and builds things at build time, but you need wxWidgets to be built earlier at configure time. I wrote an article recently for how to do at least the downloading part at configure time and you should be able to adapt it to do the whole build at configure time instead. The article uses Google Test as its example and can be found here:

在配置时获取构建内容以便稍后在CMakeLists.txt文件中使用它会有点棘手。 ExternalProject_Add()在构建时下载和构建东西,但是您需要在配置时更早地构建wxWidgets。我最近写了一篇文章,关于如何在配置时至少完成下载部分,你应该能够在配置时调整它来完成整个构建。本文使用Google Test作为示例,可在此处找到:

https://crascit.com/2015/07/25/cmake-gtest/

https://crascit.com/2015/07/25/cmake-gtest/

It would be trivial to make it put the wxWidgets build wherever you like, not just in the CMAKE_BINARY_DIR area. That would allow you to have different wxWidgets builds for each build configuration and to be able to wipe out your application's build tree independently of the wxWidgets builds.

将wxWidgets构建在任何你喜欢的地方,而不仅仅是在CMAKE_BINARY_DIR区域,这将是微不足道的。这将允许您为每个构建配置创建不同的wxWidgets构建,并且能够独立于wxWidgets构建消除应用程序的构建树。

Hope that points you in the right direction.

希望能指出你正确的方向。

#1


2  

I see three different ways of doing this:

我看到三种不同的方式:

Method 1: use find_package

Use wxWidgets as a standalone requirement for your project, and expect the devs to install it before building your project. In your CMakeLists.txt you will need to call find_package(wxWidgets), like this:

使用wxWidgets作为项目的独立需求,并期望开发人员在构建项目之前安装它。在你的CMakeLists.txt中,你需要调用find_package(wxWidgets),如下所示:

    find_package(wxWidgets COMPONENTS net gl core base)
    if(wxWidgets_FOUND)
        include(${wxWidgets_USE_FILE})
        # and for each of your dependent executable/library targets:
        target_link_libraries(<YourTarget> ${wxWidgets_LIBRARIES})
    endif()

This has the advantage of not rebuilding the lib if you rebuild your project, however it requires some work for your user (they need to handle the installation of wxWidgets by hand) and for you (you need to setup include paths / compile definitions / ... by hand).

这还没有重建,如果您重建项目的lib的优势,但是它需要用户的一些工作(他们需要处理手动安装的wxWidgets)和你(你需要设置包含路径/编译定义/。 .. 用手)。

Method 2: embed wxWidgets

The second option is to bundle wxWidgets in your repo (svn external or git submodule) and usually (re)write the CMakeLists.txt of this lib to be target-oriented. Then, in your top-most CMakeLists.txt, you can do the following:

第二个选项是在你的repo(svn external或git子模块)中捆绑wxWidgets,并且通常(重新)写这个lib的CMakeLists.txt是面向目标的。然后,在最顶层的CMakeLists.txt中,您可以执行以下操作:

    # for example, if you just need core and net:
    target_link_librairies(my_app PUBLIC wxWidgetsCore wxWidgetsNet)
    # No need to manually setup include dirs, etc...

To make a CMakeLists.txt target-oriented, you define include directories and other compilation properties for a target, not a directory. Example:

要使面向目标的CMakeLists.txt,您可以为目标而不是目录定义包含目录和其他编译属性。例:

    # When defining wxWidgetsCore, for example
    add_library(wxWidgetsCore ...)
    target_include_directories(wxWidgetsCore PUBLIC someDir)
    target_compile_definitions(wxWidgetsCore PUBLIC -pedantic)
    target_link_libraries(wxWidgetsCore PUBLIC someLib)

The drawback of this approach is that rebuilding your project will trigger a rebuild of wxWidgets. However, it is possible to trick this by not using "rebuild" but "clean just my app, then build". Here is some insight on how to achieve this.

这种方法的缺点是重建项目将触发wxWidgets的重建。但是,可以通过不使用“rebuild”但“仅清理我的应用程序,然后构建”来欺骗这一点。以下是如何实现这一目标的一些见解。

Method 3: some sort of hybrid

The big drawback of method 2 leads to the third approach: don't put wxWidgets in your project, but create a CMakeLists.txt that will "import" the lib. The idea: you ask your user for the directory where wxWidgets is installed, then this script will setup everything for your project. First, put the CMakeLists.txt here:

方法2的最大缺点导致第三种方法:不要在项目中放置wxWidgets,而是创建一个“导入”lib的CMakeLists.txt。想法:您向用户询问安装wxWidgets的目录,然后此脚本将为您的项目设置所有内容。首先,将CMakeLists.txt放在这里:

    /your-project-root
        /thirdparty
            /wxWidgets
                CMakeLists.txt
    /dir-where-wxwidgets-is-installed
        ...

Now, you define an imported target:

现在,您定义导入的目标:

    # When defining wxWidgetsCore, for example
    set(WX_INCLUDE_DIR ${USER_SPECIFIED_WX_ROOT}/include)
    add_library(wxWidgetsCore IMPORTED GLOBAL)
    set_property(TARGET wxWidgetsCore APPEND PROPERTY
        INTERFACE_INCLUDE_DIRECTORIES ${WX_INCLUDE_DIR})

See INTERFACE_INCLUDE_DIRECTORIES and INTERFACE_LINK_LIBRARIES. You need your user to have build wxWidgets somewhere in his system, but from your point of view you just do target_link_libraries(your_app PUBLIC wxWidgets...), as in method 2. The advantage is that this approach is interchangeable with method 2 transparently, and you don't put the whole dependency in your project.

请参阅INTERFACE_INCLUDE_DIRECTORIES和INTERFACE_LINK_LIBRARIES。您需要您的用户在他的系统中的某个位置构建wxWidgets,但是从您的角度来看,您只需执行target_link_libraries(your_app PUBLIC wxWidgets ...),如方法2所示。优点是此方法可以透明地与方法2互换,而且你没有把整个依赖项放在你的项目中。

#2


1  

Setting cppflags and linker flags has to be done at CMake time, but you are trying to run wx-config at build time and you are not capturing its output anyway, so your add_custom_command() isn't doing anything useful other than printing things to the build tool's output.

设置cppflags和链接器标志必须在CMake时完成,但是你试图在构建时运行wx-config并且你还没有捕获它的输出,所以你的add_custom_command()除了打印东西之外没有做任何有用的事情。构建工具的输出。

Ideally, you would use the FindwxWidgets module CMake already provides. It requires wxWidgets to already be built (but see further below). Have a look at the CMake documentation for it and see if that at least sounds like what you are trying to achieve manually by using wx-config. If you can get FindwxWidgets to do the job for you, that would be a much cleaner approach.

理想情况下,您将使用CMake已提供的FindwxWidgets模块。它需要构建wxWidgets(但请参见下文)。看看它的CMake文档,看看它是否至少听起来像你试图通过使用wx-config手动实现的。如果你能让FindwxWidgets为你完成这项工作,那将是一个更清洁的方法。

Getting something to build at configure time so you can use it later on in your CMakeLists.txt file is a bit more tricky. ExternalProject_Add() downloads and builds things at build time, but you need wxWidgets to be built earlier at configure time. I wrote an article recently for how to do at least the downloading part at configure time and you should be able to adapt it to do the whole build at configure time instead. The article uses Google Test as its example and can be found here:

在配置时获取构建内容以便稍后在CMakeLists.txt文件中使用它会有点棘手。 ExternalProject_Add()在构建时下载和构建东西,但是您需要在配置时更早地构建wxWidgets。我最近写了一篇文章,关于如何在配置时至少完成下载部分,你应该能够在配置时调整它来完成整个构建。本文使用Google Test作为示例,可在此处找到:

https://crascit.com/2015/07/25/cmake-gtest/

https://crascit.com/2015/07/25/cmake-gtest/

It would be trivial to make it put the wxWidgets build wherever you like, not just in the CMAKE_BINARY_DIR area. That would allow you to have different wxWidgets builds for each build configuration and to be able to wipe out your application's build tree independently of the wxWidgets builds.

将wxWidgets构建在任何你喜欢的地方,而不仅仅是在CMAKE_BINARY_DIR区域,这将是微不足道的。这将允许您为每个构建配置创建不同的wxWidgets构建,并且能够独立于wxWidgets构建消除应用程序的构建树。

Hope that points you in the right direction.

希望能指出你正确的方向。