From a650630f81b0204e9d96e789334a30aa5986cda5 Mon Sep 17 00:00:00 2001 From: Rick Barenthin Date: Thu, 20 Jan 2022 23:49:46 +0100 Subject: [PATCH] initial project setup --- .clang-format | 66 ++++++ .drone.yml | 29 +++ CMakeLists.txt | 27 +++ README.md | 1 + app/CMakeLists.txt | 50 +++++ app/include/waitui/version.h.in | 36 +++ app/project-meta-info.in | 4 + app/src/main.c | 163 ++++++++++++++ cmake/FetchCMocka.cmake | 35 +++ cmake/GetGitRevisionDescription.cmake | 274 +++++++++++++++++++++++ cmake/GetGitRevisionDescription.cmake.in | 43 ++++ library/log/CMakeLists.txt | 22 ++ library/log/include/waitui/log.h | 130 +++++++++++ library/log/project-meta-info.in | 3 + library/log/src/log.c | 224 ++++++++++++++++++ project-meta-info.in | 3 + tests/CMakeLists.txt | 3 + 17 files changed, 1113 insertions(+) create mode 100644 .clang-format create mode 100644 .drone.yml create mode 100644 CMakeLists.txt create mode 100644 app/CMakeLists.txt create mode 100644 app/include/waitui/version.h.in create mode 100644 app/project-meta-info.in create mode 100644 app/src/main.c create mode 100644 cmake/FetchCMocka.cmake create mode 100644 cmake/GetGitRevisionDescription.cmake create mode 100644 cmake/GetGitRevisionDescription.cmake.in create mode 100644 library/log/CMakeLists.txt create mode 100644 library/log/include/waitui/log.h create mode 100644 library/log/project-meta-info.in create mode 100644 library/log/src/log.c create mode 100644 project-meta-info.in create mode 100644 tests/CMakeLists.txt diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..0bae61f --- /dev/null +++ b/.clang-format @@ -0,0 +1,66 @@ +# Generated from CLion C/C++ Code Style settings +BasedOnStyle: LLVM +AccessModifierOffset: -4 +AlignAfterOpenBracket: Align +AlignConsecutiveAssignments: AcrossComments +AlignOperands: true +AllowAllArgumentsOnNextLine: false +AllowAllConstructorInitializersOnNextLine: false +AllowAllParametersOfDeclarationOnNextLine: false +AllowShortBlocksOnASingleLine: Always +AllowShortCaseLabelsOnASingleLine: false +AllowShortFunctionsOnASingleLine: All +AllowShortIfStatementsOnASingleLine: Always +AllowShortLambdasOnASingleLine: All +AllowShortLoopsOnASingleLine: true +AlwaysBreakAfterReturnType: None +AlwaysBreakTemplateDeclarations: Yes +BreakBeforeBraces: Custom +BraceWrapping: + AfterCaseLabel: false + AfterClass: false + AfterControlStatement: Never + AfterEnum: false + AfterFunction: false + AfterNamespace: false + AfterUnion: false + BeforeCatch: false + BeforeElse: false + IndentBraces: false + SplitEmptyFunction: false + SplitEmptyRecord: true +BreakBeforeBinaryOperators: None +BreakBeforeTernaryOperators: true +BreakConstructorInitializers: BeforeColon +BreakInheritanceList: BeforeColon +ColumnLimit: 80 +CompactNamespaces: false +ContinuationIndentWidth: 8 +IndentCaseLabels: true +IndentPPDirectives: None +IndentWidth: 4 +KeepEmptyLinesAtTheStartOfBlocks: true +MaxEmptyLinesToKeep: 2 +NamespaceIndentation: All +ObjCSpaceAfterProperty: false +ObjCSpaceBeforeProtocolList: true +PointerAlignment: Right +ReflowComments: false +SpaceAfterCStyleCast: true +SpaceAfterLogicalNot: false +SpaceAfterTemplateKeyword: false +SpaceBeforeAssignmentOperators: true +SpaceBeforeCpp11BracedList: false +SpaceBeforeCtorInitializerColon: true +SpaceBeforeInheritanceColon: true +SpaceBeforeParens: ControlStatements +SpaceBeforeRangeBasedForLoopColon: true +SpaceInEmptyParentheses: false +SpacesBeforeTrailingComments: 0 +SpacesInAngles: false +SpacesInCStyleCastParentheses: false +SpacesInContainerLiterals: false +SpacesInParentheses: false +SpacesInSquareBrackets: false +TabWidth: 4 +UseTab: Never \ No newline at end of file diff --git a/.drone.yml b/.drone.yml new file mode 100644 index 0000000..f90d3a2 --- /dev/null +++ b/.drone.yml @@ -0,0 +1,29 @@ +--- +kind: pipeline +type: docker +name: main + +steps: + - name: Tag commit + image: alpine + commands: + - echo This would create a tag + +trigger: + branch: + - main +--- +kind: pipeline +type: docker +name: develop + +steps: + - name: Tag commit + image: kitware/cmake + commands: + - cmake -DCMAKE_BUILD_TYPE=Debug . + +trigger: + branch: + - main +... \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..134616f --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,27 @@ +cmake_minimum_required(VERSION 3.21 FATAL_ERROR) + +include("${CMAKE_CURRENT_LIST_DIR}/project-meta-info.in") + +project(waitui-project + VERSION ${project_version} + DESCRIPTION ${project_description} + HOMEPAGE_URL ${project_homepage} + LANGUAGES C + ) + +set(CMAKE_C_STANDARD 17) + +list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake") + +if (CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME) + set_property(GLOBAL PROPERTY USE_FOLDERS ON) + include(CTest) +endif () + +add_subdirectory(app) +add_subdirectory(library/log) + +if ((CMAKE_PROJECT_NAME STREQUAL PROJECT_NAME OR MODERN_CMAKE_BUILD_TESTING) + AND BUILD_TESTING) + add_subdirectory(tests) +endif () diff --git a/README.md b/README.md index 7964e0e..bfb41e3 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,3 @@ # waitui +[![Build Status](https://drone.riba-interactive.de/api/badges/rick/waitui/status.svg?ref=refs/heads/main)](https://drone.riba-interactive.de/rick/waitui) \ No newline at end of file diff --git a/app/CMakeLists.txt b/app/CMakeLists.txt new file mode 100644 index 0000000..4a84547 --- /dev/null +++ b/app/CMakeLists.txt @@ -0,0 +1,50 @@ +cmake_minimum_required(VERSION 3.21 FATAL_ERROR) + +include("project-meta-info.in") + +project(waitui + VERSION ${project_version} + DESCRIPTION ${project_description} + HOMEPAGE_URL ${project_homepage} + LANGUAGES C + ) + +add_executable(waitui) + +target_sources(waitui + PRIVATE + "src/main.c" + "${CMAKE_CURRENT_BINARY_DIR}/include/waitui/version.h" + ) + +target_include_directories(waitui PRIVATE "${CMAKE_CURRENT_BINARY_DIR}/include") + +target_link_libraries(waitui PRIVATE log) + +find_package(Git) +if (GIT_FOUND AND EXISTS "${CMAKE_SOURCE_DIR}/.git") + include(GetGitRevisionDescription) + execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD OUTPUT_VARIABLE SHORT_SHA OUTPUT_STRIP_TRAILING_WHITESPACE) + + set(REVISION ${SHORT_SHA} CACHE STRING "git short sha" FORCE) + + # only use the plugin to tie the configure state to the sha to force rebuilds + # of files that depend on version.h + include(GetGitRevisionDescription) + get_git_head_revision(REFSPEC COMMITHASH) +else () + message(WARNING "Git not found, cannot set version info") + set(REVISION "unknown") +endif () + +if (NOT project_prerelease STREQUAL "") + set(WAITUI_VERSION_IS_PRERELEASE ON) +endif () + +math(EXPR PROJECT_VERSION_LONG "${PROJECT_VERSION_MAJOR} * 10000 + ${PROJECT_VERSION_MINOR} * 100 + ${PROJECT_VERSION_PATCH}") + +configure_file( + "include/waitui/version.h.in" + "${CMAKE_CURRENT_BINARY_DIR}/include/waitui/version.h" + @ONLY +) diff --git a/app/include/waitui/version.h.in b/app/include/waitui/version.h.in new file mode 100644 index 0000000..572ea5b --- /dev/null +++ b/app/include/waitui/version.h.in @@ -0,0 +1,36 @@ +/** + * @file version.h + * @author rick + * @date 30.07.20 + * @brief File for the Version implementation + */ + +#ifndef WAITUI_VERSION_H +#define WAITUI_VERSION_H + +#define WAITUI_VERSION_NAME "@PROJECT_NAME@" + +#define WAITUI_VERSION_MAJOR "@PROJECT_VERSION_MAJOR@" +#define WAITUI_VERSION_MINOR "@PROJECT_VERSION_MINOR@" +#define WAITUI_VERSION_PATCH "@PROJECT_VERSION_PATCH@" + +#define WAITUI_VERSION_PRERELEASE "@PROJECT_PRERELEASE@" + +#define WAITUI_VERSION_REVISION "@REVISION@" + +#cmakedefine WAITUI_VERSION_IS_PRERELEASE + +#ifdef WAITUI_VERSION_IS_PRERELEASE +#define WAITUI_VERSION_STRING \ + WAITUI_VERSION_MAJOR "." WAITUI_VERSION_MINOR "." WAITUI_VERSION_PATCH \ + "-" WAITUI_VERSION_PRERELEASE \ + "+" WAITUI_VERSION_REVISION +#else +#define WAITUI_VERSION_STRING \ + WAITUI_VERSION_MAJOR "." WAITUI_VERSION_MINOR "." WAITUI_VERSION_PATCH \ + "+" WAITUI_VERSION_REVISION +#endif + +#define WAITUI_VERSION_LONG @PROJECT_VERSION_LONG@L + +#endif //WAITUI_VERSION_H \ No newline at end of file diff --git a/app/project-meta-info.in b/app/project-meta-info.in new file mode 100644 index 0000000..89c0b67 --- /dev/null +++ b/app/project-meta-info.in @@ -0,0 +1,4 @@ +set(project_version 0.0.1) +set(project_description "waitui executable") +set(project_homepage "http://example.com") +set(project_prerelease "") \ No newline at end of file diff --git a/app/src/main.c b/app/src/main.c new file mode 100644 index 0000000..b556a0b --- /dev/null +++ b/app/src/main.c @@ -0,0 +1,163 @@ +#include "waitui/version.h" + +#include + +#include +#include +#include +#include + + +// ----------------------------------------------------------------------------- +// Local defines +// ----------------------------------------------------------------------------- + +#define WAITUI_SUCCESS 0 +#define WAITUI_FAILURE 1 +#define WAITUI_MEMORY_ERROR 2 + + +// ----------------------------------------------------------------------------- +// Local variables +// ----------------------------------------------------------------------------- + +// clang-format off +static const char * waitui_shortOptions = "vhqd::"; +static const struct option waitui_longOptions[] = { + {"version", no_argument, 0, 'v'}, + {"help", no_argument, 0, 'h'}, + {"quiet", no_argument, 0, 'q'}, + {"debug", optional_argument, 0,'d'}, +0 +}; +// clang-format on + + +// ----------------------------------------------------------------------------- +// Local functions +// ----------------------------------------------------------------------------- + +// clang-format off +#define waitui_printLogo(s) \ + do { \ + fprintf((s), "\n"); \ + fprintf((s), "\n"); \ + fprintf((s), " ___ __ \n"); \ + fprintf((s), " (_ ( . ) )__ -.- '. \\ : / .' \n"); \ + fprintf((s), " '(___(_____) -.- '. \\ : / .' \n"); \ + fprintf((s), " '. \\ : / .' \n"); \ + fprintf((s), " __ -----____ _ _____----- \n"); \ + fprintf((s), "_________________/. _\\________________________________(_)______________________\n"); \ + fprintf((s), " .--.|/_/__ \n"); \ + fprintf((s), " ''.--o/___ \\ ~ ~ ,-----------------------, \n"); \ + fprintf((s), " ~ /.'o|_o '.| ~ | | \n"); \ + fprintf((s), " |/ |_| ~ | Waitui | \n"); \ + fprintf((s), " ~ ' |_| ~ | by Rick | \n"); \ + fprintf((s), " __|_|-;.___.;--.,___ | | \n"); \ + fprintf((s), " ~ _,,-\"\" |_| \"\"-,,_ `------------------------' \n"); \ + fprintf((s), " .-'´'´ |_| ``-. ~ \n"); \ + fprintf((s), " \", |_| ,\" ~ \n"); \ + fprintf((s), " \"-_ _-\" ~ \n"); \ + fprintf((s), " ~ ``----..,_ , __,,..---'´ ~ ~ \n"); \ + fprintf((s), " ```'''''' ~ ~ \n"); \ + } \ + while (0) +// clang-format on + +/** + * @brief Print version. + * @param[in] stream Where the version should be printed + * @param logo Whether to print the logo + */ +void waitui_printVersion(FILE *stream, int logo) { + fprintf(stream, "%s version %s", WAITUI_VERSION_NAME, + WAITUI_VERSION_STRING); + + if (logo) { waitui_printLogo(stream); } +} + +/** + * @brief Print help. + * @param[in] stream Where the help should be printed + */ +void waitui_printHelp(FILE *stream) { + waitui_printVersion(stream, 0); + + fprintf(stream, "\n\n"); + fprintf(stream, "Usage: %s[-hvq] [-d level]\n", WAITUI_VERSION_NAME); + fprintf(stream, "\t--help, -h \t\tDisplays this help\n"); + fprintf(stream, "\t--version, -v \t\tDisplays the version\n"); + fprintf(stream, "\t--quiet, -q \t\tDisplays the version\n"); + fprintf(stream, "\t--debug=level, -d level\t\tDisplays the version\n"); +} + + +// ----------------------------------------------------------------------------- +// Main function +// ----------------------------------------------------------------------------- + +int main(int argc, char **argv) { + int result = WAITUI_SUCCESS; + + bool quite = false; + long loglevel = WAITUI_LOG_DEBUG; + + waitui_log_setQuiet(quite); + waitui_log_setLevel(loglevel); + + while (1) { + int index = -1; + struct option *opt = NULL; + + int optRes = getopt_long(argc, argv, waitui_shortOptions, + waitui_longOptions, &index); + if (optRes == -1) break; /* end of list */ + switch (optRes) { + case 'v': + waitui_printVersion(stdout, 1); + return WAITUI_SUCCESS; + case 'h': + waitui_printHelp(stdout); + return WAITUI_SUCCESS; + case 'q': + quite = true; + break; + case 'd': + if (optarg) { + char *endPtr = NULL; + errno = 0; + loglevel = strtol(optarg, &endPtr, 10); + if (errno != 0 || endPtr == optarg || + loglevel < WAITUI_LOG_TRACE || + loglevel >= WAITUI_LOG_MAX) { + waitui_log_warn( + "no valid log level given or out of range " + "(%d - %d): %s", + WAITUI_LOG_TRACE, WAITUI_LOG_MAX - 1, optarg); + } + } + break; + case 0: + /* all parameter that do not appear in the option string */ + opt = (struct option *) &(waitui_longOptions[index]); + printf("'%s' was specified.", opt->name); + if (opt->has_arg == required_argument) + printf("Arg: <%s>", optarg); + printf("\n"); + break; + default: /* unknown */ + break; + } + } + /* print all others parameters */ + while (optind < argc) { printf("other parameter: <%s>\n", argv[optind++]); } + + waitui_log_setQuiet(quite); + waitui_log_setLevel(loglevel); + + waitui_log_debug("waitui start execution"); + + waitui_log_debug("waitui execution done"); + + return result; +} \ No newline at end of file diff --git a/cmake/FetchCMocka.cmake b/cmake/FetchCMocka.cmake new file mode 100644 index 0000000..afdf3ea --- /dev/null +++ b/cmake/FetchCMocka.cmake @@ -0,0 +1,35 @@ +# Copyright 2020 OLIVIER LE DOEUFF +# +# Permission is hereby granted, free of charge, to any person obtaining a copy of this software +# and associated documentation files (the "Software"), to deal in the Software without restriction, +# including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, +# and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, +# subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in all copies or +# substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, +# INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +# IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +# DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +# TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH +# THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +include(FetchContent) + +FetchContent_Declare( + cmocka + GIT_REPOSITORY https://git.cryptomilk.org/projects/cmocka.git + GIT_TAG cmocka-1.1.5 + GIT_SHALLOW 1 +) + +set(WITH_STATIC_LIB ON CACHE BOOL "CMocka: Build with a static library" FORCE) +set(WITH_CMOCKERY_SUPPORT OFF CACHE BOOL "CMocka: Install a cmockery header" FORCE) +set(WITH_EXAMPLES OFF CACHE BOOL "CMocka: Build examples" FORCE) +set(UNIT_TESTING OFF CACHE BOOL "CMocka: Build with unit testing" FORCE) +set(PICKY_DEVELOPER OFF CACHE BOOL "CMocka: Build with picky developer flags" FORCE) + +FetchContent_MakeAvailable(cmocka) \ No newline at end of file diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake new file mode 100644 index 0000000..93c2e65 --- /dev/null +++ b/cmake/GetGitRevisionDescription.cmake @@ -0,0 +1,274 @@ +# - Returns a version string from Git +# +# These functions force a re-configure on each git commit so that you can +# trust the values of the variables in your build system. +# +# get_git_head_revision( [ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR]) +# +# Returns the refspec and sha hash of the current head revision +# +# git_describe( [ ...]) +# +# Returns the results of git describe on the source tree, and adjusting +# the output so that it tests false if an error occurs. +# +# git_describe_working_tree( [ ...]) +# +# Returns the results of git describe on the working tree (--dirty option), +# and adjusting the output so that it tests false if an error occurs. +# +# git_get_exact_tag( [ ...]) +# +# Returns the results of git describe --exact-match on the source tree, +# and adjusting the output so that it tests false if there was no exact +# matching tag. +# +# git_local_changes() +# +# Returns either "CLEAN" or "DIRTY" with respect to uncommitted changes. +# Uses the return code of "git diff-index --quiet HEAD --". +# Does not regard untracked files. +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2020 Ryan Pavlik +# http://academic.cleardefinition.com +# +# Copyright 2009-2013, Iowa State University. +# Copyright 2013-2020, Ryan Pavlik +# Copyright 2013-2020, Contributors +# SPDX-License-Identifier: BSL-1.0 +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) + +if(__get_git_revision_description) + return() +endif() +set(__get_git_revision_description YES) + +# We must run the following at "include" time, not at function call time, +# to find the path to this module rather than the path to a calling list file +get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) + +# Function _git_find_closest_git_dir finds the next closest .git directory +# that is part of any directory in the path defined by _start_dir. +# The result is returned in the parent scope variable whose name is passed +# as variable _git_dir_var. If no .git directory can be found, the +# function returns an empty string via _git_dir_var. +# +# Example: Given a path C:/bla/foo/bar and assuming C:/bla/.git exists and +# neither foo nor bar contain a file/directory .git. This wil return +# C:/bla/.git +# +function(_git_find_closest_git_dir _start_dir _git_dir_var) + set(cur_dir "${_start_dir}") + set(git_dir "${_start_dir}/.git") + while(NOT EXISTS "${git_dir}") + # .git dir not found, search parent directories + set(git_previous_parent "${cur_dir}") + get_filename_component(cur_dir "${cur_dir}" DIRECTORY) + if(cur_dir STREQUAL git_previous_parent) + # We have reached the root directory, we are not in git + set(${_git_dir_var} + "" + PARENT_SCOPE) + return() + endif() + set(git_dir "${cur_dir}/.git") + endwhile() + set(${_git_dir_var} + "${git_dir}" + PARENT_SCOPE) +endfunction() + +function(get_git_head_revision _refspecvar _hashvar) + _git_find_closest_git_dir("${CMAKE_CURRENT_SOURCE_DIR}" GIT_DIR) + + if("${ARGN}" STREQUAL "ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR") + set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR TRUE) + else() + set(ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR FALSE) + endif() + if(NOT "${GIT_DIR}" STREQUAL "") + file(RELATIVE_PATH _relative_to_source_dir "${CMAKE_SOURCE_DIR}" + "${GIT_DIR}") + if("${_relative_to_source_dir}" MATCHES "[.][.]" AND NOT ALLOW_LOOKING_ABOVE_CMAKE_SOURCE_DIR) + # We've gone above the CMake root dir. + set(GIT_DIR "") + endif() + endif() + if("${GIT_DIR}" STREQUAL "") + set(${_refspecvar} + "GITDIR-NOTFOUND" + PARENT_SCOPE) + set(${_hashvar} + "GITDIR-NOTFOUND" + PARENT_SCOPE) + 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(NOT IS_DIRECTORY ${GIT_DIR}) + # 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 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) + _git_find_closest_git_dir("${git_worktree_dir}" GIT_DIR) + set(HEAD_SOURCE_FILE "${git_worktree_dir}/HEAD") + endif() + else() + set(HEAD_SOURCE_FILE "${GIT_DIR}/HEAD") + endif() + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() + + if(NOT EXISTS "${HEAD_SOURCE_FILE}") + return() + endif() + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${HEAD_SOURCE_FILE}" "${HEAD_FILE}" COPYONLY) + + configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" + "${GIT_DATA}/grabRef.cmake" @ONLY) + include("${GIT_DATA}/grabRef.cmake") + + set(${_refspecvar} + "${HEAD_REF}" + PARENT_SCOPE) + set(${_hashvar} + "${HEAD_HASH}" + PARENT_SCOPE) +endfunction() + +function(git_describe _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} + "HEAD-HASH-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + execute_process( + COMMAND "${GIT_EXECUTABLE}" describe --tags --always ${hash} ${ARGN} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} + "${out}" + PARENT_SCOPE) +endfunction() + +function(git_describe_working_tree _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + execute_process( + COMMAND "${GIT_EXECUTABLE}" describe --dirty ${ARGN} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() + + set(${_var} + "${out}" + PARENT_SCOPE) +endfunction() + +function(git_get_exact_tag _var) + git_describe(out --exact-match ${ARGN}) + set(${_var} + "${out}" + PARENT_SCOPE) +endfunction() + +function(git_local_changes _var) + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE) + return() + endif() + if(NOT hash) + set(${_var} + "HEAD-HASH-NOTFOUND" + PARENT_SCOPE) + return() + endif() + + execute_process( + COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD -- + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE) + if(res EQUAL 0) + set(${_var} + "CLEAN" + PARENT_SCOPE) + else() + set(${_var} + "DIRTY" + PARENT_SCOPE) + endif() +endfunction() diff --git a/cmake/GetGitRevisionDescription.cmake.in b/cmake/GetGitRevisionDescription.cmake.in new file mode 100644 index 0000000..116efc4 --- /dev/null +++ b/cmake/GetGitRevisionDescription.cmake.in @@ -0,0 +1,43 @@ +# +# Internal file for GetGitRevisionDescription.cmake +# +# Requires CMake 2.6 or newer (uses the 'function' command) +# +# Original Author: +# 2009-2010 Ryan Pavlik +# http://academic.cleardefinition.com +# Iowa State University HCI Graduate Program/VRAC +# +# Copyright 2009-2012, Iowa State University +# Copyright 2011-2015, Contributors +# Distributed under the Boost Software License, Version 1.0. +# (See accompanying file LICENSE_1_0.txt or copy at +# http://www.boost.org/LICENSE_1_0.txt) +# SPDX-License-Identifier: BSL-1.0 + +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}" "@GIT_DATA@/head-ref" COPYONLY) + else() + configure_file("@GIT_DIR@/packed-refs" "@GIT_DATA@/packed-refs" COPYONLY) + file(READ "@GIT_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" "@GIT_DATA@/head-ref" COPYONLY) +endif() + +if(NOT HEAD_HASH) + file(READ "@GIT_DATA@/head-ref" HEAD_HASH LIMIT 1024) + string(STRIP "${HEAD_HASH}" HEAD_HASH) +endif() diff --git a/library/log/CMakeLists.txt b/library/log/CMakeLists.txt new file mode 100644 index 0000000..571c35f --- /dev/null +++ b/library/log/CMakeLists.txt @@ -0,0 +1,22 @@ +cmake_minimum_required(VERSION 3.21 FATAL_ERROR) + +include("project-meta-info.in") + +project(waitui-log + VERSION ${project_version} + DESCRIPTION ${project_description} + HOMEPAGE_URL ${project_homepage} + LANGUAGES C) + +add_library(log OBJECT) + +target_sources(log + PRIVATE + "src/log.c" + PUBLIC + "include/waitui/log.h" + ) + +target_include_directories(log PUBLIC "include") + +target_compile_definitions(log PUBLIC "LOG_USE_COLOR") diff --git a/library/log/include/waitui/log.h b/library/log/include/waitui/log.h new file mode 100644 index 0000000..645c61a --- /dev/null +++ b/library/log/include/waitui/log.h @@ -0,0 +1,130 @@ +/** + * @file log.h + * @author rick + * @date 28.08.20 + * @brief File for the Log implementation + */ + +#ifndef WAITUI_LOG_H +#define WAITUI_LOG_H + +#include +#include +#include +#include + + +// ----------------------------------------------------------------------------- +// Public types +// ----------------------------------------------------------------------------- + +/** + * @brief The type for log events. + */ +typedef struct waitui_log_event { + va_list ap; + const char *format; + const char *file; + int line; + struct tm *time; + int level; + void *userData; +} waitui_log_event; + +/** + * @brief The logging callback function type. + */ +typedef void (*waitui_log_logging_fn)(waitui_log_event *event); + +/** + * @brief The locking callback function type. + */ +typedef void (*waitui_log_lock_fn)(bool lock, void *userData); + +/** + * @brief The different log levels. + */ +typedef enum { + WAITUI_LOG_TRACE, + WAITUI_LOG_DEBUG, + WAITUI_LOG_INFO, + WAITUI_LOG_WARN, + WAITUI_LOG_ERROR, + WAITUI_LOG_FATAL, + WAITUI_LOG_MAX, +} waitui_log_level; + + +// ----------------------------------------------------------------------------- +// Public defines +// ----------------------------------------------------------------------------- + +#define waitui_log_trace(...) \ + waitui_log_writeLog(WAITUI_LOG_TRACE, __FILE__, __LINE__, __VA_ARGS__) +#define waitui_log_debug(...) \ + waitui_log_writeLog(WAITUI_LOG_DEBUG, __FILE__, __LINE__, __VA_ARGS__) +#define waitui_log_info(...) \ + waitui_log_writeLog(WAITUI_LOG_INFO, __FILE__, __LINE__, __VA_ARGS__) +#define waitui_log_warn(...) \ + waitui_log_writeLog(WAITUI_LOG_WARN, __FILE__, __LINE__, __VA_ARGS__) +#define waitui_log_error(...) \ + waitui_log_writeLog(WAITUI_LOG_ERROR, __FILE__, __LINE__, __VA_ARGS__) +#define waitui_log_fatal(...) \ + waitui_log_writeLog(WAITUI_LOG_FATAL, __FILE__, __LINE__, __VA_ARGS__) + + +// ----------------------------------------------------------------------------- +// Public functions +// ----------------------------------------------------------------------------- + +/** + * @brief Set locking function for log. + * @param lockFn The function to be called for lock and unlock when writing logs + * @param[in] userData Extra user data to pass to the locking function + */ +extern void waitui_log_set_lock(waitui_log_lock_fn lockFn, void *userData); + +/** + * @brief Set minimum log level to log on stderr. + * @param level The minimum log level from which on log appear on stderr + */ +extern void waitui_log_setLevel(waitui_log_level level); + +/** + * @brief Disable or enable logging to stderr. + * @param enable True to disable logging to stderr, false to enable + */ +extern void waitui_log_setQuiet(bool enable); + +/** + * @brief Add the logFn as a log callback to write logs starting at the level. + * @param logFn The function to add as a log callback + * @param[in] userData Extra user data to pass to the logFn + * @param level The level from which on this log callback is executed + * @retval 1 Successful added the callback + * @retval 0 No more space to add the callback + */ +extern int waitui_log_addCallback(waitui_log_logging_fn logFn, void *userData, + waitui_log_level level); + +/** +* @brief Add a log callback to write logs starting at the level into the file. +* @param[in] file The file into which to write the log +* @param level The level from which on this log callback is executed +* @retval 1 Successful added the file callback +* @retval 0 No more space to add the callback +*/ +extern int waitui_log_addFile(FILE *file, waitui_log_level level); + +/** +* @brief Write the log message to log with the given parameters. +* @param level The log level for this message +* @param[in] file The filename where this log is written +* @param line The line number where this log is written +* @param[in] format The message to write to the log +* @param ... Extra values to be used inside the message +*/ +extern void waitui_log_writeLog(waitui_log_level level, const char *file, + int line, const char *format, ...); + +#endif//WAITUI_LOG_H \ No newline at end of file diff --git a/library/log/project-meta-info.in b/library/log/project-meta-info.in new file mode 100644 index 0000000..6d64fc5 --- /dev/null +++ b/library/log/project-meta-info.in @@ -0,0 +1,3 @@ +set(project_version 0.0.1) +set(project_description "waitui waitui_log library") +set(project_homepage "http://example.com") \ No newline at end of file diff --git a/library/log/src/log.c b/library/log/src/log.c new file mode 100644 index 0000000..7e6fd2f --- /dev/null +++ b/library/log/src/log.c @@ -0,0 +1,224 @@ +/** + * @file log.c + * @author rick + * @date 28.08.20 + * @brief File for the Log implementation + */ + +#include "waitui/log.h" + + +// ----------------------------------------------------------------------------- +// Local defines +// ----------------------------------------------------------------------------- + +/** + * @brief The maximum number of possible callbacks to register. + */ +#define MAX_CALLBACKS 32 + + +// ----------------------------------------------------------------------------- +// Local types +// ----------------------------------------------------------------------------- + +/** + * @brief Internal type to store the log callback function with all its info. + */ +typedef struct waitui_log_callback { + waitui_log_logging_fn logFn; + waitui_log_level level; + void *userData; +} waitui_log_callback; + +/** + * @brief Internal type to store the log with all its info. + */ +typedef struct waitui_log { + waitui_log_lock_fn lockFn; + waitui_log_level level; + bool quiet; + waitui_log_callback callbacks[MAX_CALLBACKS]; + void *userData; +} log; + + +// ----------------------------------------------------------------------------- +// Local variables +// ----------------------------------------------------------------------------- + +/** + * @brief The internal state of the log lib. + */ +static struct waitui_log L; + +/** + * @brief String representations of the log levels. + */ +static const char *waitui_log_level_strings[] = { + "TRACE", "DEBUG", "INFO", "WARN", "ERROR", "FATAL", +}; + +#ifdef LOG_USE_COLOR +/** + * @brief Color representations of the log levels. + */ +static const char *waitui_log_level_colors[] = { + "\x1b[94m", "\x1b[36m", "\x1b[32m", "\x1b[33m", "\x1b[31m", "\x1b[35m", +}; +#endif + + +// ----------------------------------------------------------------------------- +// Local functions +// ----------------------------------------------------------------------------- + +/** + * @brief Return the string representations of the log level. + * @param level The level to return as a string. + * @return The string representations of the log level or empty when out of bounds. + */ +static inline const char *waitui_log_levelAsString(waitui_log_level level) { + if (level < WAITUI_LOG_TRACE || level >= WAITUI_LOG_MAX) { return ""; } + return waitui_log_level_strings[level]; +} + +/** + * @brief Return the color representations of the log level. + * @param level The level to return as a color. + * @return The color representations of the log level or empty when out of bounds. + */ +static inline const char *waitui_log_levelAsColor(waitui_log_level level) { + if (level < WAITUI_LOG_TRACE || level >= WAITUI_LOG_MAX) { return ""; } + return waitui_log_level_colors[level]; +} + +/** + * @brief Calls the locking callback function to get the lock. + */ +static inline void waitui_log_lock(void) { + if (L.lockFn) { L.lockFn(true, L.userData); } +} + +/** + * @brief Calls the locking callback function to release the lock. + */ +static inline void waitui_log_unlock(void) { + if (L.lockFn) { L.lockFn(false, L.userData); } +} + +/** + * @brief Callback that write the log event to the console. + * @param[in] event log event to write to the console + */ +static void waitui_log_console_callback(waitui_log_event *event) { + char buffer[16]; + buffer[strftime(buffer, sizeof(buffer), "%H:%M:%S", event->time)] = '\0'; + +#ifdef LOG_USE_COLOR + fprintf(event->userData, "%s %s%-5s\x1b[0m \x1b[90m%s:%d:\x1b[0m ", buffer, + waitui_log_levelAsColor(event->level), + waitui_log_levelAsString(event->level), event->file, event->line); +#else + fprintf(event->userData, "%s %-5s %s:%d: ", buffer, + waitui_log_levelAsString(event->level), event->file, event->line); +#endif + + vfprintf(event->userData, event->format, event->ap); + fprintf(event->userData, "\n"); + fflush(event->userData); +} + +/** + * @brief Callback that write the log event to a file. + * @param[in] event log event to write to a file + */ +static void waitui_log_file_callback(waitui_log_event *event) { + char buffer[64]; + buffer[strftime(buffer, sizeof(buffer), "%Y-%m-%d %H:%M:%S", event->time)] = + '\0'; + + fprintf(event->userData, "%s %-5s %s:%d: ", buffer, + waitui_log_levelAsString(event->level), event->file, event->line); + + vfprintf(event->userData, event->format, event->ap); + fprintf(event->userData, "\n"); + fflush(event->userData); +} + +/** + * @brief Initializes the log event with time and userdata. + * @param[in,out] event log event to write to initialize + * @param[in] user_data user data to set for the log event + */ +static inline void waitui_log_init_event(waitui_log_event *event, + void *userData) { + if (!event->time) { + time_t t = time(NULL); + event->time = localtime(&t); + } + + event->userData = userData; +} + + +// ----------------------------------------------------------------------------- +// Public functions +// ----------------------------------------------------------------------------- + +void waitui_log_set_lock(waitui_log_lock_fn lockFn, void *userData) { + L.lockFn = lockFn; + L.userData = userData; +} + +void waitui_log_setLevel(waitui_log_level level) { L.level = level; } + +void waitui_log_setQuiet(bool enable) { L.quiet = enable; } + +int waitui_log_addCallback(waitui_log_logging_fn logFn, void *userData, + waitui_log_level level) { + for (int i = 0; i < MAX_CALLBACKS; ++i) { + if (!L.callbacks[i].logFn) { + L.callbacks[i] = (waitui_log_callback){.logFn = logFn, + .userData = userData, + .level = level}; + return 1; + } + } + return 0; +} + +int waitui_log_addFile(FILE *file, waitui_log_level level) { + return waitui_log_addCallback(waitui_log_file_callback, file, level); +} + +void waitui_log_writeLog(waitui_log_level level, const char *file, int line, + const char *format, ...) { + waitui_log_event event = { + .format = format, + .file = file, + .line = line, + .level = level, + }; + + waitui_log_lock(); + + if (!L.quiet && level >= L.level) { + waitui_log_init_event(&event, stderr); + va_start(event.ap, format); + waitui_log_console_callback(&event); + va_end(event.ap); + } + + for (int i = 0; i < MAX_CALLBACKS && L.callbacks[i].logFn; ++i) { + waitui_log_callback *cb = &L.callbacks[i]; + if (level >= cb->level) { + waitui_log_init_event(&event, cb->userData); + va_start(event.ap, format); + cb->logFn(&event); + va_end(event.ap); + } + } + + waitui_log_unlock(); +} \ No newline at end of file diff --git a/project-meta-info.in b/project-meta-info.in new file mode 100644 index 0000000..70859b0 --- /dev/null +++ b/project-meta-info.in @@ -0,0 +1,3 @@ +set(project_version 0.0.1) +set(project_description "waitui language system") +set(project_homepage "http://example.com") \ No newline at end of file diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt new file mode 100644 index 0000000..c24e41b --- /dev/null +++ b/tests/CMakeLists.txt @@ -0,0 +1,3 @@ +include(../cmake/FetchCMocka.cmake) + +#add_subdirectory() \ No newline at end of file