{"id":5802,"date":"2021-03-15T07:00:48","date_gmt":"2021-03-15T14:00:48","guid":{"rendered":"http:\/\/writeasync.net\/?p=5802"},"modified":"2021-03-13T21:31:23","modified_gmt":"2021-03-14T05:31:23","slug":"out-with-the-old","status":"publish","type":"post","link":"http:\/\/writeasync.net\/?p=5802","title":{"rendered":"Letter Boxed: out with the old"},"content":{"rendered":"<p><a href=\"https:\/\/en.wikipedia.org\/wiki\/Software_rot\">Software rot<\/a> is inevitable if projects are not maintained with care. It is nice to think that in today&#8217;s <a href=\"https:\/\/en.wikipedia.org\/wiki\/Write_once,_run_anywhere\">write once, run anywhere<\/a> world, a rock-solid application with no planned changes can continue to work <a href=\"https:\/\/kevinchalet.com\/2019\/04\/11\/forcing-an-old-net-application-to-support-tls-1-2-without-recompiling-it\/\">perhaps without even recompiling<\/a>; of course, reality is a lot more complex. Frameworks are updated (hello, <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/core\/dotnet-five\">.NET 5<\/a>!), new libraries are released, and you never know when a <a href=\"https:\/\/github.com\/dotnet\/runtime\/issues\/49377\">security vulnerability will require an immediate patch and redeploy<\/a> regardless of how stable your own code is. To that end, let&#8217;s revisit the <a href=\"http:\/\/writeasync.net\/?p=5615\">Letter Boxed codebase<\/a> and update it for the new decade (already in progress).<\/p>\n<p><strong>First order of business: upgrade to .NET 5.0!<\/strong> Two years ago, <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/announcing-net-core-2-2\/\">.NET Core 2.2<\/a> was the latest and greatest. Needless to say, <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/introducing-net-5\/\">a lot has happened in the .NET world since then<\/a>. Luckily, <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/core\/compatibility\/\">compatibility is a strong focus<\/a> for every .NET release. In this case, I encountered no major issues in the upgrade &#8212; it was <em>almost<\/em> as simple as changing the <code>&lt;TargetFramework&gt;<\/code> in each .csproj file: <a href=\"https:\/\/github.com\/brian-dot-net\/games\/commit\/6f10f6ce3e6e2b05169a62bbbb15fc8883500bdc\">[see commit on GitHub]<\/a><\/p>\n<p><strong>Next up: use CMake instead of .vcxproj files!<\/strong> As you may recall, I wrote a series of posts to show <a href=\"http:\/\/writeasync.net\/?p=5765\">how to leverage CMake for native app development of a cross-platform project<\/a>. While cross-platform is not a primary focus here, the simplicity that CMake brings combined with relatively good Visual Studio support is a boon for development. This change was significantly more involved, since I opted to concurrently migrate to GTest for better CMake integration. However, I hope you&#8217;ll agree that the result is quite a bit better than carrying around .sln and .vcxproj files: <a href=\"https:\/\/github.com\/brian-dot-net\/games\/commit\/a0d7bd8f08a446428d228dd5ee6313d4f844f178\">[see commit on GitHub]<\/a><\/p>\n<p><strong>On to the next improvement: slngen + traversal projects!<\/strong> Checked in solution files are pass\u00e9. It&#8217;s easy to <a href=\"https:\/\/microsoft.github.io\/slngen\/\">generate a solution file with slngen<\/a> given a set of <code>dirs.proj<\/code> <a href=\"https:\/\/github.com\/Microsoft\/MSBuildSdks\/tree\/master\/src\/Traversal\">traversal projects using the corresponding MSBuild SDK<\/a>. Even though I added several files (including <a href=\"https:\/\/docs.microsoft.com\/en-us\/dotnet\/core\/tools\/global-json?tabs=netcore3x\">global.json<\/a>), I achieved net negative lines after removing the .sln: <a href=\"https:\/\/github.com\/brian-dot-net\/games\/commit\/3e3d1be4d18afb09d94f41c9cc95552528d59b70\">[see commit on GitHub]<\/a><\/p>\n<p><strong>Embrace central package versioning!<\/strong> Did you know that the .NET SDK now supports <a href=\"https:\/\/github.com\/NuGet\/Home\/issues\/6764\">central package versioning<\/a> out of the box? I didn&#8217;t either until I saw <a href=\"https:\/\/stu.dev\/managing-package-versions-centrally\/\">a tutorial<\/a> from <a href=\"https:\/\/twitter.com\/stuartblang\">Stuart Lang<\/a>. Previously I had only been aware of the <a href=\"https:\/\/github.com\/microsoft\/MSBuildSdks\/tree\/master\/src\/CentralPackageVersions\">add-on SDK<\/a> for the same, but I&#8217;ll gladly take a simpler built-in solution: <a href=\"https:\/\/github.com\/brian-dot-net\/games\/commit\/534dc8aebef91beba99de418e011b512e5c84602\">[see commit on GitHub]<\/a><\/p>\n<p><strong>Upgrade all the packages!<\/strong> With our new central registry of packages, it&#8217;s simple to do a comprehensive upgrade in one localized change: <a href=\"https:\/\/github.com\/brian-dot-net\/games\/commit\/1a75e45b08d2d3366f21fb342c07e7bd7862b83c\">[see commit on GitHub]<\/a><\/p>\n<p><strong>Unify the output directory!<\/strong> In my earlier exploration of <a href=\"http:\/\/writeasync.net\/?p=5790\">CMake + .NET interop<\/a>, I showed how to ensure the .NET and native artifacts landed in the same output root (effectively achieving a <a href=\"https:\/\/github.com\/aarnott\/ReadOnlySourceTree\">read-only source tree<\/a>). This time there was a small complication because of the <a href=\"https:\/\/benchmarkdotnet.org\/\">BenchmarkDotNet<\/a> projects; normally, these would generate customized benchmark projects at runtime and build them with the .NET SDK, but this <a href=\"https:\/\/github.com\/dotnet\/BenchmarkDotNet\/issues\/377\">causes issues<\/a> with a heavily customized output path. Luckily there is an <a href=\"https:\/\/benchmarkdotnet.org\/articles\/samples\/IntroInProcess.html\"><code>[InProcess]<\/code><\/a> job option to completely bypass the external compile\/run step which got things working perfectly: <a href=\"https:\/\/github.com\/brian-dot-net\/games\/commit\/7e70d060bfe6c44e1cfdaf80e6da594c01c1eb66\">[see commit on GitHub]<\/a><\/p>\n<p><strong>Finally, enhance the command line experience!<\/strong> Again, drawing from <a href=\"http:\/\/writeasync.net\/?p=5779\">the CMake saga<\/a>, I added a build.cmd script to automate the build steps for both C++ and .NET, all from the comfort of the command line: <a href=\"https:\/\/github.com\/brian-dot-net\/games\/commit\/e212307b7320aa6edced4ae391920c85d9708dd7\">[see commit on GitHub]<\/a><\/p>\n<p>As a final note, I have a new computer which means I need to reestablish the benchmark baselines. Let&#8217;s run <code>build release<\/code> and execute the managed and native Letter Boxed solvers for old time&#8217;s sake:<\/p>\n<table>\n<tr>\n<th>Puzzle<\/th>\n<th>.NET Core Solver<\/th>\n<th>C++ Solver<\/th>\n<\/tr>\n<tr>\n<th>RME<br \/>WCL<\/br>TGK<br \/>API<\/th>\n<td>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n&#x5B;000.000] Loading trie...\r\n&#x5B;000.052] Loaded 162309 words.\r\n&#x5B;000.053] Finding valid words...\r\n&#x5B;000.059] Found 754 valid words.\r\n&#x5B;000.059] Finding solutions...\r\nMARKETPLACE-EARWIG\r\nWIGWAM-MARKETPLACE\r\nPRAGMATIC-CAKEWALK\r\n&#x5B;000.065] Done.\r\n<\/pre>\n<\/td>\n<td>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n&#x5B;000.000] Loading trie...\r\n&#x5B;000.044] Loaded 162309 words.\r\n&#x5B;000.044] Finding valid words...\r\n&#x5B;000.048] Found 754 valid words.\r\n&#x5B;000.048] Finding solutions...\r\nMARKETPLACE-EARWIG\r\nWIGWAM-MARKETPLACE\r\nPRAGMATIC-CAKEWALK\r\n&#x5B;000.050] Done.\r\n<\/pre>\n<\/td>\n<\/tr>\n<tr>\n<th>ERG<br \/>BID<br \/>NCF<br \/>TAO<\/th>\n<td>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n&#x5B;000.000] Loading trie...\r\n&#x5B;000.052] Loaded 162309 words.\r\n&#x5B;000.052] Finding valid words...\r\n&#x5B;000.059] Found 1436 valid words.\r\n&#x5B;000.060] Finding solutions...\r\nROBAND-DEFECTING\r\n -- snipped 72 other solutions --\r\nOBTECTED-DRAFTING\r\n&#x5B;000.095] Done.\r\n<\/pre>\n<\/td>\n<td>\n<pre class=\"brush: plain; title: ; notranslate\" title=\"\">\r\n&#x5B;000.000] Loading trie...\r\n&#x5B;000.045] Loaded 162309 words.\r\n&#x5B;000.045] Finding valid words...\r\n&#x5B;000.050] Found 1436 valid words.\r\n&#x5B;000.050] Finding solutions...\r\nBAITED-DEFORCING\r\n -- snipped 72 other solutions --\r\nOBTECTED-DRAFTING\r\n&#x5B;000.080] Done.\r\n<\/pre>\n<\/td>\n<\/tr>\n<\/table>\n<p>As expected, we see a nice improvement over <a href=\"http:\/\/writeasync.net\/?p=5650\">the previous results<\/a> thanks to <a href=\"https:\/\/en.wikipedia.org\/wiki\/Moore's_law\">Moore&#8217;s Law<\/a> (and perhaps the steady <a href=\"https:\/\/devblogs.microsoft.com\/dotnet\/performance-improvements-in-net-5\/\">improvement of the .NET runtime<\/a>). Whew! Nothing like a good spring cleaning to revitalize a &#8220;legacy&#8221; project.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Software rot is inevitable if projects are not maintained with care. It is nice to think that in today&#8217;s write once, run anywhere world, a rock-solid application with no planned changes can continue to work perhaps without even recompiling; of&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,105,102,31],"tags":[],"class_list":["post-5802","post","type-post","status-publish","format-standard","hentry","category-cross-platform","category-games","category-legacy","category-scripts"],"_links":{"self":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/5802","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=5802"}],"version-history":[{"count":5,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/5802\/revisions"}],"predecessor-version":[{"id":5807,"href":"http:\/\/writeasync.net\/index.php?rest_route=\/wp\/v2\/posts\/5802\/revisions\/5807"}],"wp:attachment":[{"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=5802"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=5802"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/writeasync.net\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=5802"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}