From 2fb329a302c96d152b7412dd2a39dbbc49cf0991 Mon Sep 17 00:00:00 2001 From: tastytea Date: Fri, 2 Aug 2019 08:40:04 +0200 Subject: [PATCH] Rearranged the file structure and rewrote the CMake recipes. * Compile a library again. * Made CMake recipes modular. * Added xdgcfgConfig.cmake. * Added pkg-config file. * Updated readme. --- CMakeLists.txt | 68 ++++++++++++++----------- README.md | 43 ++++++++++------ cmake/CMakeLists.txt | 19 +++++++ cmake/xdgcfgConfig.cmake.in | 6 +++ examples/CMakeLists.txt | 9 ++++ {src => examples}/example.cpp | 18 +++++-- include/CMakeLists.txt | 3 ++ {src => include}/xdgcfg.hpp | 88 +++------------------------------ pkg-config/CMakeLists.txt | 7 +++ pkg-config/xdgcfg.pc.in | 13 +++++ src/CMakeLists.txt | 25 ++++++++++ src/xdgcfg.cpp | 93 +++++++++++++++++++++++++++++++++++ tests/CMakeLists.txt | 14 +++--- tests/test_write_read.cpp | 2 +- 14 files changed, 273 insertions(+), 135 deletions(-) create mode 100644 cmake/CMakeLists.txt create mode 100644 cmake/xdgcfgConfig.cmake.in create mode 100644 examples/CMakeLists.txt rename {src => examples}/example.cpp (50%) create mode 100644 include/CMakeLists.txt rename {src => include}/xdgcfg.hpp (50%) create mode 100644 pkg-config/CMakeLists.txt create mode 100644 pkg-config/xdgcfg.pc.in create mode 100644 src/CMakeLists.txt create mode 100644 src/xdgcfg.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index 62d6853..00cc08f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,42 +1,54 @@ -cmake_minimum_required (VERSION 3.1) +# Support version 3.1 and above, but use policy settings up to 3.14. +cmake_minimum_required(VERSION 3.1...3.14) +# Ranges are supported from 3.12, set policy to current for 3.1 - 3.11. +if(${CMAKE_VERSION} VERSION_LESS 3.12) + cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) +endif() + project(xdgcfg - VERSION 0.3.1 + VERSION 0.4.0 LANGUAGES CXX ) +# DESCRIPTION was introduced in version 3.9. +if(NOT (${CMAKE_VERSION} VERSION_LESS 3.9)) + set(PROJECT_DESCRIPTION + "Wrapper around libconfig that writes and reads files in XDG_CONFIG_HOME.") +endif() -set(WITH_EXAMPLES "NO" CACHE STRING "WITH_EXAMPLES defaults to \"NO\"") -set(WITH_TESTS "NO" CACHE STRING "WITH_TESTS defaults to \"NO\"") +# All custom build switches. +option(WITH_EXAMPLES "Compile examples." NO) +option(WITH_TESTS "Compile tests." NO) -include(GNUInstallDirs) -find_package(PkgConfig REQUIRED) -pkg_check_modules(LIBXDG_BASEDIR REQUIRED libxdg-basedir) -pkg_check_modules(LIBCONFIG REQUIRED libconfig++) +# Build shared libs by default but allow overriding. +set(BUILD_SHARED_LIBS YES CACHE BOOL "Build shared libs.") -set(CMAKE_CXX_STANDARD 14) -set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_CXX_EXTENSIONS OFF) +set_property(GLOBAL PROPERTY CXX_STANDARD 14) +set_property(GLOBAL PROPERTY CXX_STANDARD_REQUIRED YES) +set_property(GLOBAL PROPERTY CXX_EXTENSIONS NO) -set(CMAKE_CXX_FLAGS_DEBUG - "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wextra -Wpedantic -ftrapv \ --fsanitize=undefined -g -Og -fno-omit-frame-pointer") +set(DEBUG_CXXFLAGS + "-Wall" + "-Wextra" + "-Wpedantic" + "-ftrapv" + "-fsanitize=undefined" + "-g" + "-Og" + "-fno-omit-frame-pointer") +set(DEBUG_LDFLAGS + "-fsanitize=undefined") +add_compile_options("$<$:${DEBUG_CXXFLAGS}>") +add_link_options("$<$:${DEBUG_LDFLAGS}>") -include_directories(${LIBXDG_BASEDIR_INCLUDE_DIRS}) -include_directories(${LIBCONFIG_INCLUDE_DIRS}) +add_subdirectory(src) +add_subdirectory(include) +add_subdirectory(pkg-config) +add_subdirectory(cmake) -link_directories(${LIBXDG_BASEDIR_LIBRARY_DIRS}) -link_directories(${LIBCONFIG_LIBRARY_DIRS}) - -set(COMMON_LIBRARIES - ${LIBXDG_BASEDIR_LIBRARIES} ${LIBCONFIG_LIBRARIES} stdc++fs) - -if (WITH_EXAMPLES) - add_executable(example src/example.cpp) - target_link_libraries(example - ${COMMON_LIBRARIES}) +if(WITH_EXAMPLES) + add_subdirectory(examples) endif() if(WITH_TESTS) add_subdirectory(tests) endif() - -install(FILES src/xdgcfg.hpp DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) diff --git a/README.md b/README.md index f62e4f6..eb8cf55 100644 --- a/README.md +++ b/README.md @@ -2,13 +2,24 @@ and writes files in `${XDG_CONFIG_HOME}`. It creates subdirectories if necessary. -## Dependencies +## Usage -* C++ compiler (Tested: g++ 5/8) -* [cmake](https://cmake.org/) (at least: 3.1) -* [pkgconfig](https://pkgconfig.freedesktop.org/wiki/) -* [libconfig++](https://github.com/hyperrealm/libconfig) -* [libxdg-basedir](http://repo.or.cz/w/libxdg-basedir.git) +`xdgcfg.hpp` has explanatory comments in it and there is an +[example](src/example.cpp). The reference is also available at +[doc.schlomp.space/xdgcfg/](https://doc.schlomp.space/xdgcfg/classxdgcfg.html). + +Use it in your CMake project like this: + +``` cmake +find_package(xdgcfg CONFIG REQUIRED) +target_link_libraries(MyProject xdgcfg::xdgcfg) +``` + +If you don't use CMake, you can get the compile-flags with pkg-config: + +``` shell +pkg-config --libs --cflags xdgcfg +``` ## Install @@ -26,13 +37,21 @@ emerge -a dev-cpp/xdgcfg ### From source -Copy `xdgcfg.hpp` into a folder where your project can find it. +### Dependencies + +* C++ compiler (Tested: g++ 5/8/9) +* [cmake](https://cmake.org/) (at least: 3.1) +* [pkgconfig](https://pkgconfig.freedesktop.org/wiki/) (tested: 0.29) +* [libconfig++](https://github.com/hyperrealm/libconfig) (tested: 1.5) +* [libxdg-basedir](http://repo.or.cz/w/libxdg-basedir.git) (tested: 1.2) + +### Compile ``` shell mkdir build cd build cmake .. -make +cmake --build . make install ``` @@ -40,10 +59,4 @@ make install * `-DWITH_TESTS=YES` to compile the tests. * `-DWITH_EXAMPLES=YES` to compile the example. - - -## Documentation - -`xdgcfg.hpp` has explanatory comments in it and there is an -[example](src/example.cpp). The reference is also available at -[doc.schlomp.space/xdgcfg/](https://doc.schlomp.space/xdgcfg/classxdgcfg.html). +* `-DBUILD_SHARED_LIBS=NO` to build a static library. diff --git a/cmake/CMakeLists.txt b/cmake/CMakeLists.txt new file mode 100644 index 0000000..0a62f8c --- /dev/null +++ b/cmake/CMakeLists.txt @@ -0,0 +1,19 @@ +include(CMakePackageConfigHelpers) +include(GNUInstallDirs) + +write_basic_package_version_file("${PROJECT_NAME}ConfigVersion.cmake" + VERSION ${PACKAGE_VERSION} + COMPATIBILITY ExactVersion) # NOTE: Set to SameMajorVersion when stable. + +install(EXPORT ${PROJECT_NAME}Targets + FILE "${PROJECT_NAME}Targets.cmake" + NAMESPACE "${PROJECT_NAME}::" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") + +configure_file("${PROJECT_NAME}Config.cmake.in" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" @ONLY) + +install(FILES + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}Config.cmake" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}ConfigVersion.cmake" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/${PROJECT_NAME}") diff --git a/cmake/xdgcfgConfig.cmake.in b/cmake/xdgcfgConfig.cmake.in new file mode 100644 index 0000000..a54efeb --- /dev/null +++ b/cmake/xdgcfgConfig.cmake.in @@ -0,0 +1,6 @@ +include(CMakeFindDependencyMacro) + +find_dependency(PkgConfig REQUIRED) +pkg_check_modules(libconfig++ REQUIRED IMPORTED_TARGET libconfig++) + +include("${CMAKE_CURRENT_LIST_DIR}/${PROJECT_NAME}Targets.cmake") diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt new file mode 100644 index 0000000..7b3fcdb --- /dev/null +++ b/examples/CMakeLists.txt @@ -0,0 +1,9 @@ +include(GNUInstallDirs) + +add_executable(example example.cpp) +target_link_libraries(example xdgcfg) + +# # In your own project, you would link to xdgcfg like this: +# find_package(xdgcfg CONFIG REQUIRED) +# target_link_libraries(example xdgcfg::xdgcfg) + diff --git a/src/example.cpp b/examples/example.cpp similarity index 50% rename from src/example.cpp rename to examples/example.cpp index 090f47b..63792c9 100644 --- a/src/example.cpp +++ b/examples/example.cpp @@ -4,19 +4,29 @@ int main() { - xdgcfg config("test.cfg", "xdgcfg"); - config.set_verbose(true); + xdgcfg config("test.cfg", // File name. + "xdgcfg"); // Sub directory (optional). + config.set_verbose(true); // Print error messages. + if (config.read() != 0) { - config.write(); + std::cout << "File not found.\n"; } + + // Get a reference to the libconfig::Config object and use it as you would + // normally do. libconfig::Config &cfg = config.get_cfg(); libconfig::Setting &root = cfg.getRoot(); + if (!root.exists("Hello")) { root.add("Hello", libconfig::Setting::TypeString) = "World"; } - config.write(); + + if (!config.write()) + { + std::cerr << "Writing failed.\n"; + } std::cout << "Hello: " << root["Hello"].c_str() << std::endl; } diff --git a/include/CMakeLists.txt b/include/CMakeLists.txt new file mode 100644 index 0000000..8f19bfa --- /dev/null +++ b/include/CMakeLists.txt @@ -0,0 +1,3 @@ +include(GNUInstallDirs) + +install(FILES "xdgcfg.hpp" DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}") diff --git a/src/xdgcfg.hpp b/include/xdgcfg.hpp similarity index 50% rename from src/xdgcfg.hpp rename to include/xdgcfg.hpp index f5938d1..29a048c 100644 --- a/src/xdgcfg.hpp +++ b/include/xdgcfg.hpp @@ -14,7 +14,6 @@ #include #include #include -#include #if __cplusplus >= 201703L namespace fs = std::filesystem; @@ -40,82 +39,21 @@ public: * @param filename The name of the file, including extension * @param subdir The subdir (optional) */ - explicit xdgcfg(const string &filename, const string &subdir = "") - : _cfg() - , _verbose(false) - { - xdgHandle xdg; - xdgInitHandle(&xdg); - _filepath = xdgConfigHome(&xdg); - xdgWipeHandle(&xdg); - - if (!subdir.empty()) - { - _filepath /= subdir; - } - if (!fs::exists(_filepath)) - { - fs::create_directories(_filepath); - } - _filepath /= filename; - } + explicit xdgcfg(const string &filename, const string &subdir = ""); /*! * @brief Read the file * * @return 0 on success, 1 on I/O error, 2 on parse error. */ - uint_fast8_t read() - { - try - { - _cfg.readFile(_filepath.c_str()); - } - catch (const libconfig::FileIOException &e) - { - if (_verbose) - { - cerr << "I/O error while reading " << _filepath - << " - " << e.what() << endl; - } - return 1; - } - catch (const libconfig::ParseException &e) - { - if (_verbose) - { - cerr << "Parse error at " << e.getFile() << ":" << e.getLine() - << " - " << e.getError() << endl; - } - return 2; - } - - return 0; - } + uint_fast8_t read(); /*! * @brief Write the file * * @return `true` on success */ - bool write() - { - try - { - _cfg.writeFile(_filepath.c_str()); - } - catch (const libconfig::FileIOException &e) - { - if (_verbose) - { - cerr << "I/O error while writing " << _filepath - << " - " << e.what() << endl; - } - return false; - } - - return true; - } + bool write(); /*! * @brief Returns a reference to the config as libconfig::Config @@ -125,34 +63,22 @@ public: * libconfig::Config &cfg = config.get_cfg(); * @endcode */ - libconfig::Config &get_cfg() - { - return _cfg; - } + libconfig::Config &get_cfg(); /*! * @brief Returns the complete filepath */ - const fs::path get_filepath() const - { - return _filepath; - } + const fs::path get_filepath() const; /*! * @brief Sets verbosity */ - void set_verbose(bool verbose) - { - _verbose = verbose; - } + void set_verbose(bool verbose); /*! * @brief Returns verbosity */ - bool get_verbose() const - { - return _verbose; - } + bool get_verbose() const; private: /*! diff --git a/pkg-config/CMakeLists.txt b/pkg-config/CMakeLists.txt new file mode 100644 index 0000000..4640f17 --- /dev/null +++ b/pkg-config/CMakeLists.txt @@ -0,0 +1,7 @@ +include(GNUInstallDirs) + +configure_file("${PROJECT_NAME}.pc.in" + "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc" @ONLY) + +install(FILES "${CMAKE_CURRENT_BINARY_DIR}/${PROJECT_NAME}.pc" + DESTINATION "${CMAKE_INSTALL_LIBDIR}/pkgconfig") diff --git a/pkg-config/xdgcfg.pc.in b/pkg-config/xdgcfg.pc.in new file mode 100644 index 0000000..58b92fc --- /dev/null +++ b/pkg-config/xdgcfg.pc.in @@ -0,0 +1,13 @@ +name=@PROJECT_NAME@ +prefix=@CMAKE_INSTALL_PREFIX@ +exec_prefix=${prefix} +libdir=${prefix}/@CMAKE_INSTALL_LIBDIR@ +includedir=${prefix}/@CMAKE_INSTALL_INCLUDEDIR@ + +Name: ${name} +Description: @PROJECT_DESCRIPTION@ +Version: @PROJECT_VERSION@ +Libs: -L${libdir} -l${name} +Cflags: -I${includedir} +Requires: libconfig++ +Requires.private: libxdg-basedir diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt new file mode 100644 index 0000000..ee11633 --- /dev/null +++ b/src/CMakeLists.txt @@ -0,0 +1,25 @@ +include(GNUInstallDirs) + +find_package(PkgConfig REQUIRED) +pkg_check_modules(libxdg-basedir REQUIRED IMPORTED_TARGET libxdg-basedir) +pkg_check_modules(libconfig++ REQUIRED IMPORTED_TARGET libconfig++) + +add_library(${PROJECT_NAME} "xdgcfg.cpp" "../include/xdgcfg.hpp") + +set_target_properties(xdgcfg PROPERTIES + VERSION ${PROJECT_VERSION} + SOVERSION ${${PROJECT_NAME}_VERSION_MAJOR}) + +target_include_directories(${PROJECT_NAME} + PUBLIC + "$" + "$") + +target_link_libraries(${PROJECT_NAME} + PRIVATE PkgConfig::libxdg-basedir + PUBLIC PkgConfig::libconfig++ stdc++fs) + +install(TARGETS ${PROJECT_NAME} + EXPORT "${PROJECT_NAME}Targets" + LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" + ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}") diff --git a/src/xdgcfg.cpp b/src/xdgcfg.cpp new file mode 100644 index 0000000..98b1009 --- /dev/null +++ b/src/xdgcfg.cpp @@ -0,0 +1,93 @@ +/* Public Domain / CC-0 + * Author: tastytea + */ + +#include +#include "xdgcfg.hpp" + +xdgcfg::xdgcfg(const string &filename, const string &subdir) +: _cfg() +, _verbose(false) +{ + xdgHandle xdg; + xdgInitHandle(&xdg); + _filepath = xdgConfigHome(&xdg); + xdgWipeHandle(&xdg); + + if (!subdir.empty()) + { + _filepath /= subdir; + } + if (!fs::exists(_filepath)) + { + fs::create_directories(_filepath); + } + _filepath /= filename; +} + +uint_fast8_t xdgcfg::read() +{ + try + { + _cfg.readFile(_filepath.c_str()); + } + catch (const libconfig::FileIOException &e) + { + if (_verbose) + { + cerr << "I/O error while reading " << _filepath + << " - " << e.what() << endl; + } + return 1; + } + catch (const libconfig::ParseException &e) + { + if (_verbose) + { + cerr << "Parse error at " << e.getFile() << ":" << e.getLine() + << " - " << e.getError() << endl; + } + return 2; + } + + return 0; +} + +bool xdgcfg::write() +{ + try + { + _cfg.writeFile(_filepath.c_str()); + } + catch (const libconfig::FileIOException &e) + { + if (_verbose) + { + cerr << "I/O error while writing " << _filepath + << " - " << e.what() << endl; + } + return false; + } + + return true; +} + +libconfig::Config &xdgcfg::get_cfg() +{ + return _cfg; +} + +const fs::path xdgcfg::get_filepath() const +{ + return _filepath; +} + +void xdgcfg::set_verbose(bool verbose) +{ + _verbose = verbose; +} + +bool xdgcfg::get_verbose() const +{ + return _verbose; +} diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 0401ad8..ebb9a7a 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -1,20 +1,22 @@ include(CTest) file(GLOB sources_tests test_*.cpp) -find_package(Catch2) -if(Catch2_FOUND) # Catch 2.x +find_package(Catch2 CONFIG) +if(Catch2_FOUND) # Catch 2.x include(Catch) add_executable(all_tests main.cpp ${sources_tests}) - target_link_libraries(all_tests Catch2::Catch2 ${COMMON_LIBRARIES}) - target_include_directories(all_tests PRIVATE "/usr/include/catch2") + target_link_libraries(all_tests Catch2::Catch2 xdgcfg) + target_include_directories(all_tests + PRIVATE "../include" "/usr/include/catch2") catch_discover_tests(all_tests EXTRA_ARGS "${EXTRA_TEST_ARGS}") -else() # Catch 1.x +else() # Catch 1.x if(EXISTS "/usr/include/catch.hpp") message(STATUS "Catch 1.x found.") foreach(src ${sources_tests}) get_filename_component(bin ${src} NAME_WE) add_executable(${bin} main.cpp ${src}) - target_link_libraries(${bin} ${COMMON_LIBRARIES}) + target_include_directories(${bin} PRIVATE "../include") + target_link_libraries(${bin} xdgcfg) add_test(${bin} ${bin} "${EXTRA_TEST_ARGS}") endforeach() else() diff --git a/tests/test_write_read.cpp b/tests/test_write_read.cpp index 8b5ce65..0984ef3 100644 --- a/tests/test_write_read.cpp +++ b/tests/test_write_read.cpp @@ -3,7 +3,7 @@ #include #include #include -#include "../src/xdgcfg.hpp" +#include "xdgcfg.hpp" using std::string;