«Older Posts - Newer Posts» | rssFeed | My book on MSBuild and Team Build | Archives and Categories Monday, May 13, 2013

How to compress a Visual Studio Extension (VSIX) during build

Lately I’ve been working with Mads Kristensen on a cool new Visual Studio Extension, Farticus, and wanted to share with you guys one of the things that I learned.

When you are developing Visual Studio extension on of the things you should keep in mind is the download size of the extension. This is the size of the .vsix file which you upload to the gallery. If the download size is too large typically people may get impatient and cancel the install. Because of this it’s a good idea to try and get your .vsix to the smallest size possible.

The good news here is that the .vsix file is already compressed. It’s actually a .zip file renamed to .vsix. If you want to see what’s in a .vsix just rename it to .zip and extract it out. The bad news is that the CreateZipPackage task used by the Microsoft.VsSdk.targets file does not perform the best compression. Fortunately there are tasks to create Zip files that we can use. I have chosen to use the Zip task from the MSBuild Extension Pack.

Before I go over all the details let’s take a look at what the final result will be after we compress with the MSBuild Extension Pack. See the table below for the comparison for two different projects.

Project

VSIX Size Before

VSIX Size After

Reduction

Farticus 442 kb 248 kb 43 %
VS Web Essentials 2102 kb 740 kb 65 %

From the table above we can see that we can gain a significant amount of additional compression by using the MSBuild extension pack.

Below is the content that I pated into the .csproj file. I’ll paste it in it’s entirety and then explain.

<PropertyGroup>
  <EnableCompressVsix Condition=" '$(EnableCompressVsix)'=='' ">true</EnableCompressVsix>
  <BuildLib Condition=" '$(BuildLib)'=='' ">$(MSBuildProjectDirectory)\..\Build\Lib\</BuildLib>

</PropertyGroup>
<UsingTask AssemblyFile="$(BuildLib)MSBuild.ExtensionPack.dll" TaskName="MSBuild.ExtensionPack.Compression.Zip"/>

<Target Name="CompressVsix" 
        AfterTargets="CreateVsixContainer" 
        DependsOnTargets="PrepareReplceVsixTemp" 
        Condition=" '$(EnableCompressVsix)'=='true' ">
    
  <!-- copy the file to the obj folder and then party on it -->
  <MakeDir Directories="$(_TmpVsixDir);$(_TmpVsixDir)\Extracted\"/>

  <Copy SourceFiles="$(TargetVsixContainer)"
        DestinationFolder="$(_TmpVsixDir)">
    <Output TaskParameter="CopiedFiles" ItemName="_TmpVsixCopy"/>
  </Copy>
    
  <!-- extract out the .zip file -->
  <MSBuild.ExtensionPack.Compression.Zip 
    TaskAction="Extract" 
    ExtractPath="$(_TmpVsixDir)Extracted\" 
    ZipFileName="@(_TmpVsixCopy->'%(FullPath)')"/>

  <ItemGroup>
    <_FilesToZip Remove="@(_FilesToZip)"/>
    <_FilesToZip Include="$(_TmpVsixDir)Extracted\**\*"/>
  </ItemGroup>

  <MSBuild.ExtensionPack.Compression.Zip
    TaskAction="Create"
    CompressFiles="@(_FilesToZip)"
    ZipFileName="%(_TmpVsixCopy.FullPath)"
    RemoveRoot="$(_TmpVsixDir)Extracted\"
    CompressionLevel="BestCompression" />

  <Delete Files ="$(TargetVsixContainer)"/>
  <Copy SourceFiles="%(_TmpVsixCopy.FullPath)" DestinationFiles="$(TargetVsixContainer)" />
</Target>

<Target Name="PrepareReplceVsixTemp" DependsOnTargets="CreateVsixContainer">
  <ItemGroup>
    <_VsixItem Remove="@(_VsixItem)"/>
    <_VsixItem Include="$(TargetVsixContainer)" />

    <_TmpVsixPathItem Include="$(IntermediateOutputPath)VsixTemp\%(_VsixItem.Filename)%(_VsixItem.Extension)"/>
  </ItemGroup>
    
  <PropertyGroup>
    <_TmpVsixDir>%(_TmpVsixPathItem.RootDir)%(_TmpVsixPathItem.Directory)</_TmpVsixDir>
  </PropertyGroup>

  <RemoveDir Directories="$(_TmpVsixDir)"/>  
</Target>  

 

