Reputation: 143
I have the following situation: I want to compile a number of Scheme files with Gambit into an executable. To that end, I use gambit to translate/generate all Scheme files into C and object files, which in turn are then compiled and linked into an executable.
Assuming I have the following three files (example is taken from the Gambit documentation):
/* 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)
If I were to compile it by hand, this were the steps to take:
$ 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))
EDIT: Mind the last command, that's not a typo, the final step is to use gcc
for linking.
Now, I wish to use CMake to do this for me. Here's an outline of my 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 ...)
I've never worked with CMake for anything but pure C/C++ project. What's the smartest way to go about this? Ultimately, I want so have a CMakeLists.txt
that I can place in a subdirectory with all my Scheme files, have it generate all C and object files, and have CMake in the parent directory use them to create executable and libraries with add_executable
and add_library
.
Upvotes: 4
Views: 3066
Reputation: 27096
You could define the scm source files and then create a custom_command for each of them. This would create a .c file for each .scm file. The created files could be added to a list (e.g. named scm_c_files) and used in the add_executable command. Finally, the libraries used can be defined with target_link_libraries.
I don't know exactly how gsc and the corresponding incremental link file work, but if you could give the incremental link file a custom name (is there some sort of -o option for it?), it would simply be another add_custom_command that depends only on the .c files generated from the .scm files.
For example something like this:
add_custom_command(
OUTPUT incLink.c
gsc -link ${scm_c_files} -o incLink.c
DEPENDS ${scm_c_files}
)
A complete CMakeLists.txt could look something like this:
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)
Test
Since I don't have gsc I just replaced the custom commands with an echo and a touch. If I run
cmake .
make
I get as output:
[ 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
which seems to be what you're looking for.
Upvotes: 5