Embed.cmake 8.52 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#####################################################################################
# The MIT License (MIT)
#
# Copyright (c) 2015-2022 Advanced Micro Devices, Inc. All rights reserved.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
#####################################################################################
Paul Fultz II's avatar
Paul Fultz II committed
24

25
26
27
28
29
30
option(EMBED_USE_BINARY "Use data file embedding to binary" ON)

if(EMBED_USE_BINARY AND NOT WIN32)
    find_program(EMBED_LD ld REQUIRED)
    find_program(EMBED_OBJCOPY objcopy REQUIRED)
endif()
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

function(wrap_string)
    set(options)
    set(oneValueArgs VARIABLE AT_COLUMN)
    set(multiValueArgs)
    cmake_parse_arguments(PARSE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

    string(LENGTH ${${PARSE_VARIABLE}} string_length)
    math(EXPR offset "0")

    while(string_length GREATER 0)

        if(string_length GREATER ${PARSE_AT_COLUMN})
            math(EXPR length "${PARSE_AT_COLUMN}")
        else()
            math(EXPR length "${string_length}")
        endif()

        string(SUBSTRING ${${PARSE_VARIABLE}} ${offset} ${length} line)
        set(lines "${lines}\n${line}")

        math(EXPR string_length "${string_length} - ${length}")
        math(EXPR offset "${offset} + ${length}")
    endwhile()

    set(${PARSE_VARIABLE} "${lines}" PARENT_SCOPE)
endfunction()

59
function(generate_embed_source EMBED_NAME EMBED_DIR BASE_DIRECTORY)
Paul Fultz II's avatar
Paul Fultz II committed
60
    set(options)
61
    set(oneValueArgs)
62
    set(multiValueArgs SYMBOLS FILES)
Paul Fultz II's avatar
Paul Fultz II committed
63
64
    cmake_parse_arguments(PARSE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})

65
    set(RESOURCE_ID 100)
66
    foreach(SYMBOL FILE IN ZIP_LISTS PARSE_SYMBOLS PARSE_FILES)
