开发者

Custom MSBuild Task depends on compiled code but also generates code that is depended on

I'm attempting to generate a C# code file that constructs an object tree based on an XML file. The elements in the XML refer to Types that are defined in the project that is being compiled. The generated code needs to construct an object tree of Types that are found in the project that is being compiled. To further complicate things, the generated code needs to be compiled into the project that is being compiled, and is referred to by code in the current project that is being compiled.

I'm currently trying to do this with a custom MSBuild Task. This Task will be distributed to other developers for use in their own projects, so I cannot hard-code the available Types into the Task.

Here is some sample code to illustrate what I'm doing:

// This is not generated
public class SomeClass {

    public void DoSomething() {}

    public string SomeProperty { get; set; }
}

SomeClass is a Type that is defined in non-generated code in the project. Here is the XML file that refers to SomeClass and populates the SomeProperty property:

... bunch of XML...
<SomeClass SomeProperty="SomeValue" />
... bunch more XML...

SomeOtherClass has a non-generated p开发者_C百科artial declaration:

// This is not generated
public partial class SomeOtherClass {

    public void SomeMethod() {
        someField.DoSomething();
    }
}

The Task creates a partial SomeOtherClass declaration:

// This is generated
public partial class SomeOtherClass {

    private SomeClass someField = new SomeClass() {
        SomeProperty = "SomeValue"
    };
}

I think this looks a lot like what WPF would have to do for XAML files, and my usage is related (using XML to generate code which instantiates an object tree, based on types in the same project that is compiling).

What would be the best approach to this problem? Should I be doing a multiple stage compile and consuming intermediate files in the Task? Should I attempt to build a temporary assembly separately within the Task without the generated code, then do reflection on the temporary assembly, then generate the code files, then allow the normal build process to continue?


The question is too vague to be sure this is helpful, but hopefully this helps.

1. Get your .props and .targets files correct

  1. Configure the LoadTimeSensitiveTargets and LoadTimeSensitiveProperties to make sure IntelliSense will work even before the project is built.
    https://github.com/antlr/antlrcs/blob/9ee43ed9486e55afcc1db06f9f0755658974f99f/AntlrBuildTask/Antlr3.targets#L36-L45

  2. Use AvailableItemName properties to ensure users can set the Build Action of the XML files to your custom item type.
    https://github.com/antlr/antlrcs/blob/9ee43ed9486e55afcc1db06f9f0755658974f99f/AntlrBuildTask/Antlr3.targets#L75-L79

  3. If necessary, use an ItemDefinitionGroup element to define default properties for objects with this custom item type to reduce the amount of work users need to do to configure the build for the XML files.
    https://github.com/antlr/antlrcs/blob/9ee43ed9486e55afcc1db06f9f0755658974f99f/AntlrBuildTask/Antlr3.targets#L81-L88

  4. Pay very close attention to the way items are added to collections like _GeneratedCodeFiles, to ensure the Clean target properly cleans up your build.

  5. Use a .props file to specify default configuration elements. When you create a NuGet package for distribution, the .props file(s) will be included at the top of the user's project, and the .targets will be included at the bottom of the project.

2. Do not perform unnecessary validation during code generation

The C# compiler will inform you if the user made a mistake (e.g. referencing an item that does not exist in the project). By avoiding this analysis during the code generation step, the cyclic dependency described in the question is avoided.

3. Disable or uninstall ReSharper

ReSharper does not support extensions which generate code during the build. Despite the fact that the above instructions specifically follow the patterns established by XAML support for many years now, you will be stuck with the following choice:

  1. Leave ReSharper installed, but not have IntelliSense support for the generated code.
  2. Uninstall ReSharper, and let Visual Studio provide you with the same clean IntelliSense experience for your custom item type as you get with XAML files, starting from the very first moment you open your solution.

4. Examples

Here are two extensions that generate code during the build.

ANTLR 3:
https://github.com/antlr/antlrcs/blob/9ee43ed9486e55afcc1db06f9f0755658974f99f/AntlrBuildTask/Antlr3.props https://github.com/antlr/antlrcs/blob/9ee43ed9486e55afcc1db06f9f0755658974f99f/AntlrBuildTask/Antlr3.targets

ANTLR 4:
https://github.com/tunnelvisionlabs/antlr4cs/blob/9ee43ed9486e55afcc1db06f9f0755658974f99f/runtime/CSharp/Antlr4BuildTasks/Antlr4.v4.0.props https://github.com/tunnelvisionlabs/antlr4cs/blob/9ee43ed9486e55afcc1db06f9f0755658974f99f/runtime/CSharp/Antlr4BuildTasks/Antlr4.v4.0.targets

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