Friday, February 01, 2008

A while ago I ran the Disk Cleanup utility on my personal notebook. Today I needed to start the SQL server on this machine so I attempt to start the service and I get this error in the Event Viewer

So I made sure that the drive wasn't compressed, and it wasn't then I looked at the file itself and it was. I'm guessing that the Disk Cleanup deal performed this, because I know that I wouldn't. To solve this you right-click on the file and make sure that the checkbox shown is not checked.

Once you make sure this is done, you should be able to start the service again.

Sayed Ibrahim Hashimi

Friday, February 01, 2008 5:37:53 AM (GMT Standard Time, UTC+00:00)  #    Comments [0]  | 
Thursday, January 17, 2008

Previously I blogged about an MSBuild Presentation that I was going to conduct at the JaxDug. I'm pleased to say that the presentation went well. I prepared a Powerpoint, but I decided to not use that and to discuss the various elements by example. I think that ended up being a lot more effective, and interesting than the Powerpoint would have been. You can find all the files for download below. The zip file contains all the files. The ppt is the Office 2003 version.

MSBuild_Sayed_I_Hashimi_012008.zip

MSBuild_Sayed_I_Hashimi_01-2008.pdf
MSBuild_Sayed_I_Hashimi_01-2008.ppt.zip
MSBuild_Sayed_I_Hashimi_01-2008.pptx.zip

Sayed Ibrahim Hashimi

Thursday, January 17, 2008 6:57:31 AM (GMT Standard Time, UTC+00:00)  #    Comments [5]  | 
Saturday, January 05, 2008

On Wednesday January 9 I will be giving a presentation on MSBuild at the Jacksonville Developers User Group. You can read about the presentation at http://cs.jaxdug.com/blogs/events/archive/2007/11/20/1964.aspx. If you are in the Jacksonville Florida region and are interested in this presentation please drop by. MSBuild is a tough topic to present on because it is not well known and a little confusing. My goal is to create a presentation that is interesting enough to keep people awake yet give people some knowledge that they can use. Also I aim to create some samples that can be useful for demonstration to go with it. If you have suggestions please drop me a note.

Sayed Ibrahim Hashimi

Saturday, January 05, 2008 5:43:30 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]  | 
Wednesday, November 21, 2007

MSBuild: How to get all generated outputs

Earlier today a reader of my book emailed me the following question

I am trying to get a list of all the files being generated when MSBuild build a  certain project (they should be all the files in .bin directory).

When I used the TargetOutputs in MSBuild task it gives me the main output only, which is the .exe or .dll. But any other files like dependency assemblies will not be listed in the TargetOutputs collection.

Any advice??

So it sounds like he is looking for a generic way of determining all the files that have been created (moved) during the build process. I’m assuming that he has a few projects and is using a build script to run the process, and then he wants to move the files to another location. If you have a few projects, then obviously the easiest way is to manually examine the output paths of each project and copy the file as necessary. This is not an ideal scenario, because if you add a project to your process you have to make sure to remember to perform the copy manually. Anywayz, this type of behavior would be useful in many different scenarios. Now let’s talk about how to achieve this.

I took another look at the Microsoft.Common.targets file and determined that there is not a straight forward method of performing this action. So, after thinking about this for a little while I think I have come up with a solution. It’s a little confusing, but actually quite simple.

The solution lies in what I’m calling “MSBuild Inheritance”, which I’ve blogged about previously in the entries at:

·        MSBuild Property & Item Overide Problems

·        MSBuild: Find Many Project References

This is where you take an existing MSBuild file and without modifying it you add behavior and or data to it. Now let’s see how this idea can be used to add the desired behavior described above. For this sample I have created a sample Windows Forms app, as well as an external build script, buildWrapper.proj. This build file is shown below.

<Project DefaultTargets="GetOutputs" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <PropertyGroup>

    <ProjectFile>$(MSBuildProjectDirectory)\WindowsApplication1.csproj</ProjectFile>

  </PropertyGroup>

 

  <Target Name="GetOutputs">

    <MSBuild Projects="$(MSBuildProjectFullPath)"

             Properties="ImportProjectFile=true" Targets="Build">

      <Output ItemName="ProjectOutputs" TaskParameter="TargetOutputs"/>

    </MSBuild>

    <Message Text="%0a%0dProjectOutputs:%0a%0d    @(ProjectOutputs,'%0a%0d    ')" />

  </Target>

 

  <!--

    These elements will only be processed when in the context of the

    above Target. This is because of the Condition constraint on these items

  -->

  <Import Project="$(ProjectFile)" Condition="'$(ImportProjectFile)'=='true'"/>

  <!--

    Here we need to override the Build target with our own that will

    generate the correct results.

    -->

  <Target Name="Build"

        Condition="'$(ImportProjectFile)'=='true'"

        DependsOnTargets="$(BuildDependsOn)"

        Outputs="@(AllOutputs->'%(FullPath)')">

   

    <CreateItem Include="$(OutputPath)\**\*">

      <Output ItemName="AllOutputs" TaskParameter="Include"/>

    </CreateItem>

   

    <Message Text="Custom build invoked!" Importance="high"/>

   

  </Target>

</Project>

What this does is twofold:

1.      Defines the GetOutputs target which will drive the process

2.      Extends the behavior of the normal build to create the proper outputs

If you take a look at the Microsoft.Common.targets you will see that the default definition of the Build target is defined as follows:

    <Target

        Name="Build"

        Condition=" '$(_InvalidConfigurationWarning)' != 'true' "

        DependsOnTargets="$(BuildDependsOn)"

        Outputs="$(TargetPath)"/>

The purpose of this target is to define what targets it depends on and to create the outputs, by itself it doesn’t actually perform any action. Because of this we can override it in our buildWrapper.proj file. So the bottom half of the buildWrapper.proj file will conditionally import the project file and then conditionally re-define the Build target. Inside the new Build target we simply examine the output directory and create an item that contains all files that lie underneath it. In the image below you will see the result of this, specifically take a look at the region highlighted in red.

 

 

Now we can see that we have successfully achieved our goals.

Every time I describe this process I feel like it is still un-clear. If you have questions please let me know.

 

Sayed Ibrahim Hashimi

You can download all the files at http://www.sedodream.com/content/binary/OutExample.zip


(Edit: Updated MSBuild task usage to include Targets="Build")


Wednesday, November 21, 2007 6:29:57 AM (GMT Standard Time, UTC+00:00)  #    Comments [1]  | 
Monday, November 19, 2007

The MSBuild team is trying to prioritize their duties for the next release of MSBuild. Because of this they have asked for feedback on how their customers (us) would enhance MSBuild. They are doing this by distributing a virtual $100 across a possible 11 items. Their list actually ends in an item numbered 12, but there is no number 10. Here is a brief overview of their list of options.

1. Higher Preformance
2. VC Support
3. Support for other project types
4. Convert VS solution file to MSBuild format
5. Better dependency tracking
6. Distributed Build
7. Inline Tasks – ie you write a script right in your MSBuild file instead of a compilied .NET task
8. Scoping for items/properties – ie items/properties can exist in a defined scope
9. Extensible functions – ie being able to add items like the Exists function
11. MSBuild debugger
12. MSBuild visualization 

Here is how I chose to distribute my $100. 

4   - $40
6   - $10
7   - $10
11 - $30
12 - $10

About #4: In my experience, you have 2 scenarios that are the correct way to create build scripts for products. Those are

1.      Create a custom MSBuild file that acts like a solution file

2.      Use a solution file to drive the build process

The only reason to go with 1 is because the solution file is not an actual MSBuild file, otherwise that wouldn’t even be an option. As for option 2, this is not ideal either because it is difficult to inject steps into how the solution build process works. All you can do is really extend what happens before/after the process.

As for the debugger, this is a tool that I have actually wanted to write myself for a long time, but never had the time. If there was a debugger for MSBuild, I think that more people would begin to adopt MSBuild as well as get a much better understanding of how it works.

It’s not often that teams from Microsoft ask for straight customer feature feedback, if you’re interested in future versions of MSBuild then you should definetly participate in this at: http://blogs.msdn.com/msbuild/archive/2007/11/17/how-would-you-spend-100-on-msbuild.aspx. 