67
68
69
70
71
72
73
74
75
76
77
78
        cmake_path(RELATIVE_PATH FILE BASE_DIRECTORY ${BASE_DIRECTORY} OUTPUT_VARIABLE BASE_NAME)
        if(EMBED_USE_BINARY AND WIN32)
            string(TOUPPER "${SYMBOL}" SYMBOL)
            string(APPEND FILE_IDS "#define IDR_${SYMBOL} ${RESOURCE_ID}\n")
            string(APPEND RC_MAPPING "IDR_${SYMBOL} TEXTFILE \"${BASE_NAME}\"\n")
            string(APPEND INIT_KERNELS "        {\"${BASE_NAME}\", resource::read(IDR_${SYMBOL})},\n")
            math(EXPR RESOURCE_ID "${RESOURCE_ID} + 1" OUTPUT_FORMAT DECIMAL)
        else()
            set(START_SYMBOL "_binary_${SYMBOL}_start")
            set(LENGTH_SYMBOL "_binary_${SYMBOL}_length")
            if(EMBED_USE_BINARY)
                string(APPEND EXTERNS "
79
80
81
extern const char ${START_SYMBOL}[];
extern const size_t _binary_${SYMBOL}_size;
const auto ${LENGTH_SYMBOL} = reinterpret_cast<size_t>(&_binary_${SYMBOL}_size);
82
83
84
")
            else()
                string(APPEND EXTERNS "
85
86
extern const char ${START_SYMBOL}[];
extern const size_t ${LENGTH_SYMBOL};
87
88
89
90
")
            endif()
            string(APPEND INIT_KERNELS "
        { \"${BASE_NAME}\", { ${START_SYMBOL}, ${LENGTH_SYMBOL}} },")
91
        endif()
Paul Fultz II's avatar
Paul Fultz II committed
92
    endforeach()
93
94
95
    if(EMBED_USE_BINARY AND WIN32)
       file(WRITE "${EMBED_DIR}/include/resource.h" "
#define TEXTFILE 256
Paul Fultz II's avatar
Paul Fultz II committed
96

97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
${FILE_IDS}
")
        file(WRITE "${EMBED_DIR}/resource.rc" "
#include \"resource.h\"

${RC_FILE_MAPPING}
")
        set(EXTERNS "
#include <Windows.h>
#include \"resource.h\"

namespace resource {
std::string_view read(int id)
{
    HMODULE handle = GetModuleHandle(nullptr);
    HRSRC rc = FindResource(handle, MAKEINTRESOURCE(id), MAKEINTRESOURCE(TEXTFILE));
    HGLOBAL data = LoadResource(handle, rc);
    return {static_cast<const char*>(LockResource(data)), SizeofResource(handle, rc)};
}
}

")
        set(EMBED_FILES ${EMBED_DIR}/include/resource.h ${EMBED_DIR}/resource.rc)
    endif()
    file(WRITE "${EMBED_DIR}/include/${EMBED_NAME}.hpp" "
122
#include <string_view>
123
124
125
#include <unordered_map>
#include <utility>
std::unordered_map<std::string_view, std::string_view> ${EMBED_NAME}();
Paul Fultz II's avatar
Paul Fultz II committed
126
127
")

128
    file(WRITE "${EMBED_DIR}/${EMBED_NAME}.cpp" "
Paul Fultz II's avatar
Paul Fultz II committed
129
130
#include <${EMBED_NAME}.hpp>
${EXTERNS}
131
std::unordered_map<std::string_view, std::string_view> ${EMBED_NAME}()
Paul Fultz II's avatar
Paul Fultz II committed
132
{
133
    static std::unordered_map<std::string_view, std::string_view> result = {${INIT_KERNELS}};
Paul Fultz II's avatar
Paul Fultz II committed
134
135
136
    return result;
}
")
137
138
    list(APPEND EMBED_FILES ${EMBED_DIR}/${EMBED_NAME}.cpp ${EMBED_DIR}/include/${EMBED_NAME}.hpp)
    set(EMBED_FILES ${EMBED_FILES} PARENT_SCOPE)
Paul Fultz II's avatar
Paul Fultz II committed
139
140
endfunction()

141
function(embed_file FILE BASE_DIRECTORY)
142
    message(STATUS "    ${FILE}")
143
    cmake_path(RELATIVE_PATH FILE BASE_DIRECTORY "${BASE_DIRECTORY}" OUTPUT_VARIABLE REL_FILE)
144
145
146
    string(MAKE_C_IDENTIFIER "${REL_FILE}" OUTPUT_SYMBOL)
    get_filename_component(OUTPUT_FILE_DIR "${REL_FILE}" DIRECTORY)
    file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/${OUTPUT_FILE_DIR}")
147
148
149
150
151
152
153
154
155
156
157
158
    if(EMBED_USE_BINARY)
        if(NOT WIN32)
            set(OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/${REL_FILE}.o")
            add_custom_command(
                OUTPUT "${OUTPUT_FILE}"
                COMMAND ${EMBED_LD} -r -o "${OUTPUT_FILE}" -z noexecstack --format=binary "${REL_FILE}"
                COMMAND ${EMBED_OBJCOPY} --rename-section .data=.rodata,alloc,load,readonly,data,contents "${OUTPUT_FILE}"
                WORKING_DIRECTORY "${BASE_DIRECTORY}"
                DEPENDS "${FILE}"
                VERBATIM)
            set(OUTPUT_FILE ${OUTPUT_FILE} PARENT_SCOPE)
        endif()
159
160
161
162
163
164
165
166
167
168
169
170
171
    else()
        set(OUTPUT_FILE "${CMAKE_CURRENT_BINARY_DIR}/${REL_FILE}.cpp")
        # reads source file contents as hex string
        file(READ ${FILE} HEX_STRING HEX)
        # wraps the hex string into multiple lines
        wrap_string(VARIABLE HEX_STRING AT_COLUMN 80)
        # adds '0x' prefix and comma suffix before and after every byte respectively
        string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1, " ARRAY_VALUES ${HEX_STRING})
        # removes trailing comma
        string(REGEX REPLACE ", $" "" ARRAY_VALUES ${ARRAY_VALUES})
        file(WRITE "${OUTPUT_FILE}" "
#include <cstddef>
extern const char _binary_${OUTPUT_SYMBOL}_start[] = { ${ARRAY_VALUES} };
172
extern const size_t _binary_${OUTPUT_SYMBOL}_length = sizeof(_binary_${OUTPUT_SYMBOL}_start);
173
")
174
        set(OUTPUT_FILE ${OUTPUT_FILE} PARENT_SCOPE)
175
176
177
    endif()
    set(OUTPUT_SYMBOL ${OUTPUT_SYMBOL} PARENT_SCOPE)
endfunction()
Paul Fultz II's avatar
Paul Fultz II committed
178
179

function(add_embed_library EMBED_NAME)
180
    set(options)
181
    set(oneValueArgs RELATIVE)
182
183
    set(multiValueArgs)
    cmake_parse_arguments(PARSE "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
184

Paul Fultz II's avatar
Paul Fultz II committed
185
    set(EMBED_DIR ${CMAKE_CURRENT_BINARY_DIR}/embed/${EMBED_NAME})
186
187
    file(MAKE_DIRECTORY ${EMBED_DIR})
    message(STATUS "Embedding kernel files:")
188
    foreach(FILE ${PARSE_UNPARSED_ARGUMENTS})
189
        embed_file(${FILE} ${PARSE_RELATIVE})
Paul Fultz II's avatar
Paul Fultz II committed
190
191
192
        list(APPEND OUTPUT_FILES ${OUTPUT_FILE})
        list(APPEND SYMBOLS ${OUTPUT_SYMBOL})
    endforeach()
193
    message(STATUS "Generating embedding library '${EMBED_NAME}'")
194
195
196
197
198
    generate_embed_source(${EMBED_NAME} ${EMBED_DIR} "${PARSE_RELATIVE}" SYMBOLS ${SYMBOLS} FILES ${PARSE_UNPARSED_ARGUMENTS})
    set(INTERNAL_EMBED_LIB embed_lib_${EMBED_NAME})
    add_library(${INTERNAL_EMBED_LIB} OBJECT ${EMBED_FILES})
    if(NOT EMBED_USE_BINARY)
        target_sources(${INTERNAL_EMBED_LIB} PRIVATE ${OUTPUT_FILES})
199
    endif()
200
201
202
203
204
205
    target_include_directories(${INTERNAL_EMBED_LIB} PRIVATE "${EMBED_DIR}/include")
    target_compile_options(${INTERNAL_EMBED_LIB} PRIVATE -Wno-reserved-identifier -Wno-extern-initializer -Wno-missing-variable-declarations)
    set_target_properties(${INTERNAL_EMBED_LIB} PROPERTIES POSITION_INDEPENDENT_CODE On)
    add_library(${EMBED_NAME} INTERFACE $<TARGET_OBJECTS:${INTERNAL_EMBED_LIB}> ${OUTPUT_FILES})
    if(EMBED_USE_BINARY AND WIN32)
        target_link_libraries(${EMBED_NAME} INTERFACE $<TARGET_OBJECTS:${INTERNAL_EMBED_LIB}>)
206
    endif()
207
    target_include_directories(${EMBED_NAME} INTERFACE "${EMBED_DIR}/include")
Paul Fultz II's avatar
Paul Fultz II committed
208
endfunction()