From dd2b9395962328c1d95c1bae24a39f9d7cec10ca Mon Sep 17 00:00:00 2001 From: "Maarten L. Hekkelman" Date: Wed, 16 Aug 2023 11:18:49 +0200 Subject: [PATCH 1/2] using new version string --- .gitignore | 1 + CMakeLists.txt | 10 +- cmake/VersionString.cmake | 374 +++++++++++++++++++++++++++++++++++--- energyd.conf | 6 - 4 files changed, 349 insertions(+), 42 deletions(-) delete mode 100644 energyd.conf diff --git a/.gitignore b/.gitignore index ced89c6..bf68e42 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,4 @@ build/ src/revision.hpp src/mrsrc.hpp .vscode/ +energyd.conf diff --git a/CMakeLists.txt b/CMakeLists.txt index 31368e1..e648a04 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,14 +1,14 @@ cmake_minimum_required(VERSION 3.16) # set the project name -project(energyd VERSION 2.0.0 LANGUAGES CXX) +project(energyd VERSION 2.0.1 LANGUAGES CXX) include(FindPkgConfig) list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/cmake") set(CXX_EXTENSIONS OFF) -set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU") @@ -34,12 +34,10 @@ mrc_write_header(${PROJECT_SOURCE_DIR}/src/mrsrc.hpp) # Libraries find_package(zeep 6 REQUIRED) find_package(libmcfp REQUIRED) - -set(ENV{PKG_CONFIG_PATH} "$ENV{HOME}/.local/lib/pkgconfig/") -pkg_check_modules(PKG_PQ libpqxx>=7.2 libpq IMPORTED_TARGET REQUIRED) +find_package(libpqxx 7.8 REQUIRED) add_executable(energyd ${PROJECT_SOURCE_DIR}/src/energyd.cpp) -target_link_libraries(energyd PkgConfig::PKG_PQ libmcfp::libmcfp zeep::zeep) +target_link_libraries(energyd libpqxx::pqxx libmcfp::libmcfp zeep::zeep) # yarn rules for javascripts diff --git a/cmake/VersionString.cmake b/cmake/VersionString.cmake index a54638a..f050ba5 100644 --- a/cmake/VersionString.cmake +++ b/cmake/VersionString.cmake @@ -1,6 +1,6 @@ # SPDX-License-Identifier: BSD-2-Clause -# Copyright (c) 2021 NKI/AVL, Netherlands Cancer Institute +# Copyright (c) 2021-2023 Maarten L. Hekkelman # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions are met: @@ -22,60 +22,374 @@ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS # SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +# This cmake extension writes out a revision.hpp file in a specified directory. +# The file will contain a C++ inline function that can be used to write out +# version information. + cmake_minimum_required(VERSION 3.15) +# We want the revision.hpp file to be updated whenever the status of the +# git repository changes. Use the same technique as in GetGitRevisionDescription.cmake +# from https://github.com/rpavlik/cmake-modules + + +#[=======================================================================[.rst: +.. command:: write_version_header + + Write a file named revision.hpp containing version info:: + + write_version_header( + [FILE_NAME ] + [LIB_NAME ] + ) + + This command will generate the code to write a file name + revision.hpp in the directory ````. + + ``FILE_NAME`` + Specify the name of the file to create, default is ``revision.hpp``. + + ``LIB_NAME`` + Specify the library name which will be used as a prefix part for the + variables contained in the revision file. +#]=======================================================================] + +# First locate a .git file or directory. +function(_get_git_dir _start_dir _variable) + + set(cur_dir "${_start_dir}") + set(git_dir "${_start_dir}/.git") + + while(NOT EXISTS "${git_dir}") + # .git dir not found, search parent directories + set(prev_dir "${cur_dir}") + get_filename_component(cur_dir "${cur_dir}" DIRECTORY) + if(cur_dir STREQUAL prev_dir OR cur_dir STREQUAL ${_start_dir}) + # we are not in git since we either hit root or + # the ${_start_dir} which should be the top + set(${_variable} "" PARENT_SCOPE) + return() + endif() + set(git_dir "${cur_dir}/.git") + endwhile() + + set(${_variable} "${git_dir}" PARENT_SCOPE) +endfunction() + +# Locate the git refspec hash and load the hash +# This code locates the file containing the git refspec/hash +# and loads it. Doing it this way assures that each time the git +# repository changes the revision.hpp file gets out of date. +function(_get_git_hash _data_dir _variable) + + # Be pessimistic + set(_variable "" PARENT_SCOPE) + + # Load git package if needed + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + + # And fail if not found + if(NOT GIT_FOUND) + return() + endif() + + # Locate the nearest .git file or directory + _get_git_dir(${CMAKE_CURRENT_SOURCE_DIR} GIT_DIR) + + # And fail if not found + if("${GIT_DIR}" STREQUAL "") + return() + endif() + + # Check if the current source dir is a git submodule or a worktree. + # In both cases .git is a file instead of a directory. + # + if(IS_DIRECTORY ${GIT_DIR}) + set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") + else() + # The following git command will return a non empty string that + # points to the super project working tree if the current + # source dir is inside a git submodule. + # Otherwise the command will return an empty string. + # + execute_process( + COMMAND "${GIT_EXECUTABLE}" rev-parse + --show-superproject-working-tree + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT "${out}" STREQUAL "") + # If out is not empty, GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a submodule + file(READ ${GIT_DIR} submodule) + string(REGEX REPLACE "gitdir: (.*)$" "\\1" GIT_DIR_RELATIVE + ${submodule}) + string(STRIP ${GIT_DIR_RELATIVE} GIT_DIR_RELATIVE) + get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) + get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} + ABSOLUTE) + set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") + else() + # GIT_DIR/CMAKE_CURRENT_SOURCE_DIR is in a worktree + file(READ ${GIT_DIR} worktree_ref) + # The .git directory contains a path to the worktree information directory + # inside the parent git repo of the worktree. + # + string(REGEX REPLACE "gitdir: (.*)$" "\\1" git_worktree_dir + ${worktree_ref}) + string(STRIP ${git_worktree_dir} git_worktree_dir) + _get_git_dir("${git_worktree_dir}" GIT_DIR) + set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD") + endif() + endif() + + # Fail if the 'head' file was not found + if(NOT EXISTS "${HEAD_SOURCE_FILE}") + return() + endif() + + # Make a copy of the head file + set(HEAD_FILE "${_data_dir}/HEAD") + configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY) + + # Now we create a cmake file that will read the contents of this + # head file in the appropriate way + file(WRITE "${_data_dir}/grab-ref.cmake.in" [[ +set(HEAD_HASH) + +file(READ "@HEAD_FILE@" HEAD_CONTENTS LIMIT 1024) + +string(STRIP "${HEAD_CONTENTS}" HEAD_CONTENTS) +if(HEAD_CONTENTS MATCHES "ref") + # named branch + string(REPLACE "ref: " "" HEAD_REF "${HEAD_CONTENTS}") + if(EXISTS "@GIT_DIR@/${HEAD_REF}") + configure_file("@GIT_DIR@/${HEAD_REF}" "@VERSION_STRING_DATA@/head-ref" COPYONLY) + else() + configure_file("@GIT_DIR@/packed-refs" "@VERSION_STRING_DATA@/packed-refs" COPYONLY) + file(READ "@VERSION_STRING_DATA@/packed-refs" PACKED_REFS) + if(${PACKED_REFS} MATCHES "([0-9a-z]*) ${HEAD_REF}") + set(HEAD_HASH "${CMAKE_MATCH_1}") + endif() + endif() +else() + # detached HEAD + configure_file("@GIT_DIR@/HEAD" "@VERSION_STRING_DATA@/head-ref" COPYONLY) +endif() + +if(NOT HEAD_HASH) + file(READ "@VERSION_STRING_DATA@/head-ref" HEAD_HASH LIMIT 1024) + string(STRIP "${HEAD_HASH}" HEAD_HASH) +endif() +]]) + + configure_file("${VERSION_STRING_DATA}/grab-ref.cmake.in" + "${VERSION_STRING_DATA}/grab-ref.cmake" @ONLY) + + # Include the aforementioned file, this will define + # the HEAD_HASH variable we're looking for + include("${VERSION_STRING_DATA}/grab-ref.cmake") + + set(${_variable} "${HEAD_HASH}" PARENT_SCOPE) +endfunction() + # Create a revision file, containing the current git version info, if any function(write_version_header dir) + + set(flags ) + set(options LIB_NAME FILE_NAME) + set(sources ) + cmake_parse_arguments(VERSION_STRING_OPTION "${flags}" "${options}" "${sources}" ${ARGN}) + # parameter check if(NOT IS_DIRECTORY ${dir}) message(FATAL_ERROR "First parameter to write_version_header should be a directory where the final revision.hpp file will be placed") endif() - include(GetGitRevisionDescription) - if(NOT(GIT-NOTFOUND OR HEAD-HASH-NOTFOUND)) - git_describe_working_tree(BUILD_VERSION_STRING --match=build --dirty) - - if(BUILD_VERSION_STRING MATCHES "build-([0-9]+)-g([0-9a-f]+)(-dirty)?") - set(BUILD_GIT_TAGREF "${CMAKE_MATCH_2}") - if(CMAKE_MATCH_3) - set(BUILD_VERSION_STRING "${CMAKE_MATCH_1}*") - else() - set(BUILD_VERSION_STRING "${CMAKE_MATCH_1}") - endif() - endif() + if(VERSION_STRING_OPTION_FILE_NAME) + set(file_name "${VERSION_STRING_OPTION_FILE_NAME}") else() - message(WARNING "no git info available, cannot update version string") + set(file_name "revision.hpp") endif() - string(TIMESTAMP BUILD_DATE_TIME "%Y-%m-%dT%H:%M:%SZ" UTC) + # Where to store intermediate files + set(VERSION_STRING_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/VersionString") + if(NOT EXISTS "${VERSION_STRING_DATA}") + file(MAKE_DIRECTORY "${VERSION_STRING_DATA}") + endif() + + # Load the git hash using the wizzard-like code above. + _get_git_hash("${VERSION_STRING_DATA}" GIT_HASH) - if(ARGC GREATER 1) - set(VAR_PREFIX "${ARGV1}") + # If git was found, fetch the git description string + if(GIT_HASH) + execute_process( + COMMAND "${GIT_EXECUTABLE}" describe --dirty --match=build + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + + if(res EQUAL 0) + set(REVISION_STRING "${out}") + endif() endif() - file(WRITE "${PROJECT_BINARY_DIR}/revision.hpp.in" [[// Generated revision file + # Check the revision string, if it matches we fill in the required info + if(REVISION_STRING MATCHES "build-([0-9]+)-g([0-9a-f]+)(-dirty)?") + set(BUILD_NUMBER ${CMAKE_MATCH_1}) + if(CMAKE_MATCH_3) + set(REVISION_GIT_TAGREF "${CMAKE_MATCH_2}*") + else() + set(REVISION_GIT_TAGREF "${CMAKE_MATCH_2}") + endif() + + string(TIMESTAMP REVISION_DATE_TIME "%Y-%m-%dT%H:%M:%SZ" UTC) + else() + set(REVISION_GIT_TAGREF "") + set(BUILD_NUMBER 0) + set(REVISION_DATE_TIME "") + endif() + + if(VERSION_STRING_OPTION_LIB_NAME) + set(VAR_PREFIX "${VERSION_STRING_OPTION_LIB_NAME}") + set(IDENT_PREFIX "${VERSION_STRING_OPTION_LIB_NAME}_") + else() + set(VAR_PREFIX "") + set(IDENT_PREFIX "") + endif() + + # And finally, write out the header file + file(WRITE "${VERSION_STRING_DATA}/${file_name}.in" [[// This file was generated by VersionString.cmake #pragma once #include -const char k@VAR_PREFIX@ProjectName[] = "@PROJECT_NAME@"; -const char k@VAR_PREFIX@VersionNumber[] = "@PROJECT_VERSION@"; -const char k@VAR_PREFIX@VersionGitTag[] = "@BUILD_GIT_TAGREF@"; -const char k@VAR_PREFIX@BuildInfo[] = "@BUILD_VERSION_STRING@"; -const char k@VAR_PREFIX@BuildDate[] = "@BUILD_DATE_TIME@"; +constexpr const char k@VAR_PREFIX@ProjectName[] = "@PROJECT_NAME@"; +constexpr const char k@VAR_PREFIX@VersionNumber[] = "@PROJECT_VERSION@"; +constexpr int k@VAR_PREFIX@BuildNumber = @BUILD_NUMBER@; +constexpr const char k@VAR_PREFIX@RevisionGitTag[] = "@REVISION_GIT_TAGREF@"; +constexpr const char k@VAR_PREFIX@RevisionDate[] = "@REVISION_DATE_TIME@"; -inline void write_version_string(std::ostream &os, bool verbose) +#ifndef VERSION_INFO_DEFINED +#define VERSION_INFO_DEFINED 1 + +namespace version_info_v1 +{ + +class version_info_base { - os << k@VAR_PREFIX@ProjectName << " version " << k@VAR_PREFIX@VersionNumber << std::endl; - if (verbose) + public: + + static void write(std::ostream &os, bool verbose) { - os << "build: " << k@VAR_PREFIX@BuildInfo << ' ' << k@VAR_PREFIX@BuildDate << std::endl; - if (k@VAR_PREFIX@VersionGitTag[0] != 0) - os << "git tag: " << k@VAR_PREFIX@VersionGitTag << std::endl; + auto &s_head = head(); + if (s_head != nullptr) + write(s_head, os, verbose); } + + protected: + + version_info_base(const char *name, const char *version, int build_number, const char *git_tag, const char *revision_date) + : m_name(name) + , m_version(version) + , m_build_number(build_number) + , m_git_tag(git_tag) + , m_revision_date(revision_date) + { + auto &s_head = head(); + m_next = s_head; + s_head = this; + } + + static void write(const version_info_base *inst, std::ostream &os, bool verbose) + { + if (inst->m_next) + { + write(inst->m_next, os, verbose); + + if (not verbose) + return; + + os << '-' << std::endl; + } + + os << inst->m_name << " version " << inst->m_version << std::endl; + + if (verbose) + { + if (inst->m_build_number != 0) + { + os << "build: " << inst->m_build_number << ' ' << inst->m_revision_date << std::endl; + if (inst->m_git_tag[0] != 0) + os << "git tag: " << inst->m_git_tag << std::endl; + } + } + } + + using version_info_ptr = version_info_base *; + + static version_info_ptr &head() + { + static version_info_ptr s_head = nullptr; + return s_head; + } + + const char *m_name; + const char *m_version; + int m_build_number; + const char *m_git_tag; + const char *m_revision_date; + version_info_base *m_next = nullptr; +}; + +template +class version_info : public version_info_base +{ + public: + using implementation_type = T; + + version_info(const char *name, const char *version, int build_number, const char *git_tag, const char *revision_date) + : version_info_base(name, version, build_number, git_tag, revision_date) + { + } + + struct register_object + { + register_object() + { + static implementation_type s_instance; + } + }; + + template struct reference_object; + + static register_object s_registered_object; + static reference_object s_referenced_object; +}; + +template typename version_info::register_object version_info::s_registered_object; + +} + +inline void write_version_string(std::ostream &os, bool verbose) +{ + version_info_v1::version_info_base::write(os, verbose); } + +#endif + +class version_info_@IDENT_PREFIX@impl : public version_info_v1::version_info +{ + public: + version_info_@IDENT_PREFIX@impl() + : version_info(k@VAR_PREFIX@ProjectName, k@VAR_PREFIX@VersionNumber, k@VAR_PREFIX@BuildNumber, k@VAR_PREFIX@RevisionGitTag, k@VAR_PREFIX@RevisionDate) + { + } +}; ]]) - configure_file("${PROJECT_BINARY_DIR}/revision.hpp.in" "${dir}/revision.hpp" @ONLY) + configure_file("${VERSION_STRING_DATA}/${file_name}.in" "${dir}/${file_name}" @ONLY) endfunction() diff --git a/energyd.conf b/energyd.conf deleted file mode 100644 index 4895435..0000000 --- a/energyd.conf +++ /dev/null @@ -1,6 +0,0 @@ -db-user=energie-admin -db-password=geheim -db-dbname=energie -db-host=localhost -db-port=5432 -address=localhost \ No newline at end of file From aa49ead53e76edaa7fda492d30b29fe0a4cf8a52 Mon Sep 17 00:00:00 2001 From: "Maarten L. Hekkelman" Date: Wed, 16 Aug 2023 11:19:41 +0200 Subject: [PATCH 2/2] formatting --- README.md | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index c147381..28ca921 100644 --- a/README.md +++ b/README.md @@ -8,15 +8,15 @@ Installation To build and install energyd, you first need to install various other packages. Of course first of all you need [libzeep](https://github.com/mhekkel/libzeep). If you successfully installed libzeep, most of the requirements are already met, the remaining requirements are - * Postgresql - * The development version of [libpqxx](https://pqxx.org/development/libpqxx/) - * The javascript package manager [yarn](https://yarnpkg.com/) +* Postgresql +* The development version of [libpqxx](https://pqxx.org/development/libpqxx/) +* The javascript package manager [yarn](https://yarnpkg.com/) Building the software should start with running `yarn` without arguments in the source directory followed by `configure` and `make`. To summarize: -``` +```bash git clone https://github.com/mhekkel/energyd.git cd energyd yarn @@ -29,17 +29,19 @@ This should give you an executable called energyd. The next thing to do is creat The following commands are executed as a user that has admin rights on your postgres installation. First create a new user for this database, this command will ask for a password. -``` +```bash createuser -P energie-admin ``` + Then create the database with the newly created user as owner: -``` +```bash createdb -O energie-admin energie ``` + Finally fill the database with tables and some data: -``` +```bash psql -h localhost -f energie.sql energie energie-admin ``` @@ -48,7 +50,7 @@ Running Now that the database is ready, we can start the application. There are several options you can give: -``` +```bash $ ./energyd --help ./energyd [options] command: -h [ --help ] Display help message @@ -83,4 +85,4 @@ For debugging it might be useful to run the application in the foreground withou Usage ----- -The meters are hard coded, so this is perhaps not very useful for others. To complicate matters further, all text in the user interface is in Dutch. The idea is that you regularly enter (_Voeg toe_ button, or _Invoer_) the current values for the various meters. The graph (_Grafieken_) will then display your usage over time. The list of current values (_Opnames_) are displayed on the home page. \ No newline at end of file +The meters are hard coded, so this is perhaps not very useful for others. To complicate matters further, all text in the user interface is in Dutch. The idea is that you regularly enter (_Voeg toe_ button, or _Invoer_) the current values for the various meters. The graph (_Grafieken_) will then display your usage over time. The list of current values (_Opnames_) are displayed on the home page.