Skip to content

Instantly share code, notes, and snippets.

@joelio
Last active March 20, 2025 18:06
Show Gist options
  • Select an option

  • Save joelio/de10fd752e8564e0f98f0483572c9078 to your computer and use it in GitHub Desktop.

Select an option

Save joelio/de10fd752e8564e0f98f0483572c9078 to your computer and use it in GitHub Desktop.
cmake_minimum_required(VERSION 3.10)
project(CppHelloWorld)
set(CMAKE_CXX_STANDARD 17)
add_executable(CppHelloWorld Program.cpp)
EOF
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
</PropertyGroup>
</Project>
# .gitlab-ci.yml - Pipeline for building C++ application in .NET environment
# Air-gapped internal environment configuration using MSBuild
stages:
- prepare
- build
- test
- package
variables:
GIT_STRATEGY: fetch
PROJECT_NAME: "CppHelloWorld"
SOLUTION_NAME: "CppSolution"
CONFIGURATION: "Release"
PLATFORM: "x64"
# Using internal pre-built dotnet 8 image
CUSTOM_IMAGE: "internal-registry.local/dotnet8-msbuild:latest"
# Cache dependencies between jobs
cache:
key: ${CI_COMMIT_REF_SLUG}
paths:
- packages/
- .vs/
- */bin/
- */obj/
setup:
stage: prepare
image: ${CUSTOM_IMAGE}
script:
- echo "========== ENVIRONMENT SETUP =========="
- echo "Using pre-built .NET 8 image with MSBuild tools from internal registry"
- dotnet --info
- echo "MSBuild version:"
- dotnet msbuild -version
- echo "Setting up C++ project with .NET tooling..."
- dotnet new sln -n ${SOLUTION_NAME}
- dotnet new console -n ${PROJECT_NAME} -o src/${PROJECT_NAME} -lang c++
- dotnet new mstest -n ${PROJECT_NAME}Tests -o src/${PROJECT_NAME}Tests
- dotnet sln ${SOLUTION_NAME}.sln add src/${PROJECT_NAME}/${PROJECT_NAME}.csproj
- dotnet sln ${SOLUTION_NAME}.sln add src/${PROJECT_NAME}Tests/${PROJECT_NAME}Tests.csproj
artifacts:
paths:
- src/
- ${SOLUTION_NAME}.sln
expire_in: 1 week
build:
stage: build
image: ${CUSTOM_IMAGE}
dependencies:
- setup
script:
- echo "========== BUILDING C++ APPLICATION =========="
- dotnet build ${SOLUTION_NAME}.sln --configuration ${CONFIGURATION} --verbosity normal
- echo "Verifying build output..."
- ls -la src/${PROJECT_NAME}/bin/${CONFIGURATION}/net8.0/
- echo "Running the application to verify it works..."
- dotnet src/${PROJECT_NAME}/bin/${CONFIGURATION}/net8.0/${PROJECT_NAME}.dll
- echo "C++ application built successfully!"
artifacts:
paths:
- src/${PROJECT_NAME}/bin/${CONFIGURATION}/net8.0/
expire_in: 1 week
unit_tests:
stage: test
image: ${CUSTOM_IMAGE}
dependencies:
- build
script:
- echo "========== RUNNING UNIT TESTS =========="
- dotnet test src/${PROJECT_NAME}Tests/${PROJECT_NAME}Tests.csproj --configuration ${CONFIGURATION} --verbosity normal
- echo "All tests passed successfully!"
artifacts:
paths:
- src/${PROJECT_NAME}Tests/TestResults/
reports:
junit: src/${PROJECT_NAME}Tests/TestResults/testresults.xml
expire_in: 1 week
package:
stage: package
image: ${CUSTOM_IMAGE}
dependencies:
- build
script:
- echo "========== PACKAGING APPLICATION =========="
- echo "Creating package..."
- mkdir -p package
- cp -r src/${PROJECT_NAME}/bin/${CONFIGURATION}/net8.0/* package/
- echo "Adding metadata and versioning..."
- echo "CI_PIPELINE_ID" > package/build_id
- echo "CI_COMMIT_SHA" > package/commit_id
- cd package
- zip -r ../${PROJECT_NAME}-package.zip .
- cd ..
- echo "Package created successfully!"
artifacts:
paths:
- ${PROJECT_NAME}-package.zip
expire_in: 1 week
dnf install -y \
gcc-c++ \
make \
cmake \
ninja-build \
gdb \
&& dnf clean all
build_cpp:
stage: build
image: ${CUSTOM_IMAGE}
script:
- echo "Building C++ project..."
# Example with CMake
- cd src/${PROJECT_NAME}
- cmake -B build -DCMAKE_BUILD_TYPE=${CONFIGURATION}
- cmake --build build --config ${CONFIGURATION}
build_dotnet:
stage: build
image: ${CUSTOM_IMAGE}
script:
- echo "Building .NET test project..."
- dotnet build src/${PROJECT_NAME}Tests/${PROJECT_NAME}Tests.csproj --configuration ${CONFIGURATION}
dotnet new mstest -n CppHelloWorldTests -o src/CppHelloWorldTests
dotnet sln CppSolution.sln add src/CppHelloWorldTests/CppHelloWorldTests.csproj
dotnet build src/CppHelloWorldTests/CppHelloWorldTests.csproj --configuration ${CONFIGURATION}
# Simple isolated GitLab CI Pipeline for Visual C++ on RHEL 8 with .NET 8
stages:
- build
# Self-contained build job
build:
stage: build
image: registry.access.redhat.com/ubi8/ubi:latest
script:
# Install required dependencies
- dnf install -y wget tar libicu openssl-libs krb5-libs zlib libunwind sudo git findutils which
# Install .NET 8 SDK (includes MSBuild)
- wget https://dot.net/v1/dotnet-install.sh -O dotnet-install.sh
- chmod +x dotnet-install.sh
- ./dotnet-install.sh --channel 8.0
- export PATH="/root/.dotnet:${PATH}"
- ln -s /root/.dotnet/dotnet /usr/local/bin/dotnet
# Install NuGet
- wget https://dist.nuget.org/win-x86-commandline/latest/nuget.exe -O /usr/local/bin/nuget.exe
- echo '#!/bin/bash' > /usr/local/bin/nuget
- echo 'dotnet exec /usr/local/bin/nuget.exe "$@"' >> /usr/local/bin/nuget
- chmod +x /usr/local/bin/nuget
# Verify installations
- dotnet --info
- nuget help
# Create a simple C++ project for proof of concept
- mkdir -p simple_cpp_project
- cd simple_cpp_project
# Create a simple C++ file
- echo '#include <iostream>' > main.cpp
- echo 'int main() {' >> main.cpp
- echo ' std::cout << "Hello from C++ built with MSBuild and .NET 8 on RHEL 8!" << std::endl;' >> main.cpp
- echo ' return 0;' >> main.cpp
- echo '}' >> main.cpp
# Create a simple project file
- echo '<Project Sdk="Microsoft.NET.Sdk">' > simple.csproj
- echo ' <PropertyGroup>' >> simple.csproj
- echo ' <TargetFramework>net8.0</TargetFramework>' >> simple.csproj
- echo ' <EnableDefaultCompileItems>false</EnableDefaultCompileItems>' >> simple.csproj
- echo ' </PropertyGroup>' >> simple.csproj
- echo ' <ItemGroup>' >> simple.csproj
- echo ' <ClCompile Include="main.cpp" />' >> simple.csproj
- echo ' </ItemGroup>' >> simple.csproj
- echo '</Project>' >> simple.csproj
# Build the simple C++ project with MSBuild
- dotnet msbuild simple.csproj /p:Configuration=Release /p:Platform=x64
# Test the built application if successful
- if [ -f "bin/Release/net8.0/simple" ]; then
- echo "Build successful, executable found"
- bin/Release/net8.0/simple
- else
- echo "Build appears to have succeeded but executable not found in expected location"
- find . -name "simple" -o -name "*.exe"
- fi
# Create output directory and package result
- mkdir -p ../artifacts
- find . -type f -name "simple" -o -name "*.exe" -o -name "*.dll" | xargs -I{} cp {} ../artifacts/ || true
- cd ..
- ls -la artifacts/
- tar -czf cpp-build-poc.tar.gz artifacts/
artifacts:
paths:
- cpp-build-poc.tar.gz
- artifacts/
expire_in: 1 week
stages:
- build
build:
stage: build
image: mcr.microsoft.com/dotnet/sdk:8.0
script:
# Create a simple C++ project directory
- mkdir -p simple_cpp_project
- cd simple_cpp_project
# Create a simple C++ file
- echo '#include <iostream>' > main.cpp
- echo 'int main() {' >> main.cpp
- echo ' std::cout << "Hello from C++ built with MSBuild and .NET!" << std::endl;' >> main.cpp
- echo ' return 0;' >> main.cpp
- echo '}' >> main.cpp
# Create a .vcxproj file for native C++ compilation
- echo '<?xml version="1.0" encoding="utf-8"?>' > simple.vcxproj
- echo '<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">' >> simple.vcxproj
- echo ' <ItemGroup>' >> simple.vcxproj
- echo ' <ClCompile Include="main.cpp" />' >> simple.vcxproj
- echo ' </ItemGroup>' >> simple.vcxproj
- echo ' <PropertyGroup>' >> simple.vcxproj
- echo ' <ConfigurationType>Application</ConfigurationType>' >> simple.vcxproj
- echo ' <PlatformToolset>v143</PlatformToolset>' >> simple.vcxproj
- echo ' <OutDir>bin\\$(Configuration)\\</OutDir>' >> simple.vcxproj
- echo ' </PropertyGroup>' >> simple.vcxproj
- echo ' <Import Project="$(VCTargetsPath)\\Microsoft.Cpp.Default.props" />' >> simple.vcxproj
- echo ' <Import Project="$(VCTargetsPath)\\Microsoft.Cpp.targets" />' >> simple.vcxproj
- echo '</Project>' >> simple.vcxproj
# Restore dependencies (not strictly necessary here but kept for consistency)
- dotnet restore simple.vcxproj
# Build the project using dotnet msbuild
- dotnet msbuild simple.vcxproj /p:Configuration=Release /p:Platform=x64
artifacts:
paths:
- bin/Release/simple.exe
expire_in: 1 week
cat > Program.cpp << 'EOF'
#include <iostream>
int main() {
std::cout << "Hello, World from C++!" << std::endl;
return 0;
}
EOF
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Diagnostics;
using System.IO;
namespace CppHelloWorldTests
{
[TestClass]
public class UnitTest1
{
[TestMethod]
public void TestApplicationRuns()
{
// Path to the executable relative to the test binary
string exePath = Path.Combine(
"..", "..", "..", "..",
"CppHelloWorld", "bin", "Release", "net8.0", "CppHelloWorld.dll");
// Ensure the path exists
Assert.IsTrue(File.Exists(exePath), $"Executable not found at {exePath}");
// Run the application and capture output
Process process = new Process();
process.StartInfo.FileName = "dotnet";
process.StartInfo.Arguments = exePath;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
// Verify the output contains the expected message
Assert.IsTrue(output.Contains("Hello, World"), "Expected output not found");
Assert.AreEqual(0, process.ExitCode, "Process did not exit with code 0");
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment