diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..6f22675 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,499 @@ +# C23 requires CMake 3.21 or newer for CMAKE_C_STANDARD 23; 3.24 also gives +# FetchContent the git submodule and progress behaviour Concord needs. +cmake_minimum_required(VERSION 3.24) + +project(hammy + VERSION 0.1.0 + LANGUAGES C +) + +# C23 needs GCC 13+, Clang 18+ or MSVC 17.9+. Extensions are off, so anything +# outside the standard (POSIX, BSD) has to be requested by feature-test macro; +# see the target_compile_definitions block at the bottom. +set(CMAKE_C_STANDARD 23) +set(CMAKE_C_STANDARD_REQUIRED ON) +set(CMAKE_C_EXTENSIONS OFF) + +# --------------------------------------------------------- +# Build configuration +# +# cmake -S . -B build -DCMAKE_BUILD_TYPE=Debug # -Og -g3, ASan + UBSan +# cmake -S . -B build-strict -DCMAKE_BUILD_TYPE=Strict # Debug plus -Werror +# cmake -S . -B build-analyzer -DCMAKE_BUILD_TYPE=Analyzer # -Og -g3, GCC -fanalyzer +# cmake -S . -B build-release -DCMAKE_BUILD_TYPE=Release # -O3, _FORTIFY_SOURCE +# +# Debug finds memory bugs at run time, Analyzer finds them at compile time, +# Release survives them. Strict is Debug with warnings promoted to errors — the +# configuration CI should gate on, kept separate so a new warning never blocks +# someone mid-debugging. The warning set is identical in all four; only the +# instrumentation and the error policy differ. +# +# Debug and Analyzer are separate configurations rather than one Debug build +# with everything switched on, because the two tools actively interfere: ASan's +# instrumentation inflates the CFG enough that -fanalyzer exhausts its +# exploration budget and silently stops reporting. +# +# ThreadSanitizer is deliberately absent: it cannot be combined with ASan, so it +# would need a third configuration of its own. +# --------------------------------------------------------- +set(HAMMY_CUSTOM_CONFIGS Strict Analyzer) +set(HAMMY_BUILD_TYPES Debug Strict Analyzer Release RelWithDebInfo MinSizeRel) + +get_property(HAMMY_MULTI_CONFIG GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG) +if(HAMMY_MULTI_CONFIG) + foreach(_cfg IN LISTS HAMMY_CUSTOM_CONFIGS) + if(NOT "${_cfg}" IN_LIST CMAKE_CONFIGURATION_TYPES) + list(APPEND CMAKE_CONFIGURATION_TYPES ${_cfg}) + endif() + endforeach() +elseif(NOT CMAKE_BUILD_TYPE) + set(CMAKE_BUILD_TYPE Debug CACHE STRING "Build type" FORCE) + message(STATUS "No CMAKE_BUILD_TYPE specified; defaulting to Debug") +endif() +set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${HAMMY_BUILD_TYPES}) + +# Strict and Analyzer are custom configurations, so CMake has no built-in flags +# for them. Both inherit Debug's, and imported targets (CURL::libcurl) need a +# mapping because they only ship Debug/Release/NOCONFIG. +foreach(_cfg IN LISTS HAMMY_CUSTOM_CONFIGS) + string(TOUPPER ${_cfg} _cfg_upper) + set(CMAKE_C_FLAGS_${_cfg_upper} "${CMAKE_C_FLAGS_DEBUG}" + CACHE STRING "Flags used by the C compiler during ${_cfg} builds.") + mark_as_advanced(CMAKE_C_FLAGS_${_cfg_upper}) + foreach(_linker EXE SHARED MODULE STATIC) + set(CMAKE_${_linker}_LINKER_FLAGS_${_cfg_upper} "${CMAKE_${_linker}_LINKER_FLAGS_DEBUG}" + CACHE STRING "Flags used by the linker during ${_cfg} builds.") + mark_as_advanced(CMAKE_${_linker}_LINKER_FLAGS_${_cfg_upper}) + endforeach() + set(CMAKE_MAP_IMPORTED_CONFIG_${_cfg_upper} Debug "" Release RelWithDebInfo) +endforeach() + +# Debug, Strict and Analyzer share the developer flag set (-Og -g3, warnings). +set(HAMMY_IS_DEBUGLIKE "$,$,$>") +# Debug and Strict are the runtime-instrumented pair; Analyzer must stay clean +# of sanitizers or -fanalyzer goes quiet (see above). +set(HAMMY_IS_SANITIZED "$,$>") + +# The Strict configuration always errors on warnings; this option additionally +# promotes them in Debug and Analyzer. OFF until src/ is clean under the warning +# set below — until then, build Strict when you want the gate. +option(HAMMY_WERROR "Debug/Analyzer: treat warnings as errors (always on in Strict)" OFF) +option(HAMMY_ENABLE_SANITIZERS "Debug config: AddressSanitizer + UndefinedBehaviorSanitizer" ON) +option(HAMMY_ENABLE_ANALYZER "Analyzer config: the compiler's static analyzer (GCC -fanalyzer)" ON) + +# How much of the control-flow path -fanalyzer prints per report. 1 lists only +# the state transitions (opened here / first close here / leaks here), which is +# what you want while triaging; raise it when a report needs the branch-by-branch +# path that explains how it got there. GCC silently accepts out-of-range values, +# so validate here instead. +set(ANALYZER_VERBOSITY 1 CACHE STRING "Analyzer config: -fanalyzer path detail, 0 (terse) to 5 (full)") +set_property(CACHE ANALYZER_VERBOSITY PROPERTY STRINGS 0 1 2 3 4 5) +if(NOT ANALYZER_VERBOSITY MATCHES "^[0-5]$") + message(FATAL_ERROR "ANALYZER_VERBOSITY must be an integer from 0 to 5, got '${ANALYZER_VERBOSITY}'") +endif() +option(HAMMY_ENABLE_HARDENING "All configs: stack protector, _FORTIFY_SOURCE, RELRO/NOW, CFI" ON) +option(HAMMY_ENABLE_LTO "Optimized configs: link-time optimization" OFF) + +include(CheckCCompilerFlag) +include(CheckLinkerFlag) + +# Most of the interesting memory diagnostics below only exist from a certain +# GCC/Clang version onwards, and several are architecture specific. Probe every +# flag instead of gating on compiler version, so an older or foreign toolchain +# silently gets the subset it understands rather than failing to configure. +# +# A flag the driver merely tolerates is not a flag that does anything (Clang +# accepts -fstack-clash-protection on arm64 and then ignores it), so the probe +# promotes "argument unused" to an error where the compiler supports that. +check_c_compiler_flag(-Werror=unused-command-line-argument HAMMY_HAS_WERROR_UNUSED_ARG) +if(HAMMY_HAS_WERROR_UNUSED_ARG) + set(HAMMY_FLAG_PROBE_STRICT "-Werror=unused-command-line-argument") +else() + set(HAMMY_FLAG_PROBE_STRICT "") +endif() + +function(hammy_append_supported_c_flags out_var) + set(_accepted ${${out_var}}) + set(CMAKE_REQUIRED_FLAGS "${HAMMY_FLAG_PROBE_STRICT}") + foreach(_flag IN LISTS ARGN) + string(MAKE_C_IDENTIFIER "HAMMY_HAS_CFLAG_${_flag}" _cache_var) + check_c_compiler_flag("${_flag}" ${_cache_var}) + if(${_cache_var}) + list(APPEND _accepted "${_flag}") + endif() + endforeach() + set(${out_var} "${_accepted}" PARENT_SCOPE) +endfunction() + +# Instrumentation such as -fsanitize=address has to reach the linker as well, +# and is only usable if the matching runtime library actually exists, so the +# probe hands the flag to both halves of the try-compile. +# +# Flags are also probed cumulatively, in the order given: some are only legal in +# the presence of an earlier one (-fsanitize=pointer-compare is rejected unless +# -fsanitize=address is already on the command line), and probing it alone would +# quietly drop it. +function(hammy_append_supported_instrument_flags out_var) + set(_accepted ${${out_var}}) + set(_context "") + foreach(_flag IN LISTS ARGN) + string(MAKE_C_IDENTIFIER "HAMMY_HAS_INSTRUMENT_${_flag}" _cache_var) + string(JOIN " " CMAKE_REQUIRED_FLAGS ${HAMMY_FLAG_PROBE_STRICT} ${_context}) + set(CMAKE_REQUIRED_LINK_OPTIONS ${_context} "${_flag}") + check_c_compiler_flag("${_flag}" ${_cache_var}) + if(${_cache_var}) + list(APPEND _accepted "${_flag}") + list(APPEND _context "${_flag}") + endif() + endforeach() + set(${out_var} "${_accepted}" PARENT_SCOPE) +endfunction() + +function(hammy_append_supported_link_flags out_var) + set(_accepted ${${out_var}}) + foreach(_flag IN LISTS ARGN) + string(MAKE_C_IDENTIFIER "HAMMY_HAS_LDFLAG_${_flag}" _cache_var) + check_linker_flag(C "${_flag}" ${_cache_var}) + if(${_cache_var}) + list(APPEND _accepted "${_flag}") + endif() + endforeach() + set(${out_var} "${_accepted}" PARENT_SCOPE) +endfunction() + +# Warnings for our own C code; the per-config flag sets are applied through +# generator expressions so multi-config generators (Xcode, VS) work too. +set(HAMMY_C_WARNINGS "") +set(HAMMY_C_FLAGS_DEBUGLIKE "") +set(HAMMY_C_FLAGS_OPTIMIZED "") +# The two instrumentation sets, each owned by one configuration and never both. +set(HAMMY_SANITIZER_FLAGS "") +set(HAMMY_ANALYZER_FLAGS "") +# Instrumentation that must be handed to the linker as well, and that also +# covers the third-party code we link in. +set(HAMMY_INSTRUMENT_COMPILE "") +set(HAMMY_INSTRUMENT_LINK "") + +if(MSVC) + set(HAMMY_WERROR_FLAG /WX) + list(APPEND HAMMY_C_WARNINGS /W4 /permissive- /sdl) + list(APPEND HAMMY_C_FLAGS_DEBUGLIKE /Od /RTC1 /GS) + list(APPEND HAMMY_C_FLAGS_OPTIMIZED /O2 /GS /guard:cf) + if(HAMMY_WERROR) + list(APPEND HAMMY_C_FLAGS_DEBUGLIKE ${HAMMY_WERROR_FLAG}) + endif() + if(HAMMY_ENABLE_ANALYZER) + list(APPEND HAMMY_ANALYZER_FLAGS /analyze) + endif() + if(HAMMY_ENABLE_SANITIZERS) + list(APPEND HAMMY_SANITIZER_FLAGS /fsanitize=address) + endif() +else() + set(HAMMY_WERROR_FLAG -Werror) + # Portable warning set, memory-safety first. + hammy_append_supported_c_flags(HAMMY_C_WARNINGS + -Wall + -Wextra + -Wpedantic + -Wshadow + -Wconversion + -Wsign-conversion + -Wcast-qual + -Wcast-align + -Wstrict-prototypes + -Wmissing-prototypes + -Wold-style-definition + -Wbad-function-cast + -Wwrite-strings + -Wformat=2 + -Wnull-dereference + -Wdouble-promotion + -Wfloat-equal + -Wvla + -Wstack-protector + -Wpointer-arith + -Wundef + -Winit-self + -Wmissing-include-dirs + ) + + if(CMAKE_C_COMPILER_ID MATCHES "GNU") + # GCC-only diagnostics. The -W*=N forms ask for the strictest level: + # more false positives, but they catch out-of-bounds writes, dangling + # pointers and double frees that -Wall/-Wextra walk straight past. + hammy_append_supported_c_flags(HAMMY_C_WARNINGS + -Warray-bounds=2 + -Wstringop-overflow=4 + -Wstringop-truncation + -Wformat-overflow=2 + -Wformat-truncation=2 + -Wuse-after-free=3 + -Wdangling-pointer=2 + -Wfree-nonheap-object + -Walloc-zero + -Walloca + -Wduplicated-cond + -Wduplicated-branches + -Wlogical-op + -Wjump-misses-init + -Wtrampolines + -Wflex-array-member-not-at-end + ) + # -fanalyzer is a whole-path symbolic execution pass (leaks, double + # free, use-after-free, NULL derefs across function boundaries). + if(HAMMY_ENABLE_ANALYZER) + hammy_append_supported_c_flags(HAMMY_ANALYZER_FLAGS + -fanalyzer-verbosity=${ANALYZER_VERBOSITY} + -fanalyzer + ) + endif() + elseif(CMAKE_C_COMPILER_ID MATCHES "Clang") + # Clang-only diagnostics. Clang has no in-compiler equivalent of + # -fanalyzer, so the Analyzer config is simply an uninstrumented Debug + # build here — which is exactly the base `scan-build cmake --build + # build-analyzer` wants. + hammy_append_supported_c_flags(HAMMY_C_WARNINGS + -Warray-bounds-pointer-arithmetic + -Wconditional-uninitialized + -Wshift-sign-overflow + -Wassign-enum + -Wcomma + -Wloop-analysis + -Wthread-safety + -Wover-aligned + ) + endif() + + # -Og keeps the code steppable while still running the optimizer passes + # that -Wmaybe-uninitialized / -Wstringop-* rely on; at -O0 those warnings + # go quiet. -fno-omit-frame-pointer buys readable sanitizer backtraces. + hammy_append_supported_c_flags(HAMMY_C_FLAGS_DEBUGLIKE + -Og + -g3 + -fno-omit-frame-pointer + ) + if(HAMMY_WERROR) + list(APPEND HAMMY_C_FLAGS_DEBUGLIKE ${HAMMY_WERROR_FLAG}) + endif() + + if(HAMMY_ENABLE_HARDENING) + hammy_append_supported_c_flags(HAMMY_C_FLAGS_DEBUGLIKE + -fstack-protector-strong + ) + # _FORTIFY_SOURCE needs an optimized build to see through the buffer + # sizes, and it fights ASan's interceptors, so it is Release-only. + hammy_append_supported_c_flags(HAMMY_C_FLAGS_OPTIMIZED + -U_FORTIFY_SOURCE + -D_FORTIFY_SOURCE=3 + -fstack-protector-strong + -fstack-clash-protection + ) + hammy_append_supported_instrument_flags(HAMMY_INSTRUMENT_COMPILE + -fcf-protection=full # x86_64 CET + -mbranch-protection=standard # aarch64 BTI/PAC + ) + hammy_append_supported_link_flags(HAMMY_INSTRUMENT_LINK + "LINKER:-z,relro" + "LINKER:-z,now" + "LINKER:-z,noexecstack" + ) + endif() + + if(HAMMY_ENABLE_SANITIZERS) + # Everything that composes with ASan. LeakSanitizer is not listed + # because ASan already includes it where it is supported, and + # -fsanitize=leak cannot be combined with -fsanitize=address. + # -fno-sanitize-recover makes UB abort instead of printing and + # continuing, so a bad shift or overflow cannot be ignored in CI. + # pointer-compare/pointer-subtract additionally need + # ASAN_OPTIONS=detect_invalid_pointer_pairs=2 at run time. + hammy_append_supported_instrument_flags(HAMMY_SANITIZER_FLAGS + -fsanitize=address + -fsanitize-address-use-after-scope + -fsanitize=pointer-compare + -fsanitize=pointer-subtract + -fsanitize=undefined + -fsanitize=bounds-strict + -fno-sanitize-recover=undefined + -fno-omit-frame-pointer + ) + endif() +endif() + +# Each instrumentation set belongs to exactly one configuration. Keeping them in +# separate configs is the point of the Analyzer build, so never emit both. +if(HAMMY_SANITIZER_FLAGS) + list(APPEND HAMMY_INSTRUMENT_COMPILE "$<${HAMMY_IS_SANITIZED}:${HAMMY_SANITIZER_FLAGS}>") + list(APPEND HAMMY_INSTRUMENT_LINK "$<${HAMMY_IS_SANITIZED}:${HAMMY_SANITIZER_FLAGS}>") +endif() + +if(HAMMY_ENABLE_LTO) + include(CheckIPOSupported) + check_ipo_supported(RESULT HAMMY_IPO_SUPPORTED OUTPUT HAMMY_IPO_ERROR) + if(NOT HAMMY_IPO_SUPPORTED) + message(WARNING "LTO requested but unsupported by this toolchain: ${HAMMY_IPO_ERROR}") + endif() +endif() + +# --------------------------------------------------------- +# Concord (Discord API wrapper), fetched from the dev branch +# +# Concord ships a hand-written Makefile, not a CMake build, so this is a +# two-stage arrangement: FetchContent clones it at configure time (recursively, +# because its cog-utils/carray dependencies are submodules), and ExternalProject +# drives `make` / `make install` at build time into a private prefix. If a +# future dev branch grows a CMakeLists.txt, FetchContent_MakeAvailable picks it +# up on its own and the Makefile path below is skipped. +# +# libcurl and pthreads are Concord's own dependencies, not ours; they are found +# here only so the static library has something to resolve against. +# --------------------------------------------------------- +find_package(Threads REQUIRED) +find_package(CURL REQUIRED) + +include(FetchContent) +include(ExternalProject) + +FetchContent_Declare( + concord + GIT_REPOSITORY https://github.com/Cogmasters/concord.git + GIT_TAG dev + GIT_SHALLOW TRUE + GIT_PROGRESS TRUE +) +# Populates concord_SOURCE_DIR, and calls add_subdirectory() only if upstream +# actually provides a CMakeLists.txt. +FetchContent_MakeAvailable(concord) + +if(TARGET discord) + add_library(concord::concord ALIAS discord) + set(HAMMY_CONCORD_EXTERNAL "") +elseif(TARGET concord) + add_library(concord::concord ALIAS concord) + set(HAMMY_CONCORD_EXTERNAL "") +else() + if(MSVC) + message(FATAL_ERROR + "Concord's Makefile build needs a POSIX toolchain (make, sh). " + "Build under MinGW/MSYS2 or WSL, or point CMake at a prebuilt libdiscord.") + endif() + find_program(HAMMY_MAKE_EXECUTABLE NAMES gmake make REQUIRED + DOC "make(1) used to build the vendored Concord") + + set(CONCORD_PREFIX "${CMAKE_BINARY_DIR}/_deps/concord-install") + set(CONCORD_INCLUDE_DIR "${CONCORD_PREFIX}/include") + set(CONCORD_LIBRARY "${CONCORD_PREFIX}/lib/${CMAKE_STATIC_LIBRARY_PREFIX}discord${CMAKE_STATIC_LIBRARY_SUFFIX}") + # An imported target's include directory has to exist when CMake generates, + # which is before Concord has been built. + file(MAKE_DIRECTORY "${CONCORD_INCLUDE_DIR}") + + # Concord gets our instrumentation but not our warning set: ASan only sees a + # bug if the translation unit that owns the memory was compiled with it, and + # Concord allocates plenty that our code then touches. CFLAGS goes through + # the environment rather than the make command line because Concord's + # Makefile appends its own -I flags to it; a command-line assignment would + # override those and break the build. + set(CONCORD_CFLAGS "-O2 -g $") + + ExternalProject_Add(concord_external + SOURCE_DIR "${concord_SOURCE_DIR}" + DOWNLOAD_COMMAND "" # FetchContent already cloned it + UPDATE_COMMAND "" + CONFIGURE_COMMAND "" + BUILD_IN_SOURCE TRUE # upstream's Makefile has no out-of-tree mode + BUILD_COMMAND ${CMAKE_COMMAND} -E env "CC=${CMAKE_C_COMPILER}" "CFLAGS=${CONCORD_CFLAGS}" + ${HAMMY_MAKE_EXECUTABLE} + INSTALL_COMMAND ${HAMMY_MAKE_EXECUTABLE} install "PREFIX=${CONCORD_PREFIX}" + BUILD_BYPRODUCTS "${CONCORD_LIBRARY}" + USES_TERMINAL_BUILD TRUE + LOG_BUILD TRUE + LOG_INSTALL TRUE + LOG_OUTPUT_ON_FAILURE TRUE + ) + + add_library(concord::concord STATIC IMPORTED GLOBAL) + set_target_properties(concord::concord PROPERTIES + IMPORTED_LOCATION "${CONCORD_LIBRARY}" + # Imported targets' interface includes are -isystem by default, so + # Concord's headers never trip our warning set. + INTERFACE_INCLUDE_DIRECTORIES "${CONCORD_INCLUDE_DIR}" + ) + target_link_libraries(concord::concord INTERFACE CURL::libcurl Threads::Threads) + # Imported targets cannot carry add_dependencies(); the consumer does. + set(HAMMY_CONCORD_EXTERNAL concord_external) +endif() + +# --------------------------------------------------------- +# Output directories +# --------------------------------------------------------- +set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/bin) +set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) +set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/lib) + +foreach(OUTPUTCONFIG IN LISTS HAMMY_BUILD_TYPES) + string(TOUPPER ${OUTPUTCONFIG} OUTPUTCONFIG_UPPER) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/bin) + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/lib) + set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${OUTPUTCONFIG_UPPER} ${CMAKE_BINARY_DIR}/lib) +endforeach() + +# --------------------------------------------------------- +# hammy +# --------------------------------------------------------- +file(GLOB_RECURSE HAMMY_SRC CONFIGURE_DEPENDS src/*.c) +add_executable(hammy ${HAMMY_SRC}) + +target_include_directories(hammy PRIVATE + ${PROJECT_SOURCE_DIR}/include +) + +target_link_libraries(hammy PRIVATE concord::concord) +if(HAMMY_CONCORD_EXTERNAL) + add_dependencies(hammy ${HAMMY_CONCORD_EXTERNAL}) +endif() + +target_compile_options(hammy PRIVATE + "${HAMMY_C_WARNINGS}" + "${HAMMY_INSTRUMENT_COMPILE}" + "$<${HAMMY_IS_DEBUGLIKE}:${HAMMY_C_FLAGS_DEBUGLIKE}>" + "$<$:${HAMMY_C_FLAGS_OPTIMIZED}>" + # The static analyzer runs on our C sources only, and never alongside the + # sanitizers. + "$<$:${HAMMY_ANALYZER_FLAGS}>" + # Strict is Debug with the warning set turned into a build gate. + "$<$:${HAMMY_WERROR_FLAG}>" +) +target_link_options(hammy PRIVATE "${HAMMY_INSTRUMENT_LINK}") + +if(HAMMY_ENABLE_LTO AND HAMMY_IPO_SUPPORTED) + set_target_properties(hammy PROPERTIES + INTERPROCEDURAL_OPTIMIZATION_RELEASE ON + INTERPROCEDURAL_OPTIMIZATION_RELWITHDEBINFO ON + INTERPROCEDURAL_OPTIMIZATION_MINSIZEREL ON + ) +endif() + +# CMAKE_C_EXTENSIONS is OFF, so -std=c23 hides everything outside the standard; +# Concord's headers and our own use of POSIX threads and sockets need these. +target_compile_definitions(hammy PRIVATE + _POSIX_C_SOURCE=200809L + _DEFAULT_SOURCE +) + +set_target_properties(hammy PROPERTIES OUTPUT_NAME "hammy") + +# --------------------------------------------------------- +# Configuration summary +# --------------------------------------------------------- +message(STATUS "hammy: build type ${CMAKE_BUILD_TYPE}") +message(STATUS "hammy: compiler ${CMAKE_C_COMPILER_ID} ${CMAKE_C_COMPILER_VERSION} (C${CMAKE_C_STANDARD})") +message(STATUS "hammy: warnings-as-err ${HAMMY_WERROR} (always on in Strict)") +message(STATUS "hammy: sanitizers [Debug/Strict] ${HAMMY_SANITIZER_FLAGS}") +message(STATUS "hammy: static analyzer [Analyzer] ${HAMMY_ANALYZER_FLAGS}") +message(STATUS "hammy: hardening ${HAMMY_ENABLE_HARDENING}") +message(STATUS "hammy: LTO ${HAMMY_ENABLE_LTO}") +message(STATUS "hammy: concord ${concord_SOURCE_DIR} (branch dev)") +if(CMAKE_BUILD_TYPE STREQUAL "Analyzer" AND NOT HAMMY_ANALYZER_FLAGS) + message(STATUS "hammy: NOTE - Analyzer config has no static analyzer on " + "${CMAKE_C_COMPILER_ID}; use scan-build over this build tree") +endif() \ No newline at end of file diff --git a/README.md b/README.md index 88a8330..f639109 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,10 @@ A Discord Bot for Ham/Amateur Radio +# AI Disclosure +AI tools were used responsibly in the creation of this project, mainly for cmakelists off a pre-made template. It has also been used for minor things like functions, reviews, commits, etc. +Contributors are responsible for their commits, regardless of the usage of AI or not. + # Legal Licensed under the GPLv3 license, with the exception of other third-party libraries/code which may be included under compatible licenses.