Sayed Ibrahim Hashimi

Monday, November 19, 2007 6:40:30 AM (GMT Standard Time, UTC+00:00)  #    Comments [0]  | 
Tuesday, October 16, 2007

I’ve created a new release of MSBuild tasks, at www.codeplex.com/sedodream. This release includes 4 .NET 3.5 tasks built on Visual Studio B2. Those are all Linq MSBuild tasks. They are:

Name

Description

CreateLinqCode

Creates a Linq code file from a database.

CreateLinqDbml

Creates a Linq DBML file from a database.

CreateLinqMap

Creates a Linq Map file from a database.

CreateDatabase

Creates a database from an Linq DataContext which is contained in an assembly.


The first three tasks simply wrap the SqlMetal command line utility. The last, CreateDatabse, will create a database from a Linq DataContext that is contained in an assembly. Right now there is little validation of the parameters passed, but I plan on adding additional validation. Also I will add more meaningful error messages, when invalid parameters are passed through.

Below you’ll find an example of the CreateLinqCode task.

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

<Project ToolsVersion="3.5" DefaultTargets="Create" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <Import Project="$(MSBuildExtensionsPath)\Sedodream\Sedodream35.tasks"/>

  <Target Name="Create">

    <!-- sqlmetal /pluralize

              /conn:"data source=(local);initial catalog=ibrtest;integrated security=SSPI;packet size=4096"

              /timeout:15

              /language:C#

              /context:IbrTestDataContext

              /code:ibrtest.cs

              /views

              /functions

              /sprocs

              /serialization:Unidirectional

              /namespace:Sedodream.Linq.Test

              -->

    <CreateLinqCode

      CodeFileName="ibrahimTest.cs"

     

      ConnectionString="data source=(local);initial catalog=ibrtest;integrated security=SSPI;packet size=4096"

      Timeout="15"

      Language="C#"

      ContextClassName="IbrTestDataContext"

      IncludeViews="true"

      IncludeSprocs="true"

      IncludeFunctions="true"

      TargetNamespace="Sedodream.Linq.Test"

      >

    </CreateLinqCode>

</Target>

</Project>

Here is the CreateLinqDbml task usage.

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

<Project ToolsVersion="3.5" DefaultTargets="Create" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <Import Project="$(MSBuildExtensionsPath)\Sedodream\Sedodream35.tasks"/>

  <Target Name="Create">

<!-- sqlmetal /pluralize

              /conn:"data source=(local);initial catalog=ibrtest;integrated security=SSPI;packet size=4096"

              /timeout:15

              /language:C#

              /context:IbrTestDataContext

              /dbml:../LinqObjects\ibrtest.dbml

              /views

              /functions

              /sprocs

              /serialization:Unidirectional

              /namespace:Sedodream.Linq.Test

              -->

    <CreateLinqDbml

      DbmlFileName="ibrahimTest.dbml"

      OutDir="."

      ConnectionString="data source=(local);initial catalog=ibrtest;integrated security=SSPI;packet size=4096"

      Timeout="15"

      Language="C#"

      ContextClassName="IbrTestDataContext"

      IncludeViews="true"

      IncludeSprocs="true"

      IncludeFunctions="true"

      TargetNamespace="Sedodream.Linq.Test"

      >

    </CreateLinqDbml>

  </Target> 

</Project>

 

Please let me know if you have any questions/comments about these items.

Sayed Ibrahim Hashimi

Tuesday, October 16, 2007 12:50:48 AM (GMT Daylight Time, UTC+01:00)  #    Comments [7]  | 
Tuesday, September 25, 2007

Lately I've been thinking of writing a book strictly on MSBuild. My previous book Deploying .NET Applications has devoted about half of the book to the topic. But I feel that there is still a lot of material missing even from that text. When the book was published I thought that by this time there would be such a book, but there is not. Because of this need, I'm considering writing the book myself! Now I'd like to hear from you, do you think such a book would be useful? What content would you like to see covered in a book like this? You can either contact me through this website of by email at sayed [DOT] hashim [AT] NOSPAMgmail [DOT]com. All feedback is greatly appreciated.

