diff options
author | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-11-02 08:17:45 +0200 |
---|---|---|
committer | Jaakko Keränen <jaakko.keranen@iki.fi> | 2020-11-02 11:11:24 +0200 |
commit | 3543fd0eaaf3ed0f068ed0012e9da8a6d500f298 (patch) | |
tree | 7f33456ea75f6b0f83fcbbf1368abdab256e305f /res/Embed.cmake | |
parent | 8605b747b3eac83e6af7907de7659bfb989d0358 (diff) |
Embed: Build resource files faster
As part of the CMake configuration, build bincat (23 lines of C) to concatenate resource files together. This is much faster because CMake doesn't have to get involved in the contents of the binary files.
Diffstat (limited to 'res/Embed.cmake')
-rw-r--r-- | res/Embed.cmake | 162 |
1 files changed, 162 insertions, 0 deletions
diff --git a/res/Embed.cmake b/res/Embed.cmake new file mode 100644 index 00000000..8da9e1d6 --- /dev/null +++ b/res/Embed.cmake | |||
@@ -0,0 +1,162 @@ | |||
1 | # CMake Helper for Binary Resources | ||
2 | # Copyright: 2020 Jaakko Keränen <jaakko.keranen@iki.fi> | ||
3 | # License: BSD 2-Clause | ||
4 | |||
5 | option (ENABLE_RESOURCE_EMBED "Embed resources inside the executable" OFF) | ||
6 | |||
7 | # Build "bincat" for concatenating files. | ||
8 | if (NOT ENABLE_RESOURCE_EMBED) | ||
9 | message (STATUS "Compiling bincat for merging resource files...") | ||
10 | set (_catDir ${CMAKE_BINARY_DIR}/res) | ||
11 | execute_process (COMMAND ${CMAKE_COMMAND} -E make_directory ${_catDir}) | ||
12 | execute_process (COMMAND ${CMAKE_COMMAND} ${CMAKE_SOURCE_DIR}/res WORKING_DIRECTORY ${_catDir}) | ||
13 | execute_process (COMMAND ${CMAKE_COMMAND} --build . WORKING_DIRECTORY ${_catDir}) | ||
14 | set (BINCAT_COMMAND ${_catDir}/bincat) | ||
15 | endif () | ||
16 | |||
17 | function (embed_getname output fn) | ||
18 | get_filename_component (name ${fn} NAME_WE) | ||
19 | string (REPLACE "-" "" name ${name}) | ||
20 | string (REPLACE "=" "" name ${name}) | ||
21 | string (SUBSTRING ${name} 0 1 first) | ||
22 | string (TOUPPER ${first} first) | ||
23 | string (SUBSTRING ${name} 1 -1 remainder) | ||
24 | set (name "${first}${remainder}") | ||
25 | get_filename_component (ext ${fn} EXT) | ||
26 | if (ext STREQUAL .ttf) | ||
27 | set (resName "font") | ||
28 | elseif (ext STREQUAL .png) | ||
29 | set (resName "image") | ||
30 | else () | ||
31 | set (resName "blob") | ||
32 | endif () | ||
33 | set (resName "${resName}${name}_Embedded" PARENT_SCOPE) | ||
34 | endfunction (embed_getname) | ||
35 | |||
36 | function (embed_write path name fnSource fnHeader) | ||
37 | message (STATUS "${path}") | ||
38 | file (READ ${path} fileData HEX) | ||
39 | string (REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," cList ${fileData}) | ||
40 | string (REGEX REPLACE | ||
41 | "(0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],0x[0-9a-f][0-9a-f],)" | ||
42 | "\\1\n " cList ${cList}) | ||
43 | string (LENGTH ${fileData} len) | ||
44 | math (EXPR alen "${len} / 2") | ||
45 | set (src "static const uint8_t bytes_${name}_[] = {\n") | ||
46 | string (APPEND src " ${cList}\n") | ||
47 | string (APPEND src "}; | ||
48 | static iBlockData data_${name}_ = { | ||
49 | .refCount = 2, .data = iConstCast(char *, bytes_${name}_), .size = ${alen}, .allocSize = ${alen} | ||
50 | }; | ||
51 | const iBlock ${name} = { &data_${name}_ }; | ||
52 | ") | ||
53 | set (header "extern const iBlock ${name};\n") | ||
54 | # Output the results. | ||
55 | file (APPEND ${fnSource} "${src}") | ||
56 | file (APPEND ${fnHeader} "${header}") | ||
57 | endfunction (embed_write) | ||
58 | |||
59 | function (embed_make) | ||
60 | set (EMB_H ${CMAKE_CURRENT_BINARY_DIR}/embedded.h) | ||
61 | set (EMB_C ${CMAKE_CURRENT_BINARY_DIR}/embedded.c) | ||
62 | if (ENABLE_RESOURCE_EMBED) | ||
63 | set (needGen NO) | ||
64 | if (NOT EXISTS ${EMB_H} OR NOT EXISTS ${EMB_C}) | ||
65 | set (needGen YES) | ||
66 | else () | ||
67 | file (TIMESTAMP ${EMB_H} genTime %s) | ||
68 | foreach (resPath ${ARGV}) | ||
69 | set (fn "${CMAKE_CURRENT_LIST_DIR}/${resPath}") | ||
70 | file (TIMESTAMP ${fn} resTime %s) | ||
71 | if (${resTime} GREATER ${genTime}) | ||
72 | set (needGen YES) | ||
73 | endif () | ||
74 | endforeach (resPath) | ||
75 | endif () | ||
76 | else () | ||
77 | set (needGen YES) | ||
78 | endif () | ||
79 | if (needGen) | ||
80 | if (ENABLE_RESOURCE_EMBED) | ||
81 | # Compose a source file with the resource data in an array. | ||
82 | file (WRITE ${EMB_H} "#include <the_Foundation/block.h>\n") | ||
83 | file (WRITE ${EMB_C} "#include \"embedded.h\"\n") | ||
84 | foreach (fn ${ARGV}) | ||
85 | embed_getname (resName ${fn}) | ||
86 | embed_write (${fn} ${resName} ${EMB_C} ${EMB_H}) | ||
87 | endforeach (fn) | ||
88 | else () | ||
89 | # Collect resources in a single binary file. | ||
90 | set (EMB_BIN ${CMAKE_CURRENT_BINARY_DIR}/resources.binary) | ||
91 | file (REMOVE ${EMB_BIN}) | ||
92 | list (LENGTH ARGV fileCount) | ||
93 | execute_process (COMMAND ${BINCAT_COMMAND} ${EMB_BIN} ${ARGV} | ||
94 | WORKING_DIRECTORY ${CMAKE_SOURCE_DIR} | ||
95 | OUTPUT_VARIABLE fileSizes | ||
96 | ) | ||
97 | set (offsets) | ||
98 | set (fpos 0) | ||
99 | foreach (fileSize ${fileSizes}) | ||
100 | list (APPEND offsets "${fpos}") | ||
101 | math (EXPR fpos "${fpos} + ${fileSize}") | ||
102 | endforeach (fileSize) | ||
103 | file (WRITE ${EMB_H} "#include <the_Foundation/block.h>\n | ||
104 | #define iHaveLoadEmbed 1 | ||
105 | iBool load_Embed(const char *path);\n\n") | ||
106 | file (WRITE ${EMB_C} [[ | ||
107 | #include "embedded.h" | ||
108 | #include <the_Foundation/file.h> | ||
109 | #include <the_Foundation/fileinfo.h> | ||
110 | |||
111 | iDeclareType(EmbedChunk) | ||
112 | |||
113 | struct Impl_EmbedChunk { | ||
114 | size_t pos; | ||
115 | size_t size; | ||
116 | }; | ||
117 | |||
118 | static const iEmbedChunk chunks_Embed_[] = { | ||
119 | ]]) | ||
120 | set (index 0) | ||
121 | foreach (fn ${ARGV}) | ||
122 | list (GET fileSizes ${index} fileSize) | ||
123 | list (GET offsets ${index} fpos) | ||
124 | file (APPEND ${EMB_C} " { ${fpos}, ${fileSize} },\n") | ||
125 | math (EXPR index "${index} + 1") | ||
126 | endforeach (fn) | ||
127 | file (APPEND ${EMB_C} "};\n\n") | ||
128 | foreach (fn ${ARGV}) | ||
129 | embed_getname (resName ${fn}) | ||
130 | file (APPEND ${EMB_H} "extern iBlock ${resName};\n") | ||
131 | file (APPEND ${EMB_C} "iBlock ${resName};\n") | ||
132 | endforeach (fn) | ||
133 | file (APPEND ${EMB_C} "\nstatic iBlock *blocks_Embed_[] = {\n") | ||
134 | foreach (fn ${ARGV}) | ||
135 | embed_getname (resName ${fn}) | ||
136 | file (APPEND ${EMB_C} " &${resName},\n") | ||
137 | endforeach (fn) | ||
138 | file (APPEND ${EMB_C} [[ | ||
139 | }; | ||
140 | |||
141 | iBool load_Embed(const char *path) { | ||
142 | const size_t fileSize = (size_t) fileSizeCStr_FileInfo(path); | ||
143 | iFile *f = iClob(newCStr_File(path)); | ||
144 | if (open_File(f, readOnly_FileMode)) { | ||
145 | iForIndices(i, blocks_Embed_) { | ||
146 | const iEmbedChunk *chunk = &chunks_Embed_[i]; | ||
147 | iBlock *data = blocks_Embed_[i]; | ||
148 | if (chunk->pos + chunk->size > fileSize) { | ||
149 | return iFalse; | ||
150 | } | ||
151 | init_Block(data, chunk->size); | ||
152 | seek_File(f, chunk->pos); | ||
153 | readData_File(f, chunk->size, data_Block(data)); | ||
154 | } | ||
155 | return iTrue; | ||
156 | } | ||
157 | return iFalse; | ||
158 | } | ||
159 | ]]) | ||
160 | endif () | ||
161 | endif () | ||
162 | endfunction (embed_make) | ||