1
+ if (_CMRC_GENERATE_MODE)
2
+ # Read in the digits
3
+ file (READ "${INPUT_FILE} " bytes HEX)
4
+ # Format each pair into a character literal
5
+ string (REGEX REPLACE "(..)" "'\\\\ x\\ 1', " chars "${bytes} " )
6
+ file (WRITE "${OUTPUT_FILE} " "
7
+ namespace { const char file_array[] = { ${chars} }; }
8
+ namespace cmrc { namespace ${LIBRARY} { namespace res_chars {
9
+ extern const char* const ${SYMBOL} _begin = file_array;
10
+ extern const char* const ${SYMBOL} _end = file_array + sizeof(file_array);
11
+ }}}
12
+ " )
13
+ return ()
14
+ endif ()
15
+
16
+ if (COMMAND cmrc_add_resource_library)
17
+ # CMakeRC has already been included! Don't do anything
18
+ return ()
19
+ endif ()
20
+
21
+ set (this_script "${CMAKE_CURRENT_LIST_FILE} " )
22
+
23
+ set (CMRC_INCLUDE_DIR "${CMAKE_BINARY_DIR} /_cmrc/include" CACHE INTERNAL "Directory for CMakeRC include files" )
24
+ # Let's generate the primary include file
25
+ file (MAKE_DIRECTORY "${CMRC_INCLUDE_DIR} /cmrc" )
26
+ file (WRITE "${CMRC_INCLUDE_DIR} /cmrc/cmrc.hpp" [==[
27
+ #ifndef CMRC_CMRC_HPP_INCLUDED
28
+ #define CMRC_CMRC_HPP_INCLUDED
29
+
30
+ #include <string>
31
+ #include <map>
32
+ #include <mutex>
33
+
34
+ #define CMRC_INIT(libname) \
35
+ do { \
36
+ extern void cmrc_init_resources_##libname(); \
37
+ cmrc_init_resources_##libname(); \
38
+ } while (0)
39
+
40
+ namespace cmrc {
41
+
42
+ class resource {
43
+ const char* _begin = nullptr;
44
+ const char* _end = nullptr;
45
+ public:
46
+ const char* begin() const { return _begin; }
47
+ const char* end() const { return _end; }
48
+
49
+ resource() = default;
50
+ resource(const char* beg, const char* end) : _begin(beg), _end(end) {}
51
+ };
52
+
53
+ using resource_table = std::map<std::string, resource>;
54
+
55
+ namespace detail {
56
+
57
+ inline resource_table& table_instance() {
58
+ static resource_table table;
59
+ return table;
60
+ }
61
+
62
+ inline std::mutex& table_instance_mutex() {
63
+ static std::mutex mut;
64
+ return mut;
65
+ }
66
+
67
+ // We restrict access to the resource table through a mutex so that multiple
68
+ // threads can access it safely.
69
+ template <typename Func>
70
+ inline auto with_table(Func fn) -> decltype(fn(std::declval<resource_table&>())) {
71
+ std::lock_guard<std::mutex> lk{ table_instance_mutex() };
72
+ return fn(table_instance());
73
+ }
74
+
75
+ }
76
+
77
+ inline resource open(const char* fname) {
78
+ return detail::with_table([fname](const resource_table& table) {
79
+ auto iter = table.find(fname);
80
+ if (iter == table.end()) {
81
+ return resource {};
82
+ }
83
+ return iter->second;
84
+ });
85
+ }
86
+
87
+ inline resource open(const std::string& fname) {
88
+ return open(fname.data());
89
+ }
90
+
91
+ }
92
+
93
+ #endif // CMRC_CMRC_HPP_INCLUDED
94
+ ]==])
95
+
96
+ add_library (cmrc-base INTERFACE )
97
+ target_include_directories (cmrc-base INTERFACE "${CMRC_INCLUDE_DIR} " )
98
+ target_compile_features (cmrc-base INTERFACE cxx_nullptr)
99
+ set_property (TARGET cmrc-base PROPERTY INTERFACE_CXX_EXTENSIONS OFF )
100
+ add_library (cmrc::base ALIAS cmrc-base)
101
+
102
+ function (cmrc_add_resource_library name )
103
+ # Generate the identifier for the resource library's namespace
104
+ string (MAKE_C_IDENTIFIER "${name} " libident)
105
+ # Generate a library with the compiled in character arrays.
106
+ set (cpp_content [=[
107
+ #include <cmrc/cmrc.hpp>
108
+ #include <map>
109
+
110
+ namespace cmrc { namespace %{libident} {
111
+
112
+ namespace res_chars {
113
+ // These are the files which are available in this resource library
114
+ $<JOIN:$<TARGET_PROPERTY:%{libname},CMRC_EXTERN_DECLS>,
115
+ >
116
+ }
117
+
118
+ inline void load_resources() {
119
+ // This initializes the list of resources and pointers to their data
120
+ static std::once_flag flag;
121
+ std::call_once(flag, [] {
122
+ cmrc::detail::with_table([](resource_table& table) {
123
+ $<JOIN:$<TARGET_PROPERTY:%{libname},CMRC_TABLE_POPULATE>,
124
+ >
125
+ });
126
+ });
127
+ }
128
+
129
+ namespace {
130
+ extern struct resource_initializer {
131
+ resource_initializer() {
132
+ load_resources();
133
+ }
134
+ } dummy;
135
+ }
136
+
137
+ }}
138
+
139
+ // The resource library initialization function. Intended to be called
140
+ // before anyone intends to use any of the resource defined by this
141
+ // resource library
142
+ extern void cmrc_init_resources_%{libident}() {
143
+ cmrc::%{libident}::load_resources();
144
+ }
145
+ ]=])
146
+ get_filename_component (libdir "${CMAKE_CURRENT_BINARY_DIR} /${name} " ABSOLUTE )
147
+ get_filename_component (libcpp "${libdir} /lib.cpp" ABSOLUTE )
148
+ string (REPLACE "%{libname}" "${name} " cpp_content "${cpp_content} " )
149
+ string (REPLACE "%{libident}" "${libident} " cpp_content "${cpp_content} " )
150
+ string (REPLACE "\n " "\n " cpp_content "${cpp_content} " )
151
+ file (GENERATE OUTPUT "${libcpp} " CONTENT "${cpp_content} " )
152
+ # Generate the actual static library. Each source file is just a single file
153
+ # with a character array compiled in containing the contents of the
154
+ # corresponding resource file.
155
+ add_library (${name} STATIC ${libcpp} )
156
+ target_link_libraries (${name} PUBLIC cmrc::base)
157
+ set_property (TARGET ${name} PROPERTY CMRC_IS_RESOURCE_LIBRARY TRUE )
158
+ cmrc_add_resources(${name} ${ARGN} )
159
+ endfunction ()
160
+
161
+ function (cmrc_add_resources name )
162
+ get_target_property (is_reslib ${name} CMRC_IS_RESOURCE_LIBRARY)
163
+ if (NOT TARGET ${name} OR NOT is_reslib)
164
+ message (SEND_ERROR "cmrc_add_resources called on target '${name} ' which is not an existing resource library" )
165
+ return ()
166
+ endif ()
167
+
168
+ set (options )
169
+ set (args WHENCE PREFIX )
170
+ set (list_args)
171
+ cmake_parse_arguments (PARSE_ARGV 1 ARG "${options} " "${args} " "${list_args} " )
172
+
173
+ if (NOT ARG_WHENCE)
174
+ set (ARG_WHENCE ${CMAKE_CURRENT_SOURCE_DIR} )
175
+ endif ()
176
+
177
+ # Generate the identifier for the resource library's namespace
178
+ string (MAKE_C_IDENTIFIER "${name} " libident)
179
+
180
+ foreach (input IN LISTS ARG_UNPARSED_ARGUMENTS)
181
+ get_filename_component (abs_input "${input} " ABSOLUTE )
182
+ # Generate a filename based on the input filename that we can put in
183
+ # the intermediate directory.
184
+ file (RELATIVE_PATH relpath "${ARG_WHENCE} " "${abs_input} " )
185
+ if (relpath MATCHES "^\\ .\\ ." )
186
+ # For now we just error on files that exist outside of the soure dir.
187
+ message (SEND_ERROR "Cannot add file '${input} ': File must be in a subdirectory of ${ARG_WHENCE} " )
188
+ continue ()
189
+ endif ()
190
+ get_filename_component (abspath "${libdir} /intermediate/${relpath} .cpp" ABSOLUTE )
191
+ # Generate a symbol name relpath the file's character array
192
+ _cm_encode_fpath(sym "${relpath} " )
193
+ # Generate the rule for the intermediate source file
194
+ _cmrc_generate_intermediate_cpp(${libident} ${sym} "${abspath} " "${abs_input} " )
195
+ target_sources (${name} PRIVATE ${abspath} )
196
+ set_property (TARGET ${name} APPEND PROPERTY CMRC_EXTERN_DECLS
197
+ "// Pointers to ${input} "
198
+ "extern const char* const ${sym} _begin\; "
199
+ "extern const char* const ${sym} _end\; "
200
+ )
201
+ if (ARG_PREFIX AND NOT ARG_PREFIX MATCHES "/$" )
202
+ set (ARG_PREFIX "${ARG_PREFIX} /" )
203
+ endif ()
204
+ set_property (TARGET ${name} APPEND PROPERTY CMRC_TABLE_POPULATE
205
+ "// Table entry for ${input} "
206
+ "table.emplace(\" ${ARG_PREFIX}${relpath} \" , resource{res_chars::${sym} _begin, res_chars::${sym} _end})\; "
207
+ )
208
+ endforeach ()
209
+ endfunction ()
210
+
211
+ function (_cmrc_generate_intermediate_cpp libname symbol outfile infile)
212
+ add_custom_command (
213
+ # This is the file we will generate
214
+ OUTPUT "${outfile} "
215
+ # These are the primary files that affect the output
216
+ DEPENDS "${infile} " "${this_script} "
217
+ COMMAND
218
+ "${CMAKE_COMMAND} "
219
+ -D_CMRC_GENERATE_MODE=TRUE
220
+ -DLIBRARY=${libname}
221
+ -DSYMBOL=${symbol}
222
+ "-DINPUT_FILE=${infile} "
223
+ "-DOUTPUT_FILE=${outfile} "
224
+ -P "${this_script} "
225
+ COMMENT "Generating intermediate file for ${infile} "
226
+ )
227
+ endfunction ()
228
+
229
+ function (_cm_encode_fpath var fpath)
230
+ string (MAKE_C_IDENTIFIER "${fpath} " ident)
231
+ string (MD5 hash "${fpath} " )
232
+ string (SUBSTRING "${hash} " 0 4 hash)
233
+ set (${var} f_${hash} _${ident} PARENT_SCOPE)
234
+ endfunction ()
0 commit comments