- | rssFeed | My book on MSBuild and Team Build | Archives and Categories Monday, December 24, 2012

SlowCheetah build server support updated

A while back I blogged about how to setup SlowCheetah on a Build server. The solution was good but it was a bit unwieldy and error prone, especially those who are not comfortable with MSBuild (this solution still works and is still a good approach for some scenarios). I’ve made this much easier, but wasn’t able to make it as easy as what I wanted. The solution that I choose is using NuGet. Ideally you’d be able to install the SlowCheetah NuGet package into a project, enable package restore and the check in your code. Unfortunately due to how NuGet package restore is implemented this flow is not currently possible. Hopefully in the near future Nuget will better support these scenarios. For now we will have to work with the solution below.

Background

The transformations are executed when your project is built. The way that this works is that when you use the SlowCheetah VS add-in to enable transformations, your project file is modified to import an additional MSBuild .targets file which knows how to do the transformations. This .targets file is placed in your %localappdata% folder. When you add the SlowCheetah NuGet package to your project, the .targets file (and supporting files) are downloaded to the packages folder. Your project is also modified to look at the packages folder for the .targets file which is imported.

For build server scenarios, when using NuGet for this it’s a bit of a chicken and egg problem. Let me clarify, we are using NuGet to update the build process for your project. If a solution is using NuGet, you will need to enable NuGet Package Restore. This is what will enable your NuGet packages to be download during build.

Package Restore is implemented to restore NuGet packages for each project whenever it is built. This is accomplished by adding an Import into your project to NuGet.targets. This works beautiful for the majority of cases, but fails completely if you are trying to update the build process for a given project with NuGet. When the build for a project starts if the the imported .targets file do not exist on disk they are not imported. You can visualize this flow as.

image

We need to find a way to get the packages restored, before your project is built. We can achieve this by executing a build which will be used to simply restore our packages. Now let’s see how we can workaround this issue.

Solution

We need to find a way to restore the packages before the project is built. The easiest way to do this is to add a dummy solution to be built before your actual solution/project. Follow these steps.

1. Install the SlowCheetah NuGet package into the project using SlowCheetah.

You can do this by using the Package Manager Console, and executing the command Install-Package SlowCheetah –pre. Note: after the package is released you can remove the –pre.

2. Enable Package Restore for the solution

You can do this by right clicking on the solution and selecting Enable NuGet Package Restore.

3. Configure build server to build restorePackage.proj before your solution

As described we need a new solution that can be used to restore the required packages. When you install SlowCheetah it will automatically create a packageRestore.proj file in the root of your project. You can use this to easily restore your NuGet packages. All you need to do is to build this file before you build your solution or build script. If you are using Team Build this is pretty easy. Edit the build definition and then add packageRestore.proj to the Items to Build list. You can find this on the Process tab.

image

 

Make sure that packageRestore.proj is above (built before) your solution.

SNAGHTML158ea083

With these changes when the empty solution is built, it will restore the SlowCheetah .targets file. Then the target solution will be built, the .targets file will already be downloaded and your transforms will execute as needed.

I will work with the NuGet team to see if there is something better that can be done here. I will keep you all posted if there is any update.I would love if you tried out this new support and let me know if you have any issues with it.

Sayed Ibrahim Hashimi | @SayedIHashimi

SlowCheetah | Visual Studio 2010 | Visual Studio 2012 Monday, December 24, 2012 9:37:24 PM (GMT Standard Time, UTC+00:00)  #     | 
Saturday, October 27, 2012

MSBuild: how to set the configuration property

When you are building a .sln file or a .csproj/.vbproj/etc you need to be careful how you set the Configuration property. Once this property is set you do not want to change the value of it. Because of this it is best to specify this as a global property. To do that you can pass it in on the command line. If you are using the MSBuild task then you can pass it in via Properties or AdditionalProperties. What you do not want to do is to set this value inside your .csproj/.vbproj or any imported file. The only time when this is OK if there is a condition which will not set the property if it’s already been set. This is exactly how the default .csproj/.vbproj files are authored:

<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>

If you do set this property w/o the conditional not to overwrite an existing value you may experience really strange behavior during your builds. And it may be difficult to diagnose the issue as well.

Now let me explain why you cannot do this. When you build a .csproj/.vbproj MSBuild will start creating an in-memory representation of the entire project. It will start with the .csproj/.vbproj file itself. It will read the file from top to bottom. When a property is encountered it is evaluated. If a property is encountered that relies on another one, for example

<IntermediateOutputPath Condition=" '$(PlatformName)' == 'AnyCPU' ">$(BaseIntermediateOutputPath)$(Configuration)\</IntermediateOutputPath>

The properties inside the expression will be evaluated on whatever values exist for the properties at the time. In this case ItermediateOutputPath will be the value of BaseIntermediateOutputPath and Configuration.

If a property is encountered which specifies a value for a property which has been previously declared, the previous value will be discarded.

The implications of this are subtle but very simple; once a property has been set which has dependent properties you must not overwrite that property. This is because when a dependent property is encountered it is evaluated immediately. You cannot re-evaluate that property. So if you set a property during your build, some existing dependent properties which were evaluated before the value change will continue to use the old value. There is no way to re-evaluate those properties.

Now lets see how this relates to the Configuration property specifically.

The contents of the .csproj/.vbproj are properties/items along with an import to Microsoft.csharp.targets (Microsoft.VisualBasic.targets for .vbproj) which then imports Microsoft.Common.targets. If you look in Microsoft.common.targets you will see many different properties which are declared using $(Configuration). So this means that you must treat the Configuration property with care and abide by the rule that I have outlined above.

FYI If you want more details on this I have explained this entire process in great detail in my book Inside MSBuild and Team Build.

Configuration for web publishing

If you have used the new web publish experience in Visual Studio 2012 (also available for VS2010 from the Azure SDK) you may have noticed that the web publish profiles are MSBuild files. They are stored under Properties\PublishProfiles (My Project\PublishProfiles for VB) and have extensions of .pubxml. In that file (or in a .pubxml.user file associated with it) you will find a property defined as

<LastUsedBuildConfiguration>Release</LastUsedBulidConfiguration>

This value corresponds to the value for the Configuration drop down in the VS web publish dialog. When you kick off a publish in VS we use this value and kick off a build and specify Configuration as a global property which is passed in. The reason why we did not name this property Configuration is because the web publish profile is imported into the web project itself during the publish process. By the file being imported you can natively access all the properties/items of the .csproj/.vbproj and also easily extend the build process. Since that is the case we cannot set the Configuration property because we know it’s value has already been set. Due to this when publishing from the command line you must specify the Configuration property.

Sayed Ibrahim Hashimi | @SayedIHashimi

msbuild | MSDeploy | Visual Studio | Visual Studio 2008 | Visual Studio 2010 Saturday, October 27, 2012 3:49:02 AM (GMT Daylight Time, UTC+01:00)  #     | 
Sunday, August 19, 2012

Visual Studio project compatability and VisualStudioVersion

One of the most requested features of Visual Studio 2012 was the ability to open projects in both VS 2012 as well as VS 2010 (requires VS 2010 SP1). In case you haven’t heard we did implement that feature. You may be wondering how we were able to do this and how this may impact you.

If you open the .csproj/.vbproj for a Web Project created in VS2010 you will see the following import statement.

<Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\
v10.0\WebApplications\Microsoft.WebApplication.targets" />

When you open this project in VS 2012 there are a few changes made to your project file to ensure that it can be opened in both VS 2010 SP1 and VS 2012. One of the changes made to the project when it is first loaded in VS 2012 is to add the following to replace that import statement.

<PropertyGroup>
  <VisualStudioVersion Condition="'$(VisualStudioVersion)' == ''">10.0</VisualStudioVersion>
  <VSToolsPath Condition="'$(VSToolsPath)' == ''">
    $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion)</VSToolsPath>
</PropertyGroup>
<Import Project="$(VSToolsPath)\WebApplications\Microsoft.WebApplication.targets" Condition="'$(VSToolsPath)' != ''" />

We removed the hard-coded 10.0 and instead used the property VisualStudioVersion. When building in Visual Studio 2012 this value will always be 11.0, but for VS 2010 it doesn’t exist. That is why we defaulted it to 10.0 above.

There are some scenarios where building from the command line will require to set this property explicitly. Before we get there let me explain how this property gets set (in this order)

  1. If VisualStudioVersion is defined as an environment variable/global MSBuild property, that is used.
    • This is how VS and the VS developer command prompt set this value
  2. Based on the file format version of the .sln file (toolset used is sln file format –1)
    • To simplify this statement, the .sln file will build with specifying VisualStudioVersion to the value of the version of VS which created the .sln file.
  3. Choose default
    • 10.0 if VS 2010 is installed
    • Highest-versioned sub-toolset version installed

For #2 when you are building a .sln file the value of VisulStudioVersion will be –1 of the Format Version found in the .sln file. The important thing to note here is that if you build a .sln file it will build with the value of VisulStudioVersion corresponding to the version of VS which created the .sln file. So if you create a .sln file in VS2012 and you always build that .sln file the value for VisualStudioVersion will be 11.0. In many cases if you build the .sln file you are good.

If you are building .csproj/.vbproj files w/o going through a .sln file? If you build a web project from the command line (not the developer prompt) then the value for VisualStudioVersion used will be 10.0. That is an artifact of the properties which I showed above. In this case you should pass this in as an MSBuild property. For example

msbuild.exe MyAwesomeWeb.csproj /p:VisualStudioVersion=11.0

In this case I’m passing in the property explicitly. This will always override any other mechanism to determine the value for VisualStudioVersion. If you are using the MSBuild task in a build script, then you can specify the property either in the Properties attribute or the AdditionalProperties attribute. See my previous blog post on the difference between Properties and AdditionalProperties.

If you encounter any funny behavior when building/publishing and you notice that the wrong .targets files are being imported then you may need to specify this property.

Sayed Ibrahim Hashimi | @SayedIHashimi

msbuild | Visual Studio | Visual Studio 11 | Visual Studio 2010 | Visual Studio 2012 | web | Web Publishing Pipeline Sunday, August 19, 2012 10:06:56 PM (GMT Daylight Time, UTC+01:00)  #     | 
Thursday, August 16, 2012

SlowCheetah v2.4 Released

I jus released a new version of SlowCheetah, my Visual Studio extension which enables you to transform app.config, and other XML files, during F5 for non-web projects. In this update I added support for Visual Studio 2012 as well as the bug fixes below.
  1. Fix for Preview command doesn't show up for all web.config transforms
  2. Fix for XML transforms not working with Azure SDK
  3. Fix for XML transforms not working with Azure SDK
  4. Fix for .config files do no transform in VS 2012 RC
  5. Fix for In web project, project File gets modified while adding transformation
  6. Fix for Add Transform should not show up for .settings files
  7. Fix for Transforms should be added as None

If you are interested in the project you can take a look at the code at https://github.com/sayedihashimi/slow-cheetah and if you have any issues please file a bug at https://github.com/sayedihashimi/slow-cheetah/issues.

 

