【问题标题】:CMake: How to run a custom command on a number of files to generate source files?CMake:如何对多个文件运行自定义命令以生成源文件?
【发布时间】:2020-08-21 20:26:06
【问题描述】:

我有以下情况:我想将多个带有Gambit的Scheme文件编译成可执行文件。为此,我使用 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 ...) 

除了纯 C/C++ 项目,我从未使用过 CMake。最聪明的方法是什么?最终,我想要一个CMakeLists.txt,我可以将它与我的所有Scheme文件放在一个子目录中,让它生成所有C和目标文件,并让CMake在父目录中使用它们来创建带有add_executable的可执行文件和库和add_library

【问题讨论】:

  • 您真的需要单独的-obj 步骤吗?

标签: c cmake scheme


【解决方案1】:

您可以定义 scm 源文件,然后为每个文件创建一个 custom_command。这将为每个 .scm 文件创建一个 .c 文件。可以将创建的文件添加到列表(例如名为 scm_c_files)并在 add_executable 命令中使用。最后,可以使用 target_link_libraries 定义使用的库。

我不知道 gsc 和相应的增量链接文件是如何工作的,但是如果您可以给增量链接文件一个自定义名称(是否有某种 -o 选项?),它只是另一个add_custom_command 仅依赖于从 .scm 文件生成的 .c 文件。

例如这样的:

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

这似乎就是你要找的东西。

【讨论】:

  • 谢谢,这行得通!我在 Mastering CMake 一书中找到了类似的解决方案,但更好!
  • @koalag 能否发布在 Mastering CMake 中找到的解决方案?
猜你喜欢
  • 2021-12-11
  • 1970-01-01
  • 1970-01-01
  • 2020-06-23
  • 1970-01-01
  • 1970-01-01
  • 2020-07-23
  • 2021-10-29
  • 2017-01-19
相关资源
最近更新 更多