From f1a39d97ae5809b16434ed15952a31bd6e8d1498 Mon Sep 17 00:00:00 2001 From: DimitriPlotnikov Date: Wed, 29 Jun 2016 15:15:08 +0200 Subject: [PATCH] -add first draft for the CMake module infrastructure --- .../codegeneration/NESTCodeGenerator.java | 20 ++ .../org/nest/nestml/module/CMakeLists.ftl | 302 ++++++++++++++++++ .../nest/nestml/module/ModuleClassCMake.ftl | 109 +++++++ .../nest/nestml/module/ModuleHeaderCMake.ftl | 74 +++++ 4 files changed, 505 insertions(+) create mode 100644 src/main/resources/org/nest/nestml/module/CMakeLists.ftl create mode 100644 src/main/resources/org/nest/nestml/module/ModuleClassCMake.ftl create mode 100644 src/main/resources/org/nest/nestml/module/ModuleHeaderCMake.ftl diff --git a/src/main/java/org/nest/codegeneration/NESTCodeGenerator.java b/src/main/java/org/nest/codegeneration/NESTCodeGenerator.java index f40438535..0bbe0a09d 100644 --- a/src/main/java/org/nest/codegeneration/NESTCodeGenerator.java +++ b/src/main/java/org/nest/codegeneration/NESTCodeGenerator.java @@ -200,6 +200,24 @@ private void generateModuleCodeForNeurons( final GeneratorEngine generator = new GeneratorEngine(setup); + final Path cmakeLists = Paths.get("CMakeLists.txt"); + generator.generate( + "org.nest.nestml.module.CMakeLists", + cmakeLists, + neurons.get(0)); // an arbitrary AST to match the signature + + final Path cmakeModuleHeader = Paths.get(moduleName + ".h"); + generator.generate( + "org.nest.nestml.module.ModuleHeaderCMake", + cmakeModuleHeader, + neurons.get(0)); // an arbitrary AST to match the signature + + final Path cmakeModuleClass = Paths.get(moduleName + ".cpp"); + generator.generate( + "org.nest.nestml.module.ModuleClassCMake", + cmakeModuleClass, + neurons.get(0)); // an arbitrary AST to match the signature + final Path makefileFile = Paths.get("Makefile.am"); generator.generate( "org.nest.nestml.module.Makefile", @@ -313,6 +331,8 @@ private void setNeuronGenerationParameter( glex.setGlobalValue("astUtils", new ASTUtils()); glex.setGlobalValue("aliasInverter", new AliasInverter()); + glex.setGlobalValue("nestVersion", 3); + } private void defineSolverType(final GlobalExtensionManagement glex, final ASTNeuron neuron) { diff --git a/src/main/resources/org/nest/nestml/module/CMakeLists.ftl b/src/main/resources/org/nest/nestml/module/CMakeLists.ftl new file mode 100644 index 000000000..b0369dcd5 --- /dev/null +++ b/src/main/resources/org/nest/nestml/module/CMakeLists.ftl @@ -0,0 +1,302 @@ +# examples/MyModule/CMakeLists.txt +# +# This file is part of NEST. +# +# Copyright (C) 2004 The NEST Initiative +# +# NEST is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# +# NEST is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with NEST. If not, see . + +cmake_minimum_required( VERSION 2.8.12 ) + +# This CMakeLists.txt is configured to build your external module for NEST. For +# illustrative reasons this module is called 'my' (change SHORT_NAME to your +# preferred module name). NEST requires you to extend the 'SLIModule' (see +# mymodule.h and mymodule.cpp as an example) and provide a module header +# (see MODULE_HEADER). The subsequent instructions +# +# The configuration requires a compiled and installed NEST; if `nest-config` is +# not in the PATH, please specify the absolute path with `-Dwith-nest=...`. +# +# For more informations on how to extend and use your module see: +# https://nest.github.io/nest-simulator/extension_modules + +# 1) Name your module here, i.e. add later with -Dexternal-modules=my: +set( SHORT_NAME ${moduleName} ) + +# the complete module name is here: +set( MODULE_NAME ${r"$"}{SHORT_NAME}module ) + +# 2) Add all your sources here +set( MODULE_SOURCES + ${moduleName}.h ${moduleName}.cpp + <#list neurons as neuron> + ${neuron.getName()}.cpp ${neuron.getName()}.h <#if neuron_has_next>\ + + ) + +# 3) We require a header name like this: +set( MODULE_HEADER ${r"$"}{MODULE_NAME}.h ) +# containing the class description of the class extending the SLIModule + +# 4) Specify your module version +set( MODULE_VERSION_MAJOR 1 ) +set( MODULE_VERSION_MINOR 0 ) +set( MODULE_VERSION "${r"$"}{MODULE_VERSION_MAJOR}.${r"$"}{MODULE_VERSION_MINOR}" ) + +# 5) Leave the rest as is. All files in `sli` will be installed to +# `share/nest/sli/`, so that NEST will find the during initialization. + +# Leave the call to "project(...)" for after the compiler is determined. + +# Set the `nest-config` executable to use during configuration. +set( with-nest OFF CACHE STRING "Specify the `nest-config` executable." ) + +# If it is not set, look for a `nest-config` in the PATH. +if ( NOT with-nest ) + # try find the program ourselves + find_program( NEST_CONFIG + NAMES nest-config + ) + if ( NEST_CONFIG STREQUAL "NEST_CONFIG-NOTFOUND" ) + message( FATAL_ERROR "Cannot find the program `nest-config`. Specify via -Dwith-nest=... ." ) + endif () +else () + set( NEST_CONFIG ${r"$"}{with-nest} ) +endif () + +# Use `nest-config` to get the compile and installation options used with the +# NEST installation. + +# Get the compiler that was used for NEST. +execute_process( + COMMAND ${r"$"}{NEST_CONFIG} --compiler + RESULT_VARIABLE RES_VAR + OUTPUT_VARIABLE NEST_COMPILER + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +# One check on first execution, if `nest-config` is working. +if ( NOT RES_VAR EQUAL 0 ) + message( FATAL_ERROR "Cannot run `${r"$"}{NEST_CONFIG}`. Please specify correct `nest-config` via -Dwith-nest=... " ) +endif () + +# Setting the compiler has to happen before the call to "project(...)" function. +set( CMAKE_CXX_COMPILER "${r"$"}{NEST_COMPILER}" ) + +project( ${r"$"}{MODULE_NAME} CXX ) + +# Get the install prefix. +execute_process( + COMMAND ${r"$"}{NEST_CONFIG} --prefix + RESULT_VARIABLE RES_VAR + OUTPUT_VARIABLE NEST_PREFIX + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +# Use the `NEST_PREFIX` as `CMAKE_INSTALL_PREFIX`. +set( CMAKE_INSTALL_PREFIX "${r"$"}{NEST_PREFIX}" CACHE STRING "Install path prefix, prepended onto install directories." FORCE ) + +# Get the CXXFLAGS. +execute_process( + COMMAND ${r"$"}{NEST_CONFIG} --cflags + RESULT_VARIABLE RES_VAR + OUTPUT_VARIABLE NEST_CXXFLAGS + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +# Get the Includes. +execute_process( + COMMAND ${r"$"}{NEST_CONFIG} --includes + RESULT_VARIABLE RES_VAR + OUTPUT_VARIABLE NEST_INCLUDES + OUTPUT_STRIP_TRAILING_WHITESPACE +) +if ( NEST_INCLUDES ) + # make a cmake list + string( REPLACE " " ";" NEST_INCLUDES_LIST "${r"$"}{NEST_INCLUDES}" ) + foreach ( inc_complete ${r"$"}{NEST_INCLUDES_LIST} ) + # if it is actually a -Iincludedir + if ( "${r"$"}{inc_complete}" MATCHES "^-I.*" ) + # get the directory + string( REGEX REPLACE "^-I(.*)" "\\1" inc "${r"$"}{inc_complete}" ) + # and check whether it is a directory + if ( IS_DIRECTORY "${r"$"}{inc}" ) + include_directories( "${r"$"}{inc}" ) + endif () + endif () + endforeach () +endif () + +# Get, if NEST is build as a (mostly) static application. If yes, also only build +# static library. +execute_process( + COMMAND ${r"$"}{NEST_CONFIG} --static-libraries + RESULT_VARIABLE RES_VAR + OUTPUT_VARIABLE NEST_STATIC_LIB + OUTPUT_STRIP_TRAILING_WHITESPACE +) +if ( NEST_STATIC_LIB ) + set( BUILD_SHARED_LIBS OFF ) +else () + set( BUILD_SHARED_LIBS ON ) +endif () + +# Get all linked libraries. +execute_process( + COMMAND ${r"$"}{NEST_CONFIG} --libs + RESULT_VARIABLE RES_VAR + OUTPUT_VARIABLE NEST_LIBS + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +# Get the data install dir. +execute_process( + COMMAND ${r"$"}{NEST_CONFIG} --datadir + RESULT_VARIABLE RES_VAR + OUTPUT_VARIABLE NEST_DATADIR + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +# Get the documentation install dir. +execute_process( + COMMAND ${r"$"}{NEST_CONFIG} --docdir + RESULT_VARIABLE RES_VAR + OUTPUT_VARIABLE NEST_DOCDIR + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +# Get the library install dir. +execute_process( + COMMAND ${r"$"}{NEST_CONFIG} --libdir + RESULT_VARIABLE RES_VAR + OUTPUT_VARIABLE NEST_LIBDIR + OUTPUT_STRIP_TRAILING_WHITESPACE +) + +# on OS X +set( CMAKE_MACOSX_RPATH ON ) + +# Install all stuff to NEST's install directories. +set( CMAKE_INSTALL_LIBDIR ${r"$"}{NEST_LIBDIR}/nest CACHE STRING "object code libraries (lib/nest or lib64/nest or lib//nest on Debian)" FORCE ) +set( CMAKE_INSTALL_DOCDIR ${r"$"}{NEST_DOCDIR} CACHE STRING "documentation root (DATAROOTDIR/doc/nest)" FORCE ) +set( CMAKE_INSTALL_DATADIR ${r"$"}{NEST_DATADIR} CACHE STRING "read-only architecture-independent data (DATAROOTDIR/nest)" FORCE ) + +include( GNUInstallDirs ) + +# CPack stuff. Required for target `dist`. +set( CPACK_GENERATOR TGZ ) +set( CPACK_SOURCE_GENERATOR TGZ ) + +set( CPACK_PACKAGE_DESCRIPTION_SUMMARY "NEST Module ${r"$"}{MODULE_NAME}" ) +set( CPACK_PACKAGE_VENDOR "NEST Initiative (http://www.nest-initiative.org/)" ) + +set( CPACK_PACKAGE_VERSION_MAJOR ${r"$"}{MODULE_VERSION_MAJOR} ) +set( CPACK_PACKAGE_VERSION_MINOR ${r"$"}{MODULE_VERSION_MINOR} ) +set( CPACK_PACKAGE_VERSION ${r"$"}{MODULE_VERSION} ) + +set( CPACK_SOURCE_IGNORE_FILES + "\\\\.gitignore" + "\\\\.git/" + "\\\\.travis\\\\.yml" + + # if we have in source builds + "/build/" + "/_CPack_Packages/" + "CMakeFiles/" + "cmake_install\\\\.cmake" + "Makefile.*" + "CMakeCache\\\\.txt" + "CPackConfig\\\\.cmake" + "CPackSourceConfig\\\\.cmake" + ) +set( CPACK_SOURCE_PACKAGE_FILE_NAME ${r"$"}{MODULE_NAME} ) + +set( CPACK_PACKAGE_INSTALL_DIRECTORY "${r"$"}{MODULE_NAME} ${r"$"}{MODULE_VERSION}" ) +include( CPack ) + +# add make dist target +add_custom_target( dist + COMMAND ${r"$"}{CMAKE_MAKE_PROGRAM} package_source + # not sure about this... seems, that it will be removed before dist... + # DEPENDS doc + COMMENT "Creating a source distribution from ${r"$"}{MODULE_NAME}..." + ) + + +if ( BUILD_SHARED_LIBS ) + # When building shared libraries, also create a module for loading at runtime + # with the `Install` command. + add_library( ${r"$"}{MODULE_NAME}_module MODULE ${r"$"}{MODULE_SOURCES} ) + set_target_properties( ${r"$"}{MODULE_NAME}_module + PROPERTIES + COMPILE_FLAGS "${r"$"}{NEST_CXXFLAGS} -DLTX_MODULE" + LINK_FLAGS "${r"$"}{NEST_LIBS}" + PREFIX "" + OUTPUT_NAME ${r"$"}{MODULE_NAME} ) + install( TARGETS ${r"$"}{MODULE_NAME}_module + DESTINATION ${r"$"}{CMAKE_INSTALL_LIBDIR} + ) +endif () + +# Build dynamic/static library for standard linking from NEST. +add_library( ${r"$"}{MODULE_NAME}_lib ${r"$"}{MODULE_SOURCES} ) +if ( BUILD_SHARED_LIBS ) + # Dynamic libraries are initiated by a `global` variable of the `SLIModule`, + # which is included, when the flag `LINKED_MODULE` is set. + target_compile_definitions( ${r"$"}{MODULE_NAME}_lib PRIVATE -DLINKED_MODULE ) +endif () +set_target_properties( ${r"$"}{MODULE_NAME}_lib + PROPERTIES + COMPILE_FLAGS "${r"$"}{NEST_CXXFLAGS}" + LINK_FLAGS "${r"$"}{NEST_LIBS}" + OUTPUT_NAME ${r"$"}{MODULE_NAME} ) + +# Install library, header and sli init files. +install( TARGETS ${r"$"}{MODULE_NAME}_lib DESTINATION ${r"$"}{CMAKE_INSTALL_LIBDIR} ) +install( FILES ${r"$"}{MODULE_HEADER} DESTINATION ${r"$"}{CMAKE_INSTALL_INCLUDEDIR} ) +install( DIRECTORY sli DESTINATION ${r"$"}{CMAKE_INSTALL_DATADIR} ) + +# Install help. +set( HELPDIRS "${r"$"}{PROJECT_SOURCE_DIR}:${r"$"}{PROJECT_SOURCE_DIR}/sli" ) +install( CODE + "execute_process(COMMAND ${r"$"}{CMAKE_COMMAND} + -DDOC_DIR='${r"$"}{CMAKE_INSTALL_FULL_DOCDIR}' + -DDATA_DIR='${r"$"}{CMAKE_INSTALL_FULL_DATADIR}' + -DHELPDIRS='${r"$"}{HELPDIRS}' + -DINSTALL_DIR='${r"$"}{CMAKE_INSTALL_PREFIX}' + -P ${r"$"}{CMAKE_INSTALL_FULL_DOCDIR}/generate_help.cmake + WORKING_DIRECTORY \"${r"$"}{PROJECT_BINARY_DIR}\" + )" + ) + +message( "" ) +message( "-------------------------------------------------------" ) +message( "${r"$"}{MODULE_NAME} Configuration Summary" ) +message( "-------------------------------------------------------" ) +message( "" ) +message( "C++ compiler : ${r"$"}{CMAKE_CXX_COMPILER}" ) +message( "Build static libs : ${r"$"}{NEST_STATIC_LIB}" ) +message( "C++ compiler flags : ${r"$"}{CMAKE_CXX_FLAGS}" ) +message( "NEST compiler flags : ${r"$"}{NEST_CXXFLAGS}" ) +message( "NEST include dirs : ${r"$"}{NEST_INCLUDES}" ) +message( "NEST libraries flags : ${r"$"}{NEST_LIBS}" ) +message( "" ) +message( "-------------------------------------------------------" ) +message( "" ) +message( "You can build and install ${r"$"}{MODULE_NAME} now, using" ) +message( " make" ) +message( " make install" ) +message( "" ) +message( "${r"$"}{MODULE_NAME} will be installed to: ${r"$"}{CMAKE_INSTALL_FULL_LIBDIR}" ) +message( "" ) diff --git a/src/main/resources/org/nest/nestml/module/ModuleClassCMake.ftl b/src/main/resources/org/nest/nestml/module/ModuleClassCMake.ftl new file mode 100644 index 000000000..6f3fa557c --- /dev/null +++ b/src/main/resources/org/nest/nestml/module/ModuleClassCMake.ftl @@ -0,0 +1,109 @@ +/* + * ${moduleName}.cpp + * + * This file is part of NEST. + * + * Copyright (C) 2004 The NEST Initiative + * + * NEST is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * NEST is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NEST. If not, see . + * + */ + +// Includes from nestkernel: +#include "connection_manager_impl.h" +#include "connector_model_impl.h" +#include "dynamicloader.h" +#include "exceptions.h" +#include "genericmodel.h" +#include "kernel_manager.h" +#include "model.h" +#include "model_manager_impl.h" +#include "nestmodule.h" +#include "target_identifier.h" + +// Includes from sli: +#include "booldatum.h" +#include "integerdatum.h" +#include "sliexceptions.h" +#include "tokenarray.h" + +// include headers with your own stuff +#include "${moduleName}.h" + +<#list neurons as neuron> + #include "${neuron.getName()}.h" + +// -- Interface to dynamic module loader --------------------------------------- + +/* + * There are three scenarios, in which MyModule can be loaded by NEST: + * + * 1) When loading your module with `Install`, the dynamic module loader must + * be able to find your module. You make the module known to the loader by + * defining an instance of your module class in global scope. (LTX_MODULE is + * defined) This instance must have the name + * + * _LTX_mod + * + * The dynamicloader can then load modulename and search for symbol "mod" in it. + * + * 2) When you link the library dynamically with NEST during compilation, a new + * object has to be created. In the constructor the DynamicLoaderModule will + * register your module. (LINKED_MODULE is defined) + * + * 3) When you link the library statically with NEST during compilation, the + * registration will take place in the file `static_modules.h`, which is + * generated by cmake. + */ +#if defined( LTX_MODULE ) | defined( LINKED_MODULE ) +${moduleName} ${moduleName}_LTX_mod; +#endif + +// -- DynModule functions ------------------------------------------------------ + +${moduleName}::${moduleName}() +{ +#ifdef LINKED_MODULE + // register this module at the dynamic loader + // this is needed to allow for linking in this module at compile time + // all registered modules will be initialized by the main app's dynamic loader + nest::DynamicLoaderModule::registerLinkedModule( this ); +#endif +} + +${moduleName}::~${moduleName}() +{ +} + +const std::string +${moduleName}::name(void) const +{ + return std::string("${moduleName}"); // Return name of the module +} + +const std::string +${moduleName}::commandstring( void ) const +{ + // Instruct the interpreter to load ${moduleName}-init.sli + return std::string( "(${moduleName}-init) run" ); +} + +//------------------------------------------------------------------------------------- +void +${moduleName}::init( SLIInterpreter* i ) +{ + <#list neurons as neuron> + nest::kernel().model_manager.register_node_model< ${neuron.getName()} >("${neuron.getName()}" ); + +} // ${moduleName}::init() \ No newline at end of file diff --git a/src/main/resources/org/nest/nestml/module/ModuleHeaderCMake.ftl b/src/main/resources/org/nest/nestml/module/ModuleHeaderCMake.ftl new file mode 100644 index 000000000..4b35bb53e --- /dev/null +++ b/src/main/resources/org/nest/nestml/module/ModuleHeaderCMake.ftl @@ -0,0 +1,74 @@ +<#assign upperModuleName = moduleName?upper_case> +/* + * ${moduleName}.h + * + * This file is part of NEST. + * + * Copyright (C) 2004 The NEST Initiative + * + * NEST is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 2 of the License, or + * (at your option) any later version. + * + * NEST is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with NEST. If not, see . + * + */ + +#ifndef ${upperModuleName}_H +#define ${upperModuleName}_H + +#include "slimodule.h" +#include "slifunction.h" + + +/** +* Class defining your model. +* @note For each model, you must define one such class, with a unique name. +*/ +class ${moduleName} : public SLIModule +{ +public: + // Interface functions ------------------------------------------ + + /** + * @note The constructor registers the module with the dynamic loader. + * Initialization proper is performed by the init() method. + */ + ${moduleName}(); + + /** + * @note The destructor does not do much in modules. + */ + ~${moduleName}(); + + /** + * Initialize module by registering models with the network. + * @param SLIInterpreter* SLI interpreter + */ + void init( SLIInterpreter* ); + + /** + * Return the name of your model. + */ + const std::string name( void ) const; + + /** + * Return the name of a sli file to execute when ${moduleName} is loaded. + * This mechanism can be used to define SLI commands associated with your + * module, in particular, set up type tries for functions you have defined. + */ + const std::string commandstring( void ) const; + +public: + // Classes implementing your functions ----------------------------- + +}; + +#endif \ No newline at end of file