自定义 MSBuild 任务依赖于已编译的代码,但也生成依赖于的代码
我正在尝试生成一个 C# 代码文件,该文件根据 XML 文件构造对象树。 XML 中的元素引用正在编译的项目中定义的类型。生成的代码需要构建在正在编译的项目中找到的类型的对象树。更复杂的是,生成的代码需要编译到正在编译的项目中,并由当前正在编译的项目中的代码引用。
我目前正在尝试使用自定义 MSBuild 任务来执行此操作。该任务将分发给其他开发人员以在他们自己的项目中使用,因此我无法将可用类型硬编码到任务中。
下面是一些示例代码来说明我正在做的事情:
// This is not generated
public class SomeClass {
public void DoSomething() {}
public string SomeProperty { get; set; }
}
SomeClass 是在项目中的非生成代码中定义的类型。以下是引用 SomeClass 并填充 SomeProperty 属性的 XML 文件:
... bunch of XML...
<SomeClass SomeProperty="SomeValue" />
... bunch more XML...
SomeOtherClass 有一个未生成的部分声明:
// This is not generated
public partial class SomeOtherClass {
public void SomeMethod() {
someField.DoSomething();
}
}
任务创建一个部分声明SomeOtherClass 声明:
// This is generated
public partial class SomeOtherClass {
private SomeClass someField = new SomeClass() {
SomeProperty = "SomeValue"
};
}
我认为这看起来很像 WPF 对 XAML 文件所做的事情,并且我的用法是相关的(使用 XML 生成实例化对象树的代码,基于同一对象树中的类型)项目是编译)。
解决这个问题的最佳方法是什么?我应该在任务中进行多阶段编译并使用中间文件吗?我是否应该尝试在没有生成代码的情况下在任务中单独构建临时程序集,然后对临时程序集进行反射,然后生成代码文件,然后允许正常的构建过程继续?
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 partial 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?
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
这个问题太模糊,无法确定这是否有帮助,但希望这会有所帮助。
1. 确保您的 .props 和 .targets 文件正确
配置
LoadTimeSensitiveTargets
和LoadTimeSensitiveProperties
以确保 IntelliSense 在项目构建之前就可以正常工作。https://github.com/antlr/antlrcs/blob/9ee43ed9486e55afcc1db06f9f0755658974f99f/AntlrBuildTask/Antlr3.targets#L36-L45
使用
AvailableItemName
属性确保用户可以设置XML 的构建操作文件到您的自定义项目类型。https://github.com/antlr/antlrcs/blob/9ee43ed9486e55afcc1db06f9f0755658974f99f/AntlrBuildTask/Antlr3.targets#L75-L79
如有必要,请使用
ItemDefinitionGroup
元素为具有此自定义项目类型的对象定义默认属性,以减少用户需要执行的工作来配置 XML 文件的构建。https://github.com/antlr/antlrcs/blob/9ee43ed9486e55afcc1db06f9f0755658974f99f/AntlrBuildTask/Antlr3.targets#L81-L88
密切关注方式项目添加到集合中,例如
_GenerateCodeFiles
,以确保Clean目标正确清理您的构建。使用 .props 文件指定默认配置元素。当您创建用于分发的 NuGet 包时,.props 文件将包含在用户项目的顶部,而 .targets 将包含在项目底部。
2. 在代码生成过程中不要执行不必要的验证
如果用户犯了错误(例如引用了项目中不存在的项),C# 编译器会通知您。通过在代码生成步骤期间避免这种分析,可以避免问题中描述的循环依赖关系。
3. 禁用或卸载ReSharper
ReSharper 不支持在构建期间生成代码的扩展。尽管上述说明特别遵循了多年来 XAML 支持建立的模式,但您仍将面临以下选择:
4. 示例
以下是在构建期间生成代码的两个扩展。
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
The question is too vague to be sure this is helpful, but hopefully this helps.
1. Get your .props and .targets files correct
Configure the
LoadTimeSensitiveTargets
andLoadTimeSensitiveProperties
to make sure IntelliSense will work even before the project is built.https://github.com/antlr/antlrcs/blob/9ee43ed9486e55afcc1db06f9f0755658974f99f/AntlrBuildTask/Antlr3.targets#L36-L45
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
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
Pay very close attention to the way items are added to collections like
_GeneratedCodeFiles
, to ensure the Clean target properly cleans up your build.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:
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