Vanilla build server and a little NuGet gem

October 6, 2014 on 7:37 pm | In ASP.NET MVC, Continuous Integration, DotNet, MSBuild | No Comments

Vanilla build server is a concept that says that the build server should have as few dependencies as possible. It should be like vanilla ice cream without any raisins (I have raisins in ice cream). Let me cite the classic (from: Continuous Integration in .NET):

“It’s strongly suggested that you dedicate a separate machine to act as the CI server. Why? Because a correctly created CI process should have as few dependencies as possible. This means your machine should be as vanilla as possible. For a .NET setup, it’s best to have only the operating system, the .NET framework, and probably the source control client. Some CI servers also need IIS or SharePoint Services to extend their functionality. We recommend that you not install any additional applications on the build server unless they’re taking part in the build process.”

I was recently preparing a talk for a conference and setting up a brand new CI server on Windows Server 2012. My ASP.NET MVC project build ended up of course with following error:

error MSB4019: The imported project "C:\Program Files 
(x86)\MSBuild\Microsoft\VisualStudio\v11.0\
WebApplications\Microsoft.WebApplication.targets" 
was not found. Confirm that the path in the <Import> 
declaration is correct, and that the file exists on disk.

Well of course. I have a vanilla machine without any MSBuild targets for ASP.NET MVC. I was going to solve it like usual. Create a tools directory, copy the needed targets into the repository and configure the MSBuild paths to take the targets provided with the repository. It worked like a charm in the past and it would work now. But something (call it intuition) made check over at NuGet and to my joy I found this little gem:

https://www.nuget.org/packages/MSBuild.Microsoft.VisualStudio.Web.targets/12.0.1

“MSBuild targets for Web and WebApplications that come with Visual Studio. Useful for build servers that do not have Visual Studio installed.” Exactly!

I quickly installed it. Configured the MSBuild on the build server to use it like this:

/p:VSToolsPath=’..\packages\MSBuild.Microsoft.VisualStudio.Web.targets.12.0.1\tools\VSToolsPath’

It is a command line parameter I’ve added to the build arguments.

An voila!

Visual Studio 2010 Professional and Hudson / Jenkins CI and FxCop

June 15, 2011 on 9:03 am | In Continuous Integration, DotNet, MSBuild | No Comments

 

Visual Studio 2010 Premium and Ultimate do have the code analysis feature build in. What this feature does is static code analysis of your source code (or IL to be specific). Unfortunately the Professional edition lacks the build in integration (no Analysis tab in the project properties). Here is a way to easily integrate Visual Studio 2010 Professional with FxCop. FxCop is a standalone Code Analysis version that comes together with “Microsoft Windows SDK for Windows 7 and .NET Framework 4 Version 7.1”. Actually if you download and install the SDK you will get the FxCop installer in %ProgramFiles%\Microsoft SDKs\Windows\v7.1\Bin\FXCop. You will have to install it from there (yeah installer in a installer ;) .

Here is how to built it into Visual Studio 2010 and into continuous integration process. First of all, I like to have all the assets in my repository. So I went and copied all the FxCop files into the tools/FxCop directory into the repository. I didn’t wanted to use the FxCop project files (they add unnecessary friction with editing the file – sometimes on runtime). I decided to go the command line way all the way. The easiest solution is to define all the rules you want to obey in the ruleset file. Ruleset file is a XML file that looks like this:

   1: <?xml version="1.0" encoding="utf-8"?>

   2: <RuleSet Name="Codefusion Rules" Description="This is the Codefusion rule set." ToolsVersion="10.0">

   3:   <Localization ResourceAssembly="Microsoft.VisualStudio.CodeAnalysis.RuleSets.Strings.dll" ResourceBaseName="Microsoft.VisualStudio.CodeAnalysis.RuleSets.Strings.Localized">

   4:     <Name Resource="ExtendedDesignGuidelineRules_Name" />

   5:     <Description Resource="ExtendedDesignGuidelineRules_Description" />

   6:   </Localization>

   7:   <IncludeAll Action="Error" />

   8:   <Rules AnalyzerId="Microsoft.Analyzers.ManagedCodeAnalysis" RuleNamespace="Microsoft.Rules.Managed">

   9:     <Rule Id="CA1000" Action="Warining" />

  10:   </Rules>

  11: </RuleSet>

The example above states that all the rules (IncludeAll) are threated as errors with one exception being a Warning (<Rule Id=…). You can define the rules as you wish. Fortunately Visual Studio 2010 Professional has the build in editor for the ruleset files. Using this editor you can easily choose the rules to obey (or not).

image

When you are done with the ruleset place it somewhere in your repository and define post build event like this one:

image

The text for the post build event is:

if $(ConfigurationName) == Release $(SolutionDir)Codefusion.Common\Tools\FxCop\FxCopCmd.exe /file:$(SolutionDir)S000.Basic\bin\$(ConfigurationName)\Codefusion.S000.Basic.dll /ruleset:=$(SolutionDir)Codefusion.Common\FxCopRules\CodefusionRules.ruleset /rulesetdirectory:$(SolutionDir)Codefusion.Common\Tools\FxCop\Rules /console

