diff --git a/examples/cmake/CMakeLists.txt b/examples/cmake/CMakeLists.txt new file mode 100644 index 000000000..19e587037 --- /dev/null +++ b/examples/cmake/CMakeLists.txt @@ -0,0 +1,58 @@ +#============================================================================= +# 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) + diff --git a/examples/cmake/README.md b/examples/cmake/README.md new file mode 100644 index 000000000..75431ad59 --- /dev/null +++ b/examples/cmake/README.md @@ -0,0 +1,72 @@ +# 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. diff --git a/examples/cmake/cmake/Modules/FindECL.cmake b/examples/cmake/cmake/Modules/FindECL.cmake new file mode 100644 index 000000000..402811dfd --- /dev/null +++ b/examples/cmake/cmake/Modules/FindECL.cmake @@ -0,0 +1,82 @@ +#============================================================================= +# 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() diff --git a/examples/cmake/src/cxx/main.cpp b/examples/cmake/src/cxx/main.cpp new file mode 100644 index 000000000..3fb230a86 --- /dev/null +++ b/examples/cmake/src/cxx/main.cpp @@ -0,0 +1,14 @@ +#include +#include + +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; +} diff --git a/examples/cmake/src/lisp/core-lisp.asd b/examples/cmake/src/lisp/core-lisp.asd new file mode 100644 index 000000000..9a2888905 --- /dev/null +++ b/examples/cmake/src/lisp/core-lisp.asd @@ -0,0 +1,2 @@ +(defsystem "core-lisp" + :components ((:file "core-lisp"))) diff --git a/examples/cmake/src/lisp/core-lisp.lisp b/examples/cmake/src/lisp/core-lisp.lisp new file mode 100644 index 000000000..931fa9dbe --- /dev/null +++ b/examples/cmake/src/lisp/core-lisp.lisp @@ -0,0 +1 @@ +(defun hello-world () (format t "Hello World!~%"))