CMake: как запустить пользовательскую команду для нескольких файлов, чтобы сгенерировать исходные файлы?

#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?