The snippet above perform the following actions.

  1. Remove the old VsixTemp folder from any previous build if it exists
  2. Copy the source .vsix to the intermediate output path (i.e. obj\debug or obj\release)
  3. Extract out the contents to a folder
  4. Re-zip the file using MSBuild extension pack
  5. Replace the output .vsix with the compressed one

 

When building now the .vsix in the output folder should be smaller than it was before.

In my case I have copied the necessary assemblies from the MSBuild Extension pack and placed them in the projects repository. For the Zip task you’ll need the following files.

In my case I’ve placed them in a folder named Build\lib\.

You should be able to copy/paste what I have here into your VSIX projects. You’ll need to update the path to the MSBuild extension pack assemblies if you put them in a different location. Note: I’ve only tested this with Visual Studio 2012.

The MSBuild elements used here is pretty straight forward. If you have any questions on this let me know.

 

You can find the source for the Farticus project at https://github.com/ligershark/Farticus/. Please send us a Pull (my finger) Request.

 

Thanks,
Sayed Ibrahim Hashimi | http://msbuildbook.com | @SayedIHashimi

Visual Studio 11 | vs-extension | VSIX Monday, May 13, 2013 1:11:54 AM (GMT Daylight Time, UTC+01:00)  #     | 
Thursday, May 09, 2013

Book published and now in stock!

I’m happy to say that my Supplement to Inside the Microsoft Build Engine book (co-author William Bartholomew) has now been published. In fact it’s already in stock and ready to be shipped by Amazon.com.

This book is a small addition (118 pages) to the previous book, Inside the Microsoft Build Engine 2nd edition. It has a small price too, MSRP is $12.99 but it’s selling on Amazon.com for $8.99! In this book we cover the updates to MSBuild, Team Build and Web Publishing in Visual Studio 2012. The foreword was written by Scott Hanselman, and you can read the entire foreword online.

Check out how thin the supplement is in comparison to the 2nd edition #ThinIsIn.

945231_10103483509401051_968543011_n

 

If you already own the 2nd edition then you’ll love this update.

 

Table of Contents

Chapter 1: What's new in MSBuild

  1. Visual Studio project compatibility between 2010 and 2012
  2. Out of Process Tasks
  3. NuGet
  4. XML Updates with SlowCheetah
  5. Cookbook
Chapter 2: What's new in Team Build 2012
  1. Installation
  2. Team Foundation Service
  3. User interface (UI) enhancements
  4. Visual Studio Test Runner
  5. Pausing build definitions
  6. Batching
  7. Logging
  8. Windows Workflow Foundation 4.5
  9. Cookbook
Chapter 3: What's new in Web Publishing
  1. Overview of the new Publish Web Dialog
  2. Building web packages
  3. Publish profiles
  4. Database publishing support
  5. Profile-specific web.config transforms
  6. Cookbook

 

The book has been available in e-book form for a few weeks. Just long enough for us to get our first review. It was 5 stars :).

image

 

Please let us know what you think of this book!

 

You can download all the samples and learn more at msbuildbook.com.

 

Sayed Ibrahim Hashimi | http://msbuildbook.com | @SayedIHashimi

msbuild | MSDeploy | Team Build | web | Web Publishing Pipeline Thursday, May 09, 2013 6:33:20 AM (GMT Daylight Time, UTC+01:00)  #     | 
Tuesday, March 26, 2013

My first date with Comcast/Xfinity left me feeling used and dirty

OK seriously this is not really my first date with Comcast as I’ve been a long time customer, but it was the initial setup for a new account. While visiting my family in Florida, I decided to upgrade the internet at my parents house. They were using another ISP and the connection was very slow. This post is a story of the experience that I had after that.

Before I get into all the details of what really upsets me, let me say that when the Comcast technician left my house I would have given the setup 3 stars out of five. The reasons why I would give the 3 star rating are below. The issues here are pretty minor, I never would have blogged/tweeted about these issues as I encounter these types of things everyday.

The tech wore his shoes while going in and out of the house

At this house we, like many other foreigners, take our shoes of either outside or at the entrance. The Comcast tech did not have the respect to even ask if we cared to have shoes removed or not. He freely went in and out with his shoes on. I didn’t care enough to mention anything to him, but I did take notice of this. I know that several other companies have their techs/reps wear “booties” to ensure that there is no dirt tracked into the house. I’ve also had others simply take their shoes off after asking. Comcast should care more about the wishes of their customers to ask what the policy is at the given house.

The tech moved furniture to install the modem but never moved it back

While the tech was here, there was a dresser which was blocking the outlet. When he was installing the modem he moved the dresser and did the work necessary. When he left he didn’t care to move the dresser back. He also did not ask me if I would like to have him move the dresser back.

