From 1e735ec2d41e0c1ee20b6f6ce57fce68b1a3722a Mon Sep 17 00:00:00 2001 From: Andreas Gruber Date: Fri, 1 Mar 2024 23:28:07 +0100 Subject: [PATCH 01/11] Add devcontainer --- .devcontainer/Dockerfile | 36 ++++++++++++++++++++++++ .devcontainer/devcontainer.json | 50 +++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) create mode 100644 .devcontainer/Dockerfile create mode 100644 .devcontainer/devcontainer.json diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile new file mode 100644 index 0000000..c8ce354 --- /dev/null +++ b/.devcontainer/Dockerfile @@ -0,0 +1,36 @@ +ARG BASE_IMAGE=debian:bookworm-slim + +FROM ${BASE_IMAGE} + +ARG USER=build +ARG USER_ID=1000 +ARG USER_GID=1000 + +RUN apt-get clean \ + && apt-get update \ + && apt-get install -y --no-install-recommends \ + sudo \ + unzip \ + locales \ + locales-all + +ENV LANG en_US.UTF-8 +ENV LANGUAGE en_US:en +ENV LC_ALL en_US.UTF-8 +ENV TZ Europe/Vienna + +RUN groupadd --gid "$USER_GID" "$USER" && \ + useradd \ + --uid $USER_ID \ + --gid $USER_GID \ + -m --home-dir /home/$USER \ + $USER && \ + echo "$USER ALL=(ALL) NOPASSWD:ALL" >> /etc/sudoers + +RUN apt-get -y install \ + build-essential ninja-build git git-lfs libssl-dev file zip curl python3-pip && \ + curl -o "/tmp/cmake_inst.sh" "https://cmake.org/files/v3.23/cmake-3.23.0-linux-x86_64.sh" && \ + chmod +x /tmp/cmake_inst.sh && /tmp/cmake_inst.sh --skip-license --prefix=/usr && rm /tmp/cmake_inst.sh && \ + pip3 install --break-system-packages conan + +ENV SHELL=/bin/bash diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 0000000..c036713 --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,50 @@ +// For format details, see https://aka.ms/devcontainer.json. For config options, see the README at: +{ + // Uncomment the used compose files + "build": { "dockerfile": "Dockerfile" }, + + // Custom mounts can be done here or in docker-compose.custom.yml + "mounts": [ + // mount a local directory + // "source=c:/work,target=/work2,type=bind" + "source=${localWorkspaceFolder},target=/work/${localWorkspaceFolderBasename},type=bind" + ], + +// ------------- end of typical modifications --------------- + + // Name shown in the bottom-left corner in VS-Code +// "name": "winccoa-api", + + "containerUser": "build", + + // Configure tool-specific properties. + "customizations": { + // Configure properties specific to VS Code. + "vscode": { + // this list should reflect the list of recommended extensions from the .vscode/extensions.json + "extensions": [ + "eamodio.gitlens", + "ms-vscode.cmake-tools", + "ms-vscode.cpptools-extension-pack" + ] + } + }, + + // These features are only working on Debian containers and need to be enabled manually + /* + "features": { + "ghcr.io/devcontainers/features/docker-outside-of-docker:1": { + "version": "latest", + "moby": false + }, + "ghcr.io/stuartleeks/dev-container-features/shell-history:0": { + "version": "latest" + } + }, + */ + + // this is the path inside the devcontainer where the repository is mounted on + // it needs to be created before starting the first DevContainer to avoid errors + "workspaceFolder": "/work/${localWorkspaceFolderBasename}" + +} From 0c29b2d861b01fc4c8531ff8d9465c063ed3c375 Mon Sep 17 00:00:00 2001 From: Andreas Gruber Date: Fri, 1 Mar 2024 23:28:53 +0100 Subject: [PATCH 02/11] Add conan profiles for Windows --- config/conan/msvc2022vc142.txt | 10 ++++++++++ config/conan/msvc2022vc143.txt | 10 ++++++++++ 2 files changed, 20 insertions(+) create mode 100644 config/conan/msvc2022vc142.txt create mode 100644 config/conan/msvc2022vc143.txt diff --git a/config/conan/msvc2022vc142.txt b/config/conan/msvc2022vc142.txt new file mode 100644 index 0000000..5670e8a --- /dev/null +++ b/config/conan/msvc2022vc142.txt @@ -0,0 +1,10 @@ +[settings] +arch=x86_64 +build_type=Debug +compiler=msvc +#compiler.cppstd=14 +compiler.runtime=dynamic +compiler.version=192 +os=Windows +[conf] +tools.microsoft.msbuild:vs_version = 17 \ No newline at end of file diff --git a/config/conan/msvc2022vc143.txt b/config/conan/msvc2022vc143.txt new file mode 100644 index 0000000..221f251 --- /dev/null +++ b/config/conan/msvc2022vc143.txt @@ -0,0 +1,10 @@ +[settings] +arch=x86_64 +build_type=RelWithDebInfo +compiler=msvc +#compiler.cppstd=14 +compiler.runtime=dynamic +compiler.version=193 +os=Windows +[conf] +tools.microsoft.msbuild:vs_version = 17 \ No newline at end of file From cefb3679af71fb27b28eee114f2dfc3f875a92d1 Mon Sep 17 00:00:00 2001 From: Andreas Gruber Date: Fri, 1 Mar 2024 23:30:16 +0100 Subject: [PATCH 03/11] C++ related gitignore file --- .gitignore | 48 +++++++++++++++++++++++++++++++++++++----------- 1 file changed, 37 insertions(+), 11 deletions(-) diff --git a/.gitignore b/.gitignore index 46f42f8..18829bf 100644 --- a/.gitignore +++ b/.gitignore @@ -1,11 +1,37 @@ -CMakeLists.txt.user -CMakeCache.txt -CMakeFiles -CMakeScripts -Testing -Makefile -cmake_install.cmake -install_manifest.txt -compile_commands.json -CTestTestfile.cmake -_deps +# Prerequisites +*.d + +# Compiled Object files +*.slo +*.lo +*.o +*.obj + +# Precompiled Headers +*.gch +*.pch + +# Compiled Dynamic libraries +*.so +*.dylib +*.dll + +# Fortran module files +*.mod +*.smod + +# Compiled Static libraries +*.lai +*.la +*.a +*.lib + +# Executables +*.exe +*.out +*.app + +# build directory +build/ + +CMakeUserPresets.json From e805bdf4d8e5b0e0fe5327a0d07883f172092245 Mon Sep 17 00:00:00 2001 From: Andreas Gruber Date: Fri, 1 Mar 2024 23:31:23 +0100 Subject: [PATCH 04/11] Common cmake functions and macros --- cmake/general.cmake | 52 +++++++++++++++++++++++++++++++++++++++++++ cmake/packaging.cmake | 26 ++++++++++++++++++++++ cmake/testing.cmake | 48 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 126 insertions(+) create mode 100644 cmake/general.cmake create mode 100644 cmake/packaging.cmake create mode 100644 cmake/testing.cmake diff --git a/cmake/general.cmake b/cmake/general.cmake new file mode 100644 index 0000000..3d77290 --- /dev/null +++ b/cmake/general.cmake @@ -0,0 +1,52 @@ +if (${CMAKE_CURRENT_LIST_FILE}_imported) + return() # already imported +endif() +set (${CMAKE_CURRENT_LIST_FILE}_imported 1) + +# Sets a CMake cache variable with a default value if an environment variable is not defined. +# +# Usage: +# set_cache_var(VAR_NAME DEF_VAL DESC) +# +# Parameters: +# VAR_NAME - The name of the CMake cache variable. +# DEF_VAL - The default value to be used if the environment variable is not defined. +# DESC - A description for the cache variable. +macro(set_cache_var VAR_NAME DEF_VAL DESC) + if(NOT DEFINED ENV{${VAR_NAME}}) + set(${VAR_NAME} "${DEF_VAL}" CACHE STRING ${DESC} FORCE) + else() + set(${VAR_NAME} "$ENV{${VAR_NAME}}" CACHE STRING ${DESC} FORCE) + endif() +endmacro() + +# get information on Linux based systems about the host OS +# https://stackoverflow.com/questions/55165922/is-there-a-way-to-differentiate-between-fedora-and-centos-on-cmake +function(get_linux_hostos_release_information) + file(STRINGS /etc/os-release distroname REGEX "^NAME=") + string(REGEX REPLACE "NAME=\"(.*)\"" "\\1" distroname "${distroname}") + file(STRINGS /etc/os-release distroid REGEX "^ID=") + string(REGEX REPLACE "ID=(.*)" "\\1" distroid "${distroid}") + string(REPLACE "\"" "" distroid "${distroid}") + file(STRINGS /etc/os-release distroversion REGEX "^VERSION_ID=") + string(REGEX REPLACE "VERSION_ID=\"(.*)\"" "\\1" distroversion "${distroversion}") + + set(LINUX_DISTRO_NAME "${distroname}" PARENT_SCOPE) + set(LINUX_DISTRO_ID ${distroid} PARENT_SCOPE) + set(LINUX_DISTRO_VERSION "${distroversion}" PARENT_SCOPE) +endfunction() + +message(STATUS "Checking host OS...") +if ( ${CMAKE_SYSTEM_NAME} STREQUAL Linux ) + get_linux_hostos_release_information() + message(STATUS "Running on host ${LINUX_DISTRO_NAME} ${LINUX_DISTRO_VERSION} (${LINUX_DISTRO_ID})") + if ( ${LINUX_DISTRO_ID} STREQUAL sles ) + set(SLES 1) + elseif ( ${LINUX_DISTRO_ID} STREQUAL ubuntu ) + set(UBUNTU 1) + elseif ( ${LINUX_DISTRO_ID} STREQUAL debian ) + set(DEBIAN 1) + elseif ( ${LINUX_DISTRO_ID} STREQUAL rhel OR ${LINUX_DISTRO_ID} STREQUAL centos OR ${LINUX_DISTRO_ID} STREQUAL ol ) + set(REDHAT 1) + endif() +endif() diff --git a/cmake/packaging.cmake b/cmake/packaging.cmake new file mode 100644 index 0000000..8c99eb9 --- /dev/null +++ b/cmake/packaging.cmake @@ -0,0 +1,26 @@ +include(${CMAKE_CURRENT_LIST_DIR}/general.cmake) + +if (WIN32) + set(CPACK_GENERATOR "ZIP") +elseif(DEBIAN) + set(CPACK_GENERATOR "DEB") +elseif(REDHAT) + set(CPACK_GENERATOR "RPM") +endif() + +if (WIN32) + set(CPACK_PACKAGING_INSTALL_PREFIX "") +endif() + +# Set the package dependencies +set(CPACK_DEBIAN_PACKAGE_SHLIBDEPS ON) +set(CPACK_RPM_PACKAGE_AUTOREQ ON) + +set(CPACK_STRIP_FILES ON) + +if (NOT CPACK_PACKAGE_CONTACT) + message(WARNING "CPACK_PACKAGE_CONTACT is not set, using default value") + set(CPACK_PACKAGE_CONTACT "undefined") +endif() + +include(CPack) diff --git a/cmake/testing.cmake b/cmake/testing.cmake new file mode 100644 index 0000000..98baeaa --- /dev/null +++ b/cmake/testing.cmake @@ -0,0 +1,48 @@ +# Define a macro with the name 'add_gtest_with_xml' that takes three parameters: TARGET, TARGET_LIB and SOURCES +# TARGET is the target name which runs the original code +# TARGET_LIB is the name of the linked library which contains all production code and is also linked by TARGET +# the test target is called 'test_${TARGET}' automatically and also links to TARGET_LIB +# Usage: +# set(MY_SOURCES source1.cpp source2.cpp source3.cpp) +# add_gtest_with_xml(MyTarget MyTargetLib "${MY_SOURCES}") +macro(add_gtest_with_xml TARGET TARGET_LIB SOURCES ) + + # Formulate the test target name + set(TESTTARGET test_${TARGET}) + + # Define the executable for the test using the provided SOURCES + add_executable(${TESTTARGET} ${SOURCES}) + + # Determine the XML file path for test results + set(XMLFILE "${CMAKE_CURRENT_BINARY_DIR}/c${TESTTARGET}.xml") + + # Retrieve the linked libraries from the original target and apply them to the test target + get_target_property(${TARGET}_LINKLIBS ${TARGET} LINK_LIBRARIES) + set_target_properties(${TESTTARGET} PROPERTIES LINK_LIBRARIES "${${TARGET}_LINKLIBS}") + + # Retrieve the include directories from the original target and apply them to the test target + get_target_property(${TARGET}_INCLUDES ${TARGET} INCLUDE_DIRECTORIES) + if(NOT "${${TARGET}_INCLUDES}" STREQUAL "${TARGET}_INCLUDES-NOTFOUND") + set_target_properties(${TESTTARGET} PROPERTIES INCLUDE_DIRECTORIES "${${TARGET}_INCLUDES}") + endif() + + # Link the necessary libraries with the test target + target_link_libraries(${TESTTARGET} + PRIVATE + GTest::gtest_main + $) + + # Add the test executable to CTest with a custom command for XML output + add_test(NAME ${TESTTARGET} COMMAND ${TESTTARGET} --gtest_output=xml:${XMLFILE}) + + # Pre-write to the XML file with a default content indicating a failure scenario. This ensures the file exists. + file(WRITE ${XMLFILE} "The executable did not shutdown orderly and no result xml was written. This means that the test has either crashed or called exit/abort/terminate") + +endmacro() + +if (CUSTOMENABLEINCLUDE_GTEST) + return() +endif() + +find_package(GTest REQUIRED) +enable_testing() From a804df5009b7c1637899ec90940d2a39b7807648 Mon Sep 17 00:00:00 2001 From: Andreas Gruber Date: Fri, 1 Mar 2024 23:31:57 +0100 Subject: [PATCH 05/11] Add demo project --- demo/CMakeLists.txt | 29 +++++++++++++++++++++++++++++ demo/calc.cpp | 6 ++++++ demo/calc.h | 7 +++++++ demo/conanfile.py | 16 ++++++++++++++++ demo/main.cpp | 7 +++++++ demo/tests/CMakeLists.txt | 5 +++++ demo/tests/test_calc.cpp | 21 +++++++++++++++++++++ 7 files changed, 91 insertions(+) create mode 100644 demo/CMakeLists.txt create mode 100644 demo/calc.cpp create mode 100644 demo/calc.h create mode 100644 demo/conanfile.py create mode 100644 demo/main.cpp create mode 100644 demo/tests/CMakeLists.txt create mode 100644 demo/tests/test_calc.cpp diff --git a/demo/CMakeLists.txt b/demo/CMakeLists.txt new file mode 100644 index 0000000..cc6cd3b --- /dev/null +++ b/demo/CMakeLists.txt @@ -0,0 +1,29 @@ +cmake_minimum_required(VERSION 3.14) + +project(demo + VERSION 0.1.0 +) + +include(../cmake/packaging.cmake) +include(../cmake/testing.cmake) + +set(TARGET demo) + +set(SOURCES +main.cpp +) + +set(OBJLIB_SOURCES +calc.cpp +) + +add_library(${TARGET}Lib OBJECT ${OBJLIB_SOURCES}) +#target_include_directories(${TARGET}Lib PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) + +add_executable(${TARGET} ${SOURCES}) +target_include_directories(${TARGET} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}) +target_link_libraries(${TARGET} PRIVATE $) + +add_subdirectory(tests) + +install(TARGETS ${TARGET} RUNTIME) diff --git a/demo/calc.cpp b/demo/calc.cpp new file mode 100644 index 0000000..5c428fb --- /dev/null +++ b/demo/calc.cpp @@ -0,0 +1,6 @@ +#include "calc.h" + +// Implementation of the add function +int add(int a, int b) { + return a + b; +} diff --git a/demo/calc.h b/demo/calc.h new file mode 100644 index 0000000..f5d1d6d --- /dev/null +++ b/demo/calc.h @@ -0,0 +1,7 @@ +#ifndef CALC_H_ +#define CALC_H_ + +// Function prototype +int add(int a, int b); + +#endif // CALC_H_ diff --git a/demo/conanfile.py b/demo/conanfile.py new file mode 100644 index 0000000..9d5a57c --- /dev/null +++ b/demo/conanfile.py @@ -0,0 +1,16 @@ +import os +from conan import ConanFile, tools +from conan.tools.cmake import CMakeDeps, CMakeToolchain, cmake_layout + +class MyConanRecipe(ConanFile): + settings = "os", "compiler", "build_type", "arch" + generators = "CMakeDeps" + + def requirements(self): + self.requires("gtest/1.14.0") + + def generate(self): + tc = CMakeToolchain(self) + tc.cache_variables["CMAKE_INSTALL_PREFIX"] = os.getcwd() + "/../install" # To make sure we have deterministic behavior + tc.cache_variables["CPACK_OUTPUT_FILE_PREFIX"] = "build/deploy" # Relative to the source folder + tc.generate() diff --git a/demo/main.cpp b/demo/main.cpp new file mode 100644 index 0000000..18163c7 --- /dev/null +++ b/demo/main.cpp @@ -0,0 +1,7 @@ +#include +#include "calc.h" + +int main() { + std::cout << "Adding 3 and 4 gives: " << add(3, 4) << std::endl; + return 0; +} diff --git a/demo/tests/CMakeLists.txt b/demo/tests/CMakeLists.txt new file mode 100644 index 0000000..c7c0e3f --- /dev/null +++ b/demo/tests/CMakeLists.txt @@ -0,0 +1,5 @@ +set(SOURCES +test_calc.cpp +) + +add_gtest_with_xml(${TARGET} ${TARGET}Lib ${SOURCES}) diff --git a/demo/tests/test_calc.cpp b/demo/tests/test_calc.cpp new file mode 100644 index 0000000..9c66c78 --- /dev/null +++ b/demo/tests/test_calc.cpp @@ -0,0 +1,21 @@ +#include "gtest/gtest.h" +#include "calc.h" + +// Define a test case for the add function +TEST(AddFunctionTest, HandlesPositiveInput) { + EXPECT_EQ(add(1, 2), 3); +} + +TEST(AddFunctionTest, HandlesNegativeInput) { + EXPECT_EQ(add(-1, -2), -3); +} + +TEST(AddFunctionTest, HandlesMixedInput) { + EXPECT_EQ(add(-1, 2), 1); +} + +// The main function running the tests +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From ac297d035d58a139a69ec13217741e5ee9a06678 Mon Sep 17 00:00:00 2001 From: Andreas Gruber Date: Fri, 1 Mar 2024 23:32:46 +0100 Subject: [PATCH 06/11] Add Matrix generator --- pipeline/createMatrix/createMatrix.ps1 | 182 +++++++++++++++++++++++++ 1 file changed, 182 insertions(+) create mode 100644 pipeline/createMatrix/createMatrix.ps1 diff --git a/pipeline/createMatrix/createMatrix.ps1 b/pipeline/createMatrix/createMatrix.ps1 new file mode 100644 index 0000000..5e3b9ce --- /dev/null +++ b/pipeline/createMatrix/createMatrix.ps1 @@ -0,0 +1,182 @@ +param( + [string]$basedir, + [string]$prefix = "build-config", + [string]$centralFilePath, + [string]$mergeKeyFile, + [switch]$verbose, + [switch]$azpMatrix, + [switch]$githubMatrix +) + +function UpdateConanProperty($yamlContent, $config, $filedir) { + # Check for the existence of either conanfile.txt or conanfile.py + $conanfileTxtExists = Test-Path (Join-Path $filedir "conanfile.txt") + $conanfilePyExists = Test-Path (Join-Path $filedir "conanfile.py") + + if ($null -eq $yamlContent[$config]['conan'] -and ($conanfileTxtExists -or $conanfilePyExists)) { + Write-Host "Adding conan: true to $config as conanfile.txt or conanfile.py exists." + $yamlContent[$config]['conan'] = $true + } +} + +function AddRelativeDirectory($yamlContent, $config, $relativedir) { + $yamlContent[$config]['directory'] = $relativedir +} + +function UpdateProperties($yamlContent, $config, $property, $keyName, $replace) { + if ($null -ne $property) { + if ($yamlContent[$config].ContainsKey($keyName)) { + if ($replace) { + $yamlContent[$config][$keyName] = $yamlContent[$config][$keyName] + } else { + $yamlContent[$config][$keyName] = "$property " + $yamlContent[$config][$keyName] + } + } else { + $yamlContent[$config][$keyName] = $property + } + } +} + +function ProcessYamlFile($file, $basedir) { + $filedir = Split-Path -Parent $file + if ($basedir -eq ".") { + $basedir = (Get-Location).Path + } + $relativedir = $filedir.Substring($basedir.Length).TrimStart('\').TrimStart('/') + + Write-Host "Processing: $file" + + # Check for CMakeLists.txt in the same directory + if (-not (Test-Path (Join-Path $filedir "CMakeLists.txt"))) { + Write-Host "##vso[task.logissue type=warning]CMakeLists.txt not found in directory: $filedir" + return $null + } + + $mergeKeyContent = $null + if ($mergeKeyFile) { + if(Test-Path $mergeKeyFile) { + $mergeKeyContent = Get-Content $mergeKeyFile -Raw + } + else { + Write-Host "##vso[task.logissue type=error]Error loading merge keys file: $file" + return $null + } + } + + try { + # Merge the content of the merge-key file with the current file's content + $fileContent = Get-Content $file -Raw + if ($mergeKeyContent) { + $fileContent = $fileContent + "`n" + $mergeKeyContent # Append the optional file content to the current file content + } + $yamlContent = ConvertFrom-Yaml -UseMergingParser -Yaml $fileContent + } catch { + Write-Host "Error processing file $file : $($_.Exception.Message)" + return $null + } + + $allBuildConfigs = @{} + + # Assume the first non-merge key is the package name, and configuration are under 'configuration' + $packageKey = $yamlContent.keys | Where-Object { $_ -notlike "_*" } | Select-Object -First 1 + Write-Host $packageKey + if ($null -ne $packageKey -and $yamlContent[$packageKey].ContainsKey('configuration')) { + $configuration = $yamlContent[$packageKey]['configuration'] + + $packageNameKey = $yamlContent[$packageKey].Keys | Where-Object { $_ -ieq 'packagename' } | Select-Object -First 1 + $projectNameKey = $yamlContent[$packageKey].Keys | Where-Object { $_ -ieq 'projectname' } | Select-Object -First 1 + + $packageNameValue = if ($null -ne $packageNameKey) { $yamlContent[$packageKey][$packageNameKey] } else { $null } + $projectNameValue = if ($null -ne $projectNameKey) { $yamlContent[$packageKey][$projectNameKey] } else { $null } + + foreach ($configName in $configuration.keys) { + $fullName = "$($packageKey)_$($configName)" + $allBuildConfigs[$fullName] = $configuration[$configName] + + UpdateProperties -yamlContent $allBuildConfigs -config $fullName -property $packageNameValue -keyName 'packagename' -replace $true + UpdateProperties -yamlContent $allBuildConfigs -config $fullName -property $projectNameValue -keyName 'projectname' -replace $true + + UpdateConanProperty -yamlContent $allBuildConfigs -config $fullName -filedir $filedir + AddRelativeDirectory -yamlContent $allBuildConfigs -config $fullName -relativedir $relativedir + } + + return $allBuildConfigs + } else { + Write-Host "Expected 'configuration' key not found under package key: $packageKey" + return $null + } + +} + +# Check if the PowerShell-yaml module is available +if (-not (Get-Module -ListAvailable -Name powershell-yaml)) { + Write-Host "The powershell-yaml module is not installed. Please install it using 'Install-Module powershell-yaml'." + exit +} + +# Import PowerShell-yaml module +Import-Module powershell-yaml + +$WindowsBuildConfigs = @{} +$LinuxBuildConfigs = @{} + +Get-ChildItem -Path $basedir -Recurse -Filter "${prefix}*.yml" | ForEach-Object { + $yamlContent = ProcessYamlFile -file $_.FullName -basedir $basedir + $yamlContent | Out-String | Write-Host + if ($null -ne $yamlContent) { + foreach ($configName in $yamlContent.keys) { + if ($yamlContent[$configName].platform -eq 'win') { + $WindowsBuildConfigs[$configName] = $yamlContent[$configName] + } elseif (-Not $configName.StartsWith("_")) { + $LinuxBuildConfigs[$configName] = $yamlContent[$configName] + } + } + } +} + + + +$allYamlContents = $WindowsBuildConfigs + $LinuxBuildConfigs + +if ($githubMatrix) { + $githubBuildConfigs = @() + foreach ($config in $allYamlContents.GetEnumerator()) { + $config.Value["configName"] = $config.Name + $githubBuildConfigs += $config.Value + } + $allYamlContents = @{} + $allYamlContents["config"] = $githubBuildConfigs + if ($verbose) { + Write-Host "Github Build Configs (YAML):" + $allYamlContents | ConvertTo-Yaml | Out-String | Write-Host + Write-Host "Github Build Configs (JSON):" + $allYamlContents | ConvertTo-Json -Depth 10 | Out-String | Write-Host + } + + "matrixconfig=$($allYamlContents | ConvertTo-Json -Compress)" | Out-File -FilePath $env:GITHUB_OUTPUT -Encoding utf8 -Append +} + +if ($centralFilePath) { + $allYamlContents | ConvertTo-Yaml | Set-Content $centralFilePath + Write-Host "All configuration are saved in $centralFilePath." +} + +if ($azpMatrix) { + # Only write to the central file if $centralFilePath is set + + if ($verbose) { + Write-Host "Windows Build Configs (YAML):" + $WindowsBuildConfigs | ConvertTo-Yaml | Out-String | Write-Host + Write-Host "Linux Build Configs (YAML):" + $LinuxBuildConfigs | ConvertTo-Yaml | Out-String | Write-Host + + Write-Host "Windows Build Configs (JSON):" + $WindowsBuildConfigs | ConvertTo-Json -Depth 10 | Out-String | Write-Host + Write-Host "Linux Build Configs (JSON):" + $LinuxBuildConfigs | ConvertTo-Json -Depth 10 | Out-String | Write-Host + } + Write-Host "##vso[task.setvariable variable=WindowsBuildConfigs;isOutput=true]"($WindowsBuildConfigs | ConvertTo-Json -Compress) + Write-Host "##vso[task.setvariable variable=LinuxBuildConfigs;isOutput=true]"($LinuxBuildConfigs | ConvertTo-Json -Compress) +} + +Write-Host "Done." From b3ce72ca68cc3d5c66404628d9d5801702e3c53d Mon Sep 17 00:00:00 2001 From: Andreas Gruber Date: Fri, 1 Mar 2024 23:33:01 +0100 Subject: [PATCH 07/11] Add Matrix templates --- pipeline/createMatrix/mergekeys.yml | 54 +++++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 pipeline/createMatrix/mergekeys.yml diff --git a/pipeline/createMatrix/mergekeys.yml b/pipeline/createMatrix/mergekeys.yml new file mode 100644 index 0000000..f70b09e --- /dev/null +++ b/pipeline/createMatrix/mergekeys.yml @@ -0,0 +1,54 @@ +_win_msvc2022vc142: &_win_msvc2022vc142 + platform: 'win' + version: 'msvc2022vc142' + github_runs_on: 'windows-2022' + init_compiler: '"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64 -vcvars_ver=14.2' + +_win_msvc2022vc143: &_win_msvc2022vc143 + platform: 'win' + version: 'msvc2022vc143' + github_runs_on: 'windows-2022' + init_compiler: '"C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" amd64 -vcvars_ver=14.3' + +_win_msvc2022vc142_debug: &_win_msvc2022vc142_debug + <<: *_win_msvc2022vc142 + buildtype: 'Debug' + +_win_msvc2022vc142_release: &_win_msvc2022vc142_release + <<: *_win_msvc2022vc142 + buildtype: 'Release' + +_win_msvc2022vc142_relwithdebinfo: &_win_msvc2022vc142_relwithdebinfo + <<: *_win_msvc2022vc142 + buildtype: 'RelWithDebInfo' + +_win_msvc2022vc143_debug: &_win_msvc2022vc143_debug + <<: *_win_msvc2022vc143 + buildtype: 'Debug' + +_win_msvc2022vc143_release: &_win_msvc2022vc143_release + <<: *_win_msvc2022vc143 + buildtype: 'Release' + +_win_msvc2022vc143_relwithdebinfo: &_win_msvc2022vc143_relwithdebinfo + <<: *_win_msvc2022vc143 + buildtype: 'RelWithDebInfo' + +_debian_bookworm: &_debian_bookworm + platform: 'debian' + container_image: 'andygruber/cpp-multi-builder:debian-bookworm' + github_runs_on: 'ubuntu-latest' + +_debian_bullseye: &_debian_bullseye + platform: 'debian' + container_image: 'andygruber/cpp-multi-builder:debian-bullseye' + github_runs_on: 'ubuntu-latest' + +_debian_bookworm_relwithdebinfo: &_debian_bookworm_relwithdebinfo + <<: *_debian_bookworm + buildtype: 'RelWithDebInfo' + +_debian_bullseye_relwithdebinfo: &_debian_bullseye_relwithdebinfo + <<: *_debian_bullseye + buildtype: 'RelWithDebInfo' + \ No newline at end of file From 67d202e94da5b992be5da18242f7bd202c391919 Mon Sep 17 00:00:00 2001 From: Andreas Gruber Date: Fri, 1 Mar 2024 23:36:09 +0100 Subject: [PATCH 08/11] Add GitHub Action --- .github/workflows/build.yml | 271 ++++++++++++++++++++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..7271128 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,271 @@ +# This is a basic workflow to help you get started with Actions + +name: CI + +# Controls when the workflow will run +on: + # Triggers the workflow on push or pull request events but only for the "main" branch + push: + branches: [ "main" ] + pull_request: + branches: [ "main" ] + + # Allows you to run this workflow manually from the Actions tab + workflow_dispatch: + +# A workflow run is made up of one or more jobs that can run sequentially or in parallel +jobs: + # This workflow contains a single job called "build" + prepare: + # The type of runner that the job will run on + runs-on: ubuntu-latest + outputs: + matrixconfig: ${{ steps.generateMatrix.outputs.matrixconfig }} + # Steps represent a sequence of tasks that will be executed as part of the job + steps: + # Checks-out your repository under $GITHUB_WORKSPACE, so your job can access it + - uses: actions/checkout@v4 + + - name: Set build configs + shell: pwsh + id: generateMatrix + run: | + Install-Module -Name PowerShell-yaml -Scope CurrentUser -Force + ./pipeline/createMatrix/createMatrix.ps1 -basedir "." -verbose -githubMatrix -mergeKeyFile pipeline/createMatrix/mergekeys.yml + + # - name: Run bash script + # id: generateMatrix + # run: | + # #echo 'matrixconfig={"GenericDrivers_debian_3_19_Release":{"os":"ubuntu-latest","directory":"home/runner/work/build-winccoa-manager/build-winccoa-manager/driver","version":"3.19","platform":"debian","buildtype":"Release","conan":true,"container_image":"support/3.19/debian_x86_64:api","displayname":"Generic Drivers (3.19) Debian 11 Release"},"GenericDrivers_debian_3_20_Release":{"os":"ubuntu-latest","directory":"home/runner/work/build-winccoa-manager/build-winccoa-manager/driver","version":"3.20","platform":"debian","buildtype":"Release","conan":true,"container_image":"develop_3.x/debian_x86_64:api","displayname":"Generic Drivers (3.20) Debian 12 Release"}}' >> $GITHUB_OUTPUT + # echo 'matrixconfig={"config":[{"configName":"GenericDrivers_debian_3_19_Release","os":"ubuntu-latest","version":"3.19"},{"configName":"GenericDrivers_debian_3_20_Release","os":"ubuntu-latest","version":"3.20"}]}' >> $GITHUB_OUTPUT + + build: + name: 'Build ${{ matrix.config.configName }}' + needs: [prepare] + strategy: + matrix: ${{ fromJson(needs.prepare.outputs.matrixconfig) }} + runs-on: ${{ matrix.config.github_runs_on }} + container: + image: ${{ matrix.config.container_image }} + options: --user root + # credentials: + # username: ${{ secrets.DOCKERHUB_USER}} + # password: ${{ secrets.DOCKERHUB_PASSWORD}} + env: + BUILDDIR: build/make + INSTALLDIR: build/install + DEPLOYDIR: build/deploy + steps: + + - uses: actions/checkout@v4 + + - run: | + # for Conan a date is used to store the cache + if [ "${{matrix.config.conan}}" == "true" ]; then + echo "NOW=$(date +'%Y-%m-%dT-%H%M%S')" >> $GITHUB_OUTPUT + fi + shell: bash + id: initvars + name: Set Variables + + - uses: lukka/get-cmake@latest + with: + cmakeVersion: 3.23.0 + ninjaVersion: latest + useCloudCache: true + if: runner.os == 'Windows' + + - name: Install Conan + id: conan + uses: turtlebrowser/get-conan@main + if: runner.os == 'Windows' && matrix.config.conan + + - name: Restore cached Conan packages + id: cache-conan-packages-restore + uses: actions/cache/restore@v4 + with: + path: | + ~/.conan2/p + ~/.conan/p + key: ${{matrix.config.platform}}-conan-${{matrix.config.version}}-${{matrix.config.buildtype}}-${{ steps.initvars.outputs.NOW }} + restore-keys: | + ${{matrix.config.platform}}-conan-${{matrix.config.version}}-${{matrix.config.buildtype}} + if: matrix.config.conan + + - run: | + conan install . --output-folder=$BUILDDIR --profile:build='${{github.workspace}}/config/conan/${{matrix.config.platform}}_${{matrix.config.version}}.txt' --profile:host='${{github.workspace}}/config/conan/${{matrix.config.platform}}_${{matrix.config.version}}.txt' -s build_type=${{matrix.config.buildtype}} -c tools.cmake.cmaketoolchain:generator=Ninja || BUILD_MISSING=$? + if [[ "$BUILD_MISSING" == "1" ]]; then + echo "update_conan_cache=true" >> $GITHUB_OUTPUT + conan install . --output-folder=$BUILDDIR --profile:build='${{github.workspace}}/config/conan/${{matrix.config.platform}}_${{matrix.config.version}}.txt' --profile:host='${{github.workspace}}/config/conan/${{matrix.config.platform}}_${{matrix.config.version}}.txt' --build=missing -s build_type=${{matrix.config.buildtype}} -c tools.cmake.cmaketoolchain:generator=Ninja + else + echo "update_conan_cache=false" >> $GITHUB_OUTPUT + fi + working-directory: ${{ matrix.config.directory }} + name: 'Conan dependencies install Windows' + id: conanInstallWindows + shell: bash + if: runner.os == 'Windows' && matrix.config.conan + + - run: | + conan profile detect + conan install . --output-folder=$BUILDDIR -s build_type=${{matrix.config.buildtype}} -c tools.cmake.cmaketoolchain:generator=Ninja || BUILD_MISSING=$? + if [[ "$BUILD_MISSING" == "1" ]]; then + echo "update_conan_cache=true" >> $GITHUB_OUTPUT + conan install . --output-folder=$BUILDDIR --build=missing -s build_type=${{matrix.config.buildtype}} -c tools.cmake.cmaketoolchain:generator=Ninja + else + echo "update_conan_cache=false" >> $GITHUB_OUTPUT + fi + working-directory: ${{ matrix.config.directory }} + name: 'Conan dependencies install Linux' + id: conanInstallLinux + shell: bash + if: runner.os == 'Linux' && matrix.config.conan + + - name: Cache Conan packages + id: cache-conan-packages-save + uses: actions/cache/save@v4 + with: + path: | + ~/.conan2/p + ~/.conan/p + key: ${{steps.cache-conan-packages-restore.outputs.cache-primary-key}} + if: matrix.config.conan && (steps.conanInstallLinux.outputs.update_conan_cache == 'true' || steps.conanInstallWindows.outputs.update_conan_cache == 'true') + + - run: | + if "${{matrix.config.conan}}" == "true" ( + call "%BUILDDIR%/conanvcvars.bat" + cmake -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -G Ninja -DCMAKE_POLICY_DEFAULT_CMP0091=NEW -DCMAKE_BUILD_TYPE=${{matrix.config.buildtype}} -DCPACK_OUTPUT_FILE_PREFIX=%DEPLOYDIR% -DCMAKE_INSTALL_PREFIX=%INSTALLDIR% -S . -B %BUILDDIR% + ) else ( + if "${{ matrix.config.init_compiler != '' }}" == "true" ( + call ${{matrix.config.init_compiler}} + ) else ( + echo "Windows compiler not set" + ) + cmake -G Ninja -DCMAKE_BUILD_TYPE=${{matrix.config.buildtype}} -DCPACK_OUTPUT_FILE_PREFIX=%DEPLOYDIR% -DCMAKE_INSTALL_PREFIX=%INSTALLDIR% -S . -B %BUILDDIR% + ) + working-directory: ${{ matrix.config.directory }} + name: 'CMake configure Windows' + shell: cmd # Use cmd for Windows to make sure the conan vcvars script works correctly + if: runner.os == 'Windows' + + - run: | + if [ "${{matrix.config.conan}}" == "true" ]; then + cmake -DCMAKE_TOOLCHAIN_FILE=conan_toolchain.cmake -G Ninja -DCMAKE_BUILD_TYPE=${{matrix.config.buildtype}} -DCPACK_OUTPUT_FILE_PREFIX=$DEPLOYDIR -DCMAKE_INSTALL_PREFIX=$INSTALLDIR -S . -B $BUILDDIR + else + cmake -G Ninja -DCMAKE_BUILD_TYPE=${{matrix.config.buildtype}} -DCPACK_OUTPUT_FILE_PREFIX=$DEPLOYDIR -DCMAKE_INSTALL_PREFIX=$INSTALLDIR -S . -B $BUILDDIR + fi + working-directory: ${{ matrix.config.directory }} + name: 'CMake configure Linux' + shell: bash + if: runner.os == 'Linux' + + - run: | + output=$(cmake --build $BUILDDIR --target help) + if echo "$output" | grep -q "test: phony"; then + echo "testTargetExists=true" >> $GITHUB_OUTPUT + if [ "$RUNNER_OS" = "Windows" ]; then + echo "${RUNNER_TEMP}/${{matrix.config.platform}}_${{matrix.config.version}}/bin" >> $GITHUB_PATH + fi + else + echo "testTargetExists=false" >> $GITHUB_OUTPUT + fi + if echo "$output" | grep -q "package: phony"; then + echo "cpackTargetExists=true" >> $GITHUB_OUTPUT + else + echo "cpackTargetExists=false" >> $GITHUB_OUTPUT + fi + working-directory: ${{ matrix.config.directory }} + name: 'Verify targets' + shell: bash + id: verifyTargets + + - run: | + if "${{matrix.config.conan}}" == "true" ( + call "%BUILDDIR%/conanvcvars.bat" + ) else ( + if "${{ matrix.config.init_compiler != '' }}" == "true" ( + call ${{matrix.config.init_compiler}} + ) else ( + echo "Windows compiler not set" + ) + ) + cmake --build %BUILDDIR% + cmake --install %BUILDDIR% + working-directory: ${{ matrix.config.directory }} + name: 'CMake build Windows' + shell: cmd # Use cmd for Windows to make sure the conan vcvars script works correctly + if: runner.os == 'Windows' + + - run: | + cmake --build $BUILDDIR + cmake --install $BUILDDIR + working-directory: ${{ matrix.config.directory }} + name: 'CMake build Linux' + if: runner.os == 'Linux' + + - run: | + echo "Running tests..." + ctest -V --test-dir $BUILDDIR --output-on-failure --output-junit junit_global.xml + working-directory: ${{ matrix.config.directory }} + name: 'Run Tests Conditionally' + shell: bash + if: steps.verifyTargets.outputs.testTargetExists == 'true' + + - name: Upload Test Results + if: steps.verifyTargets.outputs.testTargetExists == 'true' + uses: actions/upload-artifact@v4 + with: + name: Test Results (${{ matrix.config.configName }}) + path: ${{ matrix.config.directory }}/**/ctest_*.xml + + - name: Upload Installdir (unstripped) and PDBs + uses: actions/upload-artifact@v4 + with: + name: Installdir (unstripped) and PDBs (${{ matrix.config.configName }}) + path: | + ${{ matrix.config.directory }}/${{ env.INSTALLDIR }}/**/* + ${{ matrix.config.directory }}/${{ env.BUILDDIR }}/**/*.pdb + + - run: | + cpack -V --config $BUILDDIR/CPackConfig.cmake + working-directory: ${{ matrix.config.directory }} + name: 'Deployment Conditionally' + shell: bash + if: steps.verifyTargets.outputs.cpackTargetExists == 'true' + + - name: Upload deployment + if: steps.verifyTargets.outputs.cpackTargetExists == 'true' + uses: actions/upload-artifact@v4 + with: + name: Deployment (${{ matrix.config.configName }}) + path: | + ${{ matrix.config.directory }}/${{ env.DEPLOYDIR }}/**/* + + publish-test-results: + name: "Publish Tests Results" + needs: build + runs-on: ubuntu-latest + permissions: + checks: write + + # only needed unless run with comment_mode: off + pull-requests: write + + # only needed for private repository + contents: read + + # only needed for private repository + issues: read + if: always() + + steps: + - name: Download Artifacts + uses: actions/download-artifact@v4 + with: + path: artifacts + pattern: Test* + + - name: Publish Test Results + uses: EnricoMi/publish-unit-test-result-action@v2 + with: + files: "artifacts/**/*.xml" From 65f174b86f3afaffab77286d18895ddd9a6cfc96 Mon Sep 17 00:00:00 2001 From: Andreas Gruber Date: Fri, 1 Mar 2024 23:36:46 +0100 Subject: [PATCH 09/11] Add configuration for the demo project --- demo/build-config.yml | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 demo/build-config.yml diff --git a/demo/build-config.yml b/demo/build-config.yml new file mode 100644 index 0000000..cbcc497 --- /dev/null +++ b/demo/build-config.yml @@ -0,0 +1,10 @@ +demo: + configuration: + win_msvc2022vc142_relwithdebinfo: + <<: *_win_msvc2022vc142_relwithdebinfo + win_msvc2022vc143_relwithdebinfo: + <<: *_win_msvc2022vc143_relwithdebinfo + debian_bookworm_relwithdebinfo: + <<: *_debian_bookworm_relwithdebinfo + debian_bullseye_relwithdebinfo: + <<: *_debian_bullseye_relwithdebinfo From 9bb35b0dd9b149e3193cda2770714147131ccda7 Mon Sep 17 00:00:00 2001 From: Andreas Gruber Date: Fri, 1 Mar 2024 23:37:51 +0100 Subject: [PATCH 10/11] Add documentation --- BUILD.md | 130 +++++++++++++++++++++++++++++++++++++++ README.md | 178 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 2 files changed, 307 insertions(+), 1 deletion(-) create mode 100644 BUILD.md diff --git a/BUILD.md b/BUILD.md new file mode 100644 index 0000000..df75694 --- /dev/null +++ b/BUILD.md @@ -0,0 +1,130 @@ +# Build Environment Setup Guide + +With the help of examples, this section guides you through the process of initializing a build environment for C++ projects with or without using the Conan package manager, focusing on setting up for different build types such as Debug, Release, and RelWithDebInfo. + +## Conan + +If you are using conan for managing dependencies, it is recommended to also define install and packaging paths in the `conanfile.py`. The build examples in that chapter assume this is the case. + +```python +from conan import ConanFile, tools +from conan.tools.cmake import CMakeDeps, CMakeToolchain, cmake_layout + +class MyConanRecipe(ConanFile): + settings = "os", "compiler", "build_type", "arch" + generators = "CMakeDeps" + + def requirements(self): + self.requires("gtest/1.14.0") + # Example to add conditional requirements for certain OS + # if self.settings.os == "Windows": + # self.requires("openssl/3.0.13") + + # do not change these directories to other values to maintain compatibility + # with the build automation + def generate(self): + tc = CMakeToolchain(self) + tc.cache_variables["CMAKE_INSTALL_PREFIX"] = os.getcwd() + "/../install" # To make sure we have deterministic behavior + tc.cache_variables["CPACK_OUTPUT_FILE_PREFIX"] = "build/deploy" + tc.generate() +``` + +### Visual Studio Solution + +```cmd +rem change to the directory of the CMakeLists.txt +cd demo + +rem set the build directory +set BUILDDIR=build/make + +rem remove the build directory and recreate it +rmdir /Q/S build +md build + +rem detect the conan profile, only need to run once +conan profile detect + +rem install the dependencies using conan for the different build types +conan install . --output-folder=%BUILDDIR% --profile ../config/conan/msvc2022vc143.txt --build=missing -s build_type=Debug +conan install . --output-folder=%BUILDDIR% --profile ../config/conan/msvc2022vc143.txt --build=missing -s build_type=Release +conan install . --output-folder=%BUILDDIR% --profile ../config/conan/msvc2022vc143.txt --build=missing -s build_type=RelWithDebInfo + +rem create Visual Studio solution and open it in Visual Studio +cmake --preset conan-default +start %BUILDDIR%/demoDrv.sln + +rem build/install/package/test from the command line +cmake --build %BUILDDIR% --config RelWithDebInfo +cmake --install %BUILDDIR% --config RelWithDebInfo +cpack -V --config %BUILDDIR%/CPackConfig.cmake -C RelWithDebInfo +ctest -V --test-dir %BUILDDIR% --output-on-failure -C RelWithDebInfo +``` + +### Ninja on Windows + +```cmd +rem change to the directory of the CMakeLists.txt +cd demo + +rem set the build directory +set BUILDDIR=build/make + +rem remove the build directory and recreate it +rmdir /Q/S build +md build + +rem detect the conan profile, only need to run once +conan profile detect + +rem install the dependencies using conan for RelWithDebInfo +conan install . --output-folder=%BUILDDIR% --build=missing --profile=../config/conan/msvc2022vc143.txt -s build_type=RelWithDebInfo -c tools.cmake.cmaketoolchain:generator=Ninja + +rem Conan creates the correct VCvars script, init with this +call "%BUILDDIR%/conanvcvars.bat" + +rem open the source so in Visual Studio Code and use the generated cmake-presets there +code . + +rem alternative do everything on the commandline +cmake --preset conan-relwithdebinfo + +rem build/install/package/test from the command line +cmake --build %BUILDDIR% +cmake --install %BUILDDIR% +cpack -V --config %BUILDDIR%/CPackConfig.cmake +ctest -V --test-dir %BUILDDIR% --output-on-failure +``` + +### Ninja on Linux + +```bash +# change to the directory of the CMakeLists.txt +cd demo + +# set the build directory +export BUILDDIR=build/make + +# remove the build directory and recreate it +rm -rf build +mkdir build + +# detect the conan profile, only need to run once +conan profile detect + +# install the dependencies using conan for RelWithDebInfo +# we already are on the target platform in a (dev)container, so no profile needed +conan install . --output-folder=${BUILDDIR} --build=missing -s build_type=RelWithDebInfo -c tools.cmake.cmaketoolchain:generator=Ninja + +# open the source so in Visual Studio Code and use the generated cmake-presets there +code . + +# alternative do everything on the commandline +cmake --preset conan-relwithdebinfo + +rem build/install/package/test from the command line +cmake --build ${BUILDDIR} +cmake --install ${BUILDDIR} +cpack -V --config ${BUILDDIR}/CPackConfig.cmake +ctest -V --test-dir ${BUILDDIR} --output-on-failure +``` diff --git a/README.md b/README.md index d5b11ad..2506552 100644 --- a/README.md +++ b/README.md @@ -1 +1,177 @@ -# cpp-multi-builder \ No newline at end of file +# C++ Multi Builder + +This GitHub repository provides a ready-to-use GitHub Action to build C++ projects. +It is designed to lower the entry barrier for developers and allow easy building, unit testing, and packaging of C++ projects for multiple combinations of OS using a single C++ source code. + +## Features + +- **Ease of Use**: Set up with minimal configuration required. +- **Multiple OS Support**: Build your C++ projects for various combinations of operating systems. +- **C++ Package Manager Support**: Conan package manager is supported out of the box for managing C++ dependencies. +- **Automated Testing and Packaging**: Includes support for unit testing and packaging of your projects. +- **Debug files available**: PDB and unstripped files are kept beside the build artifacts. +- **Caching Support**: Conan packages are cached automatically + +## Getting Started + +### Prerequisites + +- A GitHub account +- Basic knowledge of C++ and CMake +- Familiarity with GitHub Actions + +### Fork the Repository + +To get started, fork this repository to your own GitHub account. This will be the home for your C++ CMake project. + +### Initial general configuration + +The default configuration is saved in `pipeline/createMatrix/mergekeys.yml`. + +In this file the used Docker images and various other settings are stored. + +#### Linux + +Linux builds rely on Docker images. + +Those docker images need to have the following tools installed: +- compiler +- ninja +- conan + +### Add Your C++ CMake Project + +Place your C++ source code anywhere in the repository alongside a `CMakeLists.txt` file. +This is where you'll define your project and its dependencies. + +### Configuration + +Create a `build-config.yml` file in your repository alongside the `CMakeLists.txt`. +This file specifies the build configurations for different OS. +Here's an example based on the provided demo in the `demo`directory: + +```yaml +demo: + configuration: + win_msvc2022vc142_relwithdebinfo: + <<: *_win_msvc2022vc142_relwithdebinfo + win_msvc2022vc143_relwithdebinfo: + <<: *_win_msvc2022vc143_relwithdebinfo + debian_bookworm_relwithdebinfo: + <<: *_debian_bookworm_relwithdebinfo + debian_bullseye_relwithdebinfo: + <<: *_debian_bullseye_relwithdebinfo +``` + +For C++ package management, create a `conanfile.txt` or `conanfile.py` file alongside your `CMakeLists.txt`. + +### Utilize Packaging and Testing + +To use unit testing and/or packaging, include the necessary CMake files for packaging and testing as follows: +```cmake +project(demo + VERSION 0.1.0 +) +# make sure to place the include commands after the project +# command specifying the project name and version +include(../cmake/packaging.cmake) +include(../cmake/testing.cmake) +``` + +These includes will enable the GitHub Action pipeline to perform automated testing and packaging of your project. + +Testing also requires the [googletest](https://github.com/google/googletest) framework, just merge the following line into your `conanfile.py`: +```python + def requirements(self): + self.requires("gtest/1.14.0") +``` + +A full example of a `conanfile.py` can be found [here](BUILD.md#conan). + +### Details about packaging + +#### Key Features + +Automatically selects packaging format based on the target OS. +- **Windows**: Packages are generated as ZIP files. +- **Debian-based Linux**: Packages are generated as DEB files. +- **Red Hat-based Linux**: Packages are generated as RPM files. + +#### Customization + +Beside the mentioned [Key features](#key-features) CPack default features are used. + +For Linux it is recommended to set the `CPACK_PACKAGING_INSTALL_PREFIX` to whatever is desired. E.g.: + +```cmake +set(CPACK_PACKAGING_INSTALL_PREFIX "/usr/local/bin/") +``` + +For additional customization, such as including additional files in the package or changing package metadata, refer to the [CPack documentation](https://cmake.org/cmake/help/latest/module/CPack.html). + +### How to Add a New Test + +1. **Define Test Sources:** +List all source files associated with your test. +For a comprehensive test suite, you might separate your test sources from your main application sources. + +2. **Use the `add_gtest_with_xml` Macro:** +This macro is used to compile the test sources into an executable, link it against Google Test and any necessary project libraries, and set up XML output for test results. +The basic syntax is: + + ```cmake + add_gtest_with_xml(TARGET_NAME TARGET_LIBRARY TEST_SOURCES) + ``` + + - `TARGET_NAME`: A unique name for your test executable. + - `TARGET_LIBRARY`: The library against which your test executable should be linked. This usually includes your project's main library. + - `TEST_SOURCES`: The source files for your test. + +3. **Example:** +For a hypothetical binary named `demo` that tests functionality in `demoLib`, you might write: + + ```cmake + set(TEST_SOURCES demoTest.cxx) + add_gtest_with_xml(demo demoLib ${TEST_SOURCES}) + ``` + +4. **Integration with CMakeLists.txt:** +Add your test configuration to a `CMakeLists.txt` within your project's test directory. +If your project structure does not already include a test directory, create one as shown: + + ```cmake + add_subdirectory(tests) + ``` + +### Build Pipeline + +The `build.yml` GitHub Action workflow file orchestrates the build process. +It prepares the build environment, installs necessary dependencies, and runs the build according to configurations specified in `build-config.yml`. +This process is triggered by push or pull request events to the main branch, or can be manually dispatched. + +When running the build, certain directories are set automatically: +- **build directory:** is set set to `build/make` +- **install dir:** is set to `build/install` via `CMAKE_INSTALL_PREFIX` cache variable +- **packaging dir:** is set to `build/deploy` via `CPACK_OUTPUT_FILE_PREFIX` cache variable + +**Note:** The automation searchs in these paths for the build artifacts, like test results or the generated binaries. +When doing modifications, please make sure those directories are still be set correctly by the automation. + +#### Docker login + +If you use private container images, uncomment the following lines in `build.yml` and provide the referenced secrets. + +```yaml + # credentials: + # username: ${{ secrets.DOCKERHUB_USER}} + # password: ${{ secrets.DOCKERHUB_PASSWORD}} +``` + +## Local Build Environment Setup + +For detailed instructions on setting up your local build environment, including configuring Conan and CMake for various build types, please see the [Build Environment Setup Guide](BUILD.md). +This guide provides step-by-step instructions to help you get started quickly and efficiently. + +## Support + +For any questions or support, consider opening an issue in the repository. Contributions to improve the build process or add new features are welcome. From 561d1e76a5f265f33467c1476178aa94a2238130 Mon Sep 17 00:00:00 2001 From: Andreas Gruber Date: Fri, 1 Mar 2024 23:44:35 +0100 Subject: [PATCH 11/11] Correct usage of platform and version --- BUILD.md | 8 ++++---- config/conan/{msvc2022vc142.txt => win_msvc2022vc142.txt} | 0 config/conan/{msvc2022vc143.txt => win_msvc2022vc143.txt} | 0 3 files changed, 4 insertions(+), 4 deletions(-) rename config/conan/{msvc2022vc142.txt => win_msvc2022vc142.txt} (100%) rename config/conan/{msvc2022vc143.txt => win_msvc2022vc143.txt} (100%) diff --git a/BUILD.md b/BUILD.md index df75694..2ce0cd7 100644 --- a/BUILD.md +++ b/BUILD.md @@ -46,9 +46,9 @@ rem detect the conan profile, only need to run once conan profile detect rem install the dependencies using conan for the different build types -conan install . --output-folder=%BUILDDIR% --profile ../config/conan/msvc2022vc143.txt --build=missing -s build_type=Debug -conan install . --output-folder=%BUILDDIR% --profile ../config/conan/msvc2022vc143.txt --build=missing -s build_type=Release -conan install . --output-folder=%BUILDDIR% --profile ../config/conan/msvc2022vc143.txt --build=missing -s build_type=RelWithDebInfo +conan install . --output-folder=%BUILDDIR% --profile ../config/conan/win_msvc2022vc143.txt --build=missing -s build_type=Debug +conan install . --output-folder=%BUILDDIR% --profile ../config/conan/win_msvc2022vc143.txt --build=missing -s build_type=Release +conan install . --output-folder=%BUILDDIR% --profile ../config/conan/win_msvc2022vc143.txt --build=missing -s build_type=RelWithDebInfo rem create Visual Studio solution and open it in Visual Studio cmake --preset conan-default @@ -78,7 +78,7 @@ rem detect the conan profile, only need to run once conan profile detect rem install the dependencies using conan for RelWithDebInfo -conan install . --output-folder=%BUILDDIR% --build=missing --profile=../config/conan/msvc2022vc143.txt -s build_type=RelWithDebInfo -c tools.cmake.cmaketoolchain:generator=Ninja +conan install . --output-folder=%BUILDDIR% --build=missing --profile=../config/conan/win_msvc2022vc143.txt -s build_type=RelWithDebInfo -c tools.cmake.cmaketoolchain:generator=Ninja rem Conan creates the correct VCvars script, init with this call "%BUILDDIR%/conanvcvars.bat" diff --git a/config/conan/msvc2022vc142.txt b/config/conan/win_msvc2022vc142.txt similarity index 100% rename from config/conan/msvc2022vc142.txt rename to config/conan/win_msvc2022vc142.txt diff --git a/config/conan/msvc2022vc143.txt b/config/conan/win_msvc2022vc143.txt similarity index 100% rename from config/conan/msvc2022vc143.txt rename to config/conan/win_msvc2022vc143.txt