41

プロジェクトのさまざまなリソースに依存するアプリケーションを C++ で作成しています。現在、生成された実行可能ファイルから各リソースへの相対パスがソースにハードコーディングされているため、プログラムでファイルを開いて各リソースのデータを読み取ることができます。これは問題なく動作しますが、リソースに対する特定のパスから実行可能ファイルを開始する必要があります。そのため、他の場所から実行可能ファイルを起動しようとすると、ファイルを開くことができず、続行できません。

パスが壊れやすいファイルを開く代わりに、実行時にメモリ内のリソースに簡単にアクセスできるように、CMake でリソースを実行可能ファイル (またはライブラリ) に埋め込むポータブルな方法はありますか? 関連する質問を見つけました。リソースの埋め込みは、ld魔法で十分にできるようです。私の質問は、CMake を使用して移植可能なクロスプラットフォームの方法でこれを行うにはどうすればよいですか? 実際には、アプリケーションを x86 と ARM の両方で実行する必要があります。Linux (Embedded) のみをサポートすることは問題ありませんが、Windows (Embedded) に対してもこれを行う方法を誰かが提案できればボーナス ポイントです。

編集:ソリューションの望ましいプロパティについて言及するのを忘れていました。ARM ターゲットでネイティブにコンパイルするのではなく、ARM 用にビルドするときに CMake を使用してアプリケーションをクロスコンパイルできるようにしたいと考えています。

4

6 に答える 6

39

sfstwmanの回答の代わりに、特定のフォルダー内のすべてのファイルを C データに変換し、それらを希望の出力ファイルに書き込む小さな cmake ( 2.8 ) 関数を次に示します。

# 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()
于 2014-11-29T20:51:12.117 に答える
38

これを行う最も簡単な方法の 1 つは、リソースを読み取り、リソース データの長さと実際のリソース データを定数文字リテラルの配列として含む C ファイルを生成する、小さく移植可能な C プログラムをビルドに含めることです。これは完全にプラットフォームに依存しませんが、適度に小さいリソースにのみ使用する必要があります。より大きなリソースの場合、おそらくプログラムにファイルを埋め込みたくないでしょう。

リソース「foo」の場合、生成された C ファイル「foo.c」には以下が含まれます。

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

C++ からリソースにアクセスするには、次の 2 つのシンボルを使用するヘッダーまたは 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」リソースを必要とするターゲットのソース リストに含めます。「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;
}
于 2012-08-05T06:58:22.817 に答える