Working around a bug in the ProvideCodeGeneratorAttribute

Summary

There is a bug in the ProvideCodeGeneratorAttribute, which breaks VSIX deployment because it generates absolute codebase in .pkgdef instead of Codebase relative to $PackageFolder$. This bug can be worked-around by not using the ProvideCodeGeneratorAttribute but adding a second pkgdef file in your project with the information that this attribute would generate

 

The problem

Here is how to repro this problem:

1. Create a DSL (for instance, in fact we need a package). (Company: CompanyName, Language name: LanguageName, extension .myLanguage)

2. In the DslPackage project, add a partial implementation for the Package providing a code generator (for instance:)

 /// <summary>   /// Declaration of a partial of the package providing the code generator   /// </summary>   [ProvideCodeGenerator(typeof(MyCodeGenerator), "MyCodeGenerator", "Custom tool that generates C# files based on .xxx files", true)]   partial class MyEditorPackage   {   }

 

 /// <summary>  /// Code generator itself   /// </summary>   [Guid("38BF483F-F6AC-468C-8826-C7DDF6ECED98")]   public class MyCodeGenerator: TemplatedCodeGenerator   {    protected override byte[] GenerateCode(string inputFileName, string inputFileContent)    {     return base.GenerateCode(inputFileName, inputFileContent);    }   }

 

When you build your package, the ProvideCodeGenerator  attribute will generate some registration information for Visual Studio to take your custom tool into account. However, because of the bug, it generates a Codebase in the .pkgdef file which is absolute:

"CodeBase"="file:/// ... path on dev machine ... /DslPackage/bin/Debug/My.DslPackage.dll"

instead of relative to the $PackageFolder$

"CodeBase"="$PackageFolder$\My.DslPackage.dll"

 

thus preventing the code generator being deployed by VSIX (which would work if the registration attribute generated a codebase relative to $PackageFolder$ in the .pkgdef)

 

Workaround

Happily, a package can have several pkgdef files. The idea of the workaround is, in the DslPackage project, to add a new file named “CodeGenerator.pkgdef”, and ensure the following properties in the solution explorer for this file:
- Set Build Action to Content,
- Set Copy to Output directory to Copy if newer
- Set Include in VSIX to true

The content of the file must be something like the following (replacing the name of the custom tool its GUID, its comment, the class name for the custom tool, and the assembly name)

[$RootKey$\Generators]
[$RootKey$\Generators\{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}]
[$RootKey$\Generators\{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}\MyCodeGenerator] @="Custom tool that generates C# files based on .xxx files"
"CLSID"="{38BF483F-F6AC-468C-8826-C7DDF6ECED98}"
"GeneratesDesignTimeSource"=dword:00000001
[$RootKey$\CLSID]
[$RootKey$\CLSID\{38BF483F-F6AC-468C-8826-C7DDF6ECED98}]
@=" Custom tool that generates C# files based on .xxx files"
"Class"=" CompanyName.LanguageName.MyCodeGenerator"
"InprocServer32"="$WinDir$\SYSTEM32\MSCOREE.DLL"
"ThreadingModel"="Both"
"CodeBase"="$PackageFolder$\CompanyName.LanguageName.DslPackage.dll"
[$RootKey$\Generators\{FAE04EC1-301F-11d3-BF4B-00C04F79EFBC}\ .myLanguage] @="MyCodeGenerator"

 

For more details, please refer to the Dsl Tools Lab for VS2010, in chapter 4: code generation (pages 20 to 23). It includes a complete end to end example of this.