I found this to be extremely rude. If you go to a job and move something, it needs to be moved back to its original location before you leave. Even with this I didn’t say anything because I knew that I could simply move the item back to its original location. This is concerning for the older Comcast customers though. I know if it had been my mom or dad here alone they would have had significant issues getting that piece of furniture back in it’s original location. I am hoping that the tech which visited my home would have taken the age of my parents into consideration and moved the item back before leaving.

The tech disabled my wireless and never re-enabled it

While troubleshooting the cable modem, the tech disabled my wireless adapter on my computer. When he returned my computer to me, he had not re-enabled it. Once again, not a big deal for me as I can re-enable it quicker then I could complain to him about it. The bigger issue goes back to what if it was my parents here? If the tech disabled wireless on my dad’s notebook and returned it to him, my dad would be calling me telling me how his computer was hosed. Not a phone call I like to receive.

Comcast, please if you mess with someone’s settings have the decency to revert them back to the original settings.

For these reasons I would have given the experience 3 stars out of 5. Sure the tech did a few things to piss me off, but in the end they were small insignificant issues for me. I totally understand that for others this may not be the case, and those are the customers which Comcast needs to be more careful with.

Comcast is victimizing non-tech folks by hijacking their machines

After the tech left the house, I got back on my notebook and discovered a few really shocking things. This was the same computer the tech was using to configure the modem. I immediately took notice that there were three new shortcuts on my desktop for Xfinity. I inspected them and they looked like they were just internet shortcuts. I simply deleted them. Comcast, its my desktop not yours. Don’t litter my desktop.

After that I started up Chrome and immediately was taken to some Xfinity page. WTF, really you changed my default homepage to Xfinity? I understand you are trying to get some extra traffic to your site, but this is completely unacceptable. To top it off it was not only Chrome, it was all the browsers which I had installed on my machine. This was incredibly frustrating. Once again, what if this was my parents (or your parents perhaps)? My dad’s homepage is configured to take him to BBC Persian newspaper. My dad couldn’t figure out how to do that initially, how is he supposed to fix it after the tech left? Comcast, do you think this is OK? How would have your parents felt after this?

After further investigation, I also discovered that the search provider for my browser was changed to Xfinity. OMG this is ridiculous, what the hell else has been updated? Comcast, these are my settings on my machine. These are personal and you have no right to change these, especially with out any type of consent.

Here is the result after opening Firefox.

SNAGHTML3caa8949

Wow, you’ve got some balls to set 4 different home pages on my browser. I do agree that this is a wonderful way to drive traffic to your site. Especially for those customers who do not know how to change these.

Also take a look in the image to see what happened to my favorites.

image

I don’t use favorites that much as you can see, but there are now 8 Xfinity related favorites here. OK seriously, this getting out of hand. These are not your favorites they are mine. Do you not understand the concept that these do not belong to you?

I looked to see if there was an Xfinity/Comcast installer in Add Remove programs but there wasn’t. Comcast doesn’t even have the decency to provide a way for users to undo all the damage that they have caused without asking me. Comcast, this is the worst first date ever!

The biggest issue that I have with this is the fact that I was never given an opportunity to opt out of this. And at this point I’m not even sure if I’ve successfully reverted back to the previous state.

I’m not the only one who cares about this

Immediately as I discovered these issues I tweeted about it. What else was I supposed to do?!

image image

 

Below are some responses from those in my twitter network.

 

image image

image image

After a bit of searching around I also found many others who were similarly upset with these actions. Below are a few of the better ones.

image image

Remember when I mentioned my parents, and that they would be lost in correcting these actions. Look at these people on the Firefox forums crying for help.

image image

This could be my mom or dad, or yours. Do the Comcast execs making these decisions all have tech savvy parents that would never run into this? I find that hard to believe. You can find some other links I put together here.

Wow, its very clear that these customers do not want the homepage(s) that were set for them. I feel sorry for these guys, they sign up for a service and all of a sudden things change and they feel helpless. These are not the only two instances of this. Comcast, your customers are speaking why are you not listening?

What’s next?

At this point you might be wondering what I’m going to do about this? Am I going to cancel the account I setup for my parents? Cancel my own account in Seattle? Call to get my money back? I’m not going to be doing any of that. Since I’ll only be in Florida for this week it would be difficult to get another ISP hooked up here. More importantly I don’t hate Comcast, most of the time I don’t mind them. I’ve had their service for years  and this won’t change that.

