-
Notifications
You must be signed in to change notification settings - Fork 4
Expand file tree
/
Copy pathacecode_embed_assets.cmake
More file actions
114 lines (102 loc) · 5.51 KB
/
Copy pathacecode_embed_assets.cmake
File metadata and controls
114 lines (102 loc) · 5.51 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
# acecode_embed_assets: 把 web/ 下的所有文件嵌入成 C++ 字节数组,在 build
# 时生成 ${CMAKE_BINARY_DIR}/generated/static_assets_data.cpp。
#
# 用法:
# include(cmake/acecode_embed_assets.cmake)
# acecode_generate_embedded_assets(${CMAKE_SOURCE_DIR}/web
# ${CMAKE_BINARY_DIR}/generated/static_assets_data.cpp
# ${BUILD_VERSION})
#
# 生成的文件实现 `acecode::web::lookup_embedded_asset(name)` —— 静态 std::map
# 把相对路径(如 "index.html" / "components/ace-app.js")映射到 std::span。
# index.html 的内容会做一次模板替换,把 `?v=__VERSION__` 替换成传入的
# BUILD_VERSION 字符串(失败 fallback 不替换 — 不阻断 build)。
#
# 注意: 单文件输出会让 MSVC 编译稍慢(几百 KB cpp);若以后嵌入资源膨胀严重
# 可拆多 TU。当前 web/ 总量 << 1MB,不优化。
function(acecode_generate_embedded_assets web_dir out_file version)
if(NOT EXISTS "${web_dir}")
message(STATUS "[acecode_embed_assets] web dir not found: ${web_dir} — generating empty asset map")
file(WRITE "${out_file}" "// Auto-generated. web/ dir absent at configure time.\n#include \"web/static_assets.hpp\"\nnamespace acecode::web {\nconst std::map<std::string, std::pair<const unsigned char*, std::size_t>>& embedded_asset_map() {\n static const std::map<std::string, std::pair<const unsigned char*, std::size_t>> m;\n return m;\n}\n}\n")
return()
endif()
file(GLOB_RECURSE rels CONFIGURE_DEPENDS RELATIVE "${web_dir}" "${web_dir}/*")
list(FILTER rels EXCLUDE REGEX "(^|/)(README\\.md|\\.gitignore)$")
set(content "// Auto-generated by acecode_embed_assets. DO NOT EDIT.\n")
string(APPEND content "#include \"web/static_assets.hpp\"\n\n")
string(APPEND content "namespace acecode::web {\n\n")
set(map_entries "")
set(idx 0)
foreach(rel IN LISTS rels)
set(src_file "${web_dir}/${rel}")
if(IS_DIRECTORY "${src_file}")
continue()
endif()
# index.html 走模板替换:把 ?v=__VERSION__ 替成 ?v=${version}
# 其它文件原样读取。
#
# 关键:必须用 HEX(二进制)模式读,绝不能用文本模式 file(READ ... raw_text)。
# 压缩后的前端 bundle 里含真实控制字节(某些库把 ASCII 控制符定义成裸字节,
# 如 f.SUB="\x1a"),而 CMake 在 Windows 文本模式下会把 0x1A(Ctrl+Z)当 EOF,
# 把 index.html 在该字节处截断 → 嵌入的前端残缺 → 部署机(走嵌入)白屏。
# 因此 __VERSION__ 的替换改在 hex 空间做(token 与 version 都是安全 ASCII)。
if(rel STREQUAL "index.html")
file(READ "${src_file}" hex_data HEX)
# "__VERSION__" 的小写 hex(file(READ HEX) 输出小写)
set(_token_hex "5f5f56455253494f4e5f5f")
set(_ver_tmp "${out_file}.ver.tmp")
file(WRITE "${_ver_tmp}" "${version}")
file(READ "${_ver_tmp}" _version_hex HEX)
file(REMOVE "${_ver_tmp}")
string(REPLACE "${_token_hex}" "${_version_hex}" hex_data "${hex_data}")
else()
file(READ "${src_file}" hex_data HEX)
endif()
# 文件名 → C++ 变量名:非字母数字转 _,加序号防 collision
string(REGEX REPLACE "[^A-Za-z0-9]" "_" var_safe "${rel}")
set(var_name "asset_${idx}_${var_safe}")
# 把每两位 hex 转成 0xXX, 格式
string(REGEX REPLACE "(..)" "0x\\1," hex_arr "${hex_data}")
string(LENGTH "${hex_data}" hex_len)
math(EXPR data_len "${hex_len} / 2")
# 空文件:hex_arr 为空,需要一个最小占位避免 "{}; " 编译失败 —
# 写一个 0 字节占位,size 仍为 0。
if(data_len EQUAL 0)
string(APPEND content "static const unsigned char ${var_name}[1] = {0};\n")
else()
string(APPEND content "static const unsigned char ${var_name}[] = {${hex_arr}};\n")
endif()
# 把反斜杠路径分隔符换成 / 让 map key 跨平台一致。
string(REPLACE "\\" "/" rel_norm "${rel}")
string(APPEND map_entries " {\"${rel_norm}\", {${var_name}, ${data_len}}},\n")
math(EXPR idx "${idx} + 1")
endforeach()
string(APPEND content "\nconst std::map<std::string, std::pair<const unsigned char*, std::size_t>>& embedded_asset_map() {\n")
string(APPEND content " static const std::map<std::string, std::pair<const unsigned char*, std::size_t>> m = {\n${map_entries} };\n")
string(APPEND content " return m;\n}\n\n")
string(APPEND content "} // namespace acecode::web\n")
file(WRITE "${out_file}" "${content}")
message(STATUS "[acecode_embed_assets] wrote ${out_file} with ${idx} assets (BUILD_VERSION=${version})")
endfunction()
# 拿 git short hash 作 BUILD_VERSION;非 git 环境 fallback 用日期+秒。
function(acecode_compute_build_version out_var)
set(_version "unknown")
find_package(Git QUIET)
if(GIT_FOUND)
execute_process(
COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD
WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
OUTPUT_VARIABLE git_hash
OUTPUT_STRIP_TRAILING_WHITESPACE
ERROR_QUIET
RESULT_VARIABLE rc
)
if(rc EQUAL 0 AND git_hash)
set(_version "${git_hash}")
endif()
endif()
if(_version STREQUAL "unknown")
string(TIMESTAMP _version "%Y%m%d%H%M")
endif()
set(${out_var} "${_version}" PARENT_SCOPE)
endfunction()