使用 CMake 将资源(例如,着色器代码;图像)嵌入到可执行文件/库中
我正在用 C++ 编写一个应用程序,它依赖于我项目中的各种资源.现在,我拥有从生成的可执行文件到我的源代码中硬编码的每个资源的相对路径,这允许我的程序打开文件并读入每个资源中的数据.这工作正常,但它要求我从相对于资源的特定路径启动可执行文件.因此,如果我尝试从其他任何地方启动我的可执行文件,它将无法打开文件并且无法继续.
I am writing an application in C++ which relies on various resources in my project. Right now, I have the relative path from the produced executable to each resource hard-coded in my sources, and that allows my program to open the files and read in the data in each resource. This works ok, but it requires that I start the executable from a specific path relative to the resources. So if I try to start my executable from anywhere else, it fails to open the files and cannot proceed.
是否有一种可移植方式让 CMake 将我的资源嵌入到可执行文件(或库)中,以便我可以在运行时简单地在内存中访问它们,而不是打开路径脆弱的文件?我发现了一个相关问题,看起来嵌入资源是可以完成的加上一些 ld
魔法就足够了.所以我的问题是如何使用 CMake 以便携式、跨平台方式执行此操作?我实际上需要我的应用程序在 x86 和 ARM 上运行.我可以只支持 Linux(嵌入式),但如果有人能建议如何在 Windows(嵌入式)上执行此操作,我会加分.
Is there a portable way to have CMake embed my resources into the executables (or libraries) such that I can simply access them in memory at runtime instead of opening files whose paths are brittle? I have found a related question, and it looks like embedding resources can be done well enough with some ld
magic. So my question is how do I do this in a portable, cross platform manner using CMake? I actually need my application run on both x86 and ARM. I am ok with supporting only Linux (Embedded), but bonus points if anyone can suggest how to do this for Windows (Embedded) as well.
我忘了提及解决方案的所需属性.当我为 ARM 构建应用程序时,我希望能够使用 CMake 交叉编译应用程序,而不必在我的 ARM 目标上本地编译它.
I forgot to mention a desired property of the solution. I would like to be able to use CMake to cross-compile the application when I am building for ARM rather than have to compile it natively on my ARM target.
推荐答案
最简单的方法之一是在您的构建中包含一个小型、可移植的 C 程序,该程序读取资源并生成一个包含长度的 C 文件资源数据和实际资源数据作为常量字符文字数组.这将完全独立于平台,但应该只用于相当小的资源.对于较大的资源,您可能不想在程序中嵌入文件.
One of the easiest ways to do this is to include a small, portable C program in your build that reads the resource and generates a C file that contains the length of the resource data and the actual resource data as an array of constant character literals. This will be entirely platform independent, but should only be used for resources that are reasonably small. For larger resources, you probably don't want to embed the files in your program.
对于资源foo",生成的C文件foo.c"将包含:
For resource "foo", the generated C file "foo.c" would contain:
const char foo[] = { /* bytes of resource foo */ };
const size_t foo_len = sizeof(foo);
要从 C++ 访问资源,您可以在头文件或使用它们的 cpp 文件中声明以下两个符号:
To access the resource from C++, you declare the following two symbols in either a header or the cpp file where they're used:
extern "C" const char foo[];
extern "C" const size_t foo_len;
要在构建中生成 foo.c
,您需要一个 C 程序的目标(称为 embedfile.c),并且您需要使用 ADD_CUSTOM_COMMAND 命令来调用这个程序:
To generate foo.c
in the build, you need a target for the C program (call it embedfile.c), and you need to use the ADD_CUSTOM_COMMAND command to call this program:
add_executable(embedfile embedfile.c)
add_custom_command(
OUTPUT foo.c
COMMAND embedfile foo foo.rsrc
DEPENDS foo.rsrc)
然后,在需要foo"资源的目标的源列表中包含foo.c
.您现在可以访问foo"的字节.
Then, include foo.c
on the source list of a target that requires the "foo" resource. You now have access to the bytes of "foo".
程序 embedfile.c 是:
The program embedfile.c is:
#include <stdlib.h>
#include <stdio.h>
FILE* open_or_exit(const char* fname, const char* mode)
{
FILE* f = fopen(fname, mode);
if (f == NULL) {
perror(fname);
exit(EXIT_FAILURE);
}
return f;
}
int main(int argc, char** argv)
{
if (argc < 3) {
fprintf(stderr, "USAGE: %s {sym} {rsrc}
"
" Creates {sym}.c from the contents of {rsrc}
",
argv[0]);
return EXIT_FAILURE;
}
const char* sym = argv[1];
FILE* in = open_or_exit(argv[2], "r");
char symfile[256];
snprintf(symfile, sizeof(symfile), "%s.c", sym);
FILE* out = open_or_exit(symfile,"w");
fprintf(out, "#include <stdlib.h>
");
fprintf(out, "const char %s[] = {
", sym);
unsigned char buf[256];
size_t nread = 0;
size_t linecount = 0;
do {
nread = fread(buf, 1, sizeof(buf), in);
size_t i;
for (i=0; i < nread; i++) {
fprintf(out, "0x%02x, ", buf[i]);
if (++linecount == 10) { fprintf(out, "
"); linecount = 0; }
}
} while (nread > 0);
if (linecount > 0) fprintf(out, "
");
fprintf(out, "};
");
fprintf(out, "const size_t %s_len = sizeof(%s);
",sym,sym);
fclose(in);
fclose(out);
return EXIT_SUCCESS;
}
相关文章