My goals here are not to get money back (although I did have to pay $50 for this “service”) or anything like that. What I’d like is for Comcast to change their policies when setting up internet/cable services for new customers. Comcast, you must respect peoples property (both digitally and physically) more than you do today. This behavior is unacceptable and it should not continue. Minimally people need to be informed when a technician is making changes to ones machine, and have the ability to opt out of it. There should also be a mechanism for a clean rollback. I trusted your tech and did not watch over him. I know better for next time. When installing malicious software on a machine to set the home page(s) it’s clear that you are acting in Comcast’s best interest. You need to start putting customers first.

I’m especially concerned with elderly clients. If my parents had received the same treatment that I did, they would not be in a good spot. My dad’s homepage would have been changed from BBC Persian to Xfinty, his wireless would have been disabled, his dresser would have been displaced, his favorites cramped, his desktop cluttered and he would have been performing internet with Xfinity. Because of that I’ll be sharing this letter with the following.

  1. As many Comcast execs that I can find on twitter/email – I have personally witnessed how impactful it is for a customer to email a VP, now its time for me to use that technique
  2. Better Business Bureau – these types of business practices are not OK, they need to stop
  3. Federal Trade Commission – they are an advocate for the consumer, hopefully others have informed them of this previously
  4. AARP – the older community needs to be informed and protected here

If there are any Comcast guys out there you can reach me at sayed.hashimi@gmail.com or on twitter.

Sayed Ibrahim Hashimi | @SayedIHashimi | http://msbuildbook.com/

Tuesday, March 26, 2013 4:07:16 PM (GMT Standard Time, UTC+00:00)  #     | 
Wednesday, March 06, 2013

How to publish one web project from a solution

Today on twitter @nunofcosta asked me roughly the question “How do I publish one web project from a solution that contains many?

The issue that he is running into is that he is building from the command line and passing the following properties to msbuild.exe.

    /p:DeployOnBuild=true
    /p:PublishProfile='siteone - Web Deploy'
    /p:Password=%password%

You can read more about how to automate publishing at http://sedodream.com/2013/01/06/CommandLineWebProjectPublishing.aspx.

When you pass these properties to msbuild.exe they are known as global properties. These properties are difficult to override and are passed to every project that is built. Because of this if you have a solution with multiple web projects, when each web project is built it is passed in the same set of properties. Because of this when each project is built the publish process for that project will start and it will expect to find a file named siteone – Web Deploy.pubxml in the folder Properties\PublishProfiles\. If the file doesn’t exist the operation may fail.

Note: If you are interested in using this technique for an orchestrated publish see my comments at http://stackoverflow.com/a/14231729/105999 before doing so.

So how can we resolve this?

Let’s take a look at a sample (see links below). I have a solution, PublishOnlyOne, with the following projects.

  1. ProjA
  2. ProjB

ProjA has a publish profile named ‘siteone – Web Deploy’, ProjB does not. When trying to publish this you may try the following command line.

    msbuild.exe PublishOnlyOne.sln /p:DeployOnBuild=true /p:PublishProfile=’siteone – Web Deploy’ /p:Password=%password%

See publish-sln.cmd in the samples.

If you do this, when its time for ProjB to build it will fail because there’s no siteone – Web Deploy profile for that project. Because of this, we cannot pass DeployOnBuild. Instead here is what we need to do.

  1. Edit ProjA.csproj to define another property which will conditionally set DeployOnBuild
  2. From the command line pass in that property

 

I edited ProjA and added the following property group before the Import statements in the .csproj file.

<PropertyGroup>
  <DeployOnBuild Condition=" '$(DeployProjA)'!='' ">$(DeployProjA)</DeployOnBuild>
</PropertyGroup>

 

Here you can see that DeployOnBuild is set to whatever value DeployProjA is as long as it’s not empty. Now the revised command is:

    msbuild.exe PublishOnlyOne.sln /p:DeployProjA=true /p:PublishProfile=’siteone – Web Deploy’ /p:Password=%password%

Here instead of passing DeployOnBuild, I pass in DeployProjA which will then set DeployOnBuild. Since DeployOnBuild wasn’t passed to ProjB it will not attempt to publish.

 

You can find the complete sample at https://github.com/sayedihashimi/sayed-samples/tree/master/PublishOnlyOne.

 

Sayed Ibrahim Hashimi | @SayedIHashimi | http://msbuildbook.com/

MSDeploy | web | Web Deployment Tool | Web Development | Web Publishing Pipeline Wednesday, March 06, 2013 2:48:41 AM (GMT Standard Time, UTC+00:00)  #     | 
Saturday, March 02, 2013

