向CMake中的自定义编译器/语言添加额外的编译步骤

考拉格

这是我之前发布的一个问题的后续措施我的基本问题是使用Gambit Scheme构建应用程序

虽然上述问题中建议的解决方案有效,但它很麻烦,所以我决定尝试将Gambit Scheme作为自定义编译器/语言添加到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.c中添加以下内容

# 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 / Modules中研究了其他一些编译器,但实际上找不到能做到这一点的语言。此条似乎是增加一个新的语言中最完整的文档,在官方文档似乎没有真正提到它。

润滑脂

以下技巧应该可以帮助您实现所追求的目标。该解决方案可以消除彼此之间的冲突,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最终链接开始之前,先前的内容将被新创建的内容覆盖。这样,我们可以诱骗CMake吞下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.ogsc但是,将-如果没有特别告知-生成.c 替代.scm.c,这会导致-link步骤不匹配编译成的人产生的符号.scm.o

附带说明一下,这种方法显然不能处理方案源之间的任何动态/隐式依赖关系-如果您以需要重新转换和重新编译factmodule.scm的方式进行更改main.scm,则不会为您解决。据我所知,目前尚无办法告诉CMake您想要注册一个自定义依赖项扫描程序来解析.scm文件。

看来一个普通的老人makefile可能做得更好。

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类Dev

如何在旧版编译器中为自定义语言创建自己的旧版语言模板?

来自分类Dev

Xcode编译器自定义编译器与Apple LLVM 8.0和Apple评论

来自分类Dev

SASS编译器自定义规则/例外

来自分类Dev

自定义Java编译器(扩展名)

来自分类Dev

如何在Java中创建自定义编译器警告?

来自分类Dev

自定义Alamofire响应功能中的编译器错误

来自分类Dev

在CMake中添加自定义构建步骤

来自分类Dev

如何使用自定义编译器和自定义标志构建Qt?

来自分类Dev

wix 刻录自定义引导程序 - 自定义编译器扩展

来自分类Dev

如何在cmake工具链文件中设置编译功能以使已知的自定义编译器使用target_compile_features

来自分类Dev

向django添加自定义语言

来自分类Dev

当unordered_map中的用户自定义哈希函数时,无法解码g ++中模糊的编译器错误

来自分类Dev

当unordered_map中的用户自定义哈希函数时,无法解码g ++中模糊的编译器错误

来自分类Dev

std::set 插入器不尊重自定义比较器。(可能的编译器错误?)

来自分类Dev

在目标构建设置中找不到 Apple LLVM 8.0 - 自定义编译器标志

来自分类Dev

尽管未声明,C 编译器仍期望在函数中添加额外参数

来自分类Dev

强制setup.py使用我的自定义编译器

来自分类Dev

为什么编译器不能为自定义集合推断循环变量类型?

来自分类Dev

如何利用插件利用自定义函数扩展Less编译器

来自分类Dev

编译器未调用自定义JavaFileManager的getJavaFileForOutput(...)方法

来自分类Dev

如何自定义Microsoft cmdline编译器cl的.exe文件名?

来自分类Dev

从OCaml编译器打开模块而无需构建自定义顶层

来自分类Dev

在自定义目标中计算出的编译器附加选项

来自分类Dev

为什么编译器不能为自定义集合推断循环变量类型?

来自分类Dev

为ghc编译器指定自定义临时目录

来自分类Dev

从OCaml编译器打开模块而无需构建自定义顶层

来自分类Dev

如何在Android项目上设置自定义Java编译器参数?

来自分类Dev

cmake:如何根据当前编译器版本添加编译器标志

来自分类Dev

编译器在我的函数参数中寻找额外的属性

Related 相关文章

  1. 1

    如何在旧版编译器中为自定义语言创建自己的旧版语言模板?

  2. 2

    Xcode编译器自定义编译器与Apple LLVM 8.0和Apple评论

  3. 3

    SASS编译器自定义规则/例外

  4. 4

    自定义Java编译器(扩展名)

  5. 5

    如何在Java中创建自定义编译器警告?

  6. 6

    自定义Alamofire响应功能中的编译器错误

  7. 7

    在CMake中添加自定义构建步骤

  8. 8

    如何使用自定义编译器和自定义标志构建Qt?

  9. 9

    wix 刻录自定义引导程序 - 自定义编译器扩展

  10. 10

    如何在cmake工具链文件中设置编译功能以使已知的自定义编译器使用target_compile_features

  11. 11

    向django添加自定义语言

  12. 12

    当unordered_map中的用户自定义哈希函数时,无法解码g ++中模糊的编译器错误

  13. 13

    当unordered_map中的用户自定义哈希函数时,无法解码g ++中模糊的编译器错误

  14. 14

    std::set 插入器不尊重自定义比较器。(可能的编译器错误?)

  15. 15

    在目标构建设置中找不到 Apple LLVM 8.0 - 自定义编译器标志

  16. 16

    尽管未声明,C 编译器仍期望在函数中添加额外参数

  17. 17

    强制setup.py使用我的自定义编译器

  18. 18

    为什么编译器不能为自定义集合推断循环变量类型?

  19. 19

    如何利用插件利用自定义函数扩展Less编译器

  20. 20

    编译器未调用自定义JavaFileManager的getJavaFileForOutput(...)方法

  21. 21

    如何自定义Microsoft cmdline编译器cl的.exe文件名?

  22. 22

    从OCaml编译器打开模块而无需构建自定义顶层

  23. 23

    在自定义目标中计算出的编译器附加选项

  24. 24

    为什么编译器不能为自定义集合推断循环变量类型?

  25. 25

    为ghc编译器指定自定义临时目录

  26. 26

    从OCaml编译器打开模块而无需构建自定义顶层

  27. 27

    如何在Android项目上设置自定义Java编译器参数?

  28. 28

    cmake:如何根据当前编译器版本添加编译器标志

  29. 29

    编译器在我的函数参数中寻找额外的属性

热门标签

归档