Thursday, June 10, 2010

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

Thursday, June 10, 2010 4:24:01 AM (GMT Daylight Time, UTC+01:00)  #    Comments [4]  | 
Sunday, May 09, 2010

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

Sunday, May 09, 2010 12:19:38 AM (GMT Daylight Time, UTC+01:00)  #    Comments [0]  | 
Saturday, May 08, 2010

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

Saturday, May 08, 2010 6:33:26 AM (GMT Daylight Time, UTC+01:00)  #    Comments [0]  | 
Saturday, May 01, 2010

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

Saturday, May 01, 2010 4:09:16 AM (GMT Daylight Time, UTC+01:00)  #    Comments [1]  | 
Monday, April 26, 2010

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

Monday, April 26, 2010 5:22:06 AM (GMT Daylight Time, UTC+01:00)  #    Comments [0]  | 
Friday, March 19, 2010

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:

  • Place behavior (i.e. targets) into separate files
  • Place data (i.e. properties and items, these are called .proj files) into their own files
  • Extensibility
  • .targets files should validate assumptions

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:

  • SharedBuild.targets
  • MyProduct.proj

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

Friday, March 19, 2010 4:16:06 AM (GMT Standard Time, UTC+00:00)  #    Comments [0]  | 
Thursday, March 11, 2010

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.

Thursday, March 11, 2010 12:53:51 AM (GMT Standard Time, UTC+00:00)  #    Comments [2]  | 
Wednesday, March 10, 2010

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:

  • Deploying to a local IIS server
  • Deploying to an IIS server on the intranet
  • Deploying to a 3rd party host

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

  • Simplifies Your Views
  • Eases Re-hydrating HTML Elements with ModelState Values
  • Standardizes the Creation of Common HTML Components
  • Helps you Implement the DRY (Don’t Repeat Yourself) Principal

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

Wednesday, March 10, 2010 3:29:22 AM (GMT Standard Time, UTC+00:00)  #    Comments [2]  | 
Tuesday, February 09, 2010

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

Tuesday, February 09, 2010 3:44:10 AM (GMT Standard Time, UTC+00:00)  #    Comments [0]  | 
Friday, January 22, 2010

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

Friday, January 22, 2010 4:01:46 AM (GMT Standard Time, UTC+00:00)  #    Comments [0]  | 
Wednesday, January 20, 2010

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

Wednesday, January 20, 2010 10:31:12 PM (GMT Standard Time, UTC+00:00)  #    Comments [0]  | 

Theme design by Jelle Druyts