MSDeploy: How to update app settings on publish based on the publish profile

On twitter @vishcious asked me a question related to publishing. The question was

I have a web project that I am publishing to multiple locations using Visual Studio. I have setup multiple profiles and would like to update the values for these app settings differently for each profile. How can I achieve this? I am using parameters.xml to define the MSDeploy parameters.

 

Note: If you are publishing from VS and don’t care about packaging then you can use profile specific transforms as a simpler way to achieve this result. The rest of the post here assumes you specifically want MSDeploy parameters. To learn more about profile specific transforms see my short video at http://www.youtube.com/watch?v=HdPK8mxpKEI

If you are not familiar you can create custom MSDeploy parameters when you publish/package your application by creating an XML file named parameters.xml in the root of the project. A good resource on this is Vishal’s blog post. I have  created a sample (links below) which demonstrates how to accomplish what the task. Below are the project artifacts that I’ve created to setup the parameters.

web.config

<?xml version="1.0"?>
<configuration>
  <appSettings>
    <add key="email" value="default@example.com"/>
    <add key="url" value="example.com"/>
  </appSettings>
</configuration>

Parameters.xml

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

  <parameter name="email" description="description">
    <parameterEntry kind="XmlFile" 
                    scope="\\web.config$" 
                    match="/configuration/appSettings/add[@key='email']/@value" />
  </parameter>

  <parameter name="url" description="description">
    <parameterEntry kind="XmlFile" 
                    scope="\\web.config$" 
                    match="/configuration/appSettings/add[@key='url']/@value" />
  </parameter>

</parameters>

In the parameters.xml file I have defined two new MSDeploy parameters., email and url. This file should be placed in the root of the project directory and it will automatically be picked up by the web publish process. If you are familiar with this you may have noticed that I have left off the DefaultValue attribute here. More to come on that later. Now that we have our parameters defined let’s see what customizations need to be made to the publish profiles (.pubxml files).

You can override the values by populating the MsDeployDeclareParameters item list. Let’s see how to do that. When you create a publish profile in Visual Studio a .pubxml file is written to the folder Properties\PublishProfiles. These are MSBuild files and they are consumed during the build+publish operation. You can customize these files in order to fine tune the publish process. In my sample project I have two publish profiles created; siteone and sitetwo. Both of these are web profiles pointing to Azure Web Sites. In the publish profiles with the samples you will find two sets of elements; properties for publishing and  my parameter customizations. You can see the content of these profiles below, I’ve left off the MSDeploy settings for brevity. Those profiles are shown below.

siteone.pubxml

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
    <!-- Web deploy settings here -->  

  <ItemGroup>
    <MsDeployDeclareParameters Include="email">
      <DefaultValue>one@email.com</DefaultValue>
    </MsDeployDeclareParameters>
    <MsDeployDeclareParameters Include="url">
      <DefaultValue>http://one.example.com/</DefaultValue>
    </MsDeployDeclareParameters>
  </ItemGroup>
</Project>

sitetwo.pubxml

<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
  <!-- Web Deploy settings here -->

  <ItemGroup>
    <MsDeployDeclareParameters Include="email">
      <DefaultValue>two@email.com</DefaultValue>
    </MsDeployDeclareParameters>
    <MsDeployDeclareParameters Include="url">
      <DefaultValue>http://two.example.com/</DefaultValue>
    </MsDeployDeclareParameters>
  </ItemGroup>
</Project>

In these profiles you can see that I’ve  added a usage of MSDeployDeclareParameters for both parameters in parameters.xml. In each case the value for the Include attribute is the name of the parameter (matches the name attribute in parameters.xml). Along with that I’ve declared a value for the DefaultValue metadata. This will contain the value of the parameter when it is published using that profile.

After publishing siteone the values for the app settings are below.

When publishing with sitetwo the values

 

That’s it. Now let’s move on to the comment I made previously regarding the DefaultValue attribute that I left off of the parameters.xml file.

 

DefaultValue

When you create parameters and set their values there are a few different places that these can come from including; parameters.xml, auto generated from con strings and custom declared parameters in MSBuild. Because of this the Web Publishing Pipeline has a mechanism to prioritize parameters when there are duplicates. This is facilitated by a Priority metadata value on the MSDeployDeclareParameters item list. If you want to Declare a DefaultValue inside of the parameters.xml file you can do that, but you have to be aware of the impact of that. When you add a DefaultValue parameter in parameters.xml by default those values will take precedence over the items in MSDeployDeclareParameters. Because of that you’ll have to add a Priority value. The way the Priority works is that the item with the lowest priority value for a given parameter will win. The default priority value for params from parameters.xml is –50 (defined in Microsoft.Web.Publishing.targets). Because of that we have to add priority less than –50. For example.

