February 2009 Entries

In Parts 1 and 2 of this series we covered some of the pitfalls and precautions the need to be taken when deploying SharePoint artifacts such as Site Columns, and Content Types. Wrapping up these posts we will discuss how to deploy Page Layouts successfully. And just so that we are still on the same page, these posts are focused on deploying using the SharePoint Solution deployments not creating these artifacts using SharePoint Designer or the Web Interface.

Page Layouts

Page Layouts are a great way of reusing structure within your sites. My typical development pattern is to create a layout in a development environment using the web interface, the open it up in SharePoint Designer to polish it up.

The main reason I go about it this way is that using SPD to design a layout is just so easy. Think of it in the same vein as what Visual Studio does for regular ASP.NET applications. The astute reader may have noticed that in previous posts I have cautioned against the use of SPD. This is true, but that is always tempered with 'with caution'. Knowing what using SharePoint designer means can make all the difference. Additionally, I only use it against my development farm, no where else.

image Once I have gotten the layout to a state that I am happy with, I copy the contents using the code view of SPD, paste it into a file in Visual Studio. From there I can add it to my Feature and have it deployed properly. I then reghost the file. Remember that SPD is going to unghost (customize) the file and any attempt to update it using a feature will fail miserably.

Here's your first tip: It appears that some engineer, and I use this term loosely, decided that when you deploy a file to SharePoint, the determination of it being a Page Layout is the presence of a tag with the name 'Content'. Not 'content', or 'conTent', just 'Content'. Would you care to guess what capitalization SPD uses by default for content section in page layouts? Yep, you guessed it 'content' I'm shipping a case of wet trout to the MS campus as you read this, let the slapping begin. And it gets even worse, you have to have all the <asp:Content /> controls use the proper case.

The fallout of this is that if you deploy a file without a 'Content' tag SharePoint determines that this is just a plain old HTML file, however it does some additional inspection and doesn't find an HTML tag, so it kindly inserts one for you. Isn't that sweet? I'm not smiling. This causes your layout file to be customized and and future upgrades will appear to not work even though the file in the 12 Hive is correct. Here is the offending code that you will find at the end of you page layout if you view it in SPD.

<html xmlns:mso="urn:schemas-microsoft-com:office:office"xmlns:msdt="uuid:C2F41010-65B3-11d1-A29F-00AA00C14882"><head>
<META name="WebPartPageExpansion" content="full">
<!--[if gte mso 9]><xml>
<mso:CustomDocumentProperties>
<mso:PublishingPreviewImage msdt:dt="string"></mso:PublishingPreviewImage>
<mso:ContentType msdt:dt="string">Page Layout</mso:ContentType>
<mso:MasterPageDescription msdt:dt="string"></mso:MasterPageDescription>
<mso:PublishingAssociatedVariations msdt:dt="string"></mso:PublishingAssociatedVariations>
<mso:PublishingHidden msdt:dt="string">0</mso:PublishingHidden>
<mso:PublishingAssociatedContentType msdt:dt="string">;#Agenda item;#0x010100C568DB52D...;#</mso:PublishingAssociatedContentType>
</mso:CustomDocumentProperties>
</xml><![endif]-->
<title>Dummy Content Type</title></head>

Fortunately this can be easily corrected, if you've already been bit by it. You’ll know this is the problem, because when you go to load a page based on that layout, you’ll receive this error:

Only Content controls are allowed directly in a content page that contains Content controls.

First do a Find/Replace on your Page Layouts and make all of the asp:content tag asp:Content. Next using the STSADM Extensions from Gary Lapointe, uncustomize the file with the gl-reghostfile operation.

image
This operation uses the RevertContentStream() method of the SPFile class. Now when you upgrade your solution the updated layouts will be used.

Today I released the first Beta of the next version of Axiom. Axiom is a C# 3D rendering engine. This year marks the 6th year of it’s existence. I have been maintaining it for the past 3 years. I am particularly excited about this release because for the first time we are shipping some brand new features.

XNA Renderer

The largest feature that was added for 0.7.5.0 was the XNA Renderer. It has been in development for the past year. A large hurdle for the XNA renderer was the absence of any fixed function pipeline support. Only vertex and pixel shaders can be used. Since Axiom is old enough to still support the fixed function pipeline, it needed a way to provide that functionality using a shader. The result of this is the FixedFunctionEmulation layer. It will take the fixed function state of the engine aand generate a set of shaders on the fly. This will unfortunately only work with XNA on the windows platform as the XBox does not allow compilation of shaders at runtime. In this release the only Axiom functionality not supported is shadows. The plan is to have shader based shadows by the time this version is final.

The windows support of XNA in this release is much richer than the XBox. Axiom includes a custom pipeline component to load textures instead of using the native library DevIL. DevIL works wonderfully on the Windows platform, however native code cannot be used in XNA projects on the XBox. Rather than port the library over, I leveraged the XNA Content Pipeline to load the textures into the standard resource system. Additionally, the XNA Content Pipeline was used to precompile HLSL files into VertexShaders and PixelShaders for the XBox. Version 3 of XNA only supports FX files by default. Since Axiom has it’s own Material System, I don’t need FX files. I plan to add FX file support in a future release.

