【问题标题】:CMake resources in source code for build and install用于构建和安装的源代码中的 CMake 资源
【发布时间】:2018-02-22 19:17:54
【问题描述】:

多年来我一直在想这件事。它是关于在代码中使用带有 CMake 的资源文件,以便在构建目录中运行和安装时可以访问它们。

假设您在名为 res 的顶级目录中有运行程序所需的文件。它包含 res/file1.txt 和 res/file2.txt。您构建此程序,并将资源复制到构建目录,在开发期间可以从该目录运行程序。然后,程序与资源一起安装到 /usr/share/my_program 或其他东西中,程序通过访问这些文件运行。问题是在源代码中做什么,以便在从构建目录运行和安装时相同的代码工作。对于如何处理资源,我已经看到了几个不同的答案,但每个答案似乎都有缺陷:

  1. 程序在某些预配置的绝对目录中搜索,例如 CMAKE_INSTALL_PREFIX/share/my_program,必须先安装才能运行,不适合在开发过程中方便使用。
  2. 找到某种方法来使用二进制文件的相对路径,但我不明白这是如何工作的,因为构建树不会镜像已安装的文件路径,而是安装到 bin 和共享目录。
  3. 使用 CMake 变量区分两者,以便在一种情况下搜索相对路径或在另一种情况下搜索安装位置。这可能只是调试/发布变量。它还需要在安装之前重新运行 CMake,以使用新的资源路径进行重建。
  4. 将文件烘焙到可执行文件中。处理大多数资源时似乎没有必要,因为它们可以直接打开,并且可能不方便处理大型文件目录。

其中之一是最好的解决方案吗?有什么我没有误解的吗?我一直认为程序应该能够在安装之前从构建目录运行以查看它们是否工作。如果可能的话,我想知道 CMake 和 C/C++ 代码的样子,例如 open("my_resource_location");my_cmake_command()。我看到的与资源相关的大多数答案都不包括两者。

【问题讨论】:

  • 这个问题与 CMake 几乎没有关系:CMake 在安装时更改可执行文件的唯一内容是 RPATH。您应该手动执行的所有其他更改(通过附加脚本)。该问题也与您使用的构建工具几乎没有关系。 完全取决于开发人员,他是否想要支持运行刚刚构建的可执行文件。从我的角度来看,在使用之前安装程序的要求不会对开发造成太大影响。它甚至使开发更容易,因为您的程序只需要在单个场景(已安装)中工作。
  • 通过一些工作,您可以设置您的 CMake 构建树来镜像您的安装目录。查看 CMake 变量 CMAKE_RUNTIME_OUTPUT_DIRECTORY。

标签: installation cmake build resources


【解决方案1】:

在我看来,您正在寻找的是一个可重定位的构建

在 CMake 中实现此目的的一种方法是使用配置文件将资源路径合并到您的代码中。我准备了一个最小且可重复的示例,让您了解它的工作原理。

首先,假设您在磁盘上有以下程序结构:

main
  CMakeLists.txt
  main.cpp
data
  CMakeLists.txt
  Resources.h.in            (Thats the configuration file)
  resource.txt

resource.txt 文件只包含一个字符串。该程序只需打开该文件,读取其内容并将其显示到终端。所以你有类似的东西:

#include <fstream>
#include <iostream>
#include <string>

#include <Resources.h>

const std::string MY_FILE = "/resource.txt";

int main(int argc, char *argv[])
{
    std::string line;
    std::ifstream myfile (RESOURCE_PATH + MY_FILE);
    if(myfile.is_open())
    {
        while(std::getline(myfile,line))
        {
            std::cout << line << '\n';
        }
        
        myfile.close();
    }
    else
    {
        std::cout << "Unable to open file";
    }

    return 0;
}

注意这个文件#includes 是一个Resources.h 文件,但是这个文件在我们的项目中不存在。但是,我们有一个 Resources.h.in 配置文件,CMake 将在生成时将其转换为 Resources.h 文件。然后,这个生成的文件将在 RESOURCE_PATH 变量中包含我们需要的资源路径。以下是配置文件包含的内容:

#define RESSOURCE_PATH "@CMAKE_INSTALL_PREFIX@/@CMAKE_INSTALL_DATADIR@"

一个简单的#define 带有这个奇怪的@something@ 符号。在生成时,CMake 将进行适当的替换并将结果写入Resource.h,然后我们可以使用它。主要的CMakeLists.txt 文件非常简单:

cmake_minimum_required(VERSION 3.10)

project(example)

# Sets up classic installation directories. However,
# you can override them:
include(GNUInstallDirs)

add_subdirectory(data)
add_subdirectory(main)

为了方便起见,我使用GNUInstallDirs 填充变量,例如CMAKE_INSTALL_DATADIRmain/CMakeLists.txt 文件:

add_executable(example main.cpp)

configure_file("${CMAKE_CURRENT_SOURCE_DIR}/Ressources.h.in" "Ressources.h")
target_include_directories(example
  PRIVATE ${CMAKE_CURRENT_BINARY_DIR}
)

install(
  TARGETS example
  DESTINATION ${CMAKE_INSTALL_BINDIR}
)

这是设置配置文件的地方。添加了下面的target_include_directories 以便CMake 能够找到生成的Resource.h 文件,该文件将驻留在构建目录中。最后,data/CMakeLists.txt 文件:

install(
  FILES resource.txt 
  DESTINATION ${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_DATADIR}
)

只有一个简单的安装说明。从那里,您可以照常安装。只需创建一个构建目录并照常使用 CMake。例如,如果构建目录与项目位于同一级别:

mkdir ../build
cd ../build
cmake ../project
make
sudo make install

因为默认情况下(在 Linux 上)我们有:

CMAKE_INSTALL_PREFIX=/usr/local
CMAKE_INSTALL_BINDIR=bin
CMAKE_INSTALL_DATADIR=share

程序将安装到/usr/local/bin,资源安装到/usr/local/share。请注意,一旦由 CMake 生成,Resource.h 文件将为您提供正确的路径。然而,现在很好的是,您可以通过像这样调用 CMake 来修改 CMAKE_INSTALL_PREFIX 的值以指向其他地方(例如,这可能是一个开发构建目录):

cmake -DCMAKE_INSTALL_PREFIX=/home/someone/example ../project

你将拥有这些值:

CMAKE_INSTALL_PREFIX=/home/someone/example
CMAKE_INSTALL_BINDIR=bin
CMAKE_INSTALL_DATADIR=share

Resources.h 文件也将更新为指向正确的位置,您的资源仍会被找到。

【讨论】:

    猜你喜欢
    • 2013-08-18
    • 1970-01-01
    • 2016-04-29
    • 1970-01-01
    • 1970-01-01
    • 2021-03-17
    • 1970-01-01
    • 2018-11-25
    • 2011-08-30
    相关资源
    最近更新 更多