MSBuild: Part 2

I am going through this tutorial about automating the build with MSBuild. The first task with the Build Script seems fairly basic – adding a command to delete the files in the /bin directory. The interesting thing I found was that the valid code does not have intellisense support:

clip_image002

But when I run it:

clip_image004

Sure enough, the files are gone.

clip_image006

I guess BinFiles was added after the created the code comments? Baffling.

Advertisements

MS Build: Part 1

I was hoping that TFS2010 has a “publish website” wizard that Visual Studio 2010 has using FTP. 

image

 

Alas, they do not.  There is some community tasks, but I thought I could use the exercise to dive into MSBuild and perhaps Workflow Foundation.

My first stop was this MSDN article. I altered the .csproj file with the HelloWorld Target and Message. I found a couple of things.

The article is wrong – it tells you to run msbuild helloworld.csproj /t:helloworld but the name of the project is BuildApp. The correct commandline syntax is msbuild BuildApp.csproj /t:helloworld

When I ran msbuild the 1st time, I got this error:

image

The reason was back in my csproj file:

<Target Name="Hello World">

Should by:

<Target Name="HelloWorld">

Once I did that:

image

Pop goes the firecracker….

On a related note, I entered in the error to MSDN – I wonder how fast they will take to fix it?

My First Build

I set up a new build

Hit the General Tab:

image

Updated the Trigger:

image

Change the workspace from the main branch to the release one:

image

Put in a build target on my local file system:

image

And then finally specified the items to build:

image

I then opened my Release project and changed the configuration to Release:

image

I clicked on Build Solution and nothing happened -> good.

I then checked in the Project and nothing happened -> bad.

I went to the Build Explorer and clicked on “Queue Build” and selected the build that I just created. Luckily, the build tried to run and it failed:

image

And the error was that the folder didn’t exist:

image

And this is the reason why:

image

Changing the permission to the folder for everyone to get full control -> and it is still not working.

I then Binged the error code: TFS Build Failed to create directory. Details: The network name cannot be found.

I gave up and stopped trying to write to that file and pointed to \\DIXON08\Users\Public\Builds and made some progress.

I ran across this error next: The build controller Default Controller – dixon08 does not contain an enabled build agent with name * and no tags.

I then went to TFS Admin Console – yup, no build agent (I must have deleted it when I was getting rid of the project groups). I added an agent:

image

And Boom goes the dynamite:

image

And:

image

I then added a project alert for the build (team-> project alerts):

image

Now, I need to move the build onto our production server, perhaps like this.

Now, I want to ftp my build out to winhost.

Globalizing the carpool app

I had a question about how to internationalize the carpool app.  I ran through the exercise to try out the Globalization and Localization features of the .NET framework.  

I 1st I added a couple of resource files:

image

Next, I added a simple string to test:

image

Next, I added a reference in the markup on the site.master:

<div id="title"> <h1> <asp:Label ID="LabelMainTitle" runat="server" Text="<%$ Resources:LocalizedText, SiteTitle %>"></asp:Label> </h1> </div>

Next, I updated the web.config file:

<globalization enableClientBasedCulture="true" uiCulture="auto" />

Finally, I changed the browser settings:

image

And Boom goes the dynamite:

image

So the next step is adding the strings to the resource file…

I am using this site for translations.

A couple of random thoughts about the process:

1) ActionLink – need to refer to the class explicitly:

<ul id="menu"> <li><%: Html.ActionLink(Resources.LocalizedText.HomeTab, "Index", "Home")%></li> <li><%: Html.ActionLink(Resources.LocalizedText.PracticeTab , "Index", "Practice")%></li> <li><%: Html.ActionLink(Resources.LocalizedText.AboutTab, "About", "Home")%></li> </ul>

And I don’t know why no inteliisense from the namespace to class, but there is intliisense from the class to the property name.

2) I appreciate the split screen feature of VS2010:

image

3) I ran into a problem with a new HTML Helper Controls in MVC:

<%: Html.LabelFor(model => model.Date) %>

This is how it renders:

image