Once the final is released I am really hoping to see someone create a game using Axiom and publish it on the Community Games for XBox Live. That would be just awesome.

Project Files

Another big change for this release is in the build system. Previously I did not keep the project files in source control or ship them. Instead Prebuild was used to generate them specifically for your environment. This worked generally, and was necessary because SharpDevelop, Monodevelop and Visual Studio all supported different project file formats. As of MonoDevelop 1.0, SharpDevelop 2.2 and Visual Studio 2008, they now all support the 2008 version of the project file format. This means that now all project files are in the source control repository and shipped as part of the distribution. This should make it easier for newer users to get started with Axiom.

Additionally this will mean adoption of metabuild as the project build system, replacing the custom nAnt build scripts currently in use. The only roadblock for metabuild is Mono’s support for MSBuild scripts. Mono’s implementation of MSBuild, called xbuild, is still fairly immature, however I am testing it out with the Axiom projects to see how well it works.

The obligatory screenshots

Below you will find a few of my favorite demo screenshots. These are all running on windows using the XNA Renderer. Not bad for a managed engine.

RenderToTesture EnvironmentMapping ParticleFX SkeletalAnimation

What’s next?

After this version is complete and final, I already have another set of features hot on it’s tail. There is a post processing framework ( Blur, HDR ), better material handling, better window management, background loading of resources, an overhaul of the OpenGL Renderer and much much more.

call to action

Please, in order for Axiom to be the best it can be, I need some assistance in making sure the new features do what they are supposed to. More importantly, do what you need them to do. So go download Axiom today and kick it’s tires, file bug reports, and send me feedback.

A common problem I have with users of Axiom is that they will mismatch the assemblies need to get the engine up and running. Axiom uses a plugin system that allows for interchanging the platform in use (Win32, SDL, etc ) and which graphics system to use ( XNA, OpenGL, DirectX ). Unfortunately, there isn’t as loose coupling as I’d like, in particular the OpenGL rendering system has to be recompiled to use S.W.F forms or SDL forms. SLD is the particularly nasty one because it has to be paired with the SDL platform manager.

A few months ago I ran across the ILMerge utility and thought that it would solve some problems. I could combine the main engine with the proper combinations of a platform manger and render system there by eliminating the mismatch. I didn’t investigate it much because a quick try just created errors. More recently a request came up on the forums to be able to do this exact thing. I took a few hours and made it so that users could use ILMerge on the assemblies and have the system behave properly. However I didn’t make the assemblies myself, as I wasn’t preparing for a release.

Fast forward to this week. I should have released a beta of Axiom 0.7.5 back in January, but because things were a bit crazy I didn’t get to it until this week. With this release I wanted to be able to ship these combined assemblies. I like to automate things so I found an implementation of some MSBuild tasks that use ILMerge to produce a single assembly.

Now this week I had also been testing Mono’s xbuild, which is an implementation of MSBuild but on mono. Knowing that I have a large percentage of my users are linux based I investigated the ability of ILMerge to run on Mono. It doesn’t.

Ok, back to the drawing board. After a quick search I found MonoMerge. I tested it out a little to see if it worked, as it didn’t have as many options as ILMerge. It seemed to do the rick.

Now I needed a MSBuild task to execute MonoMerge. Using the ILMerge task as a guide I quickly put together a new MSBuild task that can now merge assemblies on Mono and .Net.

Enjoy!

UPDATE : Added hyperlinks to various resources.

using System;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
using Mono.Cecil;
using Mono.Merge;

namespace Mathoms.MSBuild.Tasks
{
    public class MonoMerge : Task 
    {
        private string _outputFile;
        private ITaskItem[] _assemblies = new ITaskItem[0];

        [Required]
        public virtual string OutputFile
        {
            get { return _outputFile; }
            set { _outputFile = System.IO.Path.Combine(BuildEngine.ProjectFileOfTaskNode,value); }
        }

        [Required]
        public virtual ITaskItem[] InputAssemblies
        {
            get { return _assemblies; }
            set { _assemblies = value; }
        }


        public override bool Execute()
        {
            var context = new MergeContext {OutputPath = _outputFile};

            var assemblies = new string[ _assemblies.Length];
            for( var i = 0; i < assemblies.Length; i++ )
            {
                assemblies[ i ] = _assemblies[ i ].ToString();
            }

            context.Assemblies.AddRange( assemblies );

            try
            {
                var primary = AssemblyFactory.GetAssembly( context.Assemblies[ 0 ] );

                for ( int i = 1; i < context.Assemblies.Count; i++ )
                {
                    var asm = AssemblyFactory.GetAssembly( context.Assemblies[ i ] );
                    asm.Accept( new StructureMerger( context, primary, asm ) );
                }

                var fix = new FixReflectionAfterMerge( context, primary, primary );
                fix.Process();

                AssemblyFactory.SaveAssembly( primary, context.OutputPath );

            }
            catch (Exception e)
            {
                Log.LogErrorFromException(e);
                return false;
            }

            return true;
        }
    }
}