This way if you compile the project in Release mode you will get all the rules defined in ruleset file checked. You will see the violations in the Error List area. You can still jump to the line where violation occurs by double-clicking the line with report.

You can always suppress the messages for the rules you chosen to obey if in this particular case they don’t make sense. You can do it globally in the GlobalSuppressions.cs file. Like that:

   1: [assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Codefusion")]

Or directly in the code like that:

   1: [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1820:TestForEmptyStringsUsingStringLength")]

   2: public static string MakeMD5Hash(this string value)

   3: {

   4:     if (value == null)

   5:     {

   6:         throw new ArgumentNullException("value");

   7:     }

   8:     else if (value == string.Empty)

   9:     {

  10:         throw new ArgumentException(Resources.BasicStrings.ErrorMsgValueCannotBeEmpty + ".", "value");

  11:     }

  12:  

  13: ...

To make the suppressions work (suppressions are inline or global exceptions from the ruleset) you will have to define the compiler directive like that:

image

It will define a constant in the project file:

<DefineConstants>CODE_ANALYSIS</DefineConstants>

If you are using the continuous integration technique you can put the command similar to the one in post build event into the build project. In MSBuild it will look like this:

   1: <Target Name="Analysis" >

   2:   <Exec Command="$(MSBuildProjectDirectory)\Codefusion.Common\Tools\FxCop\FxCopCmd.exe /file:S000.Basic\bin\$(Configuration)\Codefusion.S000.Basic.dll /ruleset:=Codefusion.Common\FxCopRules\CodefusionRules.ruleset /rulesetdirectory:Codefusion.Common\Tools\FxCop\Rules /out:FxCopReport.xml /forceoutput" ContinueOnError="false" />

   3: </Target>

This way you will get get the XML file with the violations report that your CI server can interpret and thanks to the ContinueOnError=”false” attribute you will get the broken build if any of the violations will be found.

In Hudson/Jenkins you can use the Violations plug-in (you will have to install it) to show the violations in the build report. To do so you need to to configure the project like this:

image

Happy coding!

Writing MSBuild Custom Task

March 13, 2008 on 8:16 pm | In Continuous Integration, MSBuild | 9 Comments

Scenario: we have Subversion server to manage our source code and a build server (CruiseControl.NET) to manage our deployment. We have decided to automatically set the SVN revision number to our assembly version.

[assembly: System.Reflection.AssemblyVersion("1.2.3.0")]
[assembly: System.Reflection.AssemblyFileVersion("1.2.3.0")]

So we will replace the last 0 with the current Subversion revision number. How to do this? One way to achieve this is to modify the AssemblyInfo.cs and read the modified number from that exists. The file modification is easy with MSBuild Community Task FileUpdate

<FileUpdate Files="Cic.P001001PropertiesAssemblyInfo.cs"
            Regex="(d+).(d+).(d+).(d+)"
            ReplacementText="$1.$2.$3.$(RevisionNumber)" />

But how to read it back? Well there is no easy way. I have written a custom MSBuidl task to achieve this like this:

<AssemblyInfoReader Path="PropertiesAssemblyInfo.cs"
                    Property="AssemblyVersion">
    <Output TaskParameter="Value" ItemName="ApplicationVersion" />
</AssemblyInfoReader>


Writing a custom MSBuild task is fairly easy. You have to Reference Microsoft.Build.Framework and Microsoft.Build.Utilities and implement Microsoft.Build.Framework.ITask. Just like this:

namespace Cic.MsBuildTasks
{
    public class AssemblyInfoReader : Microsoft.Build.Framework.ITask
    {
        #region Private Varaibels

        private string path;
        private string property;
        private string value;

        #endregion

        #region Fields
        [Microsoft.Build.Framework.Required]
        public string Path
        {
            get { return path; }
            set { path = value; }
        }

        [Microsoft.Build.Framework.Required]
        public string Property
        {
            get { return property; }
            set { property = value; }
        }

        [Microsoft.Build.Framework.Output]
        public string Value
        {
            get { return this.value; }
            set { this.value = value; }
        }

        #endregion

        #region ITask Members
        private Microsoft.Build.Framework.IBuildEngine engine;
        Microsoft.Build.Framework.IBuildEngine
            Microsoft.Build.Framework.ITask.BuildEngine
        {
            get
            {
                return engine;
            }
            set
            {
                engine = value;
            }
        }

        bool Microsoft.Build.Framework.ITask.Execute()
        {
            string message;
            value = string.Empty;
            try
            {
                value = MyReadAssemblyInfoProperty();
                message = string.Format(
                    "AssemblyInfo property {0} read. Property value {1}",
                    property, value);
            }
            catch (System.Exception e)
            {
                message = string.Format(
                    "Error reading AssemblyInfo property {0}. Error: {1}",
                    property, e.Message);
            }

            Microsoft.Build.Framework.BuildMessageEventArgs args =
                new Microsoft.Build.Framework.BuildMessageEventArgs(
                message, string.Empty, "AssemblyInfoReaderTask",
                Microsoft.Build.Framework.MessageImportance.Normal);
            engine.LogMessageEvent(args);

            return true;
        }

        private Microsoft.Build.Framework.ITaskHost host;
        Microsoft.Build.Framework.ITaskHost Microsoft.Build.Framework.ITask.HostObject
        {
            get
            {
                return host;
            }
            set
            {
                host = value;
            }
        }

        #endregion

        #region Internals
        private string MyReadAssemblyInfoProperty()
        {
            string propertyValue;

            // Eraly return
            if (!System.IO.File.Exists(path)) return "";

            foreach (string line in System.IO.File.ReadAllLines(path))
            {
                if (line.Contains(property))
                {
                    try
                    {
                        propertyValue = line.Remove(0, line.IndexOf('"') + 1);
                        propertyValue = propertyValue.Remove(
                            propertyValue.LastIndexOf('"'),
                            propertyValue.Length - propertyValue.LastIndexOf('"'));
                        // return matching property value
                        return propertyValue;
                    }
                    catch
                    {
                        // Ignore errors
                    }
                }
            }

            return string.Empty;
        }
        #endregion
    }
}


Developing Vista Sidebar Gadget with Script#

January 19, 2008 on 3:08 pm | In MSBuild, Vista Sidebar Gadget | 2 Comments

C# to JavaScript compiler? What? Jea! I thought the same way. It’s amazing what smart people can invent. My men is Nikhil Kothari with his Script#. What do I need the C# to JavaScript compiler for, you ask. Is IL not enough? Well think of enriching your ASP.NET web sites with JavaScript written in C#. Or using Ajax with scripts written in Visual Studio with Intelisense and refactoring. Or developing the Vista Sidebar Gadgets without writing a single line of code in Javascript!

Leasing Calculator I instantly lookd at the third possibility. since I have Vista on board I was thinking about writing my own Sidebar Gadget (you know the little programs written in HTML an JavaScript, sticked to the right side of your screen). Of course I wonted it to by at least useful ;-) I thought about writing a small Leasing Calculator. I had my leasing mathematics library ready, but it was written in C#. I didn’t feel like rewriting it in JavaScript – what for? So Script# was a blast for me. I had my Leasing Calculator gadget in view hours.

What do you need to start working with Script#:

1. download the latest binaries from Script# project page

2. install it

3. create new Visual C# -> Script# -> Sidebar Gadget project in Visual Studio

You will get quite useful project template. The inside of Content folder is your gadget. If you compile everything Script# will transform your C# into JavaScript and place it in this folder. The last thing you have to do is to copy this folder into your Vista Gadgets folder (C:\Users\user\AppData\Local\Microsoft\Windows Sidebar\Gadgets\). You will have to do the same every time you will compile. Surely it is something to automate. The easiest way is to use the Post-build event in your project and a small MSBuild script that will copy the needed files to the place they should by.

VS2005 post build event

$(WinDir)\Microsoft.NET\Framework\v2.0.50727\msbuild.exe $(SolutionDir)build.msbuild

And use this script:

<Project DefaultTargets="Deploy" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <PropertyGroup>
    <DeploymentFolder>YourVistaGadgetFolder.gadget</DeploymentFolder>
  </PropertyGroup>
  <ItemGroup>
    <DeploymentSourceFiles Include="LeasingCalculatorContent***.*" />
  </ItemGroup>
   <Target Name="Deploy">
    <RemoveDir Directories="$(DeploymentFolder)" ContinueOnError="false"></RemoveDir>
    <Copy SourceFiles="@(DeploymentSourceFiles)"
      DestinationFiles="@(DeploymentSourceFiles->
      '$(DeploymentFolder)%(RecursiveDir)%(Filename)%(Extension)')"/>
   </Target>
</Project>

So you are equipped with fully functional development platform for Vista Sidebar Gadgets.

One more thing. I do think that writing a JavaScript in C# is a good idea ;) but if you are new to Sidebar Gadgets you probably would like to take one step by step tutorial. It would help to understand Gadgets on the low level an d will get you up to speed with Schipr# later.

Continuous Integration article in SDJ

September 3, 2007 on 7:24 pm | In Continuous Integration, MSBuild | No Comments

I would like to invite all my polish speaking readers to online version of my article about continuous integration in .NET. It was published in July 2007 in Software Developers Journal. You can find it in the download section of SDJ web page. You can read there about:

  • setting up CI server with CruiseControl.NET
  • creating one click build script with MSBuild
  • incorporating unit testing and code coverage into CI process
  • using WebDashboard and CCTray to control the process
  • introducing code analyze with FxCop
  • creating install file and deploying the project.

Powered by WordPress with Pool theme design by Borja Fernandez.
Text © Marcin Kawalerowicz. Hosting CODEFUSION.