Apparently, the LabelFor does not support globalization in the view. That means I would need to add the LocalizedDisplayName attribute to each of the POCOs as recommend here OR remove the LabelFor and hand code  labels. Since I am not interested in creating something outside of the base MVC API, I split out the labels like so:

From This:

<%: Html.LabelFor(model => model.Date) %>

 

To This:

<%: Html.Label(Resources.LocalizedText.DateHeader)%>

After spending a couple of hours associating text to the resource files, I was done

Here is the login screen:

image

And the main page:

image

Resource Files In A Console Application

I am following this article about Locating and Using Resources for a Specific Culture.

I created a new Console Application. I then added a new default resource file with the following content:

image

The resource file was placed in the properties folder:

image

I then renamed the file Greeting.resx. The problem is that VS2010 now thinks that there is not a default resource file:

image

I decided to follow the article and not the default behavior of VS2010 so I pressed on without a default resource file.

I then added new resource files:

image

I then went to the AssesmblyInfo file and added the following entry:

[assembly: NeutralResourcesLanguageAttribute("en")]

Unfortunately, it is not recognized until I added a reference to System.Resources. (I looked in System.Globalization first – ooops).

I then was ready to start coding the different cultures.

I first added the System.Globalization, System.Threading,and System.Reflection namespaces and wrote a quick function to see the different cultureInfo:

static void Main(string[] args) { CultureInfo cultureInfo = Thread.CurrentThread.CurrentCulture; Console.WriteLine("The current CultureInfo is {0}", cultureInfo.EnglishName); Thread.CurrentThread.CurrentCulture = new CultureInfo("fr-FR"); Thread.CurrentThread.CurrentUICulture = Thread.CurrentThread.CurrentCulture; ResourceManager resourceManager = new ResourceManager("Com.Tff.CultureInfo", Assembly.GetExecutingAssembly()); Console.WriteLine("The current CultureInfo is {0}: {1}", cultureInfo.EnglishName, resourceManager.GetString("HelloString")); Console.ReadKey(); }

and got the following error:

image

Hum, it can’t find the resource file. I wondered if it was the spelling of the assembly in the constructor of Resource Manager. I changed it to match the Assembly name:

ResourceManager resourceManager = new ResourceManager("Com.Tff.CultureDemo", Assembly.GetExecutingAssembly());

 

image

 

It still failed so I went on MSDN and looked at the help on this exception here. That article had a different project so I fired up another instance of VS2010. Following the instructions, I created a new ASP.Net web project and added the following code the Page_Load of default.aspx:

Thread.CurrentThread.CurrentUICulture = new CultureInfo("ja"); ResourceManager resourceManager = new ResourceManager("Com.Tff.CultureTest.Strings", Assembly.Load("Com.Tff.CultureTest")); this.IntroLabel.Text = String.Format("Hello {0}", resourceManager.GetString("HelloString"));

I also added a resource file called Strings.ja.resx to the web site’s root. I hit F5 and things worked:

image

What I learned though induction is that Resource files in VS2010 depend on convention:

The CultureInfo class associates with the xx in the yyyy.xx.resx file.

The ResourceManager class associates with the yyyy in the yyyy.xx.resx file.

The rest of the tutotial deals with creating a satellite assembly. I am not going to do that yet – I am going back to my original solution to see if I could get the resource file located in a Console application.

I changed the ResourceManager constructor to this:

ResourceManager resourceManager = new ResourceManager("Com.Tff.CultureDemo.Greeting", Assembly.GetExecutingAssembly());

And boom goes the dynamite:

image

TFS Branching (Warning: Lots of Screen Shots)

For my carpool project, I attempted to create a branching strategy based on the ALM Ranger’s recommendations of a basic strategy (main, dev, release).  For my 1st task, I attempted to branch for the new feature set. However, after 1 hour, I realized that I set up my TFS projects incorrectly. I went ahead and deleted all of my project groups EXCEPT the default one. From there, I added a new Carpool Project.

 

image

image

image

Note that creating the project is a TFS-specific action, the actual file system has not been altered yet. I then needed to add this project to my file system:

image

image

Note that I added it to the Root folder for my VS2010 Projects. All of the child branches will be off of this.

image

Right now in the folder, there is nothing there (except for the build process templates).