Sayed Ibrahim Hashimi

 

Tuesday, September 25, 2007 3:48:36 PM (GMT Daylight Time, UTC+01:00)  #    Comments [12]  | 
Wednesday, June 27, 2007

Ok, I know there are many MSBuild loggers available so why create a new one? Because for a while I’ve been thinking that it would be cool to have a logger that would transform an MSBuild log into a log4net log. This is because obviously log4net has many appenders and tools that you can use out of the box. For example if you wanted to put your logs into a database, then attach the AdoNetAppender. Because of this, and because I couldn’t already find one, I wrote one. You can find it on my Sedodream MSBuild project. This logger is called SedoLogger. I figured this would be a good name, in the case that later on I wanted to abstract out the specific logging mechanism behind it. I have just created a new release that contains all the necessary files. I have posted some usage information about this logger on the SedoLogger WiKi page. I’ll be posting some more details about this logger here in the near future.

Sayed Ibrahim Hashimi

Wednesday, June 27, 2007 6:09:10 AM (GMT Daylight Time, UTC+01:00)  #    Comments [1]  | 
Friday, April 27, 2007

Hi,

Someone contacted me regarding some issues he was having with respect to using the Web Deployment Project s and that was the inspiration for this post.
There is an inherent problem with the way that Properties and Items are declared, created, overridden and used. There are a few problems those are:

·         PropertyGroup & ItemGroup elements are evaluated before any target executes.

·         Items are append only, meaning that you can’t delete items or any reference contained within them.

We will now start by examining this issue. To simplify the issue I won’t use the Web Deployment Project files, or C# project files. But simpler files that let us focus on what is going on. Below is the file that represents the Microsoft.WebDeployment.targets file. This file is called Shared.proj.  

<Project

  xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <!--

  This represents the Microsoft.WebDeployments.targets file

  -->

  <PropertyGroup>

    <_AspNetCompilerVirtualPath>$(SourceWebVirtualPath)</_AspNetCompilerVirtualPath>

  </PropertyGroup>

  <ItemGroup>

    <_SourceWebPathItem Include="$(SourceWebPhysicalPath)"/>

  </ItemGroup>

 

  <Target Name="Build">

    <Message Text="Inside build shared.proj"/>

  </Target>

 

  <Target Name="PrintValues">

    <Message Text="SourceWebVirtualPath: $(SourceWebVirtualPath)"/>

    <Message Text="SourceWebPhysicalPath: $(SourceWebPhysicalPath)"/>

 

    <Message Text="_AspNetCompilerVirtualPath: $(_AspNetCompilerVirtualPath)"/>

    <Message Text="_SourceWebPathItem: @(_SourceWebPathItem)"/>

  </Target>

 

</Project>

Now let’s see the project file that represents the project that Visual Studio would create for you when you use the Web Deployments Template. My representative file is called Your.proj, it is shown below.

<Project

  InitialTargets="PreBuild"

  DefaultTargets="PrintValues"

  xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <!-- This represents the .wdproj file -->

 

  <PropertyGroup>

    <SourceWebVirtualPath>WebPathItems</SourceWebVirtualPath>

    <SourceWebPhysicalPath>WebPathItems</SourceWebPhysicalPath>

  </PropertyGroup>

 

  <Target Name="PreBuild">

    <Message Text="Changing path to be 'OtherPath' instead of 'WebPathItems'"/>

    <CreateProperty Value="OtherPath">

      <Output PropertyName="SourceWebVirtualPath" TaskParameter="Value"/>

    </CreateProperty>

    <CreateProperty Value="OtherPath">

      <Output PropertyName="SourceWebPhysicalPath" TaskParameter="Value"/>

    </CreateProperty>

  </Target>

 

  <Import Project="Shared.proj"/>

 

</Project>

There are a couple of things to notice here, the first is that we are importing the Shared.proj file with the statement:

    <Import Project="Shared.proj"/>

The other is that we are overriding the SourceWebVirtualPath and the SourceWebPhysicalPath values, in the PreBuild event which is on the InitialTargets list. Because of this we know that the PreBuild target will be executed before any target that is not on the InitialTargets list.

