这是对早期我贴出的问题的一点跟进。我的基本问题是用Gambit方案构建一个应用程序。
虽然上述问题中提出的解决方案有效,但它有点麻烦,因此我决定尝试将Gambit方案作为自定义编译器/语言添加到CMake中。按照这个问题中的建议,我创建了以下文件:
cmake/CMakeDetermineGambitCompiler.cmake
# Find the compiler
find_program(
CMAKE_Gambit_COMPILER
NAMES "gambitc"
HINTS "${CMAKE_SOURCE_DIR}"
DOC "Gambit Scheme compiler"
)
mark_as_advanced( CMAKE_Gambit_COMPILER )
set( CMAKE_Gambit_SOURCE_FILE_EXTENSIONS scm;six )
# Remember this as a potential error
set( CMAKE_Gambit_OUTPUT_EXTENSION .c )
set( CMAKE_Gambit_COMPILER_ENV_VAR "" )
# Configure variables set in this file for fast reload later on
configure_file( ${CMAKE_CURRENT_LIST_DIR}/CMakeGambitCompiler.cmake.in
${CMAKE_PLATFORM_INFO_DIR}/CMakeGambitCompiler.cmake )cmake/CMakeGambitInformation.cmake
# This file sets the basic flags for the GAMBIT compiler
# Generate the C files
set( CMAKE_Gambit_COMPILE_OBJECT
"<CMAKE_Gambit_COMPILER> -o <OBJECT> -c <SOURCE>"
)
# Build a executable
set( CMAKE_Gambit_LINK_EXECUTABLE
"<CMAKE_Gambit_COMPILER> -o <TARGET> -exe <OBJECTS>"
)
set( CMAKE_Gambit_INFORMATION_LOADED 1 )cmake/CMakeGambitCompiler.cmake.in
set( CMAKE_Gambit_COMPILER "@CMAKE_Gambit_COMPILER@" )
set( CMAKE_Gambit_COMPILER_LOADED 1 )
set( CMAKE_Gambit_SOURCE_FILE_EXTENSIONS @CMAKE_Gambit_SOURCE_FILE_EXTENSIONS@ )
set( CMAKE_Gambit_OUTPUT_EXTENSION @CMAKE_Gambit_OUTPUT_EXTENSION@ )
set( CMAKE_Gambit_COMPILER_ENV_VAR "@CMAKE_Gambit_COMPILER_ENV_VAR@" )cmake/CMakeTestGambitCompiler.cmake
# For now do nothing
set( CMAKE_Gambit_COMPILER_WORKS 1 CACHE INTERNAL "" )然后,在我的项目根目录中,我还有两个文件:
CMakeTexts.txt
cmake_minimum_required( VERSION 3.10...3.18 )
if( ${CMAKE_VERSION} VERSION_LESS 3.12 )
cmake_policy( VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} )
endif()
# Give the project a name
project( cmake-scheme-template NONE )
# Build simple Gambit Scheme program
list( APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
enable_language( Gambit )
add_executable( ${PROJECT_NAME} main.scm )要构建的实际代码,main.scm
;;; Simple Scheme example
(begin (write "Hello, Schemer!")
(newline))给出了以下结构:
project_root/
cmake/
CMakeDetermineGambitCompiler.cmake
CMakeGambitCompiler.cmake.in
CMakeGambitInformation.cmake
CMakeTestGambitCompiler.cmake
CMakeLists.txt
main.scm虽然这适用于单个文件,但一旦我添加了另一个源文件,我需要Gambit首先为从Scheme源代码生成的所有C文件创建一个链接文件。下面是一个简单的例子:
假设我添加了第二个文件,factmodule.scm
;;; This is a simple Scheme module that provides a functions that will
;;; calculate the factorial of a number n
(define fact
(lambda (n)
(if (zero? n)
1
(* n (fact (- n 1))))))并更新main.scm
(begin (write "Hello, Schemer!")
(newline)
(write "10! = ")
(write (number->string (fact 10)))
(newline))为了建立这一“手工”,我做了以下工作:
$ gambitc -c factmodule.scm main.scm # generate C files from Scheme
$ gambitc -o link_file.c -link factmodule.c main.c # generate a link file
$ gambitc -obj factmodule.c main.c link_file.c # compile the C files in object files
$ gcc -o myexec -factmodule.o main.o link_file.o -lgambit # link the final executable我的问题是第二步,创建链接文件。理想情况下,我想在cmake/CMakeGambitInformation.cmake中添加如下内容:
# Generate the C, link, and object files
set( CMAKE_Gambit_COMPILE_OBJECT
"<CMAKE_Gambit_COMPILER> -o <OBJECT> -c <SOURCE>" # generate C files
"<CMAKE_Gambit_COMPILER> -o link_file.c -link <OBJECTS>" # generate link file
"<CMAKE_Gambit_COMPILER> -obj <OBJECTS>" # compile C and link files
)但这有两个问题。首先,<OBJECTS>保存生成的C文件;据我所知,CMAKE_Gambit_COMPILE_OBJECT中给出的命令是在每个源文件基础上执行的。第二,我显然希望只运行最后两个命令一次,但在调用授予CMAKE_Gambit_LINK_EXECUTABLE的命令之前。
是否有一种方法可以在创建对象之后,但在对象链接之前执行自定义命令?
我研究过CMake/模中的其他编译器,但实际上找不到这样的语言。这一项似乎是添加新语言的最完整的文档,官方医生似乎没有提到它。
发布于 2020-09-07 10:18:37
下面的黑客应该能达到你想要的目标。解决方案发布时,gsc和cmake并不总是友好相处,因为它们都有自己的隐式处理文件扩展名的方法。不管怎样,我们开始吧。
我打算从cmake内部复制的一系列命令(文件名略有不同)是
gsc -c linkstub.scm
gsc -c factmodule.scm
gsc -c main.scm
gsc -obj linkstub.scm
gsc -obj factmodule.scm
gsc -obj main.scm
gsc -o linkstub.c -link factmodule.c main.c
gsc -obj linkstub.c
gsc -o test -exe factmodule.o main.o linkstub.o在这里,linkstub.scm是一个空的(生成)虚拟文件。对于最终的链接,我们需要这样做,因为我们不能修改传递给<OBJECTS>的CMAKE_Gambit_LINK_EXECUTABLE列表。相应的linkstub.c文件将是gsc -o linkstub.c -link ...生成的实际链接文件。这可以通过add_custom_command和它的PRE_LINK选项来实现(每次目标被链接时执行一个命令)。同样的自定义命令也将linkstub.c编译成一个对象文件;在最后的链接开始之前,前面的linkstub.o被新创建的linkstub.o覆盖。这样,我们就可以骗CMake吞下一个linkstub.o,只要编译了所有其他对象文件,这个linkstub.o就会被更新。
开始吧。我们需要在它们各自的文件中更改编译器命令和对象扩展名:
set(CMAKE_Gambit_COMPILE_OBJECT
"<CMAKE_Gambit_COMPILER> -o <SOURCE>.c -c <SOURCE>"
"<CMAKE_Gambit_COMPILER> -o <OBJECT> -obj <SOURCE>.c")
set(CMAKE_Gambit_OUTPUT_EXTENSION .o)这有点脏,因为它会将构建工件留在源树中(也许您可以为构建树提供一个路径--在这里,稍后我们将自动删除它们)。请注意,在每个源的基础上将.scm编译成.o也会有更好的扩展,因为在您最初的尝试中,链接步骤可能会变得相当大(所有.c文件都被编译成可执行文件)。接下来,链接文件的占位符:
set(linkstub ${CMAKE_CURRENT_BINARY_DIR}/linkstub.scm)
file(WRITE ${linkstub} "")同样,这只是将相应的对象文件输入到目标的对象依赖项中的一种手段。现在剩下的:
set(sources
factmodule.scm
main.scm)
list(TRANSFORM sources
APPEND ".c"
OUTPUT_VARIABLE cSources)
list(TRANSFORM cSources
PREPEND "${CMAKE_CURRENT_SOURCE_DIR}/"
OUTPUT_VARIABLE cSources)
list(REMOVE_ITEM cSources
linkstub.scm.c)
add_executable(test ${sources} ${linkstub})
add_custom_command(TARGET test
PRE_LINK
COMMAND ${CMAKE_Gambit_COMPILER}
-o
${CMAKE_CURRENT_BINARY_DIR}/linkstub.scm.c
-link
${cSources}
COMMAND ${CMAKE_Gambit_COMPILER}
-o
${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/test.dir/linkstub.scm.o
-obj
${CMAKE_CURRENT_BINARY_DIR}/linkstub.scm.c
COMMAND ${CMAKE_COMMAND} -E remove ${cSources})最后一条命令完成了所有的魔法。它重写linkstub.scm.c,将其编译成一个对象文件,并最终删除源树中所有生成的.scm.c文件。注意,在其中一个路径中有一个丑陋的、硬编码的CMakeFiles/test.dir/,应该有一种方法可以从目标查询这个路径来绕过这个路径。
请再次注意,这里的文件扩展名有些关键,解决方案很脆弱。CMake似乎绝对需要附加.o的对象文件,即.scm.o。然而,如果不告诉gsc,它将生成一个.c而不是.scm.c,这将导致-link步骤产生与编译到.scm.o中的符号不匹配的符号。
顺便提一句,这种方法显然不会处理方案源之间的任何动态/隐式依赖--如果您以一种需要重新编译和重新编译的方式更改factmodule.scm,这将不会为您解决。据我所知,目前无法教CMake如何注册一个解析.scm文件的自定义依赖项扫描器。
看来一个普通的老makefile可能会做得更好。
https://stackoverflow.com/questions/63658231
复制相似问题