{"id":5797,"date":"2020-08-14T07:00:53","date_gmt":"2020-08-14T14:00:53","guid":{"rendered":"http:\/\/writeasync.net\/?p=5797"},"modified":"2020-08-09T19:22:15","modified_gmt":"2020-08-10T02:22:15","slug":"cross-platform-without-complexity-finishing-up","status":"publish","type":"post","link":"http:\/\/writeasync.net\/?p=5797","title":{"rendered":"Cross-platform without complexity: finishing up"},"content":{"rendered":"<p>Our cross-platform project has <a href=\"http:\/\/writeasync.net\/?p=5790\">interoperable C++ and .NET code<\/a>. However, the command line and IDE experience still only accounts for the C++ side.<\/p>\n<p>Let&#8217;s start with the IDE. First, a bit of bad news &#8212; there doesn&#8217;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 &#8220;<a href=\"https:\/\/docs.microsoft.com\/en-us\/cpp\/build\/open-folder-projects-cpp?view=vs-2019\">Open Folder<\/a>&#8221; 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 &#8212; this is where <a href=\"https:\/\/microsoft.github.io\/slngen\/\">SlnGen<\/a> comes in.<\/p>\n<p>SlnGen is a VS solution generator. Rather than hand-editing and maintaining files yourself, you can point <code>slngen<\/code> at a <code>*proj<\/code> file and it will automatically create a <code>.sln<\/code> with all the project references transitively resolved. Due to some Win32 dependencies (registry, etc.), it won&#8217;t work on Linux, but neither does Visual Studio so we can live with that. Here is a helpful <code>sln.cmd<\/code> script that will ensure the generated file goes into the ignored <code>.vs<\/code> folder and sets up the right configuration:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n@echo off\r\nsetlocal\r\n\r\npushd %~dp0\r\nset ROOT_PATH=%CD%\r\n\r\nset BUILD_TYPE=%~1\r\nif NOT DEFINED BUILD_TYPE set BUILD_TYPE=Debug\r\nif \/i &quot;%BUILD_TYPE%&quot; == &quot;Debug&quot; goto :Gen\r\nif \/i &quot;%BUILD_TYPE%&quot; == &quot;Release&quot; goto :Gen\r\nset ERR_MSG=Usage: %0 &#x5B;build_type]\r\nset EXIT_CODE=1\r\ngoto :Quit\r\n\r\n:Gen\r\nslngen.exe -c %BUILD_TYPE% -d .vs dirs.proj\r\nset EXIT_CODE=%ERRORLEVEL%\r\nif NOT &quot;%EXIT_CODE%&quot; == &quot;0&quot; set ERR_MSG=slngen.exe failed.\r\n\r\n:Quit\r\npopd\r\nif NOT &quot;%EXIT_CODE%&quot; == &quot;0&quot; echo %ERR_MSG%\r\nexit \/b %EXIT_CODE%\r\n<\/pre>\n<p>Simple! Now we have one-click VS integration for our .NET projects.<\/p>\n<p>The final step is to ensure the existing command line build script can also invoke the <code>dotnet<\/code> commands. Now that we have two different build steps, we can add some command line arguments to control the behavior (<code>--no-cpp<\/code> to skip the C++ part and <code>--no-cs<\/code> to skip the C# part). The main changes for <code>build.cmd<\/code> are as follows:<\/p>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n:MakeCS\r\nif &quot;%NO_CS%&quot; == &quot;1&quot; goto :Quit\r\n\r\ncd &quot;%ROOT_PATH%&quot;\r\n\r\nset DOTNET_BUILD_ARGS=build -c %BUILD_TYPE%\r\n\r\nif &quot;%VERBOSE%&quot; == &quot;1&quot; set DOTNET_BUILD_ARGS=%DOTNET_BUILD_ARGS% -v d\r\n\r\necho == dotnet.exe %DOTNET_BUILD_ARGS%\r\ndotnet.exe %DOTNET_BUILD_ARGS%\r\nset EXIT_CODE=%ERRORLEVEL%\r\nset ERR_MSG=dotnet build failed.\r\nif NOT &quot;%EXIT_CODE%&quot; == &quot;0&quot; goto :Quit\r\n\r\nif &quot;%NO_TEST%&quot; == &quot;1&quot; goto :Quit\r\n\r\nset DOTNET_TEST_ARGS=test -c %BUILD_TYPE%\r\n\r\nif &quot;%VERBOSE%&quot; == &quot;1&quot; set DOTNET_TEST_ARGS=%DOTNET_TEST_ARGS% -v d\r\n\r\necho == dotnet.exe %DOTNET_TEST_ARGS%\r\ndotnet.exe %DOTNET_TEST_ARGS%\r\nset EXIT_CODE=%ERRORLEVEL%\r\nset ERR_MSG=dotnet test failed.\r\n<\/pre>\n<p>Given that <a href=\"https:\/\/stackoverflow.com\/questions\/9639103\/is-there-a-goto-statement-in-bash\">lack of GOTO in Bash<\/a>, the <code>build.sh<\/code> changes require wrapping the C++ build steps in a function. Really, that&#8217;s an improvement though. The rest of the changes are pretty much equivalent to the Windows script:<\/p>\n<pre class=\"brush: bash; title: ; notranslate\" title=\"\">\r\n&#x5B;&#x5B; $NO_CS -eq 1 ]] &amp;&amp; quit 'Done' 0\r\n\r\ncd &quot;${ROOT_PATH}&quot;\r\n\r\nDOTNET_BUILD_ARGS=&quot;build -c ${BUILD_TYPE}&quot;\r\n\r\n&#x5B;&#x5B; $VERBOSE -eq 1 ]] &amp;&amp; DOTNET_BUILD_ARGS=&quot;${DOTNET_BUILD_ARGS} -v d&quot;\r\n\r\necho == dotnet $DOTNET_BUILD_ARGS\r\ndotnet $DOTNET_BUILD_ARGS\r\nEXIT_CODE=$?\r\n&#x5B;&#x5B; $EXIT_CODE -ne 0 ]] &amp;&amp; quit 'dotnet build failed' $EXIT_CODE\r\n\r\n&#x5B;&#x5B; $NO_TEST -eq 1 ]] &amp;&amp; quit 'Done' 0\r\n\r\nDOTNET_TEST_ARGS=&quot;test -c ${BUILD_TYPE}&quot;\r\n\r\n&#x5B;&#x5B; $VERBOSE -eq 1 ]] &amp;&amp; DOTNET_TEST_ARGS=&quot;${DOTNET_TEST_ARGS} -v d&quot;\r\n\r\necho == dotnet $DOTNET_TEST_ARGS\r\ndotnet $DOTNET_TEST_ARGS\r\nEXIT_CODE=$?\r\n&#x5B;&#x5B; $EXIT_CODE -ne 0 ]] &amp;&amp; quit 'dotnet test failed' $EXIT_CODE\r\n<\/pre>\n<p>GitHub repo with all changes: <a href=\"https:\/\/github.com\/bobbymcr\/CMakeSampleVS\/tree\/ce1b05fca2fff03b879275f6491c239403577182\">CMakeSampleVS<\/a><\/p>\n<p>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 <a href=\"https:\/\/simplicable.com\/new\/accidental-complexity-vs-essential-complexity\"><em>accidental<\/em> complexity<\/a> at the very least.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>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&#8217;s start with the IDE. First, a bit of bad news &#8212; there doesn&#8217;t seem to be&hellip; <\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[107,31],"tags":[],"class_list":["post-5797","post","type-post","status-publish","format-standard","hentry","category-cross-platform","category-scripts"],"_links":{"self":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/5797","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=5797"}],"version-history":[{"count":1,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/5797\/revisions"}],"predecessor-version":[{"id":5798,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/5797\/revisions\/5798"}],"wp:attachment":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5797"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5797"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5797"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}