Commit 4f392ddb authored by Marcin Kolenda's avatar Marcin Kolenda Committed by Daniel Kochmański

examples: add cmake example

parent 3b84b388
#=============================================================================
# CMake project configuration file.
# Documentation: https://cmake.org/cmake/help/v3.6/
#=============================================================================
cmake_minimum_required(VERSION 3.6) # released in 2016
project(cmake_ecl_proj)
set(CMAKE_MODULE_PATH "${CMAKE_MODULE_PATH}" "${CMAKE_SOURCE_DIR}/cmake/Modules/")
# Find ECL library. Refer to ./cmake/Modules/FindECL.cmake and
# https://cmake.org/cmake/help/v3.0/command/find_package.html
find_package(ECL REQUIRED)
include_directories(${ECL_INCLUDE_DIR})
# Put lisp sources and other files relevant for compilation here
# Any change of that files will trigger recompilation of `core-lisp` target
set(CORE_LISP_SOURCES
src/lisp/core-lisp.asd
src/lisp/core-lisp.lisp)
# Specify how `core-lisp` library is build by ECL
# The library is going to be built statically and moved to
# ${CMAKE_CURRENT_BINARY_DIR}.
#
# How to customize
# ================
# You can change "core-lisp" consistently to different name.
# Value of `:init-name` keyword must match extern declaration in C++ code.
add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/core-lisp.a
COMMAND ${ECL_BIN_PATH} --norc
--eval '(require :asdf)'
# Remember to end path with / here. Notably ....src/lisp/, not ....src/lisp
--eval '(push \"${CMAKE_CURRENT_SOURCE_DIR}/src/lisp/\" asdf:*central-registry*)'
--eval '(asdf:make-build :core-lisp :type :static-library :move-here \"${CMAKE_CURRENT_BINARY_DIR}\" :init-name \"init_lib_CORE_LISP\")'
--eval '(quit)'
DEPENDS ${CORE_LISP_SOURCES})
# This goes in pair with `add_custom_command` above.
add_custom_target(core-lisp ALL DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/core-lisp.a)
# Define executable to build. You can add other sources here.
# You may change "cmake_ecl" consistently into any name.
add_executable(cmake_ecl
src/cxx/main.cpp)
# Make cmake_ecl depend on core-lisp so it's going to be build before executable
add_dependencies(cmake_ecl core-lisp)
# Set what should be linked into `cmake_ecl` executable.
target_link_libraries(cmake_ecl
${ECL_LIBRARIES}
${CMAKE_CURRENT_BINARY_DIR}/core-lisp.a)
set_target_properties(cmake_ecl PROPERTIES
# Set c++ standard
CXX_STANDARD 17
# Do you want to use custom compiler extensions?
CXX_EXTENSION ON)
# Description
This example shows how to setup CMake to build C++ project which uses ECL library.
In `src/lisp` is definition of `core-lisp` system that's being loaded into C++
program.
Functions defined in `src/lisp/core-lisp.lisp`:
```
(defun hello-world () (format t "Hello World!~%"))
```
are used in `src/cxx/main.cpp`:
```
extern "C" {
extern void init_lib_CORE_LISP(cl_object);
}
int main(int argc, char** argv) {
cl_boot(argc, argv);
ecl_init_module(NULL, init_lib_CORE_LISP);
cl_eval(c_string_to_object("(hello-world)"));
cl_shutdown();
return 0;
}
```
## CMakeLists.txt
For more information about setup read `CMakeLists.txt` comments.
# Build
Run:
```
$ mkdir build
$ cd build
```
If ECL is built and installed with non-default prefix use:
```
$ cmake -DCMAKE_PREFIX_PATH=/home/user/local_prefix/ ..
```
Otherwise you don't have to set `CMAKE_PREFIX_PATH`:
```
$ cmake ..
```
Finally run:
```
$ make
```
It shall produce: `cmake_ecl` executable and `core-lisp.a` static library that
has been linked to executable.
# Run
```
$ ./cmake_ecl
Hello World!
```
# Notes
1. You don't have to remove `./build` directory if you want to change option
in `CMakeLists.txt`. Just run `make`.
2. If you see `undefined reference` errors look at `nm --demangle core-lisp.a`
output. You may have forgot setting `:init-name` in `CMakeLists.txt`
3. To reuse this example you must copy `cmake` directory to your project. It
contains `FindECL.cmake`
4. You don't have to have `ecl` executable in `$PATH` environment variable.
`FindECL.cmake` shall find it.
#=============================================================================
# FindECL
# --------
#
# Find ECL library and binary executables.
#
# Find the Embedded Common Lisp library headers and libraries.
#
# This module provides the following variables:
#
# ECL_INCLUDE_DIRS - where to find ecl/ecl.h, etc.
# ECL_LIBRARIES - List of libraries to link when using ecl.
# ECL_FOUND - True if ecl found.
# ECL_VERSION_STRING - the version of ecl found (since CMake 2.8.8)
#
# ECL_BIN_DIR - directory that contains `ecl` and `ecl-config` executables
# ECL_BIN_PATH - path to `ecl` binary executable
# ECL_CONFIG_BIN_PATH - path to `ecl-config` binary executable
# ECL_VERSION - full version string e.g. "16.1.3"
# ECL_VERSION_MAJOR - major version string e.g. "16"
# ECL_VERSION_MINOR - minor version string e.g. "1"
# ECL_VERSION_PATCH - patch version string e.g. "3"
#=============================================================================
find_path(ECL_INCLUDE_DIR NAMES ecl/ecl.h)
mark_as_advanced(ECL_INCLUDE_DIR)
find_library(ECL_LIBRARY NAMES
ecl
# TODO check how it's on windows
)
mark_as_advanced(ECL_LIBRARY)
# Find the ECL_VERSION_STRING
if(ECL_INCLUDE_DIR)
if(EXISTS "${ECL_INCLUDE_DIR}/ecl/config.h")
file(STRINGS "${ECL_INCLUDE_DIR}/ecl/config.h" ecl_version_str
REGEX "^#define[\t ]+ECL_VERSION_NUMBER[\t ]+[0-9]*.*")
string(REGEX REPLACE "^#define[\t ]+ECL_VERSION_NUMBER[\t ]+([0-9]*).*"
"\\1" ECL_VERSION_STR_RAW ${ecl_version_str})
# at this moment ${ECL_VERSION_STR_RAW} contain version number in format
# 160103 for version 16.1.3. Let's convert it to period separated format.
set(version_helper_regex "^([0-9][0-9])([0-9][0-9])([0-9][0-9])$")
string(REGEX REPLACE ${version_helper_regex} "\\1" major_raw
${ECL_VERSION_STR_RAW})
string(REGEX REPLACE ${version_helper_regex} "\\2" minor_raw
${ECL_VERSION_STR_RAW})
string(REGEX REPLACE ${version_helper_regex} "\\3" patch_raw
${ECL_VERSION_STR_RAW})
string(REGEX REPLACE "^[0]*([0-9]+)" "\\1" ECL_VERSION_MAJOR ${major_raw})
string(REGEX REPLACE "^[0]*([0-9]+)" "\\1" ECL_VERSION_MINOR ${minor_raw})
string(REGEX REPLACE "^[0]*([0-9]+)" "\\1" ECL_VERSION_PATCH ${patch_raw})
# version format conversion is done
set(ECL_VERSION "${ECL_VERSION_MAJOR}.${ECL_VERSION_MINOR}.${ECL_VERSION_PATCH}")
unset(ecl_version_str)
unset(ECL_VERSION_STR_RAW)
unset(version_helper_regex)
unset(major_raw)
unset(minor_raw)
unset(patch_raw)
endif()
endif()
find_path(ECL_BIN_DIR bin/ecl)
include(FindPackageHandleStandardArgs)
FIND_PACKAGE_HANDLE_STANDARD_ARGS(ECL
REQUIRED_VARS ECL_LIBRARY ECL_INCLUDE_DIR
VERSION_VAR ECL_VERSION_STRING)
if(ECL_FOUND)
set(ECL_LIBRARIES ${ECL_LIBRARY})
if(ECL_BIN_DIR)
set(ECL_BIN_DIR "${ECL_BIN_DIR}/bin")
set(ECL_BIN_PATH "${ECL_BIN_DIR}/ecl")
set(ECL_CONFIG_BIN_PATH "${ECL_BIN_DIR}/ecl-config")
endif()
endif()
#include <iostream>
#include <ecl/ecl.h>
extern "C" {
extern void init_lib_CORE_LISP(cl_object);
}
int main(int argc, char** argv) {
cl_boot(argc, argv);
ecl_init_module(NULL, init_lib_CORE_LISP);
cl_eval(c_string_to_object("(hello-world)"));
cl_shutdown();
return 0;
}
(defsystem "core-lisp"
:components ((:file "core-lisp")))
(defun hello-world () (format t "Hello World!~%"))
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment