1

I am using mono's Mono.Cecil package and I'm running into a problem when I call the following from a custom msbuild task:

ModuleDefinition.Write(AssemblyFileName, new WriterParameters { WriteSymbols = true });

The assembly is saved fine and does exactly what I need it to do. If I changed the assembly file name to test if the pdb is created, it is and when replaced with the other pdb, it's debugging correctly. However, the pdb of the assembly is still the old pdb when I build normally. I presume that mono is creating the pdb and msbuild is overriding it with the old one.

This is the target I am trying to import into the project:

<PropertyGroup>
    <TaskAssemblyFileDir>$(SolutionDir)PropertyObserver.Tasks\$(OutDir)</TaskAssemblyFileDir>
    <TaskAssemblyFileUnescapeDir>$([MSBuild]::Unescape('$(TaskAssemblyFileDir)'))</TaskAssemblyFileUnescapeDir>
    <TaskAssemblyFile>$(TaskAssemblyFileUnescapeDir)PropertyObserver.Tasks.dll</TaskAssemblyFile>
</PropertyGroup>
<UsingTask TaskName="PropertyObserverTask" AssemblyFile="$(TaskAssemblyFile)" />
<Target Name="PropertyObserver" DependsOnTargets="AfterBuild">
    <PropertyObserverTask AssemblyPath="$(TargetPath)" />
</Target>

I'm not sure what's the target msbuild calls to create the pdb. I presumed it is AfterBuild, but it's not.

Any advice would be helpful.

Willem Toerien
  • 250
  • 1
  • 3
  • 11
  • Normally the compiler generates the pdb. So the call to CSC will generate it. – jessehouwing Jul 16 '13 at 18:35
  • Hi, yes, I have looked into that at [http://msdn.microsoft.com/en-us/library/ms171479%28v=vs.90%29.aspx](http://msdn.microsoft.com/en-us/library/ms171479%28v=vs.90%29.aspx). It seemed to me that it happens in the build target. However when I change the value "DependsOnTargets" to be Build, I am still stuck with the same problem. EDIT I am altering the dll after it was built. – Willem Toerien Jul 16 '13 at 18:46
  • For reference, you may want to see how PostSharp sets up it's build targets since it also does weaving. You could also turn on detailed or diagnostic build output to see when the pdbs are being written/overwritten. – Jay Walker Jul 16 '13 at 20:32
  • I managed to figure it out. The task wasn't updated. So when I ran my exe in the post build events, it was working and I could debug. But the moment I tried to use the task, it did nothing. I restarted my visual studio.... and it worked. (-_-) Thanks guys! – Willem Toerien Jul 16 '13 at 21:14

1 Answers1

0

If you want to apply custom rewriting it is important to override the target files in the obj folder.

This is crucial as msbuild copies those files from here.

See my answer HERE

In my example i use PostSharp in addition to my rewriting task.

But this is no problem for your case:

<PropertyGroup>
    <CompileDependsOn>
        $(CompileDependsOn);
        ApplyILRewriting
    </CompileDependsOn>
    <BuildDependsOn>
        $(BuildDependsOn);
        AfterILRewritingPostBuild
    </BuildDependsOn>
</PropertyGroup>

<Target Name="ApplyILRewriting">
    <ILTransformationTask IntermediateAssemblyPath="$(ProjectDir)$(BaseIntermediateOutputPath)$(Configuration)\$(TargetFileName)" />
</Target>
<!-- Override this target in another file. Build your nuget packages here for example. -->
<Target Name="AfterILRewritingPostBuild">
</Target>

The cool part about my solution is, that the code that transforms the assembly is loaded from the target assembly. So you can just implement a interface on a attribute and place the attribute on your target type. It will the run the method on the interface for every attribute applied on a target.

This enables you to have your rewriting instructions in your target assembly meaning that for every project you can easily add your own transformations.

As you can see i pass the path of the assembly in the obj folder. My solution works like this(i do not want to release the full source at the moment, sorry):

  1. create pre transform folder in obj folder
  2. copy all files from obj/ to the folder
  3. create a post transform folder
  4. copy all files from obj/ to the folder
  5. inspect types using reflection:
    • load referenced assemblies of your output assembly
    • filter attribute types inheriting ITransformation interface
    • find types that have those attribute types applied
  6. Load the matching assemblies and types using cecil from the post transform dir
  7. execute the Transform(reflectionDeclaringType, cecilDeclaringTypeDefinition) method on the attributes that you reflected on the types; This is the point where your custom cecil code applies the rewriting using the ITransformation interface

I also created another interface to apply a transformation not to a single type but rather to the whole assembly.

So i ended up having ITypeTransformation and IAssemblyTranformation. One scoped to the assembly and also only passing reflection and cecil assembly. The ITypeTransformation passes reflection, cecil assembly and reflection, cecil type

A example for a transformation could be:

public class TestTypeTransformation : Attribute, ITypeTransformationAttribute
{
     public void ApplyTransformation(
            Assembly preTransformationReflectionAssembly, 
            AssemblyDefinition postTransformationCecilAssembly, 
            Type reflectionType, 
            TypeDefinition cecilType)
    {
        // Your custom rewriting here
    }
}
Community
  • 1
  • 1