The quick start guide for VSTS Ranger team recommends 3 branches – Main, Dev, and Release. Following that guide, I created the Main Branch off of the root and then the Development and Release branches off of Main.

image

An interesting TFS bug (I mean feature) is that if you make a new folder and then click out of VS2010, the folder is still created on the file system, but not in the Source Control Folder.

Under each branch, the guide recommends having a Bin, Docs, and SRC (Source) directory. Since I am not using a build agent (yet), this seems unnecessary. I put my source code directly in the Main branch on the file system:

image

I then added the files to the folder:

image

image

I now have my original source files in the Main Folder.

I then checked in the 118 files.

I then converted the Mian Folder to a Branch -> according to the documentation, it allows me to see it in the designer.

image

I then needed to create 2 branches off of the Main – Development and Release

image

Then I had to get latest version:

image

Automagically, 2 things happen.

My Development Folder was created into a Branch:

image

In addition, all of the main branches files were copied over to the development branch:

image

I did the same with the Release branch

Volia: 3 branches:

image

And 2 are subordinate to the Main as you can see by clicking on properties on Main and selecting Relationships:

image

 

image

And also, for the capstone:

 

image

 

image

I think I have things set up right – though I don’t think I need Main and Release files on my file system – just development. In any case, I will now try and branch to the globalization change set.

Before starting on Version 1.1 (Gloabization), I wanted to see if I could make changes correctly. I checked out the Production Version and made 1 change to the master page (added a trademark symbol).

I incremented the file and assembly version:

image

And I checked in the file

I then went to Version Control Explorer and Added a Label:

image

I then Merged the Release branch up to the Main branch:

image

I then went to Main branch and checked in the pending changes:

image

And the changes are now in Main and Release – Yippie!

I then merged the changes into Development. I went ahead and made the changes in development to see how the merge tool handles it:

image

And it is flat out awesome!

The only thing I need to keep in mind is that I might have the development branch solution in VS2010 and working with the version control explorer, forget that and think I am in a different environment.

Note that I will release from the Release Branch, not the Main branch…

I am now ready to start Globalization…

The Carpool goes global

I received an email from someone who wants to have a carpool website in Albanian. I hadn’t worked on globalization issues (except for hello worlds for my exam preps) so it seems like a good way to get my feet wet in .NET globalization in a real-world app.

My 1st step was FxCop. I deleted all of my FxCop suppressed messages for globalization. I got four errors like this:

CA1305 : Microsoft.Globalization : Because the behavior of ‘DateTime.Parse(string)’ could vary based on the current user’s locale settings, replace this call in ‘CarpoolSummaryFactory.GetCurrentWeekCarpoolSummaries()’ with a call to ‘DateTime.Parse(string, IFormatProvider)’. If the result of ‘DateTime.Parse(string, IFormatProvider)’ will be based on input from the user, specify ‘CultureInfo.CurrentCulture’ as the ‘IFormatProvider’ parameter. Otherwise, if the result will based on input stored and accessed by software, such as when it is loaded from disk or from a database, specify ‘CultureInfo.InvariantCulture’.

I replaced all instances of this

startDate = DateTime.Parse(DateTime.Now.ToShortDateString() );

With this

startDate = DateTime.Parse(DateTime.Now.ToShortDateString(), CultureInfo.CurrentCulture );

I then had to fix 1 of my ToString functions

public override string ToString() { return String.Format("{0}-{1}", Id, Name); }

To this

public override string ToString() { return String.Format(CultureInfo.CurrentCulture, "{0}-{1}", Id, Name); }

I then ran FxCop on the UI project (it is MVC2): I first changed the settings on code analysis to only look at Globalization.

image

I got 11 errors. 9 were the same as before – DateTime.Parse and 2 were Int.Parse. All of the errors were in the controller where I was taking data out of the view model and jamming it into the POCOs. I added the CultureInfo parameter to the parse function to give me a clean FxCop report.

I ran all of unit tests – all green.

I then took a look through MSDN on Globalization.

Before refactoring and adding resources files, it seemed like a good time to implement a more mature branching strategy using TFS.  That is the subject of my next blog post.