<ItemGroup>
  <MsDeployDeclareParameters Include="email">
    <DefaultValue>two@email.com</DefaultValue>
    <Priority>-100</Priority>
  </MsDeployDeclareParameters>
  <MsDeployDeclareParameters Include="url">
    <DefaultValue>http://two.example.com/</DefaultValue>
    <Priority>-100</Priority>
  </MsDeployDeclareParameters>
</ItemGroup>

 

In this case when I publish these values will override the default value from the parameters.xml.

 

You can see the latest version of this at https://github.com/sayedihashimi/publish-samples/tree/master/AppSettingsPerProfile.

 

Sayed Ibrahim Hashimi | @SayedIHashimi | http://msbuldbook.com

Saturday, March 02, 2013 3:36:00 AM (GMT Standard Time, UTC+00:00)  #     | 
Tuesday, February 19, 2013

Videos on Web Publish updates in ASP.NET 2012.2

Earlier today Scott Guthrie blogged about the release of ASP.NET 2012.2 and Web Tools 2012.2. Between this release and the previous publish update in October we have added some really cool publishing features. Today I created some videos of my favorite features. Take a look below.

 

Quick Publishing

In this video I will show some productivity enhancements that we’ve made to publish that you are going to absolutely love!

New publish support for web site project

Do you use web site projects in Visual Studio? The video below shows how we have unified the publish support for both web application project and web site project.

Updates regarding web.config transforms

We have added some really cool features related to web.config transforms. To be more specific; profile specific transforms and transform previewing. See the video below for more details.

I hope you guys like these features as much as I do. Please let me know what you think.

Sayed Ibrahim Hashimi | @SayedIHashimi | http://msbuildbook.com/

Tuesday, February 19, 2013 9:19:47 AM (GMT Standard Time, UTC+00:00)  #     | 
Tuesday, February 12, 2013

MSBuild: DependsOnTargets versus BeforeTargets, AfterTargets

Today I saw a question on StackOverflow asking about the difference between DependsOnTargets and BeforeTargets/AfterTargets. Below is my response.

In my MSBuild Book I have a section based on how to create reusable elements in MSBuild, if you are interested. I'll give some comments here as well though. This content is different from what the book has.

When creating MSBuild files you should be careful to isolate what versus how. To explain that a bit, lets examine how the managed VS projects work out of the box (which is a great model for reusable elements).

When you create a C#/VB project you get a .csproj file, this .csproj file primarily contains Properties and Items. You will not find a single target in that file. These files contains what will get built (along with some settings relating to the build). At the bottom of the project file you will find an import statement. This import bring in How the project is built.

The files which are imported include:

In this case Microsoft.common.targets defines the overall build process for all the managed languages. Then Microsoft.CSharp.targets (or one of the other lang specific .targets file) fills in the blanks of how to invoke the specific language specific tools.

DependsOnTargets versus Before/AfterTargets

In your answer above you state "I recommend to avoid DependsOnTargets unless it is really really necessary, for instance if two". I disagree with this. Here is my take on DependsOnTargets versus Before/AfterTargets.

Use DependsOnTargets when

Use Before/AfterTargets when

To tease out the difference a bit consider web projects. For web projects there are two workflows that the .csproj/.vbproj take care of: 1. Build 1. Publish

If I want to add a target to the list of targets to be executed before the Build target I can dynamically update the BuildDependsOn property for publish scenarios only. You cannot do this with Before/AfterTargets.

In an ideal world each target would have the following wrt DependsOnTargets.

For example

<MyTargetDependsOn>
    $(MyTargetDependsOn);
    Target1;
    Target2
</MyTargetDependsOn>

Unfortunately many targets do not follow this pattern, so DependsOnTargets is dead in the water for many cases.

