Cross-platform without complexity: finishing up

Spread the love

Our cross-platform project has interoperable C++ and .NET code. However, the command line and IDE experience still only accounts for the C++ side.

Let’s start with the IDE. First, a bit of bad news — there doesn’t seem to be a good way to seamlessly combine the CMake native project and the .NET projects in a single Visual Studio instance. The “Open Folder” experience will detect the .csproj and .cs files and allow you to open and edit them. For build and test purposes, though, it will only use the info from CMakeLists.txt. But all is not lost — this is where SlnGen comes in.

SlnGen is a VS solution generator. Rather than hand-editing and maintaining files yourself, you can point slngen at a *proj file and it will automatically create a .sln with all the project references transitively resolved. Due to some Win32 dependencies (registry, etc.), it won’t work on Linux, but neither does Visual Studio so we can live with that. Here is a helpful sln.cmd script that will ensure the generated file goes into the ignored .vs folder and sets up the right configuration:

@echo off
setlocal

pushd %~dp0
set ROOT_PATH=%CD%

set BUILD_TYPE=%~1
if NOT DEFINED BUILD_TYPE set BUILD_TYPE=Debug
if /i "%BUILD_TYPE%" == "Debug" goto :Gen
if /i "%BUILD_TYPE%" == "Release" goto :Gen
set ERR_MSG=Usage: %0 [build_type]
set EXIT_CODE=1
goto :Quit

:Gen
slngen.exe -c %BUILD_TYPE% -d .vs dirs.proj
set EXIT_CODE=%ERRORLEVEL%
if NOT "%EXIT_CODE%" == "0" set ERR_MSG=slngen.exe failed.

:Quit
popd
if NOT "%EXIT_CODE%" == "0" echo %ERR_MSG%
exit /b %EXIT_CODE%

Simple! Now we have one-click VS integration for our .NET projects.

The final step is to ensure the existing command line build script can also invoke the dotnet commands. Now that we have two different build steps, we can add some command line arguments to control the behavior (--no-cpp to skip the C++ part and --no-cs to skip the C# part). The main changes for build.cmd are as follows:

:MakeCS
if "%NO_CS%" == "1" goto :Quit

cd "%ROOT_PATH%"

set DOTNET_BUILD_ARGS=build -c %BUILD_TYPE%

if "%VERBOSE%" == "1" set DOTNET_BUILD_ARGS=%DOTNET_BUILD_ARGS% -v d

echo == dotnet.exe %DOTNET_BUILD_ARGS%
dotnet.exe %DOTNET_BUILD_ARGS%
set EXIT_CODE=%ERRORLEVEL%
set ERR_MSG=dotnet build failed.
if NOT "%EXIT_CODE%" == "0" goto :Quit

if "%NO_TEST%" == "1" goto :Quit

set DOTNET_TEST_ARGS=test -c %BUILD_TYPE%

if "%VERBOSE%" == "1" set DOTNET_TEST_ARGS=%DOTNET_TEST_ARGS% -v d

echo == dotnet.exe %DOTNET_TEST_ARGS%
dotnet.exe %DOTNET_TEST_ARGS%
set EXIT_CODE=%ERRORLEVEL%
set ERR_MSG=dotnet test failed.

Given that lack of GOTO in Bash, the build.sh changes require wrapping the C++ build steps in a function. Really, that’s an improvement though. The rest of the changes are pretty much equivalent to the Windows script:

[[ $NO_CS -eq 1 ]] && quit 'Done' 0

cd "${ROOT_PATH}"

DOTNET_BUILD_ARGS="build -c ${BUILD_TYPE}"

[[ $VERBOSE -eq 1 ]] && DOTNET_BUILD_ARGS="${DOTNET_BUILD_ARGS} -v d"

echo == dotnet $DOTNET_BUILD_ARGS
dotnet $DOTNET_BUILD_ARGS
EXIT_CODE=$?
[[ $EXIT_CODE -ne 0 ]] && quit 'dotnet build failed' $EXIT_CODE

[[ $NO_TEST -eq 1 ]] && quit 'Done' 0

DOTNET_TEST_ARGS="test -c ${BUILD_TYPE}"

[[ $VERBOSE -eq 1 ]] && DOTNET_TEST_ARGS="${DOTNET_TEST_ARGS} -v d"

echo == dotnet $DOTNET_TEST_ARGS
dotnet $DOTNET_TEST_ARGS
EXIT_CODE=$?
[[ $EXIT_CODE -ne 0 ]] && quit 'dotnet test failed' $EXIT_CODE

GitHub repo with all changes: CMakeSampleVS

This concludes our cross-platform code exploration. Hopefully this can serve as an example of how to provide a solid multi-OS development experience without accidental complexity at the very least.

Leave a Reply

Your email address will not be published. Required fields are marked *