Now going back to the Shared.proj file, we can see

  <PropertyGroup>

    <_AspNetCompilerVirtualPath>$(SourceWebVirtualPath)</_AspNetCompilerVirtualPath>

  </PropertyGroup>

  <ItemGroup>

    <_SourceWebPathItem Include="$(SourceWebPhysicalPath)"/>

  </ItemGroup>

So from this we are creating a new property _AspNetCompilerVirtualPath based on the original SourceWebVirtualPath, and and item _SourceWebPathItem based on SourceWebPhysicalPath. These new properties and items are used throughout the Microsoft.WebDeployment.targets  file, and is a common practice amongst other .targets files. You’ll find something similar in the Microsoft.Commons.targets files. Now is where the problem arises, these new properties and items that have been declared in the PropertyGroup & ItemGroup, so they will be evaluated and assigned before the PreBuild target executes. Because of this you don’t really have a chance to actually override these values using any logic. The only (until you read further) way you can override them is to use static elements like PropertyGroup  and ItemGroup.

You can see this is the case from the output of the PrintValues target below.

Target PreBuild:
    Changing path to be 'OtherPath' instead of 'WebPathItems'

Target PrintValues:
    SourceWebVirtualPath: OtherPath
    SourceWebPhysicalPath: OtherPath
    _AspNetCompilerVirtualPath: WebPathItems
    _SourceWebPathItem: WebPathItems

Build succeeded.

So how can we fix this? We need to be able to dynamically create properties & items and have them override static items? We can create a wrapper MSBuild file that does the logic to determine the correct values and the use the MSBuild Task. Have a look below at the file Fix.proj.

 

 

<Project

  InitialTargets="PreBuild"

  DefaultTargets="Build"

  xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <!-- This represents the .wdproj file -->

 

  <PropertyGroup>

    <SourceWebVirtualPath>WebPathItems</SourceWebVirtualPath>

    <SourceWebPhysicalPath>WebPathItems</SourceWebPhysicalPath>

  </PropertyGroup>

 

  <Target Name="PreBuild">

    <Message Text="Changing path to be 'OtherPath' instead of 'WebPathItems'"/>

    <CreateProperty Value="OtherPath">

      <Output PropertyName="SourceWebVirtualPath" TaskParameter="Value"/>

    </CreateProperty>

    <CreateProperty Value="OtherPath">

      <Output PropertyName="SourceWebPhysicalPath" TaskParameter="Value"/>

    </CreateProperty>

 

    <Message Text="SourceWebVirtualPath: $(SourceWebVirtualPath)"/>

    <Message Text="SourceWebPhysicalPath: $(SourceWebPhysicalPath)"/>

  </Target>

 

  <Target Name="Build">

    <MSBuild Projects="Your.proj"

             Properties="SourceWebVirtualPath=$(SourceWebVirtualPath);

                SourceWebPhysicalPath=$(SourceWebPhysicalPath)"

             Targets="PrintValues"

             />

  </Target>

 

</Project>

Take a look at the highlighted portion, it simply calls MSBuild on the Your.proj fle. Here is the output when executing this file.

Target PreBuild:
    Changing path to be 'OtherPath' instead of 'WebPathItems'
    SourceWebVirtualPath: OtherPath
    SourceWebPhysicalPath: OtherPath
Target Build:

    __________________________________________________
    Project "C:\Data\Community\msbuild\WebDeploymentIssues\ASPNETix.proj" is building "C:\Data\Community\msbuild\WebDeploymentIssu\Sample\Your.proj" (PrintValues target(s)):

    Target PreBuild:
        Changing path to be 'OtherPath' instead of 'WebPathItems'
    Target PrintValues:
        SourceWebVirtualPath: OtherPath
        SourceWebPhysicalPath: OtherPath
        _AspNetCompilerVirtualPath: OtherPath
        _SourceWebPathItem: OtherPath

Build succeeded.

So you can see that all the desired values have been overridden. But having 2 files is really annoying, and in some cases you don’t really have this option. So because of this we need to find a solution that will modify the original file only. And here is that file, it is shown below its called YourFix.proj.

