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
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
$(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 (
*.csprojfile) should belong to the solution and only one;
- solutions can’t be nested so if some folder has
*.slnfile – entire tree from this point should have to other
- 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;
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.