Thanks,
Sayed Ibrahim Hashimi | @SayedIHashimi

    SlowCheetah | Visual Studio | Visual Studio 11 | Visual Studio 2010 | Visual Studio 2012 Thursday, August 16, 2012 1:54:26 AM (GMT Daylight Time, UTC+01:00)  #     | 
    Monday, August 06, 2012

    Plans regarding Website projects and Web Deployment Projects

    The release of Visual Studio 2012 is right around the corner. If you’ve been following our blog/twitter then you may know that many of the Web related components of Visual Studio are now“out-of-band”, meaning that we can update them independently of Visual Studio itself. Because of this we are planning to ship updates a few times a year. We are currently in the process for planning our first update for VS 2012 and we wanted to share some of the items that we are planning to work on. We would like to get your feedback on these ideas to ensure that we are doing the right things. If you have comments please do let us know. FYI the updates that I describe below will be made available for both VS 2012 as well as VS 2010.

    Website Project Updates

    When you use Visual Studio there are two types of web projects you can create: a Web Application Project (WAP, this includes MVC) and a Website Project (WSP). In VS 2012 we did a lot of work to update the publish experience for WAPs. When we set out for these updates we planned to take these enhancements and make the available to Website projects as well. As we progressed it became evident that we did not have the resources to implement the features for both WAP and WSP in time for VS 2012. So we focused on creating the right experience for WAPs and we also added an extensibility point into WSP which will allow us to replace the existing publish dialog. The reason why we focused on WAP first was because WAPs already have support for Web Deploy (MSDeploy) based publishing as well as a formal build/publish process captured in MSBuild. WSP does not have either of these so it would have taken more time to start with WSP.

    When VS 2012 is released the publish experience for WSP will be the same which was available in VS2010 but we will have a release a few months after the release which will bring all the publish goodness to Website Projects! This will include all the features which are available to Web Application Projects including; the ability to publish using.

    Since both project systems will be using the exact same code whenever we add a feature it will immediately be available to both. In today’s world they are two entirely different code bases (the WSP publishing experience is currently native code where as the WAP publish dialog is managed). This will allow us to maintain a consistent publish experience and also enable us to deliver features more quickly.

    Since WSP does not have a “build” process you might be wondering how we are planning to hook this all together since our entire publish pipeline is captured in MSBuild. Here is a rough idea of what we are currently thinking. After you use the new publish dialog to create a publish profile for the first time we will do a number of things:

    1. Create a new folder to contain needed artifacts
      1. The folder may be in the root of the Website project, but we are considering placing it somewhere outside as well
      2. The name of this folder is not yet finalized, but it could be something like _Publish or _Project
    2. Create an MSBuild file for the Website in that folder, this will capture information like; included files, references, properties to control publish process
      1. The primary purpose of dropping this file is to facilitate command line publish scenarios, without this we could publish from Visual Studio but not from the command line
      2. When you make changes in Visual Studio like adding a reference we will automatically keep this file up to date
    3. Create the publish profile in that folder (this is a .pubxml file)
    4. When the site is being published the working directory (i.e. obj\ folder) will be outside the root of the website, most likely in the same folder which contains the WSP root

    After we have those things in place for the most part the publish dialog will be able to treat both projects in the same way. These files will by default be stored in version control and can be shared with other team members. Sensitive information such as the MSDeploy password will be stored in another file and encrypted per-user/per-machine in a .user file which is not checked in.

    Web Deployment Projects

    A few months after Visual Studio 2005 shipped we released the first version of Web Deployment Projects, and we updated WDP for both VS 2008 and VS 2010 and released them a few months after each of those releases as well. WDPs are used for a few different scenarios including the following.

    1. Publishing a Website project using MSDeploy
    2. Command line publish support
    3. Customizing the publish process for a WAP
    4. Running ASP.NET precompile/merge for a WSP or WAP

    When we looked at the scenarios that WDPs are typically used for and then compared that to features which we have for WAP and WSP (after the updates mentioned above) we noticed that most (if not all) scenarios where WDP is used can be covered without requiring a WDP. Our new publish experience already has first class support for MSDeploy, command line publishing, and for extensibility so that covers #1-#3. Regarding #4 for WAPs we have already added ASP.NET precompile/merge as a publish option (you can find it in the Package/Publish Web tab). Since WSP does not have any property pages we are likely to move that option to being on the publish dialog itself or we will expose those options in another way for WSP. We have not yet settled on that design.

    It may not seem like it but updating WDP to support VS2012 is a significant effort. Knowing that, and that most of the scenarios where WDP are used can now be transitioned to the new experience, we have decided to invest in giving WSP projects first class publishing support instead of updating VS 2012 to support WDP. We think that this is the right decision because we are unifying the publish experience for both project systems and it allows us to create a deeper set of features going forward instead of investing in different things and wasting effort. If you have a scenario that you think we have missed please do not hesitate to reach out to me by email at sayedha@microsoft.com and let me know.


    Sayed Ibrahim Hashimi | @SayedIHashimi

    asp.net | MSDeploy | Visual Studio | Visual Studio 2010 | Visual Studio 2012 | Web Publishing Pipeline Monday, August 06, 2012 10:16:47 PM (GMT Daylight Time, UTC+01:00)  #     | 
    Friday, June 15, 2012

    Downloading the Visual Studio Web Publish Updates

    I have written a few posts recently describing out updated web publish experience. These new experience is available for both Visual Studio 2010 as well as Visual Studio 2012 RC. You can use the links below to download these updates in the Azure SDK download. Below are links for both versions.

    The Web Publish experience is chained into VS 2012 RC so if you have installed VS 2012 RC with the Web features then you already have these features.

    Thanks,
    Sayed Ibrahim Hashimi @SayedIHashimi

    asp.net | Deployment | Visual Studio | Visual Studio 11 | Visual Studio 2010 | web | Web Deployment Tool | Web Publishing Pipeline Friday, June 15, 2012 8:30:40 PM (GMT Daylight Time, UTC+01:00)  #     | 

    Visual Studio 2010 Web Publish Updates

    Last week we rolled out some updates for our Visual Studio 2010 Web Publishing Experience. This post will give you an overview of the new features which we released. In the coming weeks there will be more posts getting into more details regarding individual features.

    You can get these updates in the Windows Azure SDK for Visual Studio 2010. When you download that package there you will also get the latest tools for Azure development.

    The new high level features include the following.

    Overview

    When you right click on your Web Application Project (WAP) you will now see the new publish dialog.

    image

    On this tab you can import a .publishSettngs file, which many web hosts provide, and you can also manage your publish profiles. If you are hosting your site on Windows Azure Web Sites then you can download the publish profile on the dashboard of the site using the Download publish profile link. After you import this publish profile you will be brought to the Connection tab automatically.

    image

    On this tab you can see all the server configuration values which are needed for your client machine to connect to the server. Typically you don’t have to worry about the details of these values. Next you’ll go to the Settings tab.

    image

    On the Settings tab you can set the build configuration which should be used for the publish process, the default value here is Release. There is also a checkbox to enable you to delete any files on the server which do not exist in the project.

    Below that checkbox you will see a section for databases. The sample project shown has an Entity Framework Code First model, named ContactsContext, and it uses Code First Migrations to manage the database schema. If you have any non-EF Code First connection strings in web.config then those databases will show up as well but the support for incrementally publishing the schema for those has not yet been finalized. We are currently working on that. You can visit my previous blog entry for more info on that.

    If you imported a .publishSettings file with a connection string then that connection string would automatically be inserted in the textbox/dropdown for the connection string. If you did not then you can use the … button to create a connection string with the Connection String Builder dialog or you can simply type/paste in a connection string. For the EF Code First contexts you will see the Execute Code Frist Migrations checkbox. When you check this when your site is published the web.config will be transformed to enable the Code First migrations to be executed the first time that the context is accessed. Now you can move to the Preview tab.

    When you first come to the Preview tab you will see a Start Preview button. Once you click this button you will see the file operations which would be performed once you publish. Since this site has never been published all the file operations are Add, as you can see in the image below. The other Action values include; Update and Delete.

    image

    Once you are ready to publish you can click the Publish button. You can monitor the progress of the publish process using the Output Window. If your publish profile had a value for the Destination URL then the site will automatically be opened in the default browser after the publish has successfully completed.

    Publish Profiles

    One of the other changes in the publish experience is that publish profiles are now stored as a part of your project. They are stored under the folder Properties\PublishProfiles (for VB projects its My Project\PublishProfiles) and the extension is .pubxml. You can see this in the image below.

    image

    These .pubxml files are MSBuild files and you can modify these files in order to customize the publish process. If you do not want the publish profile to be checked into version control you can simply exclude it from the project. The publish dialog will look at the files in the PublishProfiles folder so you will still be able to publish using that profile. You can also leverage these publish profiles to simply publishing from the command line. For example you can use the following syntax to publish from the command line.

    msbuild.exe WebApplication2.csproj /p:DeployOnBuild=true;PublishProfile="pubdemo - Web Deploy";Password={INSERT-PASSWORD}

     

    Resources

    If you have any questions please feel free to directly reach out to me at sayedha(at){MicrosoftDOTCom}.

    Sayed Ibrahim Hashimi @SayedIHashimi

    asp.net | Microsoft | MSDeploy | Visual Studio 2010 | web | Web Deployment Tool | Web Publishing Pipeline Friday, June 15, 2012 8:07:30 PM (GMT Daylight Time, UTC+01:00)  #     | 
    Thursday, June 07, 2012

    ASP.NET providers and SQL Azure

    We have two sets of ASP.NET providers which currently exist; the ASP.NET SQL providers, and the ASP.NET Universal Providers. In VS 2010 the SQL providers were in only providers used for our project templates. In VS 2012 we have switched to using the Universal Providers. One of the drawbacks of the SQL providers is that it leverages DB objects of SQL server which are not available in SQL Azure.

    In our updated web publish experience we have an Update Database checkbox which can be used to incrementally publish the database to the destination database. In this case if the source connection string is used by the ASP.NET SQL providers and you are publishing to SQL Azure then you will see the following message on the dialog.

    SNAGHTML48cbb8

    Note: you may see the Update Database checkbox disabled, please visit http://sedodream.com/2012/06/07/VSPublishDialogUpdateDatabaseDialogDisabled.aspx for more info on why.

    The publish dialog is letting you know that the SQL providers are not compatible with SQL Azure and helps you convert to using the Universal Providers. After you install the Universal Providers the web.config entry will be commented out and new entries will be inserted for the Universal Providers. Your existing database will not be impacted, we’ll create a new connection string pointing to a new database. If you had any data in the SQL Providers database you will have to re-create those objects in the new database.

    If you have any questions please feel free to directly reach out to me at sayedha(at){MicrosoftDOTCom}.

    Sayed Ibrahim Hashimi @SayedIHashimi

    Visual Studio | Visual Studio 11 | Visual Studio 2010 | web | Web Publishing Pipeline Thursday, June 07, 2012 11:41:46 PM (GMT Daylight Time, UTC+01:00)  #     | 

    Database settings in the VS Publish dialog

    In Visual Studio 2010 we introduced a database publishing experience in the Package/Publish SQL (PP/SQL) properties page. This support relies on generating create scripts from the source database and then executing those scripts when you publish your web application. For more details regarding this feature take a look at Package/Publish SQL Tab, Project Properties. One of the negative aspects of the implementation that we have there is that the DB schema publishing is not incremental, it always executes create scripts. So in many cases you publish your app for the first time and things are great, but the next time you try to publish you receive errors because it tries to create DB objects which already exist.

    In our improved publish dialog we have a new database publishing experience which is incremental, so you don’t run into the same issues when re-deploying your database. All of the configuration for this is within the publish dialog itself. If you have a project which already has DB publish settings on the Package/Publish SQL tab (under any build configuration/platform) then you will see the message shown in the image below.

    SNAGHTML2bb135

    The new incremental DB schema publish mechanism is not compatibly with the feature set on the PP/SQL tab. For each web publish profile you can either use the settings on the PP/SQL tab or you can opt-in to the new experience on the publish dialog. We implemented it this way in order to introduce the new feature set and to ensure that existing publish scenarios are not broken.

    FYI there is a related post on why the Update database checkbox is disabled in the publish dialog which you may be interested in.

    If you have any questions please feel free to directly reach out to me at sayedha(at){MicrosoftDOTCom}.

    Sayed Ibrahim Hashimi @SayedIHashimi

    Microsoft | Visual Studio | Visual Studio 2010 | Web Publishing Pipeline Thursday, June 07, 2012 11:09:13 PM (GMT Daylight Time, UTC+01:00)  #     | 

    VS Publish dialog Update Database dialog disabled

    If you have tried out our new Web Publish experience in Visual Studio you may have noticed that the Update Database checkbox is disabled. See the image below.

    image

    The intended behavior of this checkbox is to enable you to incrementally publish your database schema from the source (the connection string in web.config) to the destination (whatever connection string is in the text box). The difference between an incremental publish and a typical publish is that for incremental publishes only changes are transferred from source to destination. With a full publish the first time that you publish your DB schema everything is created, and the next time that you try to publish you will receive an error because it tries to re-create existing DB objects.

    The functionality of the Update database checkbox leverages an MSDeploy provider. We were hoping to complete that provider and give it to hosters in time for the release but we were unable to do so. We are working on completing the provider and partnering with hosters to install these in time for the launch of Visual Studio 2012 RTM.

    In the mean time if you need to publish your DB schema you can use the Package/Publish SQL tab (caution: the DB publishing here is not incremental). If you are going to use the PP/Sql tab to publish to SQL Azure then there are some special consideraions that you will need to take. You can learn more about those by visiting http://msdn.microsoft.com/en-us/library/dd465343.aspx and searching for “Azure” on that page.

    If you have any questions please feel free to directly reach out to me at sayedha(at){MicrosoftDOTCom}.


    Thanks,
    Sayed Ibrahim Hashimi @SayedIHashimi

    Visual Studio | Visual Studio 11 | Visual Studio 2010 | web | Web Development | Web Publishing Pipeline Thursday, June 07, 2012 10:44:26 PM (GMT Daylight Time, UTC+01:00)  #     | 
    Saturday, May 12, 2012

    web.config transforms, they are invoked on package and publish not F5

    \I receive a lot of questions regarding web.config transforms, which have existed in Visual Studio since 2010, and wanted to clear up the support that we have in this area. These transforms show up in the solution explorer underneath web.config as shown in the image below.

    image

    Since the names of these transforms include the build configuration many people expect that web.config will be transformed when they start debugging (F5) or run the app (CTRL+F5) in Visual Studio. But sadly this is not the case. These transforms are kicked in only when the web is packaged or published. I totally agree that this would be awesome, and I even blogged about how to enable it at http://sedodream.com/2010/10/21/ASPNETWebProjectsWebdebugconfigWebreleaseconfig.aspx. It may seem like it would be really easy for us to include this support in the box, but unfortunately that is not the case. The reason why we are not able to implement this feature at this time is because a lot of our tooling (and many partners) relies on web.config directly. For example when you drag and drop a database object onto a web form, it will generate a connection string into the web.config. There are a lot of features are like this. It is a significant investment for us to make a change of this level. We were not able to get this done for Visual Studio 11, but it is on our radar and we are looking to see what we can do in this area in the future.

    Sayed Ibrahim Hashimi @SayedIHashimi

    Visual Studio 2010 | web Saturday, May 12, 2012 3:29:15 AM (GMT Daylight Time, UTC+01:00)  #     | 
    Friday, April 27, 2012

    SlowCheetah VS 11 support and now on github

    In case you are not familiar with SlowCheetah, it is a Visual Studio extension which allows you to transform app.config files in the same way that web.config files are. More info on the project page http://visualstudiogallery.msdn.microsoft.com/69023d00-a4f9-4a34-a6cd-7e854ba318b5.

    I wanted to let everyone know that I have updated SlowCheetah to support Visual Studio 11 and I have also placed the project on github at https://github.com/sayedihashimi/slow-cheetah. Regarding the VS 11 support there is one known issue that we currently do not support the preview functionality on VS 11. This is an item which we will be fixing for the next release.

     

    Thanks,
    Sayed Ibrahim Hashimi @SayedIHashimi

    Reminder: even though I work for Microsoft this is a personal project and not formally affiliated with Microsoft

    msbuild | SlowCheetah | Visual Studio 11 | Visual Studio 2010 Friday, April 27, 2012 7:52:02 PM (GMT Daylight Time, UTC+01:00)  #     | 
    Sunday, January 08, 2012

    How to take your web app offline during publishing

    I received a customer email asking how they can take their web application/site offline for the entire duration that a publish is happening from Visual Studio. An easy way to take your site offline is to drop an app_offline.htm file in the sites root directory. For more info on that you can read ScottGu’s post, link in below in resources section. Unfortunately Web Deploy itself doesn’t support this Sad smile. If you want Web Deploy (aka MSDeploy) to natively support this feature please vote on it at http://aspnet.uservoice.com/forums/41199-general/suggestions/2499911-take-my-site-app-offline-during-publishing.

    Since Web Deploy doesn’t support this it’s going to be a bit more difficult and it requires us to perform the following steps:

    1. Publish app_offline.htm
    2. Publish the app, and ensure that app_offline.htm is contained inside the payload being published
    3. Delete app_offline.htm

    #1 will take the app offline before the publish process  begins.
    #2 will ensure that when we publish that app_offline.htm is not deleted (and therefore keep the app offline)
    #3 will delete the app_offline.htm and bring the site back online

    Now that we know what needs to be done let’s look at the implementation. First for the easy part. Create a file in your Web Application Project (WAP) named app_offline-template.htm. This will be the file which will end up being the app_offline.htm file on your target server. If you leave it blank your users will get a generic message stating that the app is offline, but it would be better for you to place static HTML (no ASP.NET markup) inside of that file letting users know that the site will come back up and whatever other info you think is relevant to your users. When you add this file you should change the Build Action to None in the Properties grid. This will make sure that this file itself is not published/packaged. Since the file ends in .htm it will by default be published. See the image below.

    image

    Now for the hard part. For Web Application Projects we have a hook into the publish/package process which we refer to as “wpp.targets”. If you want to extend your publish/package process you can create a file named {ProjectName}.wpp.targets in the same folder as the project file itself. Here is the file which I created you can copy and paste the content into your wpp.targets file. I will explain the significant parts but wanted to post the entire file for your convince. Note: you can grab my latest version of this file from my github repo, the link is in the resource section below.

    <?xml version="1.0" encoding="utf-8"?>
    <Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <Target Name="InitalizeAppOffline">
        <!-- 
        This property needs to be declared inside of target because this is imported before
        the MSDeployPath property is defined as well as others -->
        <PropertyGroup>
          <MSDeployExe Condition=" '$(MSDeployExe)'=='' ">$(MSDeployPath)msdeploy.exe</MSDeployExe>
        </PropertyGroup>    
      </Target>
    
      <PropertyGroup>
        <PublishAppOfflineToDest>
          InitalizeAppOffline;
        </PublishAppOfflineToDest>
      </PropertyGroup>
    
      <!--
        %msdeploy% 
          -verb:sync 
          -source:contentPath="C:\path\to\app_offline-template.htm" 
          -dest:contentPath="Default Web Site/AppOfflineDemo/app_offline.htm"
      -->
    
      <!--***********************************************************************
      Make sure app_offline-template.htm gets published as app_offline.htm
      ***************************************************************************-->
      <Target Name="PublishAppOfflineToDest" 
              BeforeTargets="MSDeployPublish" 
              DependsOnTargets="$(PublishAppOfflineToDest)">
        <ItemGroup>
          <_AoPubAppOfflineSourceProviderSetting Include="contentPath">
            <Path>$(MSBuildProjectDirectory)\app_offline-template.htm</Path>
            <EncryptPassword>$(DeployEncryptKey)</EncryptPassword>
            <WebServerAppHostConfigDirectory>$(_MSDeploySourceWebServerAppHostConfigDirectory)</WebServerAppHostConfigDirectory>
            <WebServerManifest>$(_MSDeploySourceWebServerManifest)</WebServerManifest>
            <WebServerDirectory>$(_MSDeploySourceWebServerDirectory)</WebServerDirectory>
          </_AoPubAppOfflineSourceProviderSetting>
    
          <_AoPubAppOfflineDestProviderSetting Include="contentPath">
            <Path>"$(DeployIisAppPath)/app_offline.htm"</Path>
            <ComputerName>$(_PublishMsDeployServiceUrl)</ComputerName>
            <UserName>$(UserName)</UserName>
            <Password>$(Password)</Password>
            <EncryptPassword>$(DeployEncryptKey)</EncryptPassword>
            <IncludeAcls>False</IncludeAcls>
            <AuthType>$(AuthType)</AuthType>
            <WebServerAppHostConfigDirectory>$(_MSDeployDestinationWebServerAppHostConfigDirectory)</WebServerAppHostConfigDirectory>
            <WebServerManifest>$(_MSDeployDestinationWebServerManifest)</WebServerManifest>
            <WebServerDirectory>$(_MSDeployDestinationWebServerDirectory)</WebServerDirectory>
          </_AoPubAppOfflineDestProviderSetting>
        </ItemGroup>
    
        <MSdeploy
              MSDeployVersionsToTry="$(_MSDeployVersionsToTry)"
              Verb="sync"
              Source="@(_AoPubAppOfflineSourceProviderSetting)"
              Destination="@(_AoPubAppOfflineDestProviderSetting)"
              EnableRule="DoNotDeleteRule"
              AllowUntrusted="$(AllowUntrustedCertificate)"
              RetryAttempts="$(RetryAttemptsForDeployment)"
              SimpleSetParameterItems="@(_AoArchivePublishSetParam)"
              ExePath="$(MSDeployPath)" />
      </Target>
    
      <!--***********************************************************************
      Make sure app_offline-template.htm gets published as app_offline.htm
      ***************************************************************************-->
      <!-- We need to create a replace rule for app_offline-template.htm->app_offline.htm for when the app get's published -->
      <ItemGroup>
        <!-- Make sure not to include this file if a package is being created, so condition this on publishing -->
        <FilesForPackagingFromProject Include="app_offline-template.htm" Condition=" '$(DeployTarget)'=='MSDeployPublish' ">
          <DestinationRelativePath>app_offline.htm</DestinationRelativePath>
        </FilesForPackagingFromProject>
    
        <!-- This will prevent app_offline-template.htm from being published -->
        <MsDeploySkipRules Include="SkipAppOfflineTemplate">
          <ObjectName>filePath</ObjectName>
          <AbsolutePath>app_offline-template.htm</AbsolutePath>
        </MsDeploySkipRules>
      </ItemGroup>
    
      <!--***********************************************************************
      When publish is completed we need to delete the app_offline.htm
      ***************************************************************************-->
      <Target Name="DeleteAppOffline" AfterTargets="MSDeployPublish">
        <!--
        %msdeploy% 
          -verb:delete 
          -dest:contentPath="{IIS-Path}/app_offline.htm",computerName="...",username="...",password="..."
        -->
        <Message Text="************************************************************************" />
        <Message Text="Calling MSDeploy to delete the app_offline.htm file" Importance="high" />
        <Message Text="************************************************************************" />
    
        <ItemGroup>
          <_AoDeleteAppOfflineDestProviderSetting Include="contentPath">
            <Path>$(DeployIisAppPath)/app_offline.htm</Path>
            <ComputerName>$(_PublishMsDeployServiceUrl)</ComputerName>
            <UserName>$(UserName)</UserName>
            <Password>$(Password)</Password>
            <EncryptPassword>$(DeployEncryptKey)</EncryptPassword>
            <AuthType>$(AuthType)</AuthType>
            <WebServerAppHostConfigDirectory>$(_MSDeployDestinationWebServerAppHostConfigDirectory)</WebServerAppHostConfigDirectory>
            <WebServerManifest>$(_MSDeployDestinationWebServerManifest)</WebServerManifest>
            <WebServerDirectory>$(_MSDeployDestinationWebServerDirectory)</WebServerDirectory>
          </_AoDeleteAppOfflineDestProviderSetting>
        </ItemGroup>
        
        <!-- 
        We cannot use the MSDeploy/VSMSDeploy tasks for delete so we have to call msdeploy.exe directly.
        When they support delete we can just pass in @(_AoDeleteAppOfflineDestProviderSetting) as the dest
        -->
        <PropertyGroup>
          <_Cmd>"$(MSDeployExe)" -verb:delete -dest:contentPath="%(_AoDeleteAppOfflineDestProviderSetting.Path)"</_Cmd>
          <_Cmd Condition=" '%(_AoDeleteAppOfflineDestProviderSetting.ComputerName)' != '' ">$(_Cmd),computerName="%(_AoDeleteAppOfflineDestProviderSetting.ComputerName)"</_Cmd>
          <_Cmd Condition=" '%(_AoDeleteAppOfflineDestProviderSetting.UserName)' != '' ">$(_Cmd),username="%(_AoDeleteAppOfflineDestProviderSetting.UserName)"</_Cmd>
          <_Cmd Condition=" '%(_AoDeleteAppOfflineDestProviderSetting.Password)' != ''">$(_Cmd),password=$(Password)</_Cmd>
          <_Cmd Condition=" '%(_AoDeleteAppOfflineDestProviderSetting.AuthType)' != ''">$(_Cmd),authType="%(_AoDeleteAppOfflineDestProviderSetting.AuthType)"</_Cmd>
        </PropertyGroup>
    
        <Exec Command="$(_Cmd)"/>
      </Target>  
    </Project>

    #1 Publish app_offline.htm

    The implementation for #1 is contained inside the target PublishAppOfflineToDest. The msdeploy.exe command that we need to get executed is.

    msdeploy.exe
        -source:contentPath='C:\Data\Personal\My Repo\sayed-samples\AppOfflineDemo01\AppOfflineDemo01\app_offline-template.htm'
        -dest:contentPath='"Default Web Site/AppOfflineDemo/app_offline.htm"',UserName='sayedha',Password='password-here',ComputerName='computername-here',IncludeAcls='False',AuthType='NTLM' -verb:sync -enableRule:DoNotDeleteRule

    In order to do this I will leverage the MSDeploy task. Inside of the PublishAppOfflineToDest target you can see how this is accomplished by creating an item for both the source and destination.

    #2 Publish the app, and ensure that app_offline.htm is contained inside the payload being published

    This part is accomplished by the fragment

      <!--***********************************************************************
      Make sure app_offline-template.htm gets published as app_offline.htm
      ***************************************************************************-->
      <!-- We need to create a replace rule for app_offline-template.htm->app_offline.htm for when the app get's published -->
      <ItemGroup>
        <!-- Make sure not to include this file if a package is being created, so condition this on publishing -->
        <FilesForPackagingFromProject Include="app_offline-template.htm" Condition=" '$(DeployTarget)'=='MSDeployPublish' ">
          <DestinationRelativePath>app_offline.htm</DestinationRelativePath>
        </FilesForPackagingFromProject>
    
        <!-- This will prevent app_offline-template.htm from being published -->
        <MsDeploySkipRules Include="SkipAppOfflineTemplate">
          <ObjectName>filePath</ObjectName>
          <AbsolutePath>app_offline-template.htm</AbsolutePath>
        </MsDeploySkipRules>
      </ItemGroup>

    The item value for FilesForPackagingFromProject here will convert your app_offline-template.htm to app_offline.htm in the folder from where the publish will be processed. Also there is a condition on it so that it only happens during publish and not packaging. We do not want app_offline-template.htm to be in the package (but it’s not the end of the world if it does either).

    The element for MsDeploySkiprules will make sure that app_offline-template.htm itself doesn’t get published. This may not be required but it shouldn’t hurt.

    #3 Delete app_offline.htm

    Now that our app is published we need to delete the app_offline.htm file from the dest web app. The msdeploy.exe command would be:

    %msdeploy%
          -verb:delete
          -dest:contentPath="{IIS-Path}/app_offline.htm",computerName="...",username="...",password="..."

    This is implemented inside of the DeleteAppOffline target. This target will automatically get executed after the publish because I have included the attribute AfterTargets=”MSDeployPublish”. In that target you can see that I am building up the msdeploy.exe command directly, it looks like the MSDeploy task doesn’t support the delete verb.

    If you do try this out please let me know if you run into any issues. I am thinking to create a Nuget package from this so that you can just install that package. That would take a bit of work so please let me know if you are interested in that.

    Resources

    1. The latest version of my AppOffline wpp.targets file.
    2. ScottGu’s blog on app_offline.htm

     

    Sayed Ibrahim Hashimi @SayedIHashimi

    IIS | Microsoft | msbuild | MSDeploy | Visual Studio 2010 | web | Web Deployment Tool | Web Publishing Pipeline Sunday, January 08, 2012 8:44:39 PM (GMT Standard Time, UTC+00:00)  #     | 
    Monday, December 12, 2011

    SlowCheetah XML transforms from a CI server

    Please read my updated post, the info below is no longer required for some scenarios SlowCheetah build server support updated

    A few months ago I published a Visual Studio add-in, SlowCheetah – XML Transforms, which enables you to transform any XML file during a build in the same way that you can transform the web.config during publish/package for web projects. Since then I’ve received numerous requests to demonstrate how the transforms can be kicked in from a CI server. It’s actually pretty easy because all the logic from a transform perspective is contained inside of MSBuild tasks/targets. The Visual Studio add-in itself is only delivering the support for gestures/preview.

    Before I dive into how to enable this for a CI server let me explain how the plugin works and then the CI integration will be come much clearer. When you load a project/solution in Visual Studio for the first time after you installed SlowCheetah a set of files will be written to %localappdata%\Microsoft\MSBuild\SlowCheetah\v1. Those file are:

     

    Name

    Description

    Install-Manifest.xml An XML file which describes the files that are installed. This is used by the plugin itself, you should never have to do anything with this file.
    SlowCheetah.Tasks.dll The assembly which contains the MSBuild task which transforms XML files.
    SlowCheetah.Transforms.targets The MSBuild file which enables XML file transformation

     

    The add-in adds the following menu command to any XML file for supported project types.

    image

    When you click on this for the first time the add-in needs to make some changes to your project file so you are prompted to accept that. After you do the following changes are applied to your project file.

    1. A new MSBuild property SlowCheetahTargets is added to the project and it points to the SlowCheetah.transforms.targets file in %localappdata%
    2. An import for that file is added

    More specifically the elements added to your project file are.

    <PropertyGroup>
      <SlowCheetahTargets Condition=" '$(SlowCheetahTargets)'=='' ">$(LOCALAPPDATA)\Microsoft\MSBuild\SlowCheetah\v1\SlowCheetah.Transforms.targets</SlowCheetahTargets>
    </PropertyGroup>
    
    <Import Project="$(SlowCheetahTargets)" Condition="Exists('$(SlowCheetahTargets)')" />

    Note: If you look at your project file they will not be next to each other. The property will be towards the top of the project file and the import towards the bottom.

    So now you might have an idea of why this won’t work on your CI server, the file SlowCheetah.Transforms.targets will not exist in the %localappdata% folder. There is an easy to way to fix this which doesn’t require any changes to your CI server. Check in the files and update your import. Here is what I did:

    1. Create a folder named SlowCheetah in the same folder as my solution
    2. Added SlowCheetah.Tasks.dll and SlowCheetah.Transforms.targets to that folder
    3. Create a solution folder named SlowCheetah in the solution
    4. Drag and drop the files from your SlowCheetah folder into the SolutionFolder
    5. Updated my project file to point to these files

    Now your solution should look roughly like the following image.

    image

    Note: Steps #3/#4 are not strictly required

    For #5 you’ll have to edit your project file, don’t worry its an easy modification. Just follow these steps:

    1. Right click on the project and select unload
    2. Right click on the unloaded project and select Edit
    3. Update the value for the SlowCheetahTargets import

    In my case the folder that I placed the files into is 1 directory above the project itself. So the new value for the property is as follows.

    <SlowCheetahTargets>$(MSBuildProjectDirectory)\..\SlowCheetah\SlowCheetah.Transforms.targets</SlowCheetahTargets>

    After that I checked in all the files, kicked off a build and viola the files are transformed. Let me know if you need any more info.

    Sayed Ibrahim Hashimi – @SayedIHashimi

    Note: I work for Microsoft, but these are my own opinions

    msbuild | SlowCheetah | Visual Studio | Visual Studio 2010 Monday, December 12, 2011 5:02:42 AM (GMT Standard Time, UTC+00:00)  #     | 
    Wednesday, August 17, 2011

    App.config transform Visual Studio Add in

    When I talk to people about the XDT (web.config) transforms that web projects support for package/publish one of the most common questions is “Does this work for non-web projects?” Unfortunately the answer is No, but myself and a friend of mine (Chuck England) have created a Visual Studio add in which enables just this. You can find it in the Visual Studio gallery under the name SlowCheetah – XML Transforms. Here is an overview of the features of the add in.

    1. Add tooling to desktop project to create XDT transforms
    2. Transform app.config for desktop projects based on build configuration
    3. Transform any XML file to the output folder base on build configuration
    4. Add tooling to enable previewing XDT transforms
    5. For web projects easily transform other XML files during package/publish

    After you install the add in you will get the following menu item when you right click any XML file.

    Add-Transform01

    For each build configuration defined you will get a transform created as a child item.

    App-Config

    These files are stub XDT transforms. You can place your transform content in those files and they will be executed when you debug/run your application. For example if your app.config contained the following.

    <?xml version="1.0" encoding="utf-8" ?>
    <configuration>
      <appSettings>
        <add key="appName" value="WPF Demo-Debug-default"/>
        <add key="url" value="http://localhost:8080/Default/"/>
        <add key="email" value="demo-default@contoso.com"/>
      </appSettings>
    
      <connectionStrings>
        <clear />
        <add name="RecordsDb" connectionString=".\SQLExpress;Initial Catalog=RecordsDb-Default;Integrated Security=true"/>
      </connectionStrings>
    
    </configuration>

    And your app.debug.config contained

    <?xml version="1.0" encoding="utf-8" ?>
    <!-- For more information on using transformations 
         see the web.comfig examples at http://go.microsoft.com/fwlink/?LinkId=214134. -->
    <configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
    
      <appSettings>
        <add key="appName" value="WPF Demo-Debug" xdt:Transform="Replace" xdt:Locator="Match(key)"/>
        <add key="url" value="http://localhost:8080/" xdt:Transform="Replace" xdt:Locator="Match(key)"/>
        <add key="email" value="debug@contoso.com" xdt:Transform="Replace" xdt:Locator="Match(key)"/>
      </appSettings>
    
      <connectionStrings>
        <add name="RecordsDb" connectionString=".\SQLExpress;Initial Catalog=RecordsDb;Integrated Security=true"
             xdt:Transform="Replace" xdt:Locator="Match(name)"/>
      </connectionStrings>
    
    </configuration>

    When you run your application the config file created will contain the following.

    <?xml version="1.0" encoding="utf-8"?>
    <configuration>
      <appSettings>
        <add key="appName" value="WPF Demo-Debug"/>
        <add key="url" value="http://localhost:8080/"/>
        <add key="email" value="debug@contoso.com"/>
      </appSettings>
    
      <connectionStrings>
        <clear/>
        <add name="RecordsDb" connectionString=".\SQLExpress;Initial Catalog=RecordsDb;Integrated Security=true"/>
      </connectionStrings>
    
    </configuration>

    Transforming other files

    You are not limited to transforming app.config, you can transform any XML file. When you create a transform for an arbitrary XML file when you run your application the transformed XML file will be dropped into the output (bin) folder of your application.

    Previewing Transforms

    You can easily preview transform results as well. After you have created the transform file you can right click on it and select Preview Transform.

    Preview-Transform-Command

    After you click it you should see the following.

    Preview-Transform-Diff

     

    I hope you guys find this useful. Let me know if you have any feedback on this!

    Note: This add in was created by me and a friend of mine, these are not created nor supported by Microsoft.

    Sayed Ibrahim Hashimi – @sayedihashimi

    msbuild | Visual Studio 2010 | XDT Wednesday, August 17, 2011 6:25:37 AM (GMT Daylight Time, UTC+01:00)  #     | 
    Monday, March 21, 2011

    Property Functions: GetFolderPath

    Today someone sent me an email asking how to call the System.Environment.GetFolderPath method passing in the value of MyDocuments for the value of the folder parameter. I was expecting the below to do the trick.

    <Project ToolsVersion="4.0" 
             DefaultTargets="Demo" 
             xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    
      <PropertyGroup>
        <Tempfile02>$([System.Environment]::GetFolderPath(System.Environment.SpecialFolder.MyDocuments))</Tempfile02>
      </PropertyGroup>
    
      <Target Name="Demo">
        <Message Text="TempFile01: $(TempFile02)"/>
      </Target>
    
    </Project>

    To my surprise I was faced with the following error.

    Build started 3/20/2011 6:20:36 PM.

    Project "C:\temp\_NET\msbuild\PropFunction01.proj" on node 1 (default targets).

    C:\temp\_NET\msbuild\PropFunction01.proj(20,5): error MSB4186: Invalid static method invocation syntax: "[System.Environment]::GetFolderPath(System.Environment.Spec ialFolder.MyDocuments)". Requested value 'System.Environment.MyDocuments' was not found. Static method invocation should be of the form: $([FullTypeName]::Method()), e.g. $([System.IO.Path]::Combine(`a`, `b`)).

    Done Building Project "C:\temp\_NET\msbuild\PropFunction01.proj" (default targets) -- FAILED.

    Build FAILED.

    So I sent an email to the MSBuild team asking “WTF why doesn’t this work?!”, and Dan Moseley (a lead dev there) sent me a snippet that worked, its below.

    <Project ToolsVersion="4.0" DefaultTargets="Demo" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    
      <PropertyGroup>
        <Tempfile02>$([System.Environment]::GetFolderPath(SpecialFolder.MyDocuments))</Tempfile02>
      </PropertyGroup>
    
      <Target Name="Demo">
        <Message Text="TempFile01: $(TempFile02)"/>
      </Target>
    
    </Project>

     

    In that snippet instead of using the fully qualified class name of System.Environment.SpecialFolder.MyDocuments, for some reason you have to use the shortened name of just SpecialFolder.MyDocuments. It seems like a bug to me, but at least there is a work around!

    Resources

    Sayed Ibrahim Hashimi – @sayedihashimi

    msbuild | MSBuild 4.0 | Visual Studio 2010 Monday, March 21, 2011 1:27:44 AM (GMT Standard Time, UTC+00:00)  #     | 
    Saturday, January 08, 2011

    Video on Web Deployment using Visual Studio 2010 and MSDeploy

    Back in November I participated in Virtual Tech Days which is an online conference presented by Microsoft. In the session I discussed the enhancements to web deployment using Visual Studio 2010 and MSDeploy. Some of the topics which I covered includ:

    You can download the video & all of my sample files at http://virtualtechdays.com/pastevents_2010november.aspx. In the samples you will find all of the scripts that I used and a bunch of others which I didn’t have time to cover. Enjoy!

    Sayed Ibrahim Hashimi @sayedihashimi

    Config-Transformation | IIS | msbuild | MSDeploy | speaking | Visual Studio | Visual Studio 2010 | web | Web Deployment Tool | Web Development | Web Publishing Pipeline Saturday, January 08, 2011 8:34:08 PM (GMT Standard Time, UTC+00:00)  #     | 
    Saturday, December 11, 2010

    Visual Studio 2010 SP1 Beta IIS Express Integration

    A few days ago Jason Zander blogged about the availability of Visual Studio SP1 Beta (all links below in resources section). I am happy to let you guys know that with SP1 Beta we have enabled integration of IIS Express into Visual Studio. After you have installed SP1 Beta you have to download IIS Express separately for the option to “light up”. After you’ve downloaded and installed IIS Express you can now create Web projects and Websites which use IIS Express instead of Cassini (the default web server used in Visual Studio). For more information regarding the difference between IIS Express and IIS/Cassini please see Scott’s blog linked below. Visual Studio 2010 has two types of web projects; Web Application Project (WAP) and Website projects. You can use either project type with IIS Express.

    Enabling IIS Express for web projects

    For both WAP and Website projects which were using Cassini we have a very simply method for you to use IIS Express. You can just right click then select the context menu option “Use IIS Express”. Take a look at the screen shot that follows for that command.

    image

    After you do this your project will use IIS Express instead of Cassini going forward. If you wish to revert back to using Cassini you can just right click on the project and select “Use Visual Studio Development Server…” You can also make IIS Express your default web server for new projects as well.

    For Web site projects when you create a new site you can create it such that IIS Express hosts it instead of Cassini. From Visual Studio when you select File->New-> Web Site you will see the New Web Site dialog. On that dialog you can click on the Browse button (see image below).

    SNAGHTML108dba11

    After you click that you will be prompted to enter the location where the site will be located. From there you can select “Local IIS” and the create a new IIS Express site for it.

    SNAGHTML108f284e

    In this dialog you need to select IIS Express Sites then click on the Create New Site button located towards the upper right hand corner. Once you do this it will create a new IIS Express site for your Web site.

    Making IIS Express your default web server

    In order to make IIS Express for new file based web projects you can go to the menu option Tools->Options->Projects and Solutions->Web Projects and check the “Use IIS Express for new file-based web sites and projects” check box. You can see this option in the next image.

    SNAGHTML10527286

    After you have done this  new projects/sites will use IIS Express by default. Note: if you have any existing projects/sites you will still have to “opt-in” to using IIS Express for each project.

    Using IIS Express

    After you have enabled IIS Express to be your projects web server, when you start debugging, or just start running your app, from Visual Studio you will see a new system tray icon appear (image). You can right click on this icon to to quickly see what applications it is hosting. Each hosted application will have its own context menu. From this menu you can browse out to the application as well as stop it quickly.

    image

    You can also click on “Show All Applications” to open a dialog which give you more information about the sites that IIS Express is hosting. For example I opened this dialog and selected a running application and the result is shown in the image below.

    SNAGHTML105b4a00

    There are a few notable values here

    The default location for the applicationHost.config file is your users’ directory. This enables IIS Express to run without requiring admin privileges. As you may know IIS (i.e. the full IIS) uses the applicationHost.config file to store its configuration. IIS Express is similar to IIS in that it uses a file also named applicationHost.config (not the same file as IIS though). The file that IIS uses is stored under the System32 directory and is shared across all users. The IIS Express config is specific to the current user. Regarding the runtime version, this is the version of the CLR which your application pool will use. For IIS Express application pools will, by default, use CLR 4.0. You can change this default in the applicationHost.config file for IIS Express. Visual Studio will assign the correct CLR version to your app pool based on the target framework version.

    You can customize a few options for your site directly from Visual Studio. If you select your web project/site in the Solution Explorer and then open the Properties pane (you can right-click and select Properties on the project/site if it is not visible) you should see something similar to the following.

    image

    These settings will change how IIS Express hosts your site. One of the missing features of Cassini was the ability to host SSL sites. In IIS Express you can enable this. For example for this site I changed the value for SSL Enabled to be True, then a new URL was assigned so that I can use SSL to browse to the site. Please note that IIS Express will install a self-signed cert and you will see the following security warning from Internet Explorer when you browse to an https URL hosted by IIS Express.

    image

    One thing to make a note of is that when you change the settings for IIS Express here these settings are stored in the applicationHost.config file for IIS Express and not with the project/site itself. So if you are working in a team then your other team members will have to make the same changes. If you want to edit a setting which is not shown on the properties grid then you can edit the applicationHost.config file directly. You can easily open this file by clicking on the applicationHost.config link in the IIS Express dialog shown previously. For WAP projects you can change the port that is used from the Web tab in the project properties page.

    Known issues

    There are currently some known issues with IIS Express which are listed below. If you run into any more please let us know.

     

    Resources

    Sayed Ibrahim Hashimi @sayedihashimi

    IIS | IIS Express | Visual Studio 2010 Saturday, December 11, 2010 11:22:22 PM (GMT Standard Time, UTC+00:00)  #     | 

    Visual Studio 2010 SP1 Beta IIS Express Integration

    A few days ago Jason Zander blogged about the availability of Visual Studio SP1 Beta (all links below in resources section). I am happy to let you guys know that with SP1 Beta we have enabled integration of IIS Express into Visual Studio. After you have installed SP1 Beta you have to download IIS Express separately for the option to “light up”. After you’ve downloaded and installed IIS Express you can now create Web projects and Websites which use IIS Express instead of Cassini (the default web server used in Visual Studio). For more information regarding the difference between IIS Express and IIS/Cassini please see Scott’s blog linked below. Visual Studio 2010 has two types of web projects; Web Application Project (WAP) and Website projects. You can use either project type with IIS Express.

    Enabling IIS Express for web projects

    For both WAP and Website projects which were using Cassini we have a very simply method for you to use IIS Express. You can just right click then select the context menu option “Use IIS Express”. Take a look at the screen shot that follows for that command.

    image

    After you do this your project will use IIS Express instead of Cassini going forward. If you wish to revert back to using Cassini you can just right click on the project and select “Use Visual Studio Development Server…” You can also make IIS Express your default web server for new projects as well.

    For Web site projects when you create a new site you can create it such that IIS Express hosts it instead of Cassini. From Visual Studio when you select File->New-> Web Site you will see the New Web Site dialog. On that dialog you can click on the Browse button (see image below).

    SNAGHTML108dba11

    After you click that you will be prompted to enter the location where the site will be located. From there you can select “Local IIS” and the create a new IIS Express site for it.

    SNAGHTML108f284e

    In this dialog you need to select IIS Express Sites then click on the Create New Site button located towards the upper right hand corner. Once you do this it will create a new IIS Express site for your Web site.

    Making IIS Express you default web server

    In order to make IIS Express for new file based web projects you can go to the menu option Tools->Options->Projects and Solutions->Web Projects and check the “Use IIS Express for new file-based web sites and projects” check box. You can see this option in the next image.

    SNAGHTML10527286

    After you have done this  new projects/sites will use IIS Express by default. Note: if you have any existing projects/sites you will still have to “opt-in” to using IIS Express for each project.

    Using IIS Express

    After you have enabled IIS Express to be your projects web server, when you start debugging, or just start running your app, from Visual Studio you will see a new system tray icon appear (image). You can right click on this icon to to quickly see what applications it is hosting. Each hosted application will have its own context menu. From this menu you can browse out to the application as well as stop it quickly.

    image

    You can also click on “Show All Applications” to open a dialog which give you more information about the sites that IIS Express is hosting. For example I opened this dialog and selected a running application and the result is shown in the image below.

    SNAGHTML105b4a00

    There are a few notable values here

    The default location for the applicationHost.config file is your users’ directory. This enables IIS Express to run without requiring admin privileges. As you may know IIS (i.e. the full IIS) uses the applicationHost.config file to store its configuration. IIS Express is similar to IIS in that it uses a file also named applicationHost.config (not the same file as IIS though). The file that IIS uses is stored under the System32 directory and is shared across all users. The IIS Express config is specific to the current user. Regarding the runtime version, this is the version of the CLR which your application pool will use. For IIS Express application pools will, by default, use CLR 4.0. You can change this default in the applicationHost.config file for IIS Express. Visual Studio will assign the correct CLR version to your app pool based on the target framework version.

    You can customize a few options for your site directly from Visual Studio. If you select your web project/site in the Solution Explorer and then open the Properties pane (you can right-click and select Properties on the project/site if it is not visible) you should see something similar to the following.

    image

    These settings will change how IIS Express hosts your site. One of the missing features of Cassini was the ability to host SSL sites. In IIS Express you can enable this. For example for this site I changed the value for SSL Enabled to be True, then a new URL was assigned so that I can use SSL to browse to the site. Please note that IIS Express will install a self-signed cert and you will see the following security warning from Internet Explorer when you browse to an https URL hosted by IIS Express.

    image

    One thing to make a note of is that when you change the settings for IIS Express here these settings are stored in the applicationHost.config file for IIS Express and not with the project/site itself. So if you are working in a team then your other team members will have to make the same changes. If you want to edit a setting which is not shown on the properties grid then you can edit the applicationHost.config file directly. You can easily open this file by clicking on the applicationHost.config link in the IIS Express dialog shown previously. For WAP projects you can change the port that is used from the Web tab in the project properties page.

    Known issues

    There are currently some known issues with IIS Express which are listed below. If you run into any more please let us know.

     

    Resources

     

    Sayed Ibrahim Hashimi @sayedihashimi

    IIS | IIS Express | Visual Studio 2010 Saturday, December 11, 2010 11:02:53 PM (GMT Standard Time, UTC+00:00)  #     | 
    Friday, October 22, 2010

    MSBuild: Extending the solution build

    One of the most commonly asked questions that I get is “How can I extend the build process for my solution?” In fact I was asked this soo much that 4 years ago (wow I’ve been dealing with MSBuild for far too long:) ) I wrote a blog post on Use MSBuild to build Solution files (no longer recommended technique) and that post to this day is one of the most frequently visited ones. Well in the 4+ years that have passed I’m glad to say that things have changed. The problem is that almost no one knows about it. Hopefully this blog post will be the start to that changing.

    What’s under the covers

    In all versions of Visual Studio, the solution file is not an MSBuild file. Which sucks for many of us. Essentially the solution build was like a black box.

    Blackbox

    Even in VS2010 it still kinda is, but there is a hook for us to extend it built in. Let me explain what happens when you build a solution using msbuild.exe. When you build a solution using msbuild.exe the solution file is converted to an MSBuild file in memory then that MSBuild file is used to build the solution itself. When you build from the command line here is the rough outline of the MSBuild project that gets built.

    
    
      
      
      
    
      
      
      
      
      
                        
      
    

    OK here I have left out the targets and properties because they are irrelevant to this discussion. Take a look at these import statements. There are two pairs of import statements ones that are imported before the build is defined and the imports which are after the build definition. Instead of looking at the first import statement lets look at the second one, because I think its more compelling and its easier to demo.

    This statement is pretty simple, if a file with the name of before.ExtendSolutionBuild.sln.targets exists in the same directory as the ExtendSolutionBuild.sln file then it will be imported in at the top of the build file. Also there is a corresponding entry for after.ExtendSolutionBuild.sln.targets. In stead of getting into all the nitty gritty of why two exists let me just boil it down. You should place properties/items inside of the before targets file, and inside of the after .targets file you should place targets but its OK to put properties/items here as well. The most important thing to keep a note of is that you cannot place anything inside of the before file which relies on any properties/items defined in the build because they will not be available. Because of this most times you will just need an after targets file. So in my case I will create a file named after.ExtendSolutionBuild.sln.targets, this follows the pattern after.{SolutionFile}.sln.targets where {SolutionFile} is the name of the solution file mine happens to be ExtendSolutionBuild. Every solution has 4 targets that will be defined.

    4 Targets on Every Solution file

    So let’s say that I want to extend the build file and inject two targets; GenerateCode and RunCodeAnalysis. Obviously I want the GenerateCode target to run before anything is built and the RunCodeAnalysis to be run after everything is built. So in my after.ExtendSolutionBuild.sln.targets file I place the following content.

    
    
      
      
        
      
    
      
        
      
      
    

    You can see here that I am using BeforeTargets=”Build” to make sure that the GenerateCode target is run before the Bulid target and also the RunCodeAnalysis target uses AfterTargets=”Build”. So now let’s see what happens when I build my solution from the command line using msbuild.exe.

    ExtendSolution01

    From the figure above you can see that the GenerateCode target was executed before the build process started and then the RunCodeAnalysis target was executed after the build. With MSBuild 3.5 and earlier this was simply not possible, and you would have had to write another build file to do these kinds of things for you. If you are building solution files from the command then you might be interested in the technique. I would be interested to hear you feedback and to see how you guys would use this feature.

    ImportBefore/ImportAfter

    Now let’s talk briefly about the two that we skipped. If you want an MSBuild file to be imported for every solution that is build using msbuild.exe for a given machine then you can place that file in one of the two folders below.

    So if you place any file in either of those folders (which don’t exist by default) then they will automatically be imported for every solution file which builds on that machine using msbuild.exe. Typically you do not run into many times where you want to do this, but the times that you do then it is critical and really hard to work around without something like this. This is useful if you have a build machine and you want to perform a set of actions for every build.

    Resources

    Sayed Ibrahim Hashimi | @SayedIHashimi

    msbuild | MSBuild 4.0 | Visual Studio 2010 Friday, October 22, 2010 7:14:22 AM (GMT Daylight Time, UTC+01:00)  #     | 
    Thursday, June 10, 2010

    Visual Studio file encoding issues: 

    Have you ever executed a .cmd file (or .bat) file and received a message that looks like the following at the top of your script (including image for compatibility) image ? This was really annoying to me until I find out what was happening. I noticed that Visual Studio (2010 at least, but I think previous version as well but not sure) was changing the encoding of my .cmd files to be UTF-8! For example consider this simple script (sample.cmd) that I created with Notepad.

    echo 'hello world'

    When I execute this .cmd file from the command line the results are as shown below.

    image

    What I did then was to simply create a copy of that file and placed that in a file named sample-vs.cmd and then edited the script using Visual Studio 2010 to have the contents below.

    echo 'hello world from Visual Studio'

    When I execute that .cmd file to my surprise the results shown below are displayed.

    image

    I was definitely not expecting that (OK yeah I was because I created that script for this blog post, but just go with it). I then opened the file in notepad and it looked normal. So I edited it it notpad, saved it and the result was still the same. The first line was not being processed correctly, it seemed. As I was editing the file I noticed the encoding of the files were different. The encoding for the sample.cmd file was set to ANSI and for sample-vs.cmd UTF-8. See for yourself from the screen shots below.

    image 

    image

    So I switched the setting for the sample-vs.cmd file to ANSI, executed the script and all was good!

    I’m not sure why Visual Studio is changing the encoding for these files, but it looks like a bug to me. I have logged a bug with Microsoft at https://connect.microsoft.com/VisualStudio/feedback/details/566322/vs-2010-changs-encoding-of-cmd-file-to-utf-8. The bug may not be visible yet, but hopefully it will become public so that you can vote on it if you have been bitten by this bug.

    Sayed Ibrahim Hashimi

    batch-file | bug | cmd-file | connect | Visual Studio | Visual Studio 2010 Thursday, June 10, 2010 4:24:01 AM (GMT Daylight Time, UTC+01:00)  #     | 
    Sunday, May 09, 2010

    Visual Studio: Unable to Edit and Continue?

    The past few days I was unable to Edit and Continue in Visual Studio 2010 Ultimate from my home machine, which is running Windows 7 64bit. I knew there were some issues with this on 64bit, I figured one of my projects was not targeting x86 so I just ignored it. Then today I was really getting annoyed at seeing the dialog.

    edit-and-continue

    So I started playing around with the configuration settings made sure that everything was in order and still no dice.

    After that I performed a repair of Visual Studio, and then reset all the settings under Tools>Import and Export Settings and that still didn’t solve the problem. Then I remembered that I change my IntelliTrace settings the other day so I went to disable it and to my surprise I saw the dialog box shown below.

    edit-and-continue2

    When I changed the IntelliTrace setting to collect call information I didn’t notice the warning stating “Edit and continue is disabled when collecting Call Information”! So I changed the setting and everything was good. Microsoft should add this to the Edit and Continue dialog box not available, I’ll ping a few people I know about that. FYI I created this question on Stackoverflow before I resolved it myself.

    Sayed Ibrahim Hashimi

    Edit and Continue | IntelliTrace | Visual Studio | Visual Studio 2010 Sunday, May 09, 2010 12:19:38 AM (GMT Daylight Time, UTC+01:00)  #     | 
    Saturday, May 08, 2010

    Visual Studio projects not building anymore?

    Recently I had the chance to work on a WPF app and it was behaving very strangely when I built and ran the application. For instance I tried to debug the application from Visual Studio and I received an error staging “Visual Studio cannot start debugging because the debug target ‘PATH HERE’ is missing. Please build the project and retry, or set the OutputPath and AssemblyName properties appropriately to point at the correct location for the target assembly.

    vs-build-error-01

    So I checked the properties, every thing looked good. I even built the project from the command line and it built fine. Anywayz at some point after that I was able to debug the app. But then I noticed that the UI was not getting updated with my changes. So I started digging a bit deeper. I noticed that when I ran a Clean from Visual Studio the output files for that particular project.

    I then rebuilt the solution in Visual Studio. From the Output window I selected all the text,copied it, and then pasted that into an editor. After looking at the message

    ------ Skipped Clean: Project: R…ion: Debug Any CPU ------
    Project not selected to build for this solution configuration
    ------ Skipped Clean: Project: R…Admin.Wpf, Configuration: Debug x86 ------
    Project not selected to build for this solution configuration
    ------ Clean started: Project: Test…er, Configuration: Debug Any CPU ------
    Build started 5/8/2010 1:10:02 AM.

    I immediately knew that there was an incorrect value in the build configuration manager. You can find the configuration manager from the toolbar.

    image

    Here is what I found.

    image

    In this dialog there are two project set not to build for Debug builds; the DB project and the Wpf project. The DB project is OK to not build on Debug if there are a small % of DB changes versus code changes. If that is the case the devs can just build the DB project manually when the change it. In the case of the Wpf project, it should be set to build and the fact that it wasn’t was causing all the issues. So I checked the build check box, clicked the Close button and it was all good. You should also make sure that all other configuration as setup correctly. You don’t want to get bitten by this again.

    Sayed Ibrahim Hashimi

    Visual Studio | Visual Studio 2010 | WPF Saturday, May 08, 2010 6:33:26 AM (GMT Daylight Time, UTC+01:00)  #     | 
    Saturday, May 01, 2010

    Web Deployment Tool (MSDeploy) : Build Package including extra files or excluding specific files

    If you are using Visual Studio 2010 then you may already be aware that Web Deployment Tool (aka MSDeploy) is integrated into Visual Studio. I’ve posted a few blog entries already about this tool. Two of the common questions that I get discussing this with people are

    1. How do I exclude files from being placed in the package?
    2. How do I add other files to the created package?

    I will address these two questions here, first we look at the easier one, how to exclude files but we will go over a bit of background first.

    Web Publishing Pipeline

    With Visual Studio 2010 a new concept has been created which is known as the Web Publishing Pipeline. In a nutshell this is a process which will take your web application, build it and eventually create a package that you can use to deploy your application. This process is fully captured in MSBuild. With VS 2010 many targets and many tasks are shipped to support this process. Since its captured in MSBuild format, you can customize and extend to your hearts desire. So what we need to do is hook into this process to perform the customizations that we need. This process is captured in the following files.

    %program files%\MSBuild\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets
    %program files%\MSBuild\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.targets

    The Microsoft.WebApplication.targets file is imported by the web applications projects file, then that file imports the Microsoft.Web.Publishing.targets file.

    Excluding files from being packaged

    If you open the project file of a web application created with VS 2010 towards the bottom of it you will find a line with.

    <Import Project="$(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v10.0\WebApplications\Microsoft.WebApplication.targets" />

    BTW you can open the project file inside of VS. Right click on the project pick Unload Project. Then right click on the unloaded project and select Edit Project.

    This statement will include all the targets and tasks that we need. Most of our customizations should be after that import, if you are not sure put if after! So if you have files to exclude there is an item name, ExcludeFromPackageFiles, that can be used to do so. For example let’s say that you have file named Sample.Debug.js which included in your web application but you want that file to be excluded from the created packages. You can place the snippet below after that import statement.

    <ItemGroup>
      <ExcludeFromPackageFiles Include="Sample.Debug.xml">
        <FromTarget>Project</FromTarget>
      </ExcludeFromPackageFiles>
    </ItemGroup>

    By declaring populating this item the files will automatically be excluded. Note the usage of the FromTarget metadata here. I will not get into that here, but you should know to always specify that.

    Including extra files into the package

    Including extra files into the package is a bit harder but still no bigee if you are comfortable with MSBuild, and if you are not then read this.  In order to do this we need to hook into the part of the process that collects the files for packaging. The target we need to extend is called CopyAllFilesToSingleFolder. This target has a dependency property, PipelinePreDeployCopyAllFilesToOneFolderDependsOn, that we can tap into and inject our own target. So we will create a target named CustomCollectFiles and inject that into the process. We achieve this with the following (remember after the import statement).

    <PropertyGroup>
      <CopyAllFilesToSingleFolderForPackageDependsOn>
        CustomCollectFiles;
        $(CopyAllFilesToSingleFolderForPackageDependsOn);
      </CopyAllFilesToSingleFolderForPackageDependsOn>
    </PropertyGroup>

    This will add our target to the process, now we need to define the target itself. Let’s assume that you have a folder named Extra Files that sits 1 level above your web project. You want to include all of those files. Here is the CustomCollectFiles target and we discuss after that.

    <Target Name="CustomCollectFiles">
      <ItemGroup>
        <_CustomFiles Include="..\Extra Files\**\*" />
    
        <FilesForPackagingFromProject  Include="%(_CustomFiles.Identity)">
          <DestinationRelativePath>Extra Files\%(RecursiveDir)%(Filename)%(Extension)</DestinationRelativePath>
        </FilesForPackagingFromProject>
      </ItemGroup>
    </Target>

    Here what I did was create the item _CustomFiles and in the Include attribute told it to pick up all the files in that folder and any folder underneath it. Then I use this item to populate the FilesForPackagingFromProject item. This is the item that MSDeploy actually uses to add extra files. Also notice that I declared the metadata DestinationRelativePath value. This will determine the relative path that it will be placed in the package. I used the statement Extra Files%(RecursiveDir)%(Filename)%(Extension) here. What that is saying is to place it in the same relative location in the package as it is under the Extra Files folder.

    Admittedly this could be easier, but its not too bad, and its pretty flexible.

    Sayed Ibrahim Hashimi

    msbuild | MSBuild 4.0 | MSDeploy | Visual Studio | Visual Studio 2010 | Web Deployment Tool | Web Publishing Pipeline Saturday, May 01, 2010 4:09:16 AM (GMT Daylight Time, UTC+01:00)  #     | 
    Monday, April 26, 2010

    Config transformations outside of web app builds

    If you are using Visual Studio 2010 then you may already be familiar with the Web.config transformations that are now available. What you might not know is that you can use that same technology to transform config files outside of the build process. You will need Visual Studio 2010 installed on the machine where you perform these transformations. It is very easy to perform these transformation as well. Let’s say that we start with the app.config file shown below.

    <configuration>
        <connectionStrings>
            <clear/>
            <add name="Default" connectionString="Data Source=localhost;Initial Catalog=Sample01;Integrated Security=True;" />
        </connectionStrings>
        
        <appSettings>
            <add key="contactEmail" value="contact@demo.example.com"/>
            <add key="siteUrl" value="http://demo.example.com"/>
        </appSettings>
        
    </configuration>

    Then we create another file, transform.xml, which contains our transformations. That file is shown below.

    <configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
        <connectionStrings>
            <clear/>
            <add name="Default" connectionString="Data Source=NOT-localhost;Initial Catalog=Sample01;Integrated Security=True;" 
                 xdt:Locator="Match(name)" xdt:Transform="Replace"/>
        </connectionStrings>
    
        <appSettings>
            <add key="contactEmail" value="contact@example.com" xdt:Locator="Match(key)" xdt:Transform="Replace"/>
            <add key="siteUrl" value="http://example.com" xdt:Locator="Match(key)" xdt:Transform="Replace"/>
        </appSettings>
    
    </configuration>

    Then we can easily execute the transformations by using MSBuild. So I created a file named trans.proj and it is shown below.

    <Project ToolsVersion="4.0" DefaultTargets="Demo" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
        <UsingTask TaskName="TransformXml"
                 AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll"/>
    
        <Target Name="Demo">
            <TransformXml Source="app.config"
                          Transform="Transform.xml"
                          Destination="app.prod.config"/>
        </Target>
    </Project>

    This MSBuild file uses the TransformXml task which is shipped with Visual Studio 2010. We specify the source file, transform file and the destination. Pretty straight forward.

    In order to execute this I open a Visual Studio 2010 command prompt, browse to the directory containing both files, and enter the following command

    msbuild trans.proj /t:Demo

    Once you do this then you will find the file app.prod.config with the following contents.

    <configuration>
        <connectionStrings>
            <clear/>
            <add name="Default" connectionString="Data Source=NOT-localhost;Initial Catalog=Sample01;Integrated Security=True;"/>
        </connectionStrings>
        
        <appSettings>
            <add key="contactEmail" value="contact@example.com"/>
            <add key="siteUrl" value="http://example.com"/>
        </appSettings>
        
    </configuration>

    Sayed Ibrahim Hashimi

    Config-Transformation | msbuild | MSBuild 4.0 | MSDeploy | Visual Studio | Visual Studio 2010 Monday, April 26, 2010 5:22:06 AM (GMT Daylight Time, UTC+01:00)  #     | 
    Friday, March 19, 2010

    Replacing solution files with MSBuild Files

    I recently answered a question on stackoverflow.com Replace .sln with MSBuild and wrap contained projects into targets and wanted to share that content here as well. I’ll expand a bit more on it here.

    A couple common questions that I’m frequently asked include

    1. Should I use solution files for public builds
    2. How can I replace a solution file with an MSBuild file

    My answer for 1 is that I never use .sln files for any public build. I always create my own build file and use that. My take on solution files is that they are for developer convince for use in Visual Studio only. If you disagree, you are not alone.

    Replacing a solution file with an MSBuild file is pretty easy, you just create an MSBuild file to build your projects. You will do that by using the MSBuild task. Now if you want to create a reusable .targets file (or set of .targets files) then this is a bit more involved, but makes it easier on you in the long haul.

    I wrote about 20 pages on creating reusable .targets files in my book, but I'll get you started here with the basics here. I believe that the key to creating reusable build scripts (i.e. .targets files) is three elements:

    The idea is that you want to place all of your targets into separate files and then these files will be imported by the files which will be driving the build process. These are the files which contain the data. Since you import the .targets files you get all the targets as if they had been defined inline. There will be a silent contract between the .proj and .targets files. This contract is defined in properties and items which both use. This is what needs to be validated.

    The idea here is not new. This pattern is followed by .csproj (and other projects generated by Visual Studio). If you take a look your .csproj file you will not find a single target, just properties and items. Then towards the bottom of the file it imports Microsoft.csharp.targets (may differ depending on project type). This project file (along with others that it imports) contains all the targets which actually perform the build.

    So it's layed out like this:

    Where MyProdcut.proj might look like:

    <?xml version="1.0" encoding="utf-8"?>
    <Project DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <!-- This uses a .targets file to off load performing the build -->
      <PropertyGroup>
        <Configuration Condition=" '$(Configuration)'=='' ">Release</Configuration>
        <OutputPath Condition=" '$(OutputPath)'=='' ">$(MSBuildProjectDirectory)\BuildArtifacts\bin\</OutputPath>
      </PropertyGroup>
    
      <ItemGroup>
        <Projects Include="$(MSBuildProjectDirectory)\..\ClassLibrary1\ClassLibrary1.csproj"/>
        <Projects Include="$(MSBuildProjectDirectory)\..\ClassLibrary2\ClassLibrary2.csproj"/>
        <Projects Include="$(MSBuildProjectDirectory)\..\ClassLibrary3\ClassLibrary3.csproj"/>
        <Projects Include="$(MSBuildProjectDirectory)\..\WindowsFormsApplication1\WindowsFormsApplication1.csproj"/>
      </ItemGroup>
    
      <Import Project="SharedBuild.targets"/>
    </Project>

    And SharedBuild.targets might look like:

    <Project  DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
      <!-- This represents a re-usable build file -->
      <Target Name="SharedBuild_Validate">
        <!-- See http://sedodream.com/2009/06/30/ElementsOfReusableMSBuildScriptsValidation.aspx for more info
             about this validation pattern
        -->
        <ItemGroup>
          <_RequiredProperties Include ="Configuration">
              <Value>$(Configuration)</Value>
          </_RequiredProperties>    
          <_RequiredProperties Include ="OutputPath">
              <Value>$(OutputPath)</Value>
          </_RequiredProperties>
          
          <_RequiredItems Include="Projects">
            <RequiredValue>%(Projects.Identity)</RequiredValue>
            <RequiredFilePath>%(Projects.Identity)</RequiredFilePath>
          </_RequiredItems>
        </ItemGroup>
    
        <!-- Raise an error if any value in _RequiredProperties is missing -->
        <Error Condition="'%(_RequiredProperties.Value)'==''"
               Text="Missing required property [%(_RequiredProperties.Identity)]"/>
    
        <!-- Raise an error if any value in _RequiredItems is empty -->
        <Error Condition="'%(_RequiredItems.RequiredValue)'==''"
               Text="Missing required item value [%(_RequiredItems.Identity)]" />
    
        <!-- Validate any file/directory that should exist -->
        <Error Condition="'%(_RequiredItems.RequiredFilePath)' != '' and !Exists('%(_RequiredItems.RequiredFilePath)')"
               Text="Unable to find expeceted path [%(_RequiredItems.RequiredFilePath)] on item [%(_RequiredItems.Identity)]" />
      </Target>
    
      <PropertyGroup>
        <BuildDependsOn>
          SharedBuild_Validate;
          BeforeBuild;
          CoreBuild;
          AfterBuild;
        </BuildDependsOn>
      </PropertyGroup>
      <Target Name="Build" DependsOnTargets="$(BuildDependsOn)"/>
      <Target Name="BeforeBuild"/>
      <Target Name="AfterBuild"/>
      <Target Name="CoreBuild">
        <!-- Make sure output folder exists -->
        <PropertyGroup>
          <_FullOutputPath>$(OutputPath)$(Configuration)\</_FullOutputPath>
        </PropertyGroup>
        <MakeDir Directories="$(_FullOutputPath)"/>
        <MSBuild Projects="@(Projects)"
                 BuildInParallel="true"
                 Properties="OutputPath=$(_FullOutputPath)"/>
      </Target>
    </Project>

    Don't look too much at the SharedBuild_Validate target yet. I put that there for completeness but don't focus on it. You can find more info on that at my blog at http://sedodream.com/2009/06/30/ElementsOfReusableMSBuildScriptsValidation.aspx.

    The important parts to notice are the extensibility points. Even though this is a very basic file, it has all the components of a reusable .targets file. You can customize it's behavior by passing in different properties and items to build. You can extend it's behavior by overriding a target (BeforeBuild, AfterBuild or even CoreBuild) and you can inject your own targets into the build with:

    <Project ...>
       ...
      <Import Project="SharedBuild.targets"/>
      <PropertyGroup>
        <BuildDependsOn>
          $(BuildDependsOn);
          CustomAfterBuild
        </BuildDependsOn>
      </PropertyGroup>
      <Target Name="CustomAfterBuild">
        <!-- Insert stuff here -->
      </Target>
    </Project>

    So this is the basics on how to create reusable build scripts which can help you easily create .proj files to replace your .sln files. I also recently answered. a related question Make a target run once at the Solution level in MSBuild.

    This samples for this are using Visual Studio 2010 RC You can download the samples for this at http://sedotech.com/Content/files/ReplaceSlnFile.zip

    Sayed Ibrahim Hashimi

    msbuild | Visual | Visual Studio | Visual Studio 2010 Friday, March 19, 2010 4:16:06 AM (GMT Standard Time, UTC+00:00)  #     | 
    Thursday, March 11, 2010

    MSBuild 4.0 Reserved Properties

    A while back I wrote about Reserved Properties in MSBuild 3.5, now its time to update that post to include reserved properties for MSBuild 4.0. There are a number of new properties here is the list:

    •    MSBuild
    •    MSBuildBinPath
    •    MSBuildExtensionsPath
    •    MSBuildExtensionsPath32
    •    MSBuildExtensionsPath64
    •    MSBuildLastTaskResult
    •    MSBuildNodeCount
    •    MSBuildOverrideTasksPath
    •    MSBuildProgramFiles32
    •    MSBuildProjectDefaultTargets
    •    MSBuildProjectDirectory
    •    MSBuildProjectDirectoryNoRoot
    •    MSBuildProjectExtension
    •    MSBuildProjectFile
    •    MSBuildProjectFullPath
    •    MSBuildProjectName
    •    MSBuildStartupDirectory
    •    MSBuildThisFile
    •    MSBuildThisFileDirectory
    •    MSBuildThisFileDirectoryNoRoot
    •    MSBuildThisFileExtension
    •    MSBuildThisFileFullPath
    •    MSBuildThisFileName
    •    MSBuildToolsPath
    •    MSBuildToolsVersion

    If you want to see what the values are you can execute this simple proj file that I created.

    <Project ToolsVersion="4.0" DefaultTargets="PrintValues" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    
      <Target Name="PrintValues">
        <Message Text="MSBuild: $(MSBuild)"/>
        <Message Text="MSBuildBinPath: $(MSBuildBinPath)"/>
        <Message Text="MSBuildExtensionsPath: $(MSBuildExtensionsPath)"/>
        <Message Text="MSBuildExtensionsPath32: $(MSBuildExtensionsPath32)"/>
        <Message Text="MSBuildExtensionsPath64: $(MSBuildExtensionsPath64)"/>
        <Message Text="MSBuildLastTaskResult: $(MSBuildLastTaskResult)"/>
        <Message Text="MSBuildNodeCount: $(MSBuildNodeCount)"/>
        <Message Text="MSBuildOverrideTasksPath: $(MSBuildOverrideTasksPath)"/>
        <Message Text="MSBuildProgramFiles32: $(MSBuildProgramFiles32)"/>
        <Message Text="MSBuildProjectDefaultTargets: $(MSBuildProjectDefaultTargets)"/>
        <Message Text="MSBuildProjectDirectory: $(MSBuildProjectDirectory)"/>
        <Message Text="MSBuildProjectDirectoryNoRoot: $(MSBuildProjectDirectoryNoRoot)"/>
        <Message Text="MSBuildProjectExtension: $(MSBuildProjectExtension)"/>
        <Message Text="MSBuildProjectFile: $(MSBuildProjectFile)"/>
        <Message Text="MSBuildProjectFullPath: $(MSBuildProjectFullPath)"/>
        <Message Text="MSBuildProjectName: $(MSBuildProjectName)"/>
        <Message Text="MSBuildStartupDirectory: $(MSBuildStartupDirectory)"/>
        <Message Text="MSBuildThisFile: $(MSBuildThisFile)"/>
        <Message Text="MSBuildThisFileDirectory: $(MSBuildThisFileDirectory)"/>
        <Message Text="MSBuildThisFileDirectoryNoRoot: $(MSBuildThisFileDirectoryNoRoot)"/>
        <Message Text="MSBuildThisFileExtension: $(MSBuildThisFileExtension)"/>
        <Message Text="MSBuildThisFileFullPath: $(MSBuildThisFileFullPath)"/>
        <Message Text="MSBuildThisFileName: $(MSBuildThisFileName)"/>
        <Message Text="MSBuildToolsPath: $(MSBuildToolsPath)"/>
        <Message Text="MSBuildToolsVersion: $(MSBuildToolsVersion)"/>
      </Target>
    
    </Project>

    For me the results are:

    C:\Data\Development\My Code\Community\MSBuild>msbuild ReservedProps02.proj /m /nologo
    Build started 3/18/2010 12:43:49 AM.
         1>Project "C:\Data\Development\My Code\Community\MSBuild\ReservedProps02.proj" on node 1 (default targets).
         1>PrintValues:
             MSBuild:
             MSBuildBinPath: C:\Windows\Microsoft.NET\Framework\v4.0.30128
             MSBuildExtensionsPath: C:\Program Files (x86)\MSBuild
             MSBuildExtensionsPath32: C:\Program Files (x86)\MSBuild
             MSBuildExtensionsPath64: C:\Program Files\MSBuild
             MSBuildLastTaskResult: true
             MSBuildNodeCount: 8
             MSBuildOverrideTasksPath:
             MSBuildProgramFiles32: C:\Program Files (x86)
             MSBuildProjectDefaultTargets: PrintValues
             MSBuildProjectDirectory: C:\Data\Development\My Code\Community\MSBuild
             MSBuildProjectDirectoryNoRoot: Data\Development\My Code\Community\MSBuild
             MSBuildProjectExtension: .proj
             MSBuildProjectFile: ReservedProps02.proj
             MSBuildProjectFullPath: C:\Data\Development\My Code\Community\MSBuild\ReservedProps02.proj
             MSBuildProjectName: ReservedProps02
             MSBuildStartupDirectory: C:\Data\Development\My Code\Community\MSBuild
             MSBuildThisFile: ReservedProps02.proj
             MSBuildThisFileDirectory: C:\Data\Development\My Code\Community\MSBuild\
             MSBuildThisFileDirectoryNoRoot: Data\Development\My Code\Community\MSBuild\
             MSBuildThisFileExtension: .proj
             MSBuildThisFileFullPath: C:\Data\Development\My Code\Community\MSBuild\ReservedProps02.proj
             MSBuildThisFileName: ReservedProps02
             MSBuildToolsPath: C:\Windows\Microsoft.NET\Framework\v4.0.30128
             MSBuildToolsVersion: 4.0
         1>Done Building Project "C:\Data\Development\My Code\Community\MSBuild\ReservedProps02.proj" (default targets
           ).

    If you want to see the correct valus for MSBuildNodeCount make sure to use the /m switch when you invoke msbuild.exe.

    I won’t go over these properties in detail here, because they are mostly obvious but I would like to point out a couple really useful properties, those include.

    MSBuildThisFile
    MSBuildThisFileDirectory
    MSBuildThisFileDirectoryNoRoot

    These properties can be used to locate the path to the file that you are currently in. So if you have a shared .targets file and it will execute an .exe in the same folder you can use the MSBuildThisFileDirectory property to resolve the full path to that tool reliably. This has historically been difficult. See a recent question on Stackoverflow.com about it at How can I get the path of the current msbuild file? If you are not using MSBuild 4.0 and need to resolve the location to a .targets file then see that post for what you will need to do.

    msbuild | MSBuild 4.0 | Visual Studio 2010 Thursday, March 11, 2010 12:53:51 AM (GMT Standard Time, UTC+00:00)  #     | 
    Wednesday, March 10, 2010

    Speaking on Automating Web Deployments and ASP.NET MVC

    I will be speaking at the Orlando Code Camp on Saturday March 27. I will be giving two session; one on Simplifying deployments with MSDeploy and Visual Studio 2010 and the other on ASP.NET MVC View Helpers. By the way, the other name for MSDeploy is the Web Deployment Tool.

    If you have ever had issues with deploying web applications (which includes everyone who has ever deployed a web app :) ) then you need to attend my session. I will discuss the three major scenarios of deploying web applications:

    I will be demonstrating how to perform 2 of the 3; deploying to local IIS server and to a 3rd party host. Since I won’t have any other machines besides my notebook I will not be demoing how to deploy to an IIS server on the intranet, but it is very similar to the other 2 scenarios. There has been a lot of work in the area of web deployment (deployment in general actually) recently which could really help spare you of a lot of headache. I presented this at the South Florida Code Camp a couple weeks ago and a person actually stated in the session “There are a lot of people who wish they were in here right now”! If you are in the area then you should attend my session, you won’t regret it.

    Here is the abstract:

    Visual Studio 2010 will be shipped including integration with Microsoft’s Web Deployment Tool, MSDeploy. For quite a while web deployments have been very difficult to manage and automate. With MSDeploy you can manage the complexities of web deployments. One of the great aspects of the Web Deployment Tool is that it is integrated into Visual Studio with MSBuild tasks and targets. Since Team Foundation Build can leverage MSBuild we can take advantage of those tasks and targets to automate web deployments using Team Build.

    My other talk will be on creating leaner views with ASP.NET MVC View Helpers. If you are using ASP.NET MVC then this is one of the sessions you’ll be interested in. I will be getting in depth about ASP.NET View Helpers, and just talking ASP.NET MVC in general. I gave this talk at the Jacksonville Developers User Group last week and it was great. I’m very excited about these two talks, I’m sure they will be great. Here is the abstract.

    If you have been using ASP.NETMVC then you certainly have been using some of the built in view helper methods that are available, you know those expressions like Html.TextBox("textBoxName") and Html.ValidationMessage("Required"). View helpers are nothing more than extension methods which create HTML that is injected into your views based on the method and its parameters. Creating your own view helpers is very simple and can be extremely beneficial. By writing your own custom view helpers you will benefit in at least the following ways

    I have published a 22 page paper discussing custom ASP.NET MVC view helpers along with a sample app at http://mvcviewhelpers.codeplex.com/ if you are interested.

     

    If you are in the area this weekend its going to be a great event. I think there were >400 people there last year, so it should be a good turn out this year as well. I hope to see you there.

    Sayed Ibrahim Hashimi

    MSDeploy | speaking | Visual Studio 2010 | Web Deployment Tool Wednesday, March 10, 2010 3:29:22 AM (GMT Standard Time, UTC+00:00)  #     | 
    Tuesday, February 09, 2010

    Visual Studio 2010 RC and TFS 2010 RC Now Available

    If you are an MSDN subscriber you can now download Visual Studio 2010 Release Candidate as well as the TFS 2010 Release Candidate. You can read Brian Hurry's post about the release. Here are some key dates, if you are an MSDN subscriber you can get it now, and if not then you will have to wait until Feb 10. Still no word on when the RTM will ship, but hopefully it will be soon so that more organizations will begin adopting it and we can leave VS 2008 in the dust. Since the Visual Studio 2010 Launch is on April 12, 2010 it should be just around the corner. You should be aware that the launch date doesn't always equal the release date. Launch date is set by marketing to start the launch events, but the RTM date can vary, but will typically be in that same ball park.

    Sayed Ibrahim Hashimi

    TFS 2010 | Visual Studio 2010 Tuesday, February 09, 2010 3:44:10 AM (GMT Standard Time, UTC+00:00)  #     | 
    Friday, January 22, 2010

    MSBuild 4.0: Inline Tasks Part 2

    This post contains based on .NET 4.0 Beta 2 and Visual Studio 2010 Beta 2 which may change.

    The other day I wrote my first post on Inline Tasks in MSBuild 4.0, this post will add onto that topic. In the previous post we covered some basics, but there were a lot that was skipped. Last time we demonstrated how to pass parameters but we never declared what type those parameters were. If you declare a parameter and leave off the type, then it will be declared as a string. If you need to declare parameters of other types then you need to use the ParameterType attribute on the parameter. You have to pass in the full name of the type to be used. For example if you need to declare an int you must use System.Int32, not int and not Int32 but System.Int32. It would be good if they supported aliases like int, string, long, etc but right now they don't. Below you'll find a new inline task which can be used to perform a substring. I've placed this in a file named IT-Substring-01.proj.

    <!--

    Sample Demonstrates Inline Tasks

    © 2010 Sayed Ibrahim Hashimi

    -->

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

     

      <UsingTask

        TaskName="Substring"

        TaskFactory="CodeTaskFactory"

        AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >

        <ParameterGroup>

          <Input Required="true" />

          <StartIndex Required="true" ParameterType="System.Int32" />

          <Length ParameterType="System.Int32" />

          <Result Output="true" />

        </ParameterGroup>

        <Task>

          <Code Type="Fragment" Language="cs">

            <![CDATA[

            if (Length > 0)

            {

                Result = Input.Substring(StartIndex, Length);

            }

            else

            {

                Result = Input.Substring(StartIndex);

            }

            ]]>

          </Code>

        </Task>

      </UsingTask>

     

      <Target Name="Demo">

        <Substring StartIndex="8" Input="Demo of Inline Tasks">

          <Output PropertyName="taskValue" TaskParameter="Result"/>

        </Substring>

        <Message Text="Value from task: $(taskValue)" />

       

        <Substring StartIndex="0" Length="4" Input="Demo of Inline Tasks">

          <Output PropertyName="taskValue" TaskParameter="Result"/>

        </Substring>

        <Message Text="Value from task: $(taskValue)" />

       

      </Target>

    </Project>

    In the task above I have declared 4 parameters, from those two have the type specified to be int. If we execute the Demo target here is the result.

    From the above image you can see that the task performs the actions requested. One thing to make a mental note of above is my usage of a CDATA tag to wrap the definition of the task. I would suggest that you do so for all inline tasks that you write.

    The types supported for parameters on inline types are the same for normal types. For a detailed account of that see my book, but as a general guideline it accepts string, primitive types, ITaskItem, and arrays of all three.

    Let's see how we can use an array in an inline task. I created a simple task which will create a list of Guids and place them into an array and return the result back to the calling MSBuild script, the file I placed this is in named IT-CreateGuid-02.proj. The contents of that file are shown in the snippet below.

    <!--

    Sample Demonstrates Inline Tasks

    © 2010 Sayed Ibrahim Hashimi

    -->

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

     

      <UsingTask

        TaskName="CreateGuid02"

        TaskFactory="CodeTaskFactory"

        AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >

        <ParameterGroup>

          <NumToCreate ParameterType="System.Int32" Required="true" />

          <Guids ParameterType="System.String[]" Output="true" />

        </ParameterGroup>

        <Task>

          <Code Type="Fragment" Language="cs">

            <![CDATA[

                List<string> guids = new List<string>();

                for (int i = 0; i < NumToCreate; i++)

                {

                    guids.Add(Guid.NewGuid().ToString());

                }

                Guids = guids.ToArray();

            ]]>

          </Code>

        </Task>

      </UsingTask>

     

      <Target Name="Demo">

        <CreateGuid02 NumToCreate="1">

          <Output ItemName="Id01" TaskParameter="Guids" />

        </CreateGuid02>

        <Message Text="Id01: @(Id01)" Importance="high" />

     

        <CreateGuid02 NumToCreate="4">

          <Output ItemName="Id02" TaskParameter="Guids" />

        </CreateGuid02>

        <Message Text=" " Importance="high" />

        <Message Text="Id02: @(Id02)" Importance="high" />

      </Target>

     

    </Project>

    Above you can see that I declared the parameter Guids as System.String[]. I typically prefer to use generic lists instead of arrays so in my task definitions I will use a list and then just call ToArray when I assign the output parameter. Here is a nifty, but very simplistic task. Given an item list, it will filter them based on a Regular Expression passed in.

    <!--

    Sample Demonstrates Inline Tasks

    © 2010 Sayed Ibrahim Hashimi

    -->

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

     

      <UsingTask

        TaskName="FilterList"

        TaskFactory="CodeTaskFactory"

        AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >

        <ParameterGroup>

          <ListToFilter ParameterType="Microsoft.Build.Framework.ITaskItem[]" Required="true" />

          <Filter Required="true" />

          <FilteredList ParameterType="Microsoft.Build.Framework.ITaskItem[]" Output="true" />

        </ParameterGroup>

        <Task>

          <Using Namespace="System.Text.RegularExpressions" />

          <Code Type="Fragment" Language="cs">

            <![CDATA[

                var results = (from l in ListToFilter

                               where Regex.IsMatch(l.ItemSpec, Filter)

                               select l).ToList();

     

                FilteredList = results.ToArray();

            ]]>

          </Code>

        </Task>

      </UsingTask>

     

      <ItemGroup>

        <Source Include="src\01.cs" />

        <Source Include="src\02.cs" />

        <Source Include="test\test01.cs" />

        <Source Include="test\sub\test02.cs" />

        <Source Include="test\sub\test03.cs" />

        <Source Include="test\sub\sub2\test04.cs" />

      </ItemGroup>

     

      <Target Name="Demo">

        <FilterList ListToFilter="@(Source)" Filter="test">

          <Output ItemName="_filteredList" TaskParameter="FilteredList" />

        </FilterList>

        <Message Text="Filter: test. Results: @(_filteredList)" />

       

        <!-- Clear the list before calling again -->

        <ItemGroup>

          <_filteredList Remove="@(_filteredList)" />

        </ItemGroup>

     

        <Message Text="======" />

        <FilterList ListToFilter="@(Source)" Filter="sub\\">

          <Output ItemName="_filteredList" TaskParameter="FilteredList" />

        </FilterList>

        <Message Text="Filter: .\sub. Results: @(_filteredList)" />

      </Target>

    </Project>

    Here you see that you can you can perform LINQ queries inside of inline tasks without any additional setup. Here is the result of executing the Demo target on this script.

    Another thing to notice is the Using element under the Task element. This injects a Using statement into the generated class for the given namespace. If you need to reference another assembly you can do this with the Reference element under the Task element.

     

    Sayed Ibrahim Hashimi

    inline task | msbuild | MSBuild 4.0 | Visual Studio 2010 Friday, January 22, 2010 4:01:46 AM (GMT Standard Time, UTC+00:00)  #     | 
    Wednesday, January 20, 2010

    MSBuild 4.0: Inline Tasks

    This post contains based on .NET 4.0 Beta 2 and Visual Studio 2010 Beta 2 which may change.

    If you didn't already know, MSBuild will have a new version shipped with .NET 4.0 (Visual Studio 2010 will use this new version). I will cover many of those features here. This is the first in a series of posts that I will make regarding MSBuild 4.0. One of the big additions to MSBuild 4.0 is Inline Tasks. I'm pretty excited about this new addition. The story before was if you wanted to perform any action it was always through a Task. Which worked pretty well, but the major drawback is that the tasks needed to be written in code and then compiled into an assembly in order for it to be used. What this meant was if you wanted to perform an action (however simple) that wasn't covered out of the box you would have to look for 3rd party tasks or write one yourself. This is time consuming and can be tricky from a "deployment" perspective. Now with Inline Tasks you can declare the behavior of the task right inside of the MSBuild file itself.

    Inline Task

    The way that Inline Tasks are supported is by using the UsingTask XML element. This element was around before but it has some new options. If you read my book, you probably saw a few different Hello World tasks that I created. In order to demonstrate Inline Tasks I have taken a similar set of examples. Take a look at the project file (IT-HelloWorld-01.proj) below.

    <!--

    Sample Demonstrates Inline Tasks

    © 2010 Sayed Ibrahim Hashimi

    -->

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

     

      <UsingTask

        TaskName="HelloWorld"

        TaskFactory="CodeTaskFactory"

        AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >

        <Task>

          <Code Type="Fragment" Language="cs">

            Log.LogMessage("Hello World!");

          </Code>

        </Task>

      </UsingTask>

     

      <Target Name="Demo">

        <HelloWorld />

      </Target>

     

    </Project>

    For this basic inline task here are the key things to note, the name of the task is specified in the TaskName attribute. You will use this like you would any other task inside of targets. The definition of the task will be contained in the Code XML element. In order to call that task, I use the syntax <HelloWorld /> inside of the Demo target. The same exact way "classic" tasks are called. Here is the result.

    The result shown above is as expected.

    Inline Task with Parameters

    You can also make inline tasks with parameters, both required and optional. Here is an example (IT-HelloWorld-02.proj) with a lone required parameter.

    <!--

    Sample Demonstrates Inline Tasks

    © 2010 Sayed Ibrahim Hashimi

    -->

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

     

      <UsingTask

        TaskName="HelloWorld"

        TaskFactory="CodeTaskFactory"

        AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >

        <ParameterGroup>

          <Name Required="true"/>

        </ParameterGroup>

        <Task>

          <Code Type="Fragment" Language="cs">

            Log.LogMessage(string.Format("Hello {0}",Name));

          </Code>

        </Task>

      </UsingTask>

     

      <PropertyGroup>

        <YourName Condition=" '$(YourName)'=='' ">Sayed</YourName>

      </PropertyGroup>

     

      <Target Name="Demo">

        <HelloWorld Name="$(YourName)" />

      </Target>

     

      <Target Name="DemoWithNoName">

        <!-- This shows the result if you don't pass in a required param -->

        <HelloWorld />

      </Target>

     

    </Project>

    If you take a look at the UsingTask declaration above you will see that I included a ParameterGroup element. All parameters must be included under that element. In this case we just have one. If we execute the Demo target the result will be as follows.

    We can see that the message was sent to the console as we expected. You can execute the DemoWithNoName if you are interested in verifying that MSBuild will ensure that required parameters are set.

    Inline Tasks with Output Parameter

    You can also create your own inline tasks which have one or more output parameters. You will define those output parameters under the ParameterGroup element and use the Output="true" attribute. Take a look at IT-HelloWorld-03.proj below.

    <!--

    Sample Demonstrates Inline Tasks

    © 2010 Sayed Ibrahim Hashimi

    -->

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

     

      <UsingTask

        TaskName="HelloWorld"

        TaskFactory="CodeTaskFactory"

        AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll" >

        <ParameterGroup>

          <Name Required="true"/>

          <TaskMessage Output="true"/>

        </ParameterGroup>

        <Task>

          <Code Type="Fragment" Language="cs">

            TaskMessage = string.Format("Hello {0}",Name);

            Log.LogMessage(TaskMessage);

          </Code>

        </Task>

      </UsingTask>

     

      <PropertyGroup>

        <YourName Condition=" '$(YourName)'=='' ">Sayed</YourName>

      </PropertyGroup>

     

      <Target Name="Demo">

        <HelloWorld Name="$(YourName)">

          <Output PropertyName="MsgFromTask" TaskParameter="TaskMessage"/>

        </HelloWorld>

        <Message Text="Message from task: $(MsgFromTask)" Importance="high" />

      </Target>

     

    </Project>

    In this example I created a HelloWorld task to output the entire message back to the calling MSBuild task invocation inside the target. This output parameter is the TaskMessage parameter. Inside the Demo target I call the task and then print the value that was passed back from the task. Here is the result of that.

    From here we can see that the value was correctly sent back to the script.

     

    There is a lot more to inline tasks and I plan on covering more features very soon here, but this should get you started.

     

    Sayed Ibrahim Hashimi

    inline task | msbuild | MSBuild 4.0 | Visual Studio 2010 Wednesday, January 20, 2010 10:31:12 PM (GMT Standard Time, UTC+00:00)  #     |