Skip to main content

Hello, PostCompiler World!

Sometimes doing the simplest things is difficult. In order to make any headway on SharpMock, I’ll need to be able to understand the basics of PostSharp. What could be more basic than getting PostSharp to execute a task that ouputs a ‘Hello World’ message during the postcompilation process? We’re not decorating any types with attributes; we’re not intercepting any calls; we’re not outputting any IL. This should be a snap! And yet, I haven’t posted in weeks….

First thing’s first – we need a ‘Hello World’ task. In a project called PostSharpPlugin I have defined the following class:

using PostSharp.Extensibility;

namespace PostSharpPlugin
{
public class SayHelloTask : Task
{
public override bool Execute()
{
Console.WriteLine("Hello, PostCompiler!");

// Task executed successfully
return true;
}
}
}


I have also created a .psplugin file that looks like this:




<?xml version="1.0" encoding="utf-8" ?>
<PlugIn xmlns="http://schemas.postsharp.org/1.0/configuration">
<TaskType Name="SayHello" Implementation="PostSharpPlugin.SayHelloTask, PostSharpPlugin" Phase="Analyze">
</TaskType>
</PlugIn>


So, there we have a plugin that will perform some tasks during postcompilation. We want this task to be performed during postcompilation of some other assembly if it references this plugin, so we need some way to tell PostSharp about this requirement. To do so, we add an attribute to PostSharpPlugin:




[assembly: ReferencingAssembliesRequirePostSharp("SayHello", "PostSharpPlugin")]


I have created another assembly called (very cleverly) ReferencingAssembly and added a reference to PostSharpPlugin. That assembly contains this .psproj file:




<Project xmlns="http://schemas.postsharp.org/1.0/configuration" ReferenceDirectory="{$ReferenceDirectory}">
<SearchPath Directory="{$SearchPath}" />
<Using PlugInFile="../PostSharpPlugin/bin/Debug/PostSharpPlugin.psplugin" />
<Tasks>
<AutoDetect />
<SayHello />
<Compile TargetFile="{$Output}"
IntermediateDirectory="{$IntermediateDirectory}"
CleanIntermediate="{$CleanIntermediate}"
SignAssembly="{$SignAssembly}"
PrivateKeyLocation="{$PrivateKeyLocation}"/>
</Tasks>
</Project>


The Using element tells PostSharp where to get the definition for the task SayHello. This is critical. I spent every spare hour I had for weeks trying to get this seemingly simple example to work without defining or importing the definition of the SayHello task in this file. In fact, I don’t think I should have to explicitly specify the SayHello task to be executed here either. All the tracing, debugging, and specifying different search paths I could do didn’t help me get this example working, so for now importing the task definition like so is the solution I’m rolling with. It will allow us to make some headway, like figuring out how to check a test assembly for methods that need to be mocked. Ultimately, though, we’ll need PostSharp to automatically detect and use tasks that provide services like ICustomAttributeProvider, and this solution won’t handle that.



Oh, by the way, don’t forget that we need to invoke PostSharp when we compile our assembly. For that we need to import the PostSharp MSBuild task. The modified ReferencingAssembly.csproj file for this example looks like this at the end (the second element is what has been added):




  <Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<Import Project="..\ThirdParty\PostSharp.targets" />


I have uploaded the code for this example here. I have included all of the needed PostSharp files in the ThirdParty directory. (I’m not sure this distribution is allowed.) So, it should “just work”… you can open the solution in Visual Studio, build, and see something like this in the build output window:




...
PostSharp 1.0 [1.0.11.424] - Copyright (c) Gael Fraiteur, 2005-2008.

Hello, PostCompiler!
...

Comments

Gael Fraiteur said…
Ryan,

ReferencingAssembliesRequirePostSharp should work, but PostSharp should know where your plug-in lays.

So either you add it to the plug-in directory, either you add the location of your plug-in to "Reference Path", in Visual Studio properties.

In case of problem, you can always try to ask on the forum.

Good luck!

-gael

Popular posts from this blog

Migrating Hg Repos with hg-fast-export and Windows Subsystem for Linux

Introduction I prefer Mercurial (hg) to git . I don’t really have any reason for this preference - they both do the same thing, and the user experience for 90% of the use cases is the same. It probably comes from the conditions of the DVCS landscape when I started using these systems. Some of this may have been perception only, but it looked like this: GitHub didn’t have free private repos BitBucket did have free private repos BitBucket was very hg-friendly Joel Spolsky had an amazing tutorial that served as both a how-to for hg as well as a general intro to DVCS hg was much more Windows-friendly than git Since hg was written in python, I felt like extending it would be easier than doing so for git if I ever needed to (admittedly, this is a pretty ridiculous reason) hg felt like a more unified, “coherent” system than the very linux-y feeling git and its extensions (also pretty ridiculous) Where they differed, I liked the verbs hg used better than git’s counterparts ...

Enabling Globalization Invariant Mode for .NET Core App on Raspberry Pi Running LibreElec

I had an app I wanted to run on my Raspberry Pi 3 running LibreElec . In LibreElec you can install the dotnet core 2.2 runtime as an addon, and in Visual Studio you can compile for ARM processors with ‘Target Runtime’ set to ‘linux-arm’ in the publish profile. So, I published to a folder from VS using that profile, and I copied the output over to my RPi which had the dotnet runtime installed. I did a simple dotnet Whatever.dll to run the app (actually in this case, it was /storage/.kodi/addons/tools.dotnet-runtime/bin/dotnet Whatever.dll because of the way the addon is installed) and was met with this error: FailFast: Couldn't find a valid ICU package installed on the system. Set the configuration flag System.Globalization.Invariant to true if you want to run with no globalization support. at System.Environment.FailFast(System.String) at System.Globalization.GlobalizationMode.GetGlobalizationInvariantMode() at System.Globalization.GlobalizationMode..cctor() at Syste...

Stubbing Static Methods with PostSharp

TypeMock uses the Profiler API to allow mocking, stubbing, etc. of classes used by code under test. It has the ability to handle sealed classes, static classes, non-virtual methods, and other troublesome-yet-oft-encountered scenarios in the world of unit testing. Other frameworks rely on proxies to intercept method calls, limiting them to be able to only fake virtual, abstract, and interface members. They also rely on dependecy injection to place the proxies as the concrete implementation of calls to the abstracted interface members. Anyone working with a legacy codebase is bound to run into static method calls (especially in the data access layer), dependencies on concrete types with non-virtual methods, and sealed class dependencies (HttpContext anyone?). The only way to unit test this without refactoring is with TypeMock. I've never used TypeMock, and I'm sure it's a great product, but it's not free. I decided to spike some code to see if I could solve the prob...