Buliding .net and Silverlight without Links

02Jul09

Often I will write class libraries for Silverlight but also want to make the library available for .net. The CLRs are not binary compatible so you need to have both a Silverlight and .net project and somehow share the code – you don’t want to have to maintain two sets of identical files. The common method of doing this is to develop the Silverlight library and link the source files in the .net library. You can do this by right clicking on the .net project and clicking Add Existing Item then selecting Link instead of Add. This works ok but you’ll still have to maintain two projects when you add/remove/rename files and directories in the Silverlight project.

I like to re-factor my code often and the task of manually modifying the .net project is a real pain. So I wrote a tool to update the .net csproj as per the Silverlight csproj and to copy the source files to the correct location. It only updates the Compile ItemGroup, so any manual changes to the .net csproj are not lost. The tool runs as part of the build process. A NAnt script builds the Silverlight solution, runs the copysrc tool to update the .net solution projects, builds the .net solution, and then runs unit tests for both solutions. I then only need to worry about editing the Silverlight source.

public class Program
{
	public static int Main(string[] args)
	{
		if (args.Length != 2)
		{
			Console.Error.WriteLine("usage: copysrc sourceDir destDir");
			return 1;
		}

		try
		{
			Copy(args[0], args[1]);
		}
		catch (Exception e)
		{
			Console.Error.WriteLine(e.Message);
			return 1;
		}

		return 0;
	}

	private static void Copy(string sourceDir, string destDir)
	{
		const string namespaceName = "http://schemas.microsoft.com/developer/msbuild/2003";
		XName itemGroupName = XName.Get("ItemGroup", namespaceName);
		XName compileName = XName.Get("Compile", namespaceName);

		Func<string, string> projNameFromDir =
			dir => Path.Combine(dir, dir.Split(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar).Last() + ".csproj");

		string sourceProjFileName = projNameFromDir(sourceDir);
		string destProjFileName = projNameFromDir(destDir);

		Func<XDocument, XElement> findCompileItemGroup =
			doc =>
				{
					if (doc.Root == null)
						throw new InvalidOperationException("Xml document has no root.");

					return doc.Root.Elements(itemGroupName).Where(element => element.Elements(compileName).Count() > 0).FirstOrDefault();
				};

		// load dest csproj
		XDocument destDocument = XDocument.Load(destProjFileName);
		// find and clear dest compile itemgroup
		XElement destCompileItemGroup = findCompileItemGroup(destDocument);
		if (destCompileItemGroup != null)
			destCompileItemGroup.RemoveAll();
		else
			destCompileItemGroup = new XElement(itemGroupName);

		// load source csproj
		XDocument sourceDocument = XDocument.Load(sourceProjFileName);
		XElement sourceCompileItemGroup = findCompileItemGroup(sourceDocument);
		if (sourceCompileItemGroup != null)
		{
			// copy files and populate dest compile itemgroup
			foreach (XElement compileElement in sourceCompileItemGroup.Elements(compileName))
			{
				XAttribute includeAttribute = compileElement.Attribute("Include");
				if (includeAttribute == null)
					continue;

				// copy include to dest csproj
				destCompileItemGroup.Add(compileElement);

				// create dest dir
				string filename = includeAttribute.Value;
				string directoryName = Path.GetDirectoryName(filename);
				if (!string.IsNullOrEmpty(directoryName))
					Directory.CreateDirectory(Path.Combine(destDir, directoryName));

				//copy file
				File.Copy(Path.Combine(sourceDir, filename), Path.Combine(destDir, filename), true);
			}
		}

		destDocument.Save(destProjFileName);
	}
}

I use this tool for ValidationAspects, Bloom, and many other projects and it works fine. I use ifdefs in the .cs file to separate Silverlight and .net code for incompatibilities. I don’t know how well this would work for xaml files that describe UIs. Does xaml have something similar to ifdef? I personally haven’t found this an issue though as I develop UIs for their specific platform and haven’t had the need to share controls’ View code. ViewModels are shared however.

To use it I create both a Silverlight and a .net solution. For every project in the Silverlight solution I create its partner in the .net solution. I set the .net projects’ properties as required and then only check-in the .net csproj files. I set subversion to ignore all source files under the .net solution dir. If I do need to open the .net solution in Visual Studio, I first run the build script to update and populate the .net solution’s projects. If you want to see the tool in action and what the NAnt and project structure look like, take a look at Bloom on codeplex.



One Response to “Buliding .net and Silverlight without Links”

  1. Hey, I came across this blog article while searching for help with fixing Microsoft Silverlight. I’ve recently switched browsers from Google Chrome to Microsoft IE 6. Just recently I seem to have a issue with loading websites that use Microsoft Silverlight. Everytime I go on a website that needs Microsoft Silverlight, the site crashes and I get a “npctrl.dll” error. I cannot seem to find out how to fix it. Any aid getting Microsoft Silverlight to function is greatly appreciated! Thanks


Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s


%d bloggers like this: