A place for spare thoughts

25/07/2011

Checking solutions and project files against some set of rules at build-time

Filed under: MSBuild, Unit testing, VisualStudio — Ivan Danilov @ 04:23

Currently I’m busy with multi-solution project. There’re several teams on the project and each team works on one or several solutions. Sometimes together, sometimes not. There are network of dependencies between solutions (e.g. some shared library that is produced as build artifact of one solution and used by two other).

Clearly that we have some set of rules how things should be organized. And this set at some stage became complex enough to be almost unmaintainable manually.

For example, each project should contain property named OutputRoot defined relative to $(SolutionDir) and OutputPath should be relative to it. Thus we achieve that every artifact is placed to some well-known place under OutputRoot which could be redefined from msbuild command line.

Or the rule that requires HintPaths properties for references should be relative to that $(OutputRoot) or $(SolutionDir). So that no one could place absolute path there that will work only in his environment. Well, it could work on others machines as well by some lucky accident. But build robustness is not that sort of things we want to have by accident.

Also there’s some general rules:

  • every C# project (*.csproj file) should belong to the solution and only one;
  • solutions can’t be nested so if some folder has *.sln file – entire tree from this point should have to other *.slns;
  • ProjectReference couldn’t point outside of solution directory or have absolute path;
  • every project should have property TreatWarningsAsErrors with value true;
  • if Deployment project exists in solution – every other project should have reference to it;
  • test projects (we are using convenience that test projects should be named like *.Tests) are marked with corresponding guids so that test runner can run them;
  • etc…

And so the idea of having some kind of source checker was born. In fact, *.sln and *.csproj are not very complex formats to parse. And we’re not going to support every possible thing in them. We want only to read and check some set of rules.

This source checker now runs on each commit in the source control from the build server and tests every rule we decided to check. If some rule was violated – it reports that, fails build and suggests what should you do to fix the thing.

It is able also to ignore some directories (for example it ignores itself because it shouldn’t be built into $(OutputRoot)). Other useful thing is that it could be integrated with TeamCity so that build log looks pretty there (you should specify /teamcity key in the command line).

Also rules could be easily added or removed.

It proved to be very useful in our day-to-day work and so I’d like to share them with everybody.

It could be not production quality sometimes and several hard-coded things could be inside but nevertheless it is still usable.

Sources could be found here. It is targeting .NET 4.0, but currently it is almost trivial to retarget it against .NET 3.5, so if you’re still there – it is not a problem.

Advertisements

20/07/2011

Deployment as part of development build

Filed under: MSBuild, VisualStudio — Ivan Danilov @ 19:25

Last two years or so I’ve been mostly developing solutions that are plugins to some other systems. Sometimes it is third-party library, sometimes it is some kind of corporate-standard-framework with some common presentation functionality whatever.

The shared feature of every such project is that is should be installed in some way into that external system. Most often installing is just copying DLLs into some predefined folder for plugins. Sometimes it is also required to register something in registry, settings of the system or somewhere else. And that is the problem. When you’re working from Visual Studio – the most natural process is to make changes and hit F5 to run and see your changes live (I don’t take testing into consideration here but it is an important part of course). Well, VS can run external application under debugger. But who will make installation steps?

I’ve seen several approaches there. Some teams just point OutputPath to external system’s plugins folder so that building takes place there. It is not a good idea as csproj files are under source version control system and paths could be different on different machines. Most obvious example is that when external system is 32bit – it has different paths on 32bit and 64bit windows. Other example is build server that could even not having this external system installed. Its task usually is to build (and probably run unit tests, code coverage, make installation kit etc), not to run actual system. Moreover if external system is running and have your assemblies from previous build locked (as they’re loaded into it) – VS can’t clean them or replace with new ones which sometimes lead to confusion.

Yet another teams perform build with default settings and then manually (or with help of some batch file) copying needed artifacts to the place where they should be. It is slightly better in terms of build server integration and locked files. But it requires three steps to run application: build solution from VS, switch to explorer (or whatever you’re using) and run batch file, switch back to VS and run external system with debugger attached. It is not only a pain, it is also error-prone as one could easily forget to do one of these steps.

And yet another teams have post-build step in some project. And that post-build step executes actual install. Here we have problem with project build skipping. VS has some heuristics to determine which projects should be built and which could be skipped. First of all it skips everything that is up-to-date (i.e. hasn’t any file or referenced project changed after last successful build). Also VS skips everything that wasn’t set up for build in current configuration (in the solution properties). Thus said if project build is skipped – post-build step is skipped also. Oops. VS doesn’t know that you have some dependencies in the post-build step. And worse it would be skipped without any warning so that you can find it only when something goes wrong. Not good definitely.

