diff --git a/CMakeLists.txt b/CMakeLists.txt index 004dcac..d8e5492 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,110 +1,160 @@ cmake_minimum_required(VERSION 2.8.8) # Start project, set version here. project("RoSA") set(ROSA_VERSION_MAJOR 0) set(ROSA_VERSION_MINOR 1) set(ROSA_VERSION_PATCH 0) # Add path for custom modules set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_CURRENT_SOURCE_DIR}/cmake" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/modules" ) # Package information if( NOT PACKAGE_VERSION ) set(PACKAGE_VERSION "${ROSA_VERSION_MAJOR}.${ROSA_VERSION_MINOR}.${ROSA_VERSION_PATCH}") endif() set(PACKAGE_NAME "Research on Self-Awareness Framework") set(PACKAGE_STRING "${PACKAGE_NAME} ${PACKAGE_VERSION}") set(PACKAGE_BUGREPORT "david.juhasz@tuwien.ac.at") #TODO: cpack? # Sanity check we are to make out-of-tree build if( CMAKE_SOURCE_DIR STREQUAL CMAKE_BINARY_DIR ) message(FATAL_ERROR "In-source builds are not allowed.") endif() string(TOUPPER "${CMAKE_BUILD_TYPE}" uppercase_CMAKE_BUILD_TYPE) # Set various paths set(ROSA_MAIN_SRC_DIR ${CMAKE_CURRENT_SOURCE_DIR}) set(ROSA_MAIN_INCLUDE_DIR ${ROSA_MAIN_SRC_DIR}/include) set(ROSA_MAIN_BIN_DIR ${ROSA_MAIN_SRC_DIR}/bin) set(ROSA_BINARY_DIR ${CMAKE_CURRENT_BINARY_DIR}) set(ROSA_RUNTIME_OUTPUT_INTDIR ${ROSA_BINARY_DIR}/${CMAKE_CFG_INTDIR}/bin) set(ROSA_LIBRARY_OUTPUT_INTDIR ${ROSA_BINARY_DIR}/${CMAKE_CFG_INTDIR}/lib) set(ROSA_TOOLS_BINARY_DIR ${ROSA_RUNTIME_OUTPUT_INTDIR}) set(ROSA_LIBRARY_DIR ${ROSA_LIBRARY_OUTPUT_INTDIR}) set(ROSA_INCLUDE_DIR ${ROSA_BINARY_DIR}/include) # Set build options option(ROSA_INCLUDE_TOOLS "Generate build targets for RoSA tools." ON) option(ROSA_INCLUDE_EXAMPLES "Generate build target for RoSA examples." ON) option(ROSA_ENABLE_CXX1Y "Compile with C++1y enabled." ON) option(ROSA_ENABLE_PEDANTIC "Compile with pedantic enabled." ON) if( NOT uppercase_CMAKE_BUILD_TYPE STREQUAL "DEBUG" ) option(ROSA_ENABLE_ASSERTIONS "Enable assertions." OFF) else() option(ROSA_ENABLE_ASSERTIONS "Enable assertions." ON) endif() include(HandleROSAOptions) # Configure the ROSA configuration header file. +if( ROSA_LOG_LEVEL ) + set(ROSA_LOG_LEVEL_INT ${ROSA_LOG_LEVEL}) +else() + set(ROSA_LOG_LEVEL_INT -1) +endif() + macro(to_int_value name) if(${name}) set(${name}_INT 1) else() set(${name}_INT -1) endif() endmacro() to_int_value(ROSA_ENABLE_ASSERTIONS) configure_file( ${ROSA_MAIN_INCLUDE_DIR}/rosa/config/rosa-config.h.cmake ${ROSA_INCLUDE_DIR}/rosa/config/rosa-config.h ) # They are not referenced. See set_output_directory(). set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${ROSA_BINARY_DIR}/bin) set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${ROSA_BINARY_DIR}/lib) set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${ROSA_BINARY_DIR}/lib) # Set include directories set(CMAKE_INCLUDE_CURRENT_DIR ON) include_directories(${ROSA_INCLUDE_DIR} ${ROSA_MAIN_INCLUDE_DIR}) # Set extra compiler options for Visual Studio if ( MSVC ) add_definitions(-EHsc) # Use Exception Handling endif() # Set up YCM set(CMAKE_EXPORT_COMPILE_COMMANDS 1) message("Set compilation_database_folder in .ymc_extra_conf.py in the source directory if you want to use YMC.") # Add parts of the project include(AddROSATools) add_subdirectory(lib) if( ROSA_INCLUDE_TOOLS ) add_subdirectory(tools) endif() if( ROSA_INCLUDE_EXAMPLES ) add_subdirectory(examples) endif() #TODO: install? +# Print summary +set(LOG_LEVEL_STR "none") +if(ROSA_LOG_LEVEL) + if(${ROSA_LOG_LEVEL} EQUAL 0) + set(LOG_LEVEL_STR "ERROR") + elseif(${ROSA_LOG_LEVEL} EQUAL 1) + set(LOG_LEVEL_STR "WARNING") + elseif(${ROSA_LOG_LEVEL} EQUAL 2) + set(LOG_LEVEL_STR "INFO") + elseif(${ROSA_LOG_LEVEL} EQUAL 3) + set(LOG_LEVEL_STR "DEBUG") + elseif(${ROSA_LOG_LEVEL} EQUAL 4) + set(LOG_LEVEL_STR "TRACE") + else() + set(LOG_LEVEL_STR "invalid") + endif() +endif() + +message(STATUS + "\n====================| Build Summary |====================" + "\n" + "\nRoSA version: ${PACKAGE_VERSION}" + "\n" + "\nBuild type: ${CMAKE_BUILD_TYPE}" + "\nAssertions: ${ROSA_ENABLE_ASSERTIONS}" + "\nLog level: ${LOG_LEVEL_STR}" + "\n" + "\nBuild tools: ${ROSA_INCLUDE_TOOLS}" + "\nBuild examples: ${ROSA_INCLUDE_EXAMPLES}" + "\n" + "\nCC: ${CMAKE_C_COMPILER}" + "\nCFLAGS: ${CMAKE_C_FLAGS}" + "\nCXX: ${CMAKE_CXX_COMPILER}" + "\nCXXFLAGS: ${CMAKE_CXX_FLAGS}" + "\nLIBRARIES: ${LD_FLAGS}" + "\n" + "\nSource directory: ${ROSA_MAIN_SRC_DIR}" + "\nBuild directory: ${ROSA_BINARY_DIR}" + "\nExecutable path: ${ROSA_TOOLS_BINARY_DIR}" + "\nLibrary path: ${ROSA_LIBRARY_DIR}" + "\nGenerator: ${CMAKE_GENERATOR}" + "\n" + "\n===========================================================\n") + diff --git a/README.txt b/README.txt index 889fa69..b1b87a8 100644 --- a/README.txt +++ b/README.txt @@ -1,55 +1,62 @@ ==================================== Research on Self-Awareness Framework ==================================== Build: ----- The provided CMake project supports only out-of-tree builds, i.e. one needs to create a build directory and run cmake in that using the source directory path as argument. Prerequisites: ------------- Nothing yet. Build options: ------------- Beyond the usual CMake options, the following project-related options are available: * ROSA_INCLUDE_TOOLS, defaults to ON * ROSA_INCLUDE_EXAMPLES, defaults to ON * ROSA_ENABLE_CXX1Y, defaults to ON * ROSA_ENABLE_PEDANTIC, defaults to ON * ROSA_ENABLE_ASSERTIONS, defaults to ON only for debug builds, and its value is respected only for non-debug builds + * ROSA_LOG_LEVEL, defaults to none, + use a valid integer value to enable logging + ** 0 - ERROR + ** 1 - WARNING + ** 2 - INFO + ** 3 - DEBUG + ** 4 - TRACE Set C and C++ compilers with the environment variables CC and CXX, respectively. Use the CMake variable CMAKE_BUILD_TYPE for single-configuration generators to set the type of build: Debug, Release. Using YCM: --------- If you happen to use YMC, just make a copy of the provided ycm_extra_conf.py.template file as .ycm_extra_conf.py in the project directory, and set the following two variables in it: * compilation_database_folder: the absolute path of your build directory * extra_system_include_dirs: any system include directory which might not be search by libclang. See: https://github.com/Valloric/YouCompleteMe/issues/303; use the following command to figure out the used system directories: 'echo | clang -std=c++11 -v -E -x c++ -'. You probably want compile with clang if you use YCM, so run cmake with environment variables CC=clang and CXX=clang++ set. Also note that headers file in the include directory are compiled for YCM with the compiler flags of a corresponding source file in the lib directory, if any. Header files in other locations are supposed to have a corresponding source file in the same directory. If the project's include directory (include/rosa) would be ever changed, then that needs to be also modified in the YCM configuration file. diff --git a/include/rosa/config/config.h b/include/rosa/config/config.h index 0d1686e..0359759 100644 --- a/include/rosa/config/config.h +++ b/include/rosa/config/config.h @@ -1,35 +1,51 @@ /******************************************************************************* * * File: config.h * * Contents: Configuration nformation on the build of the library. * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ -#ifndef ROSA_CONFIG_SYSTEM_H -#define ROSA_CONFIG_SYSTEM_H +#ifndef ROSA_CONFIG_CONFIG_H +#define ROSA_CONFIG_CONFIG_H + +#include // Various preprocessor macros containing config information. -#include "rosa-config.h" +#include "rosa/config/rosa-config.h" // This OS-specific block defines one of the following: // - ROSA_LINUX // - ROSA_WINDOWS // It also defines ROSA_POSIX for POSIX-compatible systems #if defined(__linux__) #define ROSA_LINUX #elif defined(WIN32) || defined(_WIN32) #define ROSA_WINDOWS #else #error Platform and/or compiler not supported #endif #if defined(ROSA_LINUX) #define ROSA_POSIX #endif -#endif // ROSA_CONFIG_SYSTEM_H +// Convenience macros. +#define ROSA_VOID_STMT static_cast(0) + +#define ROSA_IGNORE_UNUSED(x) static_cast(x) + +#define ROSA_CRITICAL(error) \ + do { \ + std::cerr << "[FATAL] " << __FILE__ << ":" << __LINE__ \ + << ": critical error: '" << (error) << "'" << std::endl; \ + ::abort(); \ + } while (false) + +#define ROSA_RAISE_ERROR(msg) throw std::runtime_error(msg) + +#endif // ROSA_CONFIG_CONFIG_H diff --git a/include/rosa/config/rosa-config.h.cmake b/include/rosa/config/rosa-config.h.cmake index 3698825..828cb9e 100644 --- a/include/rosa/config/rosa-config.h.cmake +++ b/include/rosa/config/rosa-config.h.cmake @@ -1,38 +1,42 @@ #ifndef ROSA_CONFIG_ROSA_CONFIG_H #define ROSA_CONFIG_ROSA_CONFIG_H #define CMAKE_SYSTEM "${CMAKE_SYSTEM}" #define CMAKE_GENERATOR "${CMAKE_GENERATOR}" #define CMAKE_C_COMPILER_ID "${CMAKE_C_COMPILER_ID}" #define CMAKE_C_COMPILER_VERSION "${CMAKE_C_COMPILER_VERSION}" #define CMAKE_CXX_COMPILER_ID "${CMAKE_CXX_COMPILER_ID}" #define CMAKE_CXX_COMPILER_VERSION "${CMAKE_CXX_COMPILER_VERSION}" #define ROSA_VERSION_MAJOR ${ROSA_VERSION_MAJOR} #define ROSA_VERSION_MINOR ${ROSA_VERSION_MINOR} #define ROSA_VERSION_PATCH ${ROSA_VERSION_PATCH} #define PACKAGE_NAME "${PACKAGE_NAME}" #define PACKAGE_STRING "${PACKAGE_STRING}" #define PACKAGE_VERSION "${PACKAGE_VERSION}" #define PACKAGE_BUGREPORT "${PACKAGE_BUGREPORT}" #define BUILD_DATE __DATE__ " " __TIME__ -#if @ROSA_ENABLE_ASSERTIONS_INT@ != -1 +#if ${ROSA_LOG_LEVEL_INT} != -1 +#define ROSA_LOG_LEVEL ${ROSA_LOG_LEVEL} +#endif + +#if ${ROSA_ENABLE_ASSERTIONS_INT} != -1 #define ROSA_ENABLE_ASSERTIONS #endif #endif // ROSA_CONFIG_ROSA_CONFIG_H diff --git a/include/rosa/support/debug.hpp b/include/rosa/support/debug.hpp index 0392388..f4d9c2a 100644 --- a/include/rosa/support/debug.hpp +++ b/include/rosa/support/debug.hpp @@ -1,81 +1,82 @@ /******************************************************************************* * * File: debug.hpp * * Contents: Facility for debugging * * Copyright 2017 * * Author: David Juhasz (david.juhasz@tuwien.ac.at) * ******************************************************************************/ #ifndef ROSA_SUPPORT_DEBUG_HPP #define ROSA_SUPPORT_DEBUG_HPP +#include "rosa/config/config.h" #include "rosa/support/types.hpp" -#include #include +#include namespace rosa { // Returns an output stream to use for debugging. std::ostream &dbgs(void); // Prints an array to the ostream. template std::ostream &operator<<(std::ostream &os, const std::array &arr) { os << '['; for (unsigned I = 0; I < size; ++I) { if (I) { os << ','; } os << (PRINTABLE(T)) arr[I]; } os << ']'; return os; } } // End namespace rosa #ifndef ROSA_ENABLE_ASSERTIONS -#define ASSERT(unused) static_cast(0) +#define ASSERT(stmt) ROSA_IGNORE_UNUSED(stmt) #elif defined(ROSA_WINDOWS) #define ASSERT(stmt) \ if (static_cast(stmt) == false) { \ - rosa::dbgs() << __FILE__ << ":" << __LINE__ << ": requirement failed '" \ + rosa::dbgs() << __FILE__ << ":" << __LINE__ << ": requirement failed: '" \ << #stmt << "'" << std::endl; \ ::abort(); \ } \ - static_cast(0) + ROSA_VOID_STMT #else // defined(ROSA_LINUX) #include #define ASSERT(stmt) \ if (static_cast(stmt) == false) { \ - rosa::dbgs() << __FILE__ << ":" << __LINE__ << ": requirement failed '" \ + rosa::dbgs() << __FILE__ << ":" << __LINE__ << ": requirement failed: '" \ << #stmt << "'" << std::endl; \ void *array[20]; \ auto bt_size = ::backtrace(array, 20); \ ::backtrace_symbols_fd(array, bt_size, 2); \ ::abort(); \ } \ - static_cast(0) + ROSA_VOID_STMT #endif // defined(ROSA_ENABLE_ASSERTIONS) #ifndef NDEBUG #define DEBUG(X) \ do { \ X; \ - } while (0) + } while (false) #define DEBUGVAR(V) \ do { \ rosa::dbgs() << #V << " (" << __FILE__ << ":" << __LINE__ << "): " << (V) \ << std::endl; \ - } while (0) + } while (false) #else // defined(NDEBUG) -#define DEBUG(X) static_cast(0) -#define DEBUGVAR(X) static_cast(0) +#define DEBUG(X) ROSA_IGNORE_UNUSED(X) +#define DEBUGVAR(X) ROSA_IGNORE_UNUSED(X) #endif // defined(NDEBUG) #endif // ROSA_SUPPORT_DEBUG_HPP diff --git a/include/rosa/support/log.h b/include/rosa/support/log.h new file mode 100644 index 0000000..0959866 --- /dev/null +++ b/include/rosa/support/log.h @@ -0,0 +1,97 @@ +/******************************************************************************* + * + * File: log.h + * + * Contents: Facility for logging + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#ifndef ROSA_SUPPORT_LOG_H +#define ROSA_SUPPORT_LOG_H + +#include "debug.hpp" +#include + +// Valid log levels. +// NOTE: Keep in sync with the values of enum class rosa::LogLevel. +#define ROSA_LOG_LEVEL_ERROR 0 +#define ROSA_LOG_LEVEL_WARNING 1 +#define ROSA_LOG_LEVEL_INFO 2 +#define ROSA_LOG_LEVEL_DEBUG 3 +#define ROSA_LOG_LEVEL_TRACE 4 + +namespace rosa { + +// Type-safe definition of log levels. +// NOTE: Keep values in sync with the corresponding preprocessor definitions. +enum class LogLevel { + Error = ROSA_LOG_LEVEL_ERROR, + Warning = ROSA_LOG_LEVEL_WARNING, + Info = ROSA_LOG_LEVEL_INFO, + Debug = ROSA_LOG_LEVEL_DEBUG, + Trace = ROSA_LOG_LEVEL_TRACE +}; + +// Converts a LogLevel to its string representation. +std::string logLevelToString(const LogLevel logLevel); + +} // End namespace rosa + +// Simple logging implementation. +// FIXME: Make logging thread-safe. +#define ROSA_LOG_IMPL(level, output) \ + do { \ + rosa::dbgs() << "[" << rosa::logLevelToString(level) << "] " << __FILE__ \ + << ":" << __LINE__ << ": " << (output) << std::endl; \ + } while (false) + +// Define logging macros if logging is enabled. +#ifdef ROSA_LOG_LEVEL + +#define LOG_ERROR(output) ROSA_LOG_IMPL(rosa::LogLevel::Error, output) + +#if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_WARNING +#define LOG_WARNING(output) ROSA_LOG_IMPL(rosa::LogLevel::Warning, output) +#endif + +#if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_INFO +#define LOG_INFO(output) ROSA_LOG_IMPL(rosa::LogLevel::Info, output) +#endif + +#if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_DEBUG +#define LOG_DEBUG(output) ROSA_LOG_IMPL(rosa::LogLevel::Debug, output) +#endif + +#if ROSA_LOG_LEVEL >= ROSA_LOG_LEVEL_TRACE +#define LOG_TRACE(output) ROSA_LOG_IMPL(rosa::LogLevel::Trace, output) +#endif + +#endif // defined ROSA_LOG_LEVEL + +// Define all disabled logging features as void. +#ifndef LOG_ERROR +#define LOG_ERROR(output) ROSA_IGNORE_UNUSED(output) +#endif + +#ifndef LOG_WARNING +#define LOG_WARNING(output) ROSA_IGNORE_UNUSED(output) +#endif + +#ifndef LOG_INFO +#define LOG_INFO(output) ROSA_IGNORE_UNUSED(output) +#endif + +#ifndef LOG_DEBUG +#define LOG_DEBUG(output) ROSA_IGNORE_UNUSED(output) +#endif + +#ifndef LOG_TRACE +#define LOG_TRACE(output) ROSA_IGNORE_UNUSED(output) +#endif + +#endif // ROSA_SUPPORT_LOG_H + diff --git a/lib/support/CMakeLists.txt b/lib/support/CMakeLists.txt index aa8e4cd..9d716be 100644 --- a/lib/support/CMakeLists.txt +++ b/lib/support/CMakeLists.txt @@ -1,4 +1,5 @@ add_library(ROSASupport debug.cpp + log.cpp ) diff --git a/lib/support/log.cpp b/lib/support/log.cpp new file mode 100644 index 0000000..b9b8fc5 --- /dev/null +++ b/lib/support/log.cpp @@ -0,0 +1,29 @@ +/******************************************************************************* + * + * File: log.cpp + * + * Contents: Facility for logging + * + * Copyright 2017 + * + * Author: David Juhasz (david.juhasz@tuwien.ac.at) + * + ******************************************************************************/ + +#include "rosa/support/log.h" + +namespace rosa { + +std::string logLevelToString(const LogLevel logLevel) { + switch(logLevel) { + case LogLevel::Error: return "ERROR"; + case LogLevel::Warning: return "WARNING"; + case LogLevel::Info: return "INFO"; + case LogLevel::Debug: return "DEBUG"; + case LogLevel::Trace: return "TRACE"; + default: ROSA_CRITICAL(""); + } +} + +} // End namespace rosa +