私のプロジェクトのさまざまなリソースに依存するアプリケーションをC++で作成しています。現在、生成された実行可能ファイルから各リソースへの相対パスがソースにハードコーディングされているため、プログラムでファイルを開いて各リソースのデータを読み取ることができます。これは問題なく動作しますが、実行可能ファイルをリソースに相対する特定のパスから起動する必要があります。したがって、他の場所から実行可能ファイルを起動しようとすると、ファイルを開くことができず、続行できません。
CMakeが実行可能ファイル(またはライブラリ)にリソースを埋め込むポータブル方法はありますか?パスがもろいファイル? 関連する質問 を見つけました。リソースの埋め込みは、いくつかのld
マジックで十分に実行できるようです。だから私の質問は、CMakeを使用してこれをポータブルなクロスプラットフォームの方法で行う方法ですか?実際には、x86とARMの両方でアプリケーションを実行する必要があります。私はLinux(Embedded)のみをサポートすることに問題はありませんが、Windows(Embedded)でこれを行う方法を誰かが提案できる場合はボーナスポイントも同様です。
編集:私はソリューションの望ましいプロパティを言及するのを忘れていました。 ARMターゲットでネイティブにコンパイルする必要があるのではなく、ARMターゲットでビルドするのではなく、CMakeを使用してアプリケーションをクロスコンパイルできるようにしたい。
これを行う最も簡単な方法の1つは、リソースを読み取り、定数データリテラルの配列としてリソースデータと実際のリソースデータの長さを含むCファイルを生成する、ビルドに小さなポータブルCプログラムを含めることです。これは完全にプラットフォームに依存しませんが、かなり小さいリソースにのみ使用する必要があります。より大きなリソースの場合、ファイルをプログラムに埋め込む必要はおそらくありません。
リソース「foo」の場合、生成されたCファイル「foo.c」には次のものが含まれます。
const char foo[] = { /* bytes of resource foo */ };
const size_t foo_len = sizeof(foo);
C++からリソースにアクセスするには、ヘッダーまたはcppファイルで次の2つのシンボルを宣言して、それらを使用します。
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」リソースを必要とするターゲットのソースリストに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;
}
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()
C++にリソースを埋め込む最もエレガントな方法は、さまざまなプラットフォーム間で移植可能で、CMakeと互換性のあるQtリソースシステムを使用することであり、圧縮を提供するだけでなく、上記の答えで行われたすべてを本質的にラップします。テストされ、誰にでもできる、その他すべて。
Qtリソースファイルを作成します-埋め込むファイルをリストしたXML:
<RCC>
<qresource prefix="/">
<file>uptriangle.png</file>
<file>downtriangle.png</file>
</qresource>
</RCC>
Qtres.qrcファイルを呼び出します。上記のリソースファイルには、2つの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);
これで、QPixmap、QImageなど、Qt Resource Systemと互換性のあるQtクラスを使用して上記のリソースにアクセスできます。さらに、最も重要なのは、一般的なケースとして、埋め込まれたQtリソースをラップし、それを介したアクセスを可能にするQResourceラッパークラスです。フレンドリーなインターフェース。例として、上記のリソースの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を使用して、res.data()、res.size()...を使用してデータに直接アクセスできます。ファイルの画像コンテンツを解析するには、pmを使用します。 pm.size()、pm.width()を使用します...
そして、あなたは行ってもいいです。お役に立てば幸いです。
別の代替案を提案したいと思います。 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" );