The solution to this problem that I came with in the end is to have separate project in the solution dedicated to this installation step. And this solution should have references to any other project in the solution. To be clear here, this Deployment project is not going to call compiler or produce build errors if referenced projects were not built (due to current configuration for example). The single purpose of these references is to give VS knowledge about when it could really skip build of Deployment project – only when no projects in the solution changed. Otherwise it should build Deployment.

The downside of this approach is evident: you should carefully watch over rule that Deployment has these references actual. But this task could be automated more or less easily. Maybe I will write a blog post soon about such thing. We have this checking as a part of build process so if anyone makes a change violating the rules we have failed build. It just works.

UPDATE: I’ve written such post. See here.

And here is example of Deployment.csproj file:

<Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003" ToolsVersion="4.0">
  <PropertyGroup>
    <ProjectGuid>{5739A0D8-2EE2-4CEE-BFCD-C748E47E320D}</ProjectGuid>
    <OutputType>Library</OutputType>
    <!-- VS2010 for some reason requires OutputType property set. Otherwise it throws
         an error when you try to open project properties window -->
  </PropertyGroup>
  <ItemGroup>
    <ProjectReference Include="..\YourProject\YourProject.csproj">
      <Project>{3612C9DD-7086-436A-9AB7-A3EAA995881F}</Project>
      <Name>YourProject</Name>
    </ProjectReference>
    <!-- Other project references goes there -->
  </ItemGroup>
  <Target Name="Build">
    <ItemGroup>
      <ToCopy Include="..\YourProject\bin\Debug\*.dll" />
      <ToCopy Include="..\YourProject\bin\Debug\*.pdb" />
      <ToCopy Include="..\YourProject\bin\Debug\*.config" />
      <ToCopy Include="..\AnotherDirToCopyFrom\*.*" />
      <!-- Actually you could have any set of things to copy here -->
    </ItemGroup>
    <Message Text="ToCopy = $(ToCopy)" />
    <Message Text="DestinationFolder = $(InstallDir)\Plugins\YourPluginName" />
    <Copy Condition=" '$(InstallDir)' != '') " 
          SourceFiles="@(ToCopy)" 
          DestinationFolder="$(InstallDir)\Plugins\YourPluginName" />
    <!-- This target copy things only if %InstallDir% environment variable exists.
         It prevents copying from executing on build server if it doesn't have the system
         installed.
         You could have every conditional logic you want instead. -->
  </Target>
  <Target Name="Clean" />
  <!-- If you need to do something on the Clean - put it here. But don't delete
       this target at all because VS will give you an error that project does not have
       Clean target on each Clean or Rebuild -->
  <Target Name="Rebuild" DependsOnTargets="Build" />
</Project>

The main point here is that we don’t import $(MSBuildToolsPath)\Microsoft.CSharp.targets and define our own Build, Clean and Rebuild targets which are called by VS. Probably you will have to define also Publish target if you use it.

19/07/2011

Double-star pattern in .gitignore

Filed under: git — Ivan Danilov @ 14:36

Today I was asked why pattern /folder/**/*.abc doesn’t work. The goal is pretty clear here – to ignore every file with abc extension in the folder and its subfolders.

Countless tries lead to nothing. Nevertheless here and there I’ve found several mentioning of the double-star pattern, so I thought there should be some way to get it working. Then I encountered this blog post. Mentioning slash in the pattern remembered me something and I rechecked gitignore(5) manual page:

  • If the pattern does not contain a slash /, git treats it as a shell glob pattern and checks for a match against the pathname relative to the location of the .gitignore file (relative to the toplevel of the work tree if not from a .gitignore file).
  • Otherwise, git treats the pattern as a shell glob suitable for consumption by fnmatch(3) with the FNM_PATHNAME flag: wildcards in the pattern will not match a / in the pathname.

Turns out git uses two different approaches to handling ignore pattern depending on slash symbol presence in the pattern! It was a surprise. After checking fnmatch(3) desctiption and sources I assured that double-star pattern is unsupported by .gitignore. At least directly.

What git DOES support is hierarchical .gitignore files, i.e. having several .gitignores at different locations in the folder structure. So the equivalent solution to using /folder/**/*.abc pattern would be placing another .gitignore file into /folder/ and having pattern *.abc there.