When I am authoring MSBuild scripts I always use DependsOnTargets unless there is a solid reason why I should chose to use Before/AfterTargets. I feel that (I have no insight on the real reasons to the design as I wasn't with Microsoft at the time) Before/AfterTargets was really created to allow users to inject targets to be executed before/after a target which they did not own, and the creators did not use the pattern above.

 

 

dddd

msbuild | MSBuild 4.0 Tuesday, February 12, 2013 2:16:30 AM (GMT Standard Time, UTC+00:00)  #     | 
Saturday, February 02, 2013

How to invoke XDT from code

A few weeks ago we released XDT on NuGet, XDT is the technology driving web.config transforms, on NuGet with a license allowing you to redistribute it with your applications. We are working to open source XDT as well so hopefully I will be able to announce that soon as well.

In this post I will show you how you can create a simple application which uses XDT. For this sample we will be creating a console application which will invoke XDT for you. In Visual Studio create a Console Application. I named this project XdtSample. After that we need to add the XDT library. We can easily do this using NuGet. You can execute the following command to insert the package into your project.

 Install-Package Microsoft.Web.Xdt -Pre
  

Note: after we release the final version you can remove the –Pre flag.

Executing this command will download the XDT assembly as well as add a reference to your project. In program.cs to have the following content.

namespace XdtSample {
    using Microsoft.Web.XmlTransform;
    using System;
    using System.IO;
    using System.Text;

    class Program {
        static void Main(string[] args) {
            if (args == null || args.Length < 3) {
                ShowUsage();
                return;
            }

            string sourceFile = args[0];
            string transformFile = args[1];
            string destFile = args[2];

            if (!File.Exists(sourceFile)) { throw new FileNotFoundException("sourceFile doesn't exist"); }
            if (!File.Exists(transformFile)) { throw new FileNotFoundException("transformFile doesn't exist"); }

            using (XmlTransformableDocument document = new XmlTransformableDocument()) {
                document.PreserveWhitespace = true;
                document.Load(sourceFile);

                using (XmlTransformation transform = new XmlTransformation(transformFile)) {

                    var success = transform.Apply(document);

                    document.Save(destFile);

                    System.Console.WriteLine(
                        string.Format("\nSaved transformation at '{0}'\n\n",
                        new FileInfo(destFile).FullName));

                    int exitCode = (success == true) ? 0 : 1;
                    Environment.Exit(exitCode);
                }
            }
        }

        static void ShowUsage() {
            StringBuilder sb = new StringBuilder();
            sb.AppendLine("\n\nIncorrect set of arguments");
            sb.AppendLine("\tXdtSample.exe sourceXmlFile transformFile destFile\n\n");
            System.Console.Write(sb.ToString());
        }
    }
}

The files will be passed in as command line arguments. Below is the usage of the .exe.

xdtsample. sourceFile transformFile destFile 

In the main method here you can see that we load up the source file in an instance of XmlTransformableDocument. We use the XmlTransformation class to represent the transformFile. After applying the transform we simply save the result to the target location.

Pretty simple. Let me know if you have any questions.

I’ve posted this sample on github https://github.com/sayedihashimi/XdtSample.

Sayed Ibrahim Hashimi | http://msbuildbook.com | @SayedIHashimi

XDT Saturday, February 02, 2013 8:51:37 PM (GMT Standard Time, UTC+00:00)  #     | 
Sunday, January 13, 2013

Web packaging fixing the long path issue

Someone sent me an email the other day to discuss some issues with the web packing experience in Visual Studio. One of his concerns were the directory structure in the .zip file that VS generates. If you are not familiar with this I’ll give you a quick overview. In Visual Studio 2012 when you create a web deploy package you typically end up with the full directory structure of your source inside the package. For example I have a project named WebApplication1 located at c:\temp\package\, when I create a package it contains the following files.

archive.xml
Content\C_C\Temp\package\WebApplication1\obj\Release\Package\PackageTmp
Content\C_C\Temp\package\WebApplication1\obj\Release\Package\PackageTmp\bin
Content\C_C\Temp\package\WebApplication1\obj\Release\Package\PackageTmp\bin\WebApplication1.dll
Content\C_C\Temp\package\WebApplication1\obj\Release\Package\PackageTmp\index.html
Content\C_C\Temp\package\WebApplication1\obj\Release\Package\PackageTmp\Web.config
parameters.xml
systemInfo.xml
 

These paths can get pretty long depending on where you keep your source. This has been one of my pet peeves as well.

I decided to take a closer look at this and I have a solution. To create a web deploy package in VS you will first create a publish profile for that. When you do this, a .pubxml file will be created for you under Properties\PublishProfiles. This is your publish profile file, its an MSBuild file. You can customize your publish process by editing this file. We will modify this file in order to update these paths in the package.

The best way to solve the issue here is to update the package process to include a replace rule. This replace rule will update the full path into a simple path. We already have this concept baked into our Web Publish Pipeline. The replace rule that we would like to create will replace the paths C:\Temp\package\WebApplication1\obj\Release\Package\PackageTmp with website. Here is how to do it.

Edit the .pubxml file for the profile and add the following before the closing </Project> tag.

<PropertyGroup>
  <PackagePath Condition=" '$(PackagePath)'=='' ">website</PackagePath>
  <EnableAddReplaceToUpdatePacakgePath Condition=" '$(EnableAddReplaceToUpdatePacakgePath)'=='' ">true</EnableAddReplaceToUpdatePacakgePath>
  <PackageDependsOn>
    $(PackageDependsOn);
    AddReplaceRuleForAppPath;
    </PackageDependsOn>
</PropertyGroup>
<Target Name="AddReplaceRuleForAppPath" Condition=" '$(EnableAddReplaceToUpdatePacakgePath)'=='true' ">
  <PropertyGroup>
    <_PkgPathFull>$([System.IO.Path]::GetFullPath($(WPPAllFilesInSingleFolder)))</_PkgPathFull>
  </PropertyGroup>
  
  <!-- escape the text into a regex -->
  <EscapeTextForRegularExpressions Text="$(_PkgPathFull)">
    <Output TaskParameter="Result" PropertyName="_PkgPathRegex" />
  </EscapeTextForRegularExpressions>
  
  <!-- add the replace rule to update the path -->
  <ItemGroup>
    <MsDeployReplaceRules Include="replaceFullPath">
      <Match>$(_PkgPathRegex)</Match>
      <Replace>$(PackagePath)</Replace>
    </MsDeployReplaceRules>
  </ItemGroup>
</Target>

 

Let me break this down a bit. You create the target AddReplaceRuleForAppPath, and inject that into the package process by adding it to PackageDependsOn property. Once this target is executed it will add a replace rule into the MSDeployReplaceRules item group. The replace rule added is equivalent to the command line switch shown below.

-replace:match='C:\\Temp\\package\\WebApplication1\\obj\\Release\\Package\\PackageTmp',replace='website'

Now when I create a package the contents are.

archive.xml
Content\website
Content\website\bin
Content\website\bin\WebApplication1.dll
Content\website\index.html
Content\website\Web.config
parameters.xml
systemInfo.xml

That's a lot better! If you try this out let me know if you have any issues.

 

Sayed Ibrahim Hashimi | @SayedIHashimi

Sunday, January 13, 2013 1:37:29 AM (GMT Standard Time, UTC+00:00)  #     | 
Sunday, January 06, 2013

Command line web project publishing

With the release of VS2012 we have improved the command line publish experience. We’ve also made all the web publish related features available for VS2010 users in the Azure SDK.

The easies way to publish a project from the command line is to create a publish profile in VS and then use that. To create a publish profile in Visual Studio right click on the web project and select Publish. After that it will walk you though creating a publish profile. VS Web publish profile support the following publish methods.

Command line publishing is only supported for Web Deploy, Web Deploy Package, and File System. If you think we should support command line scenarios for other publish methods the best thing to do would be to create a suggestion at http://aspnet.uservoice.com. If there is enough interest we may work on that support.

Let’s first take a look at how you can publish a simple Web project from the command line. I have created a simple Web Forms project and want to publish that. I’ve created a profile named SayedProfile. In order to publish this project I will execute the following command.

msbuild MyProject.csproj /p:DeployOnBuild=true /p:PublishProfile=<profile-name> /p:Password=<insert-password> /p:VisualStudioVersion=11.0

In this command you can see that I have passed in these properties;

You may not have expected the VisualStudioVersion property here. This is a new property which was introduced with VS 2012. It is related to how VS 2010 and VS 2012 are able to share the same projects. Take a look at my previous blog post at http://sedodream.com/2012/08/19/VisualStudioProjectCompatabilityAndVisualStudioVersion.aspx. If you are building the project file, instead of the solution file then you should always set this property.

If you are publishing using the .sln file you can omit the VisualStudioVersion property. That property will be derived from the version of the solution file itself. Note that there is one big difference when publishing using the project or solution file. When you build an individual project the properties you pass in are given to that project alone. When you build from the command line using the solution file, the properties you have specified are passed to all the projects. So if you have multiple web projects in the same solution it would attempt to publish each of the web projects.

FYI in case you haven’t already heard I’m working on an update to my book. More info at msbuildbook.com

Sayed Ibrahim Hashimi | @SayedIHashimi

msbuild | MSBuild 4.0 | MSDeploy | web | Web Deployment Tool Sunday, January 06, 2013 2:56:37 AM (GMT Standard Time, UTC+00:00)  #     |