|
| 1 | +cmake_minimum_required(VERSION 3.14) |
| 2 | + |
| 3 | +function(write_header target_name target_path) |
| 4 | + file(WRITE "${target_path}" |
| 5 | +"// Generated by crl |
| 6 | +#pragma once |
| 7 | +
|
| 8 | +#include <crl.h> |
| 9 | +
|
| 10 | +#include <string_view> |
| 11 | +
|
| 12 | +namespace ${target_name} { |
| 13 | +
|
| 14 | +const crl::DirectoryEntry& root(); |
| 15 | +
|
| 16 | +std::optional<std::span<const std::byte>> get_file(std::string_view path); |
| 17 | +constexpr std::span<const std::byte> get_file_static(std::string_view path); |
| 18 | +
|
| 19 | +} // namespace ${target_name} |
| 20 | +" |
| 21 | + ) |
| 22 | +endfunction() |
| 23 | + |
| 24 | +function(write_cpp_intro target_path) |
| 25 | + file(WRITE "${target_path}" |
| 26 | +"// Generated by crl |
| 27 | +#include <array> |
| 28 | +#include <cstddef> |
| 29 | +#include <crl.h> |
| 30 | +
|
| 31 | +using crl::DirectoryEntry; |
| 32 | +using crl::FileEntry; |
| 33 | +
|
| 34 | +template<typename... Ts> |
| 35 | +constexpr std::array<std::byte, sizeof...(Ts)> make_bytes(Ts&&... args) noexcept { |
| 36 | + return{std::byte(std::forward<Ts>(args))...}; |
| 37 | +} |
| 38 | +
|
| 39 | +") |
| 40 | +endfunction() |
| 41 | + |
| 42 | +function(write_cpp_outro target_name target_path) |
| 43 | + file(APPEND "${target_path}" |
| 44 | +"namespace ${target_name} { |
| 45 | +
|
| 46 | +const crl::DirectoryEntry& root() { |
| 47 | + return directory_root; |
| 48 | +} |
| 49 | +
|
| 50 | +std::optional<std::span<const std::byte>> get_file(std::string_view path) { |
| 51 | + return crl::get_file(root(), path); |
| 52 | +} |
| 53 | +
|
| 54 | +constexpr std::span<const std::byte> get_file_static(std::string_view path) { |
| 55 | + if (path == \"logs/source/t.txt\") { |
| 56 | + return file_0_data; |
| 57 | + } |
| 58 | + return file_0_data; |
| 59 | + // static_assert(false); |
| 60 | +} |
| 61 | +
|
| 62 | +} // namespace ${target_name} |
| 63 | +") |
| 64 | +endfunction() |
| 65 | + |
| 66 | +function(write_data_array input_path variable_name target_path) |
| 67 | + file(READ "${input_path}" file_content HEX) |
| 68 | + string(LENGTH "${file_content}" data_length_hex) |
| 69 | + math(EXPR data_length_bytes "${data_length_hex} / 2") |
| 70 | + file(APPEND "${target_path}" "constexpr std::array<std::byte, ${data_length_bytes}> ${variable_name} = make_bytes(") |
| 71 | + |
| 72 | + if(data_length_bytes GREATER 0) |
| 73 | + if(data_length_bytes GREATER 1) |
| 74 | + math(EXPR data_length_bytes_minus_two "${data_length_bytes} - 2") |
| 75 | + foreach(i RANGE 0 ${data_length_bytes_minus_two}) |
| 76 | + math(EXPR index "2 * ${i}") |
| 77 | + string(SUBSTRING "${file_content}" ${index} 2 BYTE_HEX) |
| 78 | + file(APPEND "${target_path}" "0x${BYTE_HEX},") |
| 79 | + endforeach() |
| 80 | + endif() |
| 81 | + math(EXPR index "2 * ${data_length_bytes} - 2") |
| 82 | + string(SUBSTRING "${file_content}" ${index} 2 BYTE_HEX) |
| 83 | + file(APPEND "${target_path}" "0x${BYTE_HEX}") |
| 84 | + endif() |
| 85 | + file(APPEND "${target_path}" ");\n") |
| 86 | +endfunction() |
| 87 | + |
| 88 | +function(write_file_entry file_index file_path target_path) |
| 89 | + cmake_path(GET file_path FILENAME file_name) |
| 90 | + file(APPEND |
| 91 | + "${target_path}" |
| 92 | + "constexpr FileEntry file_${file_index} = { \"${file_name}\", file_${file_index}_data };\n\n" |
| 93 | + ) |
| 94 | +endfunction() |
| 95 | + |
| 96 | +function(write_file_entries base_dir resource_files target_path) |
| 97 | + set(index 0) |
| 98 | + foreach(resource_file IN LISTS resource_files) |
| 99 | + set(absolute_resource_file "${base_dir}/${resource_file}") |
| 100 | + message(STATUS "Embed ${resource_file} into ${target_path}") |
| 101 | + write_data_array("${absolute_resource_file}" "file_${index}_data" "${target_path}") |
| 102 | + write_file_entry("${index}" "${resource_file}" "${target_path}") |
| 103 | + math(EXPR index "${index} + 1") |
| 104 | + endforeach() |
| 105 | +endfunction() |
| 106 | + |
| 107 | + |
| 108 | +function (add_directory_to_directory_list candidate directory_list out) |
| 109 | + list(FIND directory_list "${candidate}" pos) |
| 110 | + if("${candidate}" IN_LIST directory_list) |
| 111 | + else() |
| 112 | + list(APPEND directory_list "${candidate}") |
| 113 | + set(${out} "${directory_list}" PARENT_SCOPE) |
| 114 | + endif() |
| 115 | +endfunction() |
| 116 | + |
| 117 | +function (expand_directories_for_single_file relative_file_path directory_list out) |
| 118 | + get_filename_component(dir_path "${relative_file_path}" DIRECTORY) |
| 119 | + if("${dir_path}" STREQUAL "${relative_file_path}") |
| 120 | + message("Skip file ${dir_path}") |
| 121 | + return() |
| 122 | + endif() |
| 123 | + string(REPLACE "/" ";" dir_part_list "${dir_path}") |
| 124 | + foreach(dir_part IN LISTS dir_part_list) |
| 125 | + set(dir "${dir}/${dir_part}") |
| 126 | + add_directory_to_directory_list("${dir}" "${directory_list}" x) |
| 127 | + set(directory_list "${x}") |
| 128 | + endforeach() |
| 129 | + set(${out} "${directory_list}" PARENT_SCOPE) |
| 130 | +endfunction() |
| 131 | + |
| 132 | +function (expand_directories relative_file_list out) |
| 133 | + foreach(relative_file IN LISTS relative_file_list) |
| 134 | + expand_directories_for_single_file("${relative_file}" "${directory_list}" x) |
| 135 | + set(directory_list "${x}") |
| 136 | + endforeach() |
| 137 | + set(${out} "${directory_list}" PARENT_SCOPE) |
| 138 | +endfunction() |
| 139 | + |
| 140 | +function(find_children dir expanded_dirs out) |
| 141 | + foreach(candidate IN LISTS "${expanded_dirs}") |
| 142 | + cmake_path(GET candidate PARENT_PATH x) |
| 143 | + if ("${x}" STREQUAL "${dir}") |
| 144 | + list(APPEND result ${candidate}) |
| 145 | + endif() |
| 146 | + endforeach() |
| 147 | + set(${out} ${result} PARENT_SCOPE) |
| 148 | +endfunction() |
| 149 | + |
| 150 | +function(write_directory_subdirectories dir dir_index subdirectories resource_directories target_file) |
| 151 | + list(LENGTH subdirectories subdirs_length) |
| 152 | + file(APPEND "${target_file}" "constexpr std::array<const DirectoryEntry*, ${subdirs_length}> directory_${dir_index}_subdirectories = {") |
| 153 | + foreach(subdir IN LISTS subdirectories) |
| 154 | + list(FIND resource_directories "${subdir}" index) |
| 155 | + if (${index} EQUAL -1) |
| 156 | + message(FATAL_ERROR "Internal error: directory '${subdir}' is not part of the resource directories: '${resource_directories}'") |
| 157 | + endif() |
| 158 | + file(APPEND "${target_file}" "&directory_${index},") |
| 159 | + endforeach() |
| 160 | + file(APPEND "${target_file}" "};\n") |
| 161 | +endfunction() |
| 162 | + |
| 163 | +function(write_directory_subfiles dir dir_index subfiles resource_files target_file) |
| 164 | + list(LENGTH subfiles subfiles_length) |
| 165 | + file(APPEND "${target_file}" "constexpr std::array<const FileEntry*, ${subfiles_length}> directory_${dir_index}_subfiles = {") |
| 166 | + foreach(subfile IN LISTS subfiles) |
| 167 | + list(FIND resource_files "${subfile}" index) |
| 168 | + if (${index} EQUAL -1) |
| 169 | + message(FATAL_ERROR "Internal error: file '${subfile}' is not part of the resource files: '${resource_files}'") |
| 170 | + endif() |
| 171 | + file(APPEND "${target_file}" "&file_${index},") |
| 172 | + endforeach() |
| 173 | + file(APPEND "${target_file}" "};\n") |
| 174 | +endfunction() |
| 175 | + |
| 176 | +function(write_directory_entries resource_files resource_directories target_file) |
| 177 | + list(REVERSE resource_directories) |
| 178 | + foreach(dir IN LISTS resource_directories) |
| 179 | + list(FIND resource_directories "${dir}" dir_index) |
| 180 | + if (${dir_index} EQUAL -1) |
| 181 | + message(FATAL_ERROR "Internal error: directory '${dir}' is not part of the resource directories: '${resource_directories}'") |
| 182 | + endif() |
| 183 | + find_children("${dir}" resource_directories subdirs) |
| 184 | + STRING(REGEX REPLACE "^/" "" dir_without_leading_slash "${dir}") |
| 185 | + find_children("${dir_without_leading_slash}" resource_files subfiles) |
| 186 | + write_directory_subdirectories("${dir}" "${dir_index}" "${subdirs}" "${resource_directories}" "${target_file}") |
| 187 | + write_directory_subfiles("${dir}" "${dir_index}" "${subfiles}" "${resource_files}" "${target_file}") |
| 188 | + cmake_path(GET dir FILENAME dir_name) |
| 189 | + file(APPEND "${target_file}" "constexpr DirectoryEntry directory_${dir_index} = { \"${dir_name}\", directory_${dir_index}_subdirectories, directory_${dir_index}_subfiles };\n\n") |
| 190 | + endforeach() |
| 191 | + find_children("/" resource_directories subdirs) |
| 192 | + find_children("" resource_files subfiles) |
| 193 | + write_directory_subdirectories("${dir}" "root" "${subdirs}" "${resource_directories}" "${target_file}") |
| 194 | + write_directory_subfiles("${dir}" "root" "${subfiles}" "${resource_files}" "${target_file}") |
| 195 | + file(APPEND "${target_file}" "constexpr DirectoryEntry directory_root = { \"\", directory_root_subdirectories, directory_root_subfiles };\n\n") |
| 196 | +endfunction() |
| 197 | + |
| 198 | +function(main) |
| 199 | + if(NOT IS_DIRECTORY "${SOURCE_DIR}") |
| 200 | + message(FATAL_ERROR "The source directory '${SOURCE_DIR}' provided for the resource library does not exist.") |
| 201 | + endif() |
| 202 | + file(GLOB_RECURSE resource_files RELATIVE "${SOURCE_DIR}" "${SOURCE_DIR}/*") |
| 203 | + expand_directories("${resource_files}" resource_directories) |
| 204 | + |
| 205 | + set(output_cpp_file "src/${TARGET_NAME}.cpp") |
| 206 | + set(output_h_file "include/${TARGET_NAME}.h") |
| 207 | + |
| 208 | + write_cpp_intro("${output_cpp_file}") |
| 209 | + write_file_entries("${SOURCE_DIR}" "${resource_files}" "${output_cpp_file}") |
| 210 | + write_directory_entries("${resource_files}" "${resource_directories}" "${output_cpp_file}") |
| 211 | + write_cpp_outro("${TARGET_NAME}" "${output_cpp_file}") |
| 212 | + |
| 213 | + write_header("${TARGET_NAME}" "${output_h_file}") |
| 214 | +endfunction() |
| 215 | + |
| 216 | +main() |
0 commit comments