<Project

  InitialTargets="PreBuild"

  DefaultTargets="PrintValues"

  xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <!-- This represents the .wdproj file -->

 

  <PropertyGroup>

    <SourceWebVirtualPath>WebPathItems</SourceWebVirtualPath>

    <SourceWebPhysicalPath>WebPathItems</SourceWebPhysicalPath>

  </PropertyGroup>

 

  <Target Name="PreBuild">

    <Message Text="Changing path to be 'OtherPath' instead of 'WebPathItems'"/>

    <CreateProperty Value="OtherPath">

      <Output PropertyName="SourceWebVirtualPath" TaskParameter="Value"/>

    </CreateProperty>

    <CreateProperty Value="OtherPath">

      <Output PropertyName="SourceWebPhysicalPath" TaskParameter="Value"/>

    </CreateProperty>

  </Target>

 

  <PropertyGroup>

    <DoingFix>true</DoingFix>

  </PropertyGroup>

 

  <Import Project="Shared.proj"/>

 

  <!-- Here we conditionally override the PrintValues -->

  <Target Name="PrintValues" Condition="$(DoingFix)=='true'">

    <!-- Make a call to this file -->

    <MSBuild Projects="$(MSBuildProjectFullPath)"

             Properties="DoingFix=false;

              SourceWebVirtualPath=$(SourceWebVirtualPath);

              SourceWebPhysicalPath=$(SourceWebPhysicalPath)"

             Targets="PrintValues"

             />

  </Target>

</Project>

In this approach we need to modify the behavior of the PrintValues target so we override it and have it call itself with the correct values. The MSBuild ProjectFullPath is an MSBuild Reserved Property, and it will return the full path of the current project file. The trick here is the conditional override of this target based on the DoingFix value. So we can see that the DoingFix property will default to true, so the PrintValues target will be overridden. But when the file is called back into with DoingFix=false it will not be overridden, and the PrintValues in Shared.proj will be used. In your use this target will most likely be Build. The drawback of the approach is that you’d have to overwrite each target that you are interested in. The other drawback is that it is confusing if you are not paying close attention to what this does. I posted something previously that used a similar technique read it if this interests you http://www.sedodream.com/PermaLink,guid,9c235811-3078-45e7-b122-6a239fb33fc0.aspx.

Here is an outline of the files I have attached:

                Shared.proj        => Represents a shared file (ie Miscrosoft.WebDeployments.targets)

                Your.proj             => Represents your extension of the shared.proj file (ie SiteDeployment.wdproj)

                Fix.proj                 => The wrapper that corrects the problems

                YourFix.proj       => The real solution to this problem.

Sayed Ibrahim Hashimi

Friday, April 27, 2007 6:18:53 AM (GMT Daylight Time, UTC+01:00)  #    Comments [11]  | 
Monday, February 19, 2007

I’ve got a new article titled “WiX Tricks: Automate Releases With MSBuild And Windows Installer XML”. This article covers in detail how to create an automated build & release process using MSBuild & the Windows Installer Xml Toolset. Not only does it describe how to achieve an automated process, but I actually provide you will an extensible set of MSBuild files that can be used to easily do it for you! If you are already using WiX to create your installation packages then integrating my targets into your process should be very straight forward. You can download all the sources from article site, but you may be better off getting it from my Sedodream MSBuild project site. I’ll be maintaining the files on that site, so it is better to get the latest directly from there. If you do use my targets, please let me know what your experiences are. I’ll be continuing to expand on those and your feedback is important.  One of the items that are pretty important for me is to integrate Install Shield installations into this process. I’ve created an internal process to do this for me, but it is not re-usable as the WiX ones are. Soon I hope to be able to complete this.

Also in this article I do cover some more advanced MSBuild topics such as MSBuild batching in pretty good detail. As far as I know this is the only published article that covers batching in any kind of detail.

 

Sayed Ibrahim Hashimi

Monday, February 19, 2007 3:15:58 AM (GMT Standard Time, UTC+00:00)  #    Comments [1]  | 

Theme design by Jelle Druyts