Well, I’d still prefer single .gitignore if it is possible because it is easier to manage single file… but several of them also solve the problem well. And so be it.

UPDATE: Since git 1.8.4 according to the announce by JC Hamano (last point):

Use of platform fnmatch(3) function (many places like pathspec matching, .gitignore and .gitattributes) have been replaced with wildmatch, allowing “foo/**/bar” to match “foo/bar”, “foo/a/bar”, etc.

UPDATE2: msysgit (git on windows) supports this feature since version 1.8.2 according to RelNotes.

18/07/2011

How to establish git central repository for working against TFS with git-tfs bridge

Filed under: git, git-tfs — Ivan Danilov @ 17:40

As I described here previously my goal was to establish central git repository and avoid redundant round-trips to TFS whenever possible.

To achieve this now you probably want to follow my advise in the above mentioned article about having user.name=<tfs account name without domain> and same user.email for everyone fetching changes from TFS.
In the latest git-tfs sources this was already fixed, so you don’t need to set this kind of things.

When I established central repository I faced some inconveniences: firstly pattern “checkout master, check central repository for changes already there but not in your local repository, pull new changes from TFS, push to central repository if anything new was pulled, checkout old working branch again, optionally rebase it onto master” was very boring and very repeatable. And the second inconvenience: this pattern should be repeated each time something new appears in TFS. I want to have some scheduled updating so I wouldn’t think about such things as when and how I should update latest version etc.

Well, it is perfect target for automation. So now I want to share my bash script that does these things and saves you several dozens of key-pressing each time you need get changes from TFS.

#!/bin/sh

check_err()
{
    # parameter 1 is last exit code
    # parameter 2 is error message that should be shown if error code is not 0
    if [ "${1}" -ne "0" ]; then
        cat '~temp.log'
        echo ${2}
        rm -f '~temp.log' &gt; /dev/null
        exit ${1}
    fi;
    rm -f '~temp.log' &gt; /dev/null
}

echo "$(date) Update launched"

if [ ! -z "$(git status --porcelain)" ]; then
    echo "$(date) Your status is not clean, can't update"
    exit 1;
fi;

