<?xml version="1.0" encoding="utf-8"?>
<rss xmlns:atom="https://www.w3.org/2005/Atom" xmlns:sy="https://purl.org/rss/1.0/modules/syndication/" xmlns:dc="https://purl.org/dc/elements/1.1/" xmlns:content="https://purl.org/rss/1.0/modules/content/" xmlns:wfw="https://wellformedweb.org/CommentAPI/" xmlns:slash="https://purl.org/rss/1.0/modules/slash/"
  version="2.0">
  <channel>
    <title>Kirill Osenkov</title>
    <atom:link
      href="https://docs.microsoft.com/archive/blogs/kirillosenkov/feed.xml"
      rel="self"
      type="application/rss+xml" />
    <link>https://docs.microsoft.com/archive/blogs/kirillosenkov/feed.xml</link>
    <description>Visual Studio Team, Roslyn Services QA</description>
    <lastBuildDate>Sun, 16 Jun 2019 23:40:07 GMT</lastBuildDate>
    <language>en-US</language>
    <sy:updatePeriod>hourly</sy:updatePeriod>
    <sy:updateFrequency>1</sy:updateFrequency>
    <item>
      <title>Kirill's Blog</title>
      <link>https://docs.microsoft.com/archive/blogs/kirillosenkov/kirills-blog</link>
      <pubDate>Sun, 16 Jun 2019 23:40:07 GMT</pubDate>
      <dc:creator><![CDATA[Kirill Osenkov - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/kirillosenkov/?p=1745</guid>
      <description><![CDATA[Microsoft is making some changes to this blogging platform and I decided it's time to move this blog...]]></description>
      <content:encoded><![CDATA[Microsoft is making some changes to this blogging platform and I decided it's time to move this blog to my own website that I fully control. It will probably take time for me to re-establish my blog presence elsewhere, so for now you can find me on:

Twitter: <a href="https://twitter.com/KirillOsenkov">https://twitter.com/KirillOsenkov</a>

GitHub: <a href="https://github.com/KirillOsenkov">https://github.com/KirillOsenkov</a>

and probably my website <a href="https://osenkov.com">https://osenkov.com</a>

It's been great having a blog here and thank you so much everyone for following along!]]></content:encoded>
    </item>
    <item>
      <title>Announcing MSBuild Structured Log: record and visualize your builds</title>
      <link>https://docs.microsoft.com/archive/blogs/kirillosenkov/announcing-msbuild-structured-log-record-and-visualize-your-builds</link>
      <pubDate>Fri, 13 May 2016 01:13:42 GMT</pubDate>
      <dc:creator><![CDATA[Kirill Osenkov - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/kirillosenkov/?p=1725</guid>
      <description><![CDATA[I’ve just published a new tool: https://github.com/KirillOsenkov/MSBuildStructuredLog It is an...]]></description>
      <content:encoded><![CDATA[<p>I’ve just published a new tool:</p> <p><a title="https://github.com/KirillOsenkov/MSBuildStructuredLog" href="https://github.com/KirillOsenkov/MSBuildStructuredLog">https://github.com/KirillOsenkov/MSBuildStructuredLog</a></p> <p>It is an MSBuild logger that can be passed to MSBuild during a build and it records information about everything that happens in the build. But instead of just dumping everything into a huge text file it preserves the structure and relationships between elements, and lets you save and open the structured log in .xml format. This has significant advantages to help you understand and navigate large build logs.</p> <p>Give it a try and let me know what you think!</p> <p><a href="https://msdnshared.blob.core.windows.net/media/2016/05/Screenshot1.png"><img title="Screenshot1" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="Screenshot1" src="https://msdnshared.blob.core.windows.net/media/2016/05/Screenshot1_thumb.png" width="1554" height="1568"></a></p>]]></content:encoded>
    </item>
    <item>
      <title>Where to Download WPF Performance Suite? (Perforator, Visual Profiler)</title>
      <link>https://docs.microsoft.com/archive/blogs/kirillosenkov/where-to-download-wpf-performance-suite-perforator-visual-profiler</link>
      <pubDate>Tue, 05 Apr 2016 04:11:56 GMT</pubDate>
      <dc:creator><![CDATA[Kirill Osenkov - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/kirillosenkov/?p=1693</guid>
      <description><![CDATA[I was having trouble finding the WPF performance tools mentioned in this blog:...]]></description>
      <content:encoded><![CDATA[<p>I was having trouble finding the WPF performance tools mentioned in this blog:</p> <p><a title="https://blogs.msdn.microsoft.com/jgoldb/2008/09/26/updated-wpfperf-performance-profiling-tools-for-wpf/" href="https://blogs.msdn.microsoft.com/jgoldb/2008/09/26/updated-wpfperf-performance-profiling-tools-for-wpf/">https://blogs.msdn.microsoft.com/jgoldb/2008/09/26/updated-wpfperf-performance-profiling-tools-for-wpf/</a></p> <p>I had to ask around, but here are the links to download the tools (Windows Performance Toolkit Setup):</p> <p><a title="https://download.microsoft.com/download/A/6/A/A6AC035D-DA3F-4F0C-ADA4-37C8E5D34E3D/setup/WinSDKPerformanceToolKit_amd64/wpt_x64.msi" href="https://download.microsoft.com/download/A/6/A/A6AC035D-DA3F-4F0C-ADA4-37C8E5D34E3D/setup/WinSDKPerformanceToolKit_amd64/wpt_x64.msi">https://download.microsoft.com/download/A/6/A/A6AC035D-DA3F-4F0C-ADA4-37C8E5D34E3D/setup/WinSDKPerformanceToolKit_amd64/wpt_x64.msi</a></p> <p><a href="https://msdnshared.blob.core.windows.net/media/2016/04/image111.png"><img title="image" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/04/image_thumb98.png" width="622" height="484"></a></p> <p>After installing this one, also download and run the patch:</p> <p><a title="https://download.microsoft.com/download/1/8/9/189A7832-49D8-4978-85E8-3DFFF44E6C04/WpfPerf_timezone_patch.msp" href="https://download.microsoft.com/download/1/8/9/189A7832-49D8-4978-85E8-3DFFF44E6C04/WpfPerf_timezone_patch.msp">https://download.microsoft.com/download/1/8/9/189A7832-49D8-4978-85E8-3DFFF44E6C04/WpfPerf_timezone_patch.msp</a></p> <p>When the patch installer prompts, choose “Repair”.</p> <p>Then you can launch WPF Performance Suite (which has two tools: Perforator and Visual Profiler):</p> <p><a href="https://msdnshared.blob.core.windows.net/media/2016/04/image112.png"><img title="image" style="border-top: 0px;border-right: 0px;border-bottom: 0px;padding-top: 0px;padding-left: 0px;border-left: 0px;padding-right: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/2016/04/image_thumb99.png" width="597" height="484"></a></p>]]></content:encoded>
    </item>
    <item>
      <title>Added ContentSync command line arguments</title>
      <link>https://docs.microsoft.com/archive/blogs/kirillosenkov/added-contentsync-command-line-arguments</link>
      <pubDate>Sat, 23 Jan 2016 16:27:03 GMT</pubDate>
      <dc:creator><![CDATA[Kirill Osenkov - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/kirillosenkov/2016/01/23/added-contentsync-command-line-arguments/</guid>
      <description><![CDATA[I’ve added various command line arguments to the tool I blogged about the last time.   The new...]]></description>
      <content:encoded><![CDATA[<p>I’ve added various command line arguments to the tool I <a href="https://blogs.msdn.com/b/kirillosenkov/archive/2016/01/09/contentsync-a-content-based-file-copy-tool.aspx">blogged about the last time</a>. </p>  <p>The new release is here:</p>  <p><a title="https://github.com/KirillOsenkov/ContentSync/releases/tag/v1.1" href="https://github.com/KirillOsenkov/ContentSync/releases/tag/v1.1">https://github.com/KirillOsenkov/ContentSync/releases/tag/v1.1</a></p>  <p>Now you can customize what operations to perform:</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/96/75/metablogapi/1346.image_1376BC9E.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-96-75-metablogapi/1346.image_5F00_1376BC9E.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/96/75/metablogapi/6746.image_thumb_1A29C621.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-96-75-metablogapi/6746.image_5F00_thumb_5F00_1A29C621.png" width="1164" height="97" /></a></p>  <p><strong>-c</strong>&#160;&#160;&#160;&#160;&#160;&#160; Copy files from source that don't exist in destination (left-only).</p>  <p><strong>-u</strong>&#160;&#160;&#160;&#160;&#160;&#160; Update files that have changed between source and destination. This only overwrites the destination file if the contents are different.</p>  <p><strong>-d</strong>&#160;&#160;&#160;&#160;&#160;&#160; Delete right-only files (that are in destination but not in source).</p>  <p><strong>-ds</strong>&#160;&#160;&#160;&#160;&#160; Delete same files (that exist in source and destination and are same).</p>  <p><strong>-dc</strong>&#160;&#160;&#160;&#160;&#160; Delete changed files from destination (can't be used with -u).</p>  <p><strong>-whatif</strong>&#160; Print what would have been done (without changing anything).</p>  <p><strong>-h</strong>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; Help (duh!)</p>  <p><strong>Examples:</strong></p>  <p>Update only the files that exist both in source and destination:</p>  <p><strong>ContentSync.exe C:\Source D:\Destination –u</strong></p>  <p>This scenario is useful when you’ve selected a subset from a folder and copied it to a separate folder, but now realized that you want to copy the same subset to the new folder again from updated source.</p>  <p>This command will copy new files and update changed ones, but won’t delete any extra remaining files from destination:</p>  <p><strong>ContentSync.exe C:\Source D:\Destination –cu</strong></p>  <p>Additionally you can use <strong>–whatif</strong> for speculative mode, when it prints what it would have done without actually changing anything on disk:</p>  <p><strong>ContentSync.exe C:\Source D:\Destination –d –whatif</strong></p>  <p>This prints out all the files that are in destination but not in source (without actually deleting them from the destination). Just passing –whatif results in diffing the contents of both folders (it doesn’t actually show detailed file diffs, just shows which files are identical and which are different).</p>  <p>I wanted to just take robocopy options, but they didn’t map exactly to the concepts I wanted to represent. So I opted for a new argument syntax.</p>  <p>Finally it prints out a summary at the end:</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/96/75/metablogapi/5554.image_130A89A9.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-96-75-metablogapi/5554.image_5F00_130A89A9.png"><img title="image" style="border-top: 0px; border-right: 0px; background-image: none; border-bottom: 0px; padding-top: 0px; padding-left: 0px; border-left: 0px; display: inline; padding-right: 0px" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/96/75/metablogapi/5001.image_thumb_4ECA225C.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-96-75-metablogapi/5001.image_5F00_thumb_5F00_4ECA225C.png" width="551" height="249" /></a></p>]]></content:encoded>
    </item>
    <item>
      <title>EnableVSIPLogging to show VS command info</title>
      <link>https://docs.microsoft.com/archive/blogs/kirillosenkov/enablevsiplogging-to-show-vs-command-info</link>
      <pubDate>Tue, 19 Jan 2016 20:43:53 GMT</pubDate>
      <dc:creator><![CDATA[Kirill Osenkov - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/kirillosenkov/2016/01/19/enablevsiplogging-to-show-vs-command-info/</guid>
      <description><![CDATA[Found an interesting trick here:...]]></description>
      <content:encoded><![CDATA[<p>Found an interesting trick here:</p>  <p><a title="https://blogs.msdn.com/b/dr._ex/archive/2007/04/17/using-enablevsiplogging-to-identify-menus-and-commands-with-vs-2005-sp1.aspx" href="https://blogs.msdn.com/b/dr._ex/archive/2007/04/17/using-enablevsiplogging-to-identify-menus-and-commands-with-vs-2005-sp1.aspx">https://blogs.msdn.com/b/dr._ex/archive/2007/04/17/using-enablevsiplogging-to-identify-menus-and-commands-with-vs-2005-sp1.aspx</a></p>  <p>Run this .reg file:</p>  <p>Windows Registry Editor Version 5.00</p>  <p>[HKEY_CURRENT_USER\SOFTWARE\Microsoft\VisualStudio\14.0\General]   <br />&quot;EnableVSIPLogging&quot;=dword:00000001</p>  <p>Then in Visual Studio hold Ctrl+Shift and click any menu, toolbar command or context menu item. You will get a message box with the info about the command (like below):</p>  <p>---------------------------   <br />VSDebug Message    <br />---------------------------    <br />Command data:</p>  <p>&#160;&#160;&#160; Guid = {1492DB0A-85A2-4E43-BF0D-CE55B89A8CC6}   <br />&#160;&#160;&#160; GuidID = 849    <br />&#160;&#160;&#160; CmdID = 1    <br />&#160;&#160;&#160; Type = 0x00000001    <br />&#160;&#160;&#160; Flags = 0x80000000    <br />&#160;&#160;&#160; Canonical name = View.C#Interactive    <br />&#160;&#160;&#160; Localized name = (Not set)    <br />---------------------------    <br />OK&#160;&#160; <br />---------------------------    </p>]]></content:encoded>
    </item>
    <item>
      <title>ContentSync – a content-based file copy tool</title>
      <link>https://docs.microsoft.com/archive/blogs/kirillosenkov/contentsync-a-content-based-file-copy-tool</link>
      <pubDate>Sat, 09 Jan 2016 20:59:24 GMT</pubDate>
      <dc:creator><![CDATA[Kirill Osenkov - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/kirillosenkov/2016/01/09/contentsync-a-content-based-file-copy-tool/</guid>
      <description><![CDATA[Suppose you need to sync the contents of two large folders, Source and Destination. Normally...]]></description>
      <content:encoded><![CDATA[<p>Suppose you need to sync the contents of two large folders, Source and Destination. Normally <b>robocopy *.* Source Destination /MIR</b> does the job. However even if the byte content of a file didn’t change, but the timestamp did, robocopy will copy the file and change the timestamp of the destination to match the source.</p>  <p>This is the safe and expected behavior, however there are whole classes of tools that track file modification based off the timestamp alone. For instance, MSBuild will trigger a cascading rebuild of all dependencies of a file if its timestamp has changed (even if the actual file contents is exactly the same). This is called overbuilding. A scalable build system should detect that the file contents didn’t change and avoid doing any work in this case.</p>  <p>Or take another example, suppose you need to upload hundreds of thousands of files to Azure using MSDeploy. If the timestamp on those files has changed, MSDeploy will upload those files even if the actual content is the same.</p>  <p>In general, if you’re deciding whether a file was modified based off the timestamp, you’re bound to schedule unnecessary work that could have been avoided if you checked whether the actual file bytes have changed.</p>  <p>Long story short, I wrote an open-source tool to sync/mirror directories based on the file content, not the timestamp:</p>  <p><a href="https://github.com/KirillOsenkov/ContentSync">https://github.com/KirillOsenkov/ContentSync</a></p>  <p>Usage: ContentSync.exe &lt;Source&gt; &lt;Destination&gt;</p>  <p>I needed this for the exact Azure website deployment scenario: I maintain <a href="https://source.roslyn.io">https://source.roslyn.io</a>, and we’ve recently moved from on-premises Microsoft hosting to Azure. I use <a href="https://github.com/KirillOsenkov/SourceBrowser">https://github.com/KirillOsenkov/SourceBrowser</a> to regenerate the website every night, and that’s 160,000 modified files totaling over 1 GB. You don’t want to upload that to Azure every night.</p>  <p>So I inserted an intermediate step into my deployment script – I ContentSync from the freshly built Index folder (it’s not incremental, all the files are generated from scratch every time) into a Staging folder from the last time. ContentSync doesn’t touch the files that haven’t actually changed (and that’s 99.9…%), and so MSDeploy doesn’t upload them since the timestamp is the same.</p>  <p>While I’m here, kudos to David Ebbo and his excellent <a href="https://github.com/davidebbo/WAWSDeploy">https://github.com/davidebbo/WAWSDeploy</a> that I use to simply deploy a folder to Azure (read more here: <a href="https://blog.davidebbo.com/2014/03/WAWSDeploy.html">https://blog.davidebbo.com/2014/03/WAWSDeploy.html</a>). It uses MSDeploy internally and hides all the details I don’t want to know about behind a super simple UX.</p>]]></content:encoded>
    </item>
    <item>
      <title>Live Writer is open source!</title>
      <link>https://docs.microsoft.com/archive/blogs/kirillosenkov/live-writer-is-open-source</link>
      <pubDate>Wed, 09 Dec 2015 14:35:36 GMT</pubDate>
      <dc:creator><![CDATA[Kirill Osenkov - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/kirillosenkov/2015/12/09/live-writer-is-open-source/</guid>
      <description><![CDATA[All blog posts in this blog have been posted using Live Writer, a great blogging tool we’ve had for...]]></description>
      <content:encoded><![CDATA[<p>All blog posts in this blog have been posted using Live Writer, a great blogging tool we’ve had for years. However it stopped evolving in recent years and I almost lost hope… but!</p>  <p>I’m now posting this from the brand new OpenLiveWriter which I just compiled and built myself!</p>  <p><a title="https://github.com/OpenLiveWriter/OpenLiveWriter" href="https://github.com/OpenLiveWriter/OpenLiveWriter">https://github.com/OpenLiveWriter/OpenLiveWriter</a></p>  <p>Awesome!!!</p>]]></content:encoded>
    </item>
    <item>
      <title>An open-source full-fidelity XML parser</title>
      <link>https://docs.microsoft.com/archive/blogs/kirillosenkov/an-open-source-full-fidelity-xml-parser</link>
      <pubDate>Tue, 25 Aug 2015 23:10:44 GMT</pubDate>
      <dc:creator><![CDATA[Kirill Osenkov - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/kirillosenkov/2015/08/25/an-open-source-full-fidelity-xml-parser/</guid>
      <description><![CDATA[A while back I needed to understand XML at a low-level, including whitespace, line breaks and...]]></description>
      <content:encoded><![CDATA[<p>A while back I needed to understand XML at a low-level, including whitespace, line breaks and comments. While XLinq is a fantastic and powerful library, it does lack a few things, for instance I noticed it doesn’t preserve whitespace around attributes. Nor does it expose the position/line/column information about the nodes.</p>  <p>So I took a part of Roslyn VB parser that parses XML literals, ported it to C# and made it into a standalone library that I could use for my purposes. Realizing that it might fill some gaps for certain scenarios out there I’m putting it out as open-source:</p>  <p><a title="https://github.com/KirillOsenkov/XmlParser" href="https://github.com/KirillOsenkov/XmlParser">https://github.com/KirillOsenkov/XmlParser</a></p>  <p>And here’s an example of what I needed it for: notice the colored XML with hyperlinks?</p>  <p><a title="https://referencesource.microsoft.com/#MSBuildFiles/C/ProgramFiles(x86)/MSBuild/14.0/bin_/amd64/Microsoft.CSharp.Core.targets,67" href="https://referencesource.microsoft.com/#MSBuildFiles/C/ProgramFiles(x86)/MSBuild/14.0/bin_/amd64/Microsoft.CSharp.Core.targets,67">https://referencesource.microsoft.com/#MSBuildFiles/C/ProgramFiles(x86)/MSBuild/14.0/bin_/amd64/Microsoft.CSharp.Core.targets,67</a></p>  <p>The parser is also available on NuGet: </p>  <p><a title="Microsoft.Language.Xml" href="https://www.nuget.org/packages/Microsoft.Language.Xml">Microsoft.Language.Xml</a></p>  <p><a href="https://www.nuget.org/packages/Microsoft.Language.Xml.Editor">Microsoft.Language.Xml.Editor</a></p>  <p>The editor one is a simple language service for Visual Studio, which is less useful since Visual Studio already has a far more powerful language services for XML than this one. The advantage here however is that you can tweak this one any way you want to add further extensibility features to XML files in Visual Studio on top of what’s already there.</p>]]></content:encoded>
    </item>
    <item>
      <title>Why is Environment.CurrentDirectory bad?</title>
      <link>https://docs.microsoft.com/archive/blogs/kirillosenkov/why-is-environment-currentdirectory-bad</link>
      <pubDate>Tue, 16 Jun 2015 22:50:15 GMT</pubDate>
      <dc:creator><![CDATA[Kirill Osenkov - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/kirillosenkov/2015/06/16/why-is-environment-currentdirectory-bad/</guid>
      <description><![CDATA[It is bad in general for various reasons, but it’s especially bad in build tools and related space...]]></description>
      <content:encoded><![CDATA[<p>It is bad in general for various reasons, but it’s especially bad in build tools and related space (like CI servers and such).</p>  <p>MSBuild could have had fine-grained parallelism where it could execute multiple tasks on different threads in parallel within the same process, significantly speeding up builds. However it can’t do that because many tasks rely on Environment.CurrentDirectory which is per process, and so to make sure the current directory is deterministic and well-known at the time a task executes, there can only be one task running at a time within the same process.</p>  <p>Additionally, it is terrible for discoverability and ability to reason about assumptions. Relying on Environment.CurrentDirectory makes an implicit assumption that is not recorded anywhere. For instance, here’s a field in Team Build that lets you select unit-test assemblies to run:</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/96/75/metablogapi/5758.image_46677B9E.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-96-75-metablogapi/5758.image_5F00_46677B9E.png"><img title="image" style="border: 0px currentcolor; display: inline; background-image: none;" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/96/75/metablogapi/1157.image_thumb_3AD1BE5F.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-96-75-metablogapi/1157.image_5F00_thumb_5F00_3AD1BE5F.png" width="403" height="42" /></a></p>  <p>Where is this pattern rooted? To be able to write a pattern you need to know if its relative (to what?) and in which directory it is rooted. It obviously can’t be a full path since on the CI server the path varies by build agent id. After reading the source code for the build activity that calculates the files from this spec I found this:</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/96/75/metablogapi/2627.image_48A4045A.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-96-75-metablogapi/2627.image_5F00_48A4045A.png"><img title="image" style="border: 0px currentcolor; display: inline; background-image: none;" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/96/75/metablogapi/3288.image_thumb_56764A55.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-96-75-metablogapi/3288.image_5F00_thumb_5F00_56764A55.png" width="757" height="109" /></a></p>  <p>This code relies on the current directory being set… where? To what? It’s a nightmare scenario for maintainers.</p>  <p>Last but not least, Environment.CurrentDirectory is global mutable state. Worse, it is global mutable state that makes it impossible to track causality. Who set this last? Where to put a breakpoint to intercept mutation?</p>  <p>I’m sure there are multiple reasons I’m missing but I don’t even care to enumerate exhaustively. The above is terrifying enough to send a clear message: avoid at all costs!</p>  <p>But, a reasonable response to that would be: what to do instead? Use patterns like $(SolutionRoot)\bin\Release\*.Tests.dll instead of just bin\Release. Every path should be rooted (maybe with a variable that is well defined) so that its clear where it is. Pass known variables around, avoid the untraceable global mutable state from the 80’s.</p>  <p>Same goes for environment variables. Avoid at all costs! If a build requires an environment variable set to work correctly, it has failed.</p>  <p>P.S. Also riddle me this. Why on earth is there a Directory.GetCurrentDirectory()?</p>]]></content:encoded>
    </item>
    <item>
      <title>MSBuild: unnecessary rebuilds because of generated AssemblyAttributes.cs</title>
      <link>https://docs.microsoft.com/archive/blogs/kirillosenkov/msbuild-unnecessary-rebuilds-because-of-generated-assemblyattributes-cs</link>
      <pubDate>Tue, 12 May 2015 22:44:33 GMT</pubDate>
      <dc:creator><![CDATA[Kirill Osenkov - MSFT]]></dc:creator>
      <guid
        isPermaLink="false">https://blogs.msdn.microsoft.com/kirillosenkov/2015/05/12/msbuild-unnecessary-rebuilds-because-of-generated-assemblyattributes-cs/</guid>
      <description><![CDATA[A surprisingly common cause for unnecessary rebuilds of C# MSBuild projects is an unfortunate design...]]></description>
      <content:encoded><![CDATA[<p>A surprisingly common cause for unnecessary rebuilds of C# MSBuild projects is an unfortunate design in a part of MSBuild tooling that deals with generating AssemblyAttributes.cs. See for example this bug on Connect: <a title="https://connect.microsoft.com/VisualStudio/feedback/details/1269019/visualstudio-caches-wrong-path-for-netframework-version-v4-5-assemblyattributes-cs-file" href="https://connect.microsoft.com/VisualStudio/feedback/details/1269019/visualstudio-caches-wrong-path-for-netframework-version-v4-5-assemblyattributes-cs-file">https://connect.microsoft.com/VisualStudio/feedback/details/1269019/visualstudio-caches-wrong-path-for-netframework-version-v4-5-assemblyattributes-cs-file</a></p>  <p>But let us step back.</p>  <p>Let’s create a new C# Console Application in Visual Studio and build it with diagnostic verbosity. The most commonly useful tool to investigate build issues is the MSBuild diagnostic log:</p>  <p><strong>msbuild.exe ConsoleApplication1.csproj /fl1 /noconlog /v:diag</strong></p>  <p>(fl1 = write log to file msbuild1.log, noconlog = don’t write to console, v:diag = diagnostic verbosity)</p>  <p>When you open msbuild1.log and search for “csc”, you will see that the compiler invocation indeed includes a strange file:</p>  <p>csc.exe /noconfig /nowarn:1701,1702 /nostdlib+ /platform:anycpu32bitpreferred /errorreport:prompt /warn:4 /define:DEBUG;TRACE /highentropyva+ /reference:&quot;C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5\Microsoft.CSharp.dll&quot;&#160; ... /debug+ /debug:full /filealign:512 /optimize- /out:obj\Debug\CS9.exe /subsystemversion:6.00 /target:exe /utf8output Program.cs Properties\AssemblyInfo.cs <strong>&quot;<font style="background-color: rgb(255, 255, 0);">C:\Users\kirill\AppData\Local\Temp\.NETFramework,Version=v4.5.AssemblyAttributes.cs</font>&quot;</strong></p>  <p>Turns out, every time you build a C# project, MSBuild makes sure there’s a generated file in the TEMP directory called AssemblyAttributes.cs with content similar to this:</p>  <p>// &lt;autogenerated /&gt;    <br />using System;     <br /> using System.Reflection;     <br /> [assembly: global::System.Runtime.Versioning.TargetFrameworkAttribute(&quot;.NETFramework,Version=v4.5&quot;, FrameworkDisplayName = &quot;.NET Framework 4.5&quot;)]</p>  <p>But how does it get there?</p>  <p>Let’s open the excellent free MSBuild Explorer tool (<a href="https://msbuildexplorer.com">https://msbuildexplorer.com</a>) and open our .csproj file:</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/96/75/metablogapi/4188.image_47813DFB.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-96-75-metablogapi/4188.image_5F00_47813DFB.png"><img title="image" style="border: 0px currentcolor; display: inline; background-image: none;" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/96/75/metablogapi/8473.image_thumb_4544B53F.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-96-75-metablogapi/8473.image_5F00_thumb_5F00_4544B53F.png" width="959" height="240" /></a></p>  <p>In the list of properties we find the TargetFrameworkMonikerAssemblyAttributesPath MSBuild property which is the same path that’s passed to the C# compiler. But who sets this MSBuild property and where?</p>  <p>Let’s now open <a href="https://source.roslyn.io">https://source.roslyn.io</a> which happens to include semantically hyperlinked MSBuild targets for C# and search for our property name: <a title="https://source.roslyn.io/#q=TargetFrameworkMonikerAssemblyAttributesPath" href="https://source.roslyn.io/#q=TargetFrameworkMonikerAssemblyAttributesPath">https://source.roslyn.io/#q=TargetFrameworkMonikerAssemblyAttributesPath</a>.</p>  <p>We indeed find an MSBuild property with this name and can view its declarations and usages:</p>  <p><a title="https://source.roslyn.io/#MSBuildProperty=TargetFrameworkMonikerAssemblyAttributesPath" href="https://source.roslyn.io/#MSBuildProperty=TargetFrameworkMonikerAssemblyAttributesPath">https://source.roslyn.io/#MSBuildProperty=TargetFrameworkMonikerAssemblyAttributesPath</a></p>  <p>We see the one place in the common targets where this property is written to:</p>  <p><a title="https://source.roslyn.io/#MSBuildFiles/C/ProgramFiles(x86)/MSBuild/14.0/bin_/amd64/Microsoft.Common.CurrentVersion.targets,2928" href="https://source.roslyn.io/#MSBuildFiles/C/ProgramFiles(x86)/MSBuild/14.0/bin_/amd64/Microsoft.Common.CurrentVersion.targets,2928">https://source.roslyn.io/#MSBuildFiles/C/ProgramFiles(x86)/MSBuild/14.0/bin_/amd64/Microsoft.Common.CurrentVersion.targets,2928</a></p>  <p>Oh hey, look:</p>  <p>&lt;TargetFrameworkMonikerAssemblyAttributesPath Condition=&quot;'$(TargetFrameworkMonikerAssemblyAttributesPath)' == ''&quot;&gt;$([System.IO.Path]::Combine('<font style="background-color: rgb(255, 255, 0);"><strong>$([System.IO.Path]::GetTempPath())</strong></font>','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))&lt;/TargetFrameworkMonikerAssemblyAttributesPath&gt;</p>  <p>This is the unfortunate place in MSBuild default targets that generates the file to the TEMP directory. Why is this bad? Well, it turns out, the location of TEMP may change if, for example, you use Remote Desktop to log on to your machine – the TEMP folder is different than your console sessions: <a title="https://blogs.msdn.com/b/oldnewthing/archive/2011/01/25/10119675.aspx" href="https://blogs.msdn.com/b/oldnewthing/archive/2011/01/25/10119675.aspx">https://blogs.msdn.com/b/oldnewthing/archive/2011/01/25/10119675.aspx</a></p>  <p>And so MSBuild sees that the file timestamp is newer than it remembers and decides to rebuild the project.</p>  <p>Let’s do an experiment. If you haven’t yet, go and set <a href="https://blogs.msdn.com/b/kirillosenkov/archive/2014/08/04/how-to-investigate-rebuilding-in-visual-studio-when-nothing-has-changed.aspx">this registry key that I blogged about earlier</a>. This will enable VS to tell you every time it decides to rebuild a project why it thought the project wasn’t up-to-date. Now build your console app once in VS (or in command line). Build again. Notice how it immediately says Build succeeded? Now go and edit that temporary file. Rebuild again – VS will decide that the project is out-of-date and rebuild it:</p>  <p><a href="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/96/75/metablogapi/3515.image_37726F44.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-96-75-metablogapi/3515.image_5F00_37726F44.png"><img title="image" style="border: 0px currentcolor; display: inline; background-image: none;" border="0" alt="image" src="https://msdnshared.blob.core.windows.net/media/MSDNBlogsFS/prod.evol.blogs.msdn.com/CommunityServer.Blogs.Components.WeblogFiles/00/00/00/96/75/metablogapi/6747.image_thumb_057ACEBA.png" original-url="https://blogs.msdn.com/cfs-file.ashx/__key/communityserver-blogs-components-weblogfiles/00-00-00-96-75-metablogapi/6747.image_5F00_thumb_5F00_057ACEBA.png" width="630" height="82" /></a></p>  <p>This is the problem that I mentioned in the beginning of this post. This breaks incrementality if multiple projects touch this file (and they often do!).</p>  <p>Fortunately for us, MSBuild is flexible enough so we can work around it. The good design is to generate this file into the Intermediate directory (usually called obj), because this is where all transient and temporary files should go during a build process. We can either set this property in our project file:</p>  <pre style="background: white; color: black; font-family: consolas;"><span style="color: blue;">  &lt;</span><span style="color: rgb(163, 21, 21);">PropertyGroup</span><span style="color: blue;">&gt;</span>
<span style="color: blue;">    &lt;</span><span style="color: rgb(163, 21, 21);">TargetFrameworkMonikerAssemblyAttributesPath</span><span style="color: blue;">&gt;</span>$([System.IO.Path]::Combine('$(<font style="background-color: rgb(255, 255, 0);"><strong>IntermediateOutputPath</strong></font>)','$(TargetFrameworkMoniker).AssemblyAttributes$(DefaultLanguageSourceExtension)'))<span style="color: blue;">&lt;/</span><span style="color: rgb(163, 21, 21);">TargetFrameworkMonikerAssemblyAttributesPath</span><span style="color: blue;">&gt;</span>
<span style="color: blue;">  &lt;/</span><span style="color: rgb(163, 21, 21);">PropertyGroup</span><span style="color: blue;">&gt;</span>
</pre>

<p>Or if your build uses a common .props file, set this property there. This will ensure that your build doesn’t depend on the TEMP directory and is more isolated, repeatable and incremental.</p>]]></content:encoded>
    </item>
  </channel>
</rss>