Работа с множеством экранированных символов в add_custom_command
У меня есть файл, который содержит кучу данных. Я хочу превратить его в строковый литерал C++, потому что мне нужно скомпилировать эти данные в двоичный файл - я не могу прочитать их с диска.
Один из способов сделать это - просто сгенерировать исходный файл C++, который объявляет строковый литерал с известным именем. Код CMake для этого прост, хотя и несколько ужасен:
function(make_literal_from_file dest_file source_file literal_name)
add_custom_command(
OUTPUT ${dest_file}
COMMAND printf \'char const* ${literal_name} = R\"\#\(\' > ${dest_file}
COMMAND cat ${source_file} >> ${dest_file}
COMMAND printf \'\)\#\"\;\' >> ${dest_file}
DEPENDS ${source_file})
endfunction()
Это работает и делает то, что я хочу (printf
необходимо избегать новой строки после вводчика необработанных строк). Однако из-за того, что здесь происходит побег, очень трудно увидеть, что происходит. Есть ли способ написать эту функцию так, чтобы она действительно читалась?
Обратите внимание, что я не могу использовать file(READ ...)
/configure_file(...)
комбо здесь, потому что source_file
может быть чем-то, что генерируется CMake во время сборки и может отсутствовать во время конфигурации.
1 ответ
Я бы порекомендовал написать скрипт для этого. Вы можете написать это на CMake, но я лично предпочитаю лучший язык, такой как Python:
# Untested, just to show roughly how to do it
import sys
dest_file, source_file, literal_name = sys.argv[1:]
with open(dest_file) as dest, open(source_file) as source:
literal_contents = source.read()
dest.write(f'char const* {literal_name} = R"({literal_contents})";\n')
Соответствующий код CMake:
# String interpolation came in Python 3.6, thus the requirement on 3.6.
# If using CMake < 3.12, use find_package(PythonInterp) instead.
find_package(Python3 3.6 COMPONENTS Interpreter)
# Make sure this resolves correctly. ${CMAKE_CURRENT_LIST_DIR} is helpful;
# it's the directory containing the current file (this cmake file)
set(make_literal_from_file_script "path/to/make_literal_from_file.py")
function(make_literal_from_file dest_file source_file literal_name)
add_custom_command(
OUTPUT "${dest_file}"
COMMAND
"${Python3_EXECUTABLE}" "${make_literal_from_file_script}"
"${dest_file}"
"${source_file}"
"${literal_name}"
DEPENDS "${source_file}")
endfunction()
Если вам не нужна зависимость от Python, вы можете использовать C++ (показан только код CMake):
add_executable(make_literal_from_file_exe
path/to/cpp/file.cpp
)
function(make_literal_from_file dest_file source_file literal_name)
add_custom_command(
OUTPUT "${dest_file}"
COMMAND
make_literal_from_file_exe
"${dest_file}"
"${source_file}"
"${literal_name}"
DEPENDS "${source_file}")
endfunction()