#c #cmake #scheme
#c #cmake #схема
Вопрос:
У меня следующая ситуация: я хочу скомпилировать несколько файлов Scheme с помощью Gambit в исполняемый файл. С этой целью я использую gambit для перевода / генерации всех файлов Scheme в C и объектные файлы, которые, в свою очередь, затем компилируются и связываются в исполняемый файл.
Предполагая, что у меня есть следующие три файла (пример взят из документации Gambit):
/* File: "m1.c" */
int power_of_2 (int x) { return 1<<x; }
; File: "m2.scm"
(c-declare "extern int power_of_2 ();")
(define pow2 (c-lambda (int) int "power_of_2"))
(define (twice x) (cons x x))
; File: "m3.scm"
(write (map twice (map pow2 '(1 2 3 4)))) (newline)
Если бы я должен был скомпилировать ее вручную, вот какие шаги нужно было предпринять:
$ gsc -c m2.scm # create m2.c (note: .scm is optional)
$ gsc -c m3.scm # create m3.c (note: .scm is optional)
$ gsc -link m2.c m3.c # create the incremental link file m3_.c
$ gsc -obj m1.c m2.c m3.c m3_.c # create all object files *.o
m1.c:
m2.c:
m3.c:
m3_.c:
$ gcc m1.o m2.o m3.o m3_.o -lgambit -lm -ldl -lutil # link object files
$ ./a.out
((2 . 2) (4 . 4) (8 . 8) (16 . 16))
РЕДАКТИРОВАТЬ: Обратите внимание на последнюю команду, это не опечатка, последний шаг — использовать gcc
для компоновки.
Теперь я хочу использовать CMake, чтобы сделать это за меня. Вот схема моего CMakeLists.txt
:
cmake_minimum_required( VERSION 3.8...3.18 )
if( ${CMAKE_VERSION} VERSION_LESS 3.12 )
cmake_policy( VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} )
endif()
# Give this project a name
project( schemetemplate VERSION 0.0.1 )
# compile all .scm files into *.c files
# the C files as target
add_custom_target(
compiledSchemeFiles ALL
SOURCES m2.scm
m3.scm
COMMAND # this is where I call gsc(?)
VERBATIM )
# or do I use custom command?
add_custom_command( ... )
# the final executable, how to tell add_executable I want all generated files as dependency?
add_exectuable( finalexec m1.c ...)
Я никогда не работал с CMake ни для чего, кроме чистого проекта на C / C . Какой самый разумный способ сделать это? В конечном счете, я хочу иметь so, CMakeLists.txt
который я мог бы поместить в подкаталог со всеми моими файлами Scheme, заставить его генерировать все C и объектные файлы, а CMake в родительском каталоге использовать их для создания исполняемых файлов и библиотек с add_executable
и add_library
.
Комментарии:
1. Вам действительно нужен отдельный
-obj
шаг?
Ответ №1:
Вы могли бы определить исходные файлы scm, а затем создать custom_command для каждого из них. Это создало бы файл .c для каждого файла .scm. Созданные файлы можно добавить в список (например, с именем scm_c_files) и использовать в команде add_executable. Наконец, используемые библиотеки могут быть определены с помощью target_link_libraries .
Я не знаю точно, как работают gsc и соответствующий файл инкрементной ссылки, но если бы вы могли присвоить файлу инкрементной ссылки пользовательское имя (есть ли для него какая-то опция -o?), это была бы просто другая add_custom_command, которая зависит только от файлов .c, сгенерированных из файлов .scm.
Например, что-то вроде этого:
add_custom_command(
OUTPUT incLink.c
gsc -link ${scm_c_files} -o incLink.c
DEPENDS ${scm_c_files}
)
Полный CMakeLists.txt может выглядеть примерно так:
cmake_minimum_required(VERSION 3.17)
project(finalexec C)
set(CMAKE_C_STANDARD 99)
set(SCM_SOURCES m2.scm m3.scm)
set(scm_c_files)
foreach(scm_source ${SCM_SOURCES})
get_filename_component(file_c ${scm_source} NAME_WE)
set(file_c "${file_c}.c")
add_custom_command(
OUTPUT ${file_c}
COMMAND gsc -c ${CMAKE_CURRENT_SOURCE_DIR}/${scm_source}
DEPENDS ${scm_source}
VERBATIM
)
list(APPEND scm_c_files ${file_c})
endforeach()
add_custom_command(
OUTPUT incLink.c
COMMAND gsc -link ${scm_c_files} -o incLink.c
DEPENDS ${scm_c_files}
)
add_executable(finalexec m1.c ${scm_c_files} incLink.c)
target_link_libraries(finalexec gambit m dl util)
Тест
Поскольку у меня нет gsc, я просто заменил пользовательские команды echo и touch. Если я запущу
cmake .
make
Я получаю в качестве выходных данных:
[ 12%] Generating m3.c
gsc -c /Users/stephan/kk/m3.scm
[ 25%] Generating m2.c
gsc -c /Users/stephan/kk/m2.scm
[ 37%] Generating incLink.c
gsc -link m2.c m3.c -o incLink.c
[ 50%] Building C object CMakeFiles/finalexec.dir/m1.c.o
[ 62%] Building C object CMakeFiles/finalexec.dir/m2.c.o
[ 75%] Building C object CMakeFiles/finalexec.dir/m3.c.o
[ 87%] Building C object CMakeFiles/finalexec.dir/incLink.c.o
[100%] Linking C executable finalexec
[100%] Built target finalexec
похоже, это то, что вы ищете.
Комментарии:
1. Спасибо, это работает! Я нашел похожее решение в книге Mastering CMake, но оно еще лучше!
2. @koalag Не могли бы вы опубликовать решение, найденное при освоении CMake?