嵌入资源(例如,着色器代码;图像)与CMake的可执行/库(Embed resources (eg,

2019-06-26 18:22发布

我写在C ++依赖于各种资源在我的项目的应用程序。 现在,我已经从生产可执行每个资源在我的消息来源硬编码的相对路径,并允许我的程序打开文件,并在每个资源中的数据读取。 该工程确定,但它要求我开始从相对的特定资源的路径可执行文件。 所以,如果我尝试从其他地方开始我的可执行文件,它无法打开文件,无法继续。

有有CMake的嵌入我的资源到可执行文件(或库),这样我可以简单地在运行时访问它们在内存而不是打开文件,文件的路径是易碎的可移植的方式? 我已经找到了相关的问题 ,它看起来像嵌入资源可以做得够好了一些ld魔术。 所以我的问题是我如何使用CMake的便携式,跨平台的方式做到这一点? 我确实需要在x86和ARM我的应用程序运行。 我行与仅支持Linux操作系统(嵌入式),但加分,如果任何人都可以建议如何为Windows(嵌入式)做到这一点。

编辑:我忘了提及的解决方案所需的特性。 我想能够使用CMake的交叉编译应用程序时,我建设ARM,而不是要本地编译它在我的手臂的目标。

Answer 1:

一要做到这一点最简单的方法就是在您建立一个小型的,可移植的C程序,读取资源和生成包含资源数据和实际资源数据的长度为常量字符常量数组的C文件。 这将是完全独立的平台,但只能用于那些相当小的资源。 对于较大的资源,你可能不希望嵌入在你的程序中的文件。

对于资源“富”,生成的C文件“foo.c的”将包含以下内容:

const char foo[] = { /* bytes of resource foo */ };
const size_t foo_len = sizeof(foo);

要访问C ++的资源,你在声明无论是头部还是他们用在cpp文件以下两个符号:

extern "C" const char foo[];
extern "C" const size_t foo_len;

要生成foo.c在构建,你需要一个目标C程序(称之为embedfile.c),你需要使用ADD_CUSTOM_COMMAND命令来调用这个程序:

add_executable(embedfile embedfile.c)

add_custom_command(
  OUTPUT foo.c
  COMMAND embedfile foo foo.rsrc
  DEPENDS foo.rsrc)

随后,包括foo.c是需要“foo”的资源目标的源列表上。 您现在可以访问“富”的字节。

该方案embedfile.c是:

#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}\n\n"
        "  Creates {sym}.c from the contents of {rsrc}\n",
        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>\n");
  fprintf(out, "const char %s[] = {\n", 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, "\n"); linecount = 0; }
    }
  } while (nread > 0);
  if (linecount > 0) fprintf(out, "\n");
  fprintf(out, "};\n");
  fprintf(out, "const size_t %s_len = sizeof(%s);\n\n",sym,sym);

  fclose(in);
  fclose(out);

  return EXIT_SUCCESS;
}


Answer 2:

作为一种替代的答案sfstewman ,这里是一个小的CMake(2.8)功能的所有文件转换成一个特定的文件夹到C的数据,并把它们写在希望的输出文件:

# Creates C resources file from files in given directory
function(create_resources dir output)
    # Create empty output file
    file(WRITE ${output} "")
    # Collect input files
    file(GLOB bins ${dir}/*)
    # Iterate through input files
    foreach(bin ${bins})
        # Get short filename
        string(REGEX MATCH "([^/]+)$" filename ${bin})
        # Replace filename spaces & extension separator for C compatibility
        string(REGEX REPLACE "\\.| |-" "_" filename ${filename})
        # Read hex data from file
        file(READ ${bin} filedata HEX)
        # Convert hex data for C compatibility
        string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," filedata ${filedata})
        # Append data to output file
        file(APPEND ${output} "const unsigned char ${filename}[] = {${filedata}};\nconst unsigned ${filename}_size = sizeof(${filename});\n")
    endforeach()
endfunction()


Answer 3:

我想说的最优雅的方式在C具有嵌入式资源++是简单地使用Qt的资源系统,这是在不同的平台之间移植,与CMake的兼容,而且基本上包了一切的答案做上述,除了提供压缩,得到充分测试和防呆,一切。

创建一个Qt资源文件 - 一个XML上市被嵌入的文件:

<RCC>
    <qresource prefix="/">
        <file>uptriangle.png</file>
        <file>downtriangle.png</file>
    </qresource>
</RCC>

调用文件qtres.qrc。 上面的资源文件将有两个PNG文件(位于同一目录qtres.qrc)嵌入到最终的可执行文件。 您可以轻松地添加/删除监控资源使用QtCreator(Qt的IDE)一个QRC文件。

现在,在您的CMakeLists.txt文件中加入:

set(CMAKE_AUTOMOC ON)
find_package(Qt5Core)
qt5_add_resources(QT_RESOURCE qtres.qrc)

在你的main.cpp中,您需要访问资源之前,添加以下行:

Q_INIT_RESOURCE(qtres);

现在,您可以访问任何资源的使用上面的Qt类使用Qt资源系统兼容,比如,QPixmap就QImage的...和mosty重要的也许在一般情况下,QResource包装类,它封装的嵌入式Qt的资源,并通过允许它访问友好的界面。 作为一个例子,到内在上述资源downtriangle.png访问数据,以下行就可以了:

#include <QtCore>
#include <QtGui>

// ...

int main(int argc, char **argv)
{

    // ...

    Q_INIT_RESOURCE(qtres);

    // ...
    QResource res("://downtriangle.png"); // Here's your data, anyway you like
    // OR
    QPixmap pm("://downtriangle.png");  // Use it with Qt classes already

    // ...

}

在这里,资源可以用来使用res.data(),res.size()直接访问数据...为了解析文件使用时的图像内容。 使用pm.size(),pm.width()...

你是好去。 我希望它帮助。



Answer 4:

我想提出另一种选择。 它使用GCC接头直接嵌入二进制文件到可执行的,没有中间源文件。 这在我看来是更简单,更高效。

set( RC_DEPENDS "" )

function( add_resource input )
    string( MAKE_C_IDENTIFIER ${input} input_identifier )
    set( output "${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}/${input_identifier}.o" )
    target_link_libraries( ${PROJECT_NAME} ${output} )

    add_custom_command(
        OUTPUT ${output}
        COMMAND ${CMAKE_LINKER} --relocatable --format binary --output ${output} ${input}
        DEPENDS ${input}
    )

    set( RC_DEPENDS ${RC_DEPENDS} ${output} PARENT_SCOPE )
endfunction()

# Resource file list
add_resource( "src/html/index.html" )

add_custom_target( rc ALL DEPENDS ${RC_DEPENDS} )

然后在你的C / C ++文件所有你需要的是:

extern char index_html_start[] asm( "_binary_src_html_index_html_start" );
extern char index_html_end[]   asm( "_binary_src_html_index_html_end" );
extern size_t index_html_size  asm( "_binary_src_html_index_html_size" );


文章来源: Embed resources (eg, shader code; images) into executable/library with CMake