branch_name="$(git symbolic-ref HEAD 2&gt;/dev/null)"
branch_name=${branch_name##refs/heads/}

if [ ! "$branch_name" == "master" ]; then
    git checkout master
fi;

echo "$(date) Pulling from central repo first to avoid redundant round-trips to TFS..."
git pull &gt; '~temp.log'
check_err $? "Pulling from central repo failed"

echo "$(date) Pulling from TFS..."
git tfs pull -d &gt; '~temp.log'
check_err $? "Pulling from TFS resulted in error";

remote_conflicting_commits="$(git rev-list origin/master ^master)"
if [ ! -z "$remote_conflicting_commits" ]; then
    echo "origin/master has conflicting commits, can't push"
    exit 1;
fi;

local_commits_to_push="$(git rev-list master ^origin/master)"
if [ -z "$local_commits_to_push" ]; then
    echo "$(date) Central repo is up-to-date, nothing to push"
else
    echo "$(date) Pushing updates to central repo"
    git push --tags origin master &gt; '~temp.log'
    check_err $? "Push to central resulted in error";
fi;

if [ ! "$branch_name" == "master" ]; then
	git checkout $branch_name
	if [ "$1" == "-r" ]; then
		echo "Rebasing $branch_name on master";
		git rebase master
	fi;
fi;

This script assumes you want master branch to mirror TFS and rebases your working branch onto new changes only if you have specified -r switch. And for the second inconvenience you have just to set-up some dedicated working copy and run this script each, say, five minutes in it. It is very easy to do with built-in Windows Task Scheduler:

  1. Go to Start -> Administrative tools -> Task Scheduler
  2. Click Create Task on the right side
  3. Check radio button ‘Run whether user is logged on or not’
  4. Select Triggers tab, click ‘New’. Set Start=’One time’ and ‘Repeat task every’ whatever, check ‘Enabled’, click OK.
  5. Go to Actions tab, ‘New’. Set Program/script to ‘cmd‘, Add arguments=’/c "sh.exe update >> log.txt"‘ and in the Start in field put path to your working copy, dedicated to central repository updating.
    1. That’s all. Now you should have updates regularly without any efforts. It is important to note though, that script assumes your remote named origin/master and noone pushes conflicting changes to central repo’s master branch.

      And not forget to add /log.txt to .gitignore. Otherwise your updates will just fail to start because of not clean working tree.

12/07/2011

MSTest 2010 on the build server without VS2010 installed

Filed under: Unit testing, VisualStudio — Ivan Danilov @ 23:24

Some time ago I found description how to get MSTest 2008 running without Visual Studio. This time I faced the same question with VS2010. Quick googling hadn’t result in any decent answer, so I did some digging.

I won’t enumerate countless exceptions, silent hangs, unclear error messages and happy MSTest’s reports about ‘Not executed’ tests without any reason. I’m pissed as hell with MSTest and its problems but forced to use it. So I will just describe how to have it working.

What you’ll need is some machine with VS2010 SP1 installed to get assemblies and MSTest.exe from. SP1 is very desirable if you want to test .NET 4.0 project and absolutely mandatory in case of earlier platform versions because without SP1 it is impossible AFAIK. Go to that box and take:

  1. c:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Microsoft.VisualStudio.QualityTools.*.dll
  2. c:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.QualityTools.*.dll
  3. c:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\MSTest.exe
    c:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\MSTest.exe.config
  4. c:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\QTAgent.exe
    c:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\QTAgent.exe.config
    c:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\QTAgent32.exe
    c:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\QTAgent32.exe.config
    c:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\QTAgent32_35.exe
    c:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\QTAgent32_35.exe.config
    c:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\QTAgent_35.exe
    c:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\QTAgent_35.exe.config

Make on target machine folder C:\VS2010Stub\ and copy these assemblies/configs there preserving the structure (so you’ll have C:\VS2010Stub\Common7\IDE\ and everything mentioned there and in PrivateAssemblies/PublicAssemblies).

The first step is done. The next is getting dependencies from GAC. Take gacutil.exe (check that you have one from .NET 4, i.e. it should have version like 4.0.30319.1 – older versions will result in an error), take gacutlrc.dll (without it gacutil.exe will exit silently – at least it was so on my build server) and copy them to target machine. We will need them to register extracted dependencies to GAC.

Now you need to take Microsoft.VisualStudio.QualityTools.*.dll from source machine’s GAC. Notice that some of these assemblies have two versions if you have VS2010 SP1 installed: 10.0.0.0 and 10.1.0.0. You need them both. Do not rename assembly files because gacutil.exe won’t be able to register renamed assemblies afterwards. Just put different versions with same name in different folders.

In my dev box these files was placed in C:\Windows\assembly\GAC_MSIL\Microsoft.VisualStudio.QualityTools.*\Microsoft.VisualStudio.QualityTools.*.dll and in c:\Windows\Microsoft.NET\assembly\GAC_MSIL\Microsoft.VisualStudio.QualityTools.*\…\Microsoft.VisualStudio.QualityTools.*.dll. Most probably you will find them there as well, but as I don’t know actual GAC structure (I’m not sure official documentation on GAC internals exists actually) – I can’t guarantee that.

UPD: if you will use database unit testing, you will need three more assemblies from the GAC – Microsoft.Data.Schema.dll, Microsoft.Data.Schema.Utilities.dll and Microsoft.Data.Schema.UnitTesting.dll. Note that archive at the end of this post doesn’t include them.

In any case take each of these assemblies to the target box and execute ‘gacutil.exe /i Microsoft.etc-etc-etc.dll’. You should see ‘Assembly successfully added to the cache’ for each one.

The second step is done. Now goes the last one. MSTest has some settings in the registry about file extensions that could be tested. Actually now you have MSTest functional, but if you try to run it on any assembly with tests it will say something about ‘dll is not correct test container’. Well, lets fix that.

Go to registry on the source box, find key HKEY_LOCAL_MACHINE\SOFTWARE\Wow6432Node\Microsoft\VisualStudio\10.0\EnterpriseTools\QualityTools\TestTypes (*)  and export it in some *.reg. And import on the target box. Be careful though to run reg file from 64-bit program. Explorer will go well. I have Total Commander which is 32-bit and sometimes accidentally run reg files from it. Which results in doubling Wow6432Node part.

That’s  it. Now you should be able to run MSTest.exe and test something with it successfully.

UPD: If you’re using MSTest private accessors – you’ll also need this as I discovered much later. Zip-archive linked below is already updated.

In case you’re lazy, there‘s zip-archive with everything already made for you. Just extract contents of the MSTest2010Install.zip to the target box and run install.bat.

 

(*) I’m assuming both source and target boxes have 64-bit OS. Otherwise you should cope with Wow6432Node part. It is easy but somewhat annoying. Feel free to ask me if you don’t know how to do this.

11/07/2011

Troubleshooting: MSTest assembly doesn’t get discovered by VS native test runner

Filed under: Unit testing, VisualStudio — Ivan Danilov @ 16:06

Well, I already faced similar problem here so I know where to look. And it was really the cause.

To have test runner see tests in the assembly its csproj file should contain this line:

<ProjectTypeGuids>{3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</ProjectTypeGuids>

Troubleshooting: Resharper, Visual Studio 2010, MSTest and .NET 3.5 in single bottle

Filed under: Unit testing, VisualStudio — Ivan Danilov @ 15:59

We are in process of moving .NET 3.5 project to .NET 4.0 and VS2010. We’re still not ready to move our app to .NET 4.0 but decided to adopt VS2010 first. It is somewhat unusual situation so it caused some troubles.

First of all, unit testing projects under MSTest just can’t target framework v3.5 prior to VS2010 SP1.

VS2008 for unit testing was using Microsoft.VisualStudio.QualityTools.UnitTestFramework assembly, version 9.0.0.0. It was for v3.5 so everything is cool. Except the fact that if VS2010 sees version 9.0.0.0 in any reference to this assembly – it starts Project Conversion Dialog and prompts you to convert your solution. Thus in VS2010 you just can’t have reference to 9.0.0.0 (nevertheless MSBuild works well with it, so it seems it is VS limitation).

VS2010 (without SP1) switched to 10.0.0.0 with the same name. Well, to be precise file version is 10.0.30319.1. If your unit testing project references 10.0.0.0 – it can’t have TargetFrameworkVersion=v3.5. VS just sets it back to v4.0. So if you need to test v3.5 assemblies with it – well, you’re in trouble.

Fortunately, MS recognized the problem before I faced it and with SP1 there’s new version of the assembly – 10.1.0.0 or 10.0.40219.1. It allows you to target v3.5 framework and test in the VS2010. After I realized it and changed my references to 10.1.0.0 – native VS test runner get test correctly. Everything was green and bright.

Just until I ran it in the ReSharper 5.1 which I had installed. Well, it had test count correctly but don’t run anything. After some stack overflow searching I ensured that I’m not only one with the problem and that it is fixed in ReSharper 6.0. So I installed 6.0, run tests and… half of them were broken! Something is not right. Native runner got them well.

After some debugging I find out in Debug -> Windows -> Modules that resharper’s runner had 10.0.30319.1 version loaded. Maybe it is the problem was my first thought. Well, how could I force program to load other version of the assembly in .NET? With configuration, namely with assembly binding redirection. I had just happened to see MS guide to troubleshoot VS2010 unit testing. So my problem seemed very close…

So, I had opened my c:\Windows\Microsoft.NET\Framework\v4.0.30319\Config\machine.config and added there such lines:

<runtime>
    <assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
        <dependentAssembly>
            <assemblyIdentity name="Microsoft.VisualStudio.QualityTools.UnitTestFramework" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
            <bindingRedirect oldVersion="10.1.0.0" newVersion="10.0.0.0"/>
          </dependentAssembly>
        <dependentAssembly>    
            <assemblyIdentity name="Microsoft.VisualStudio.QualityTools.Tips.UnitTest.Adapter" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
            <bindingRedirect oldVersion="10.1.0.0" newVersion="10.0.0.0"/>
        </dependentAssembly>
        <dependentAssembly>
            <assemblyIdentity name="Microsoft.VisualStudio.QualityTools.Tips.UnitTest.ObjectModel" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
            <bindingRedirect oldVersion="10.1.0.0" newVersion="10.0.0.0"/>
        </dependentAssembly>
        <dependentAssembly>
            <assemblyIdentity name="Microsoft.VisualStudio.QualityTools.Tips.UnitTest.Tip" publicKeyToken="b03f5f7f11d50a3a" culture="neutral"/>
            <bindingRedirect oldVersion="10.1.0.0" newVersion="10.0.0.0"/>
        </dependentAssembly>
    </assemblyBinding>
</runtime>

After that ReSharper gets my tests correctly.

To be honest I don’t understand why this redirection actually solved the problem. We are telling runtime to load 10.0.0.0 instead of 10.1.0.0 in case it is requested, not vice-versa. But it is analogous to config file in many MS applications like devenv.exe.config, mstest.exe.config etc, so probably they know what to do with their own libraries better than I do.

Blog at WordPress.com.