结束目标:
我希望有一个自定义构建任务,它接受我编译的程序集,并提取特定属性的所有实例以进行自动文档和卸载。 (在这种情况下,一组COM可见类型的GUID属性)。
问题:
在阅读了一些例子后,使用Inline Build Task的前景相当诱人。但是,我的任务需要反映构建的程序集并从中提取某些元数据(特别是属性)。
在程序集上反映的catch会锁定输出文件,直到卸载AppDomain,在这种情况下,只有在Visual Studio关闭时才会出现这种情况。结果:每次会话只能构建一次。
我看到存在特殊的构建任务类,即AppDomainIsolatedTask
,但我找不到任何示例或证据表明此类可用于内联任务。
问题:
是否可以在单独的AppDomain中运行内联构建任务?如果是这样,那怎么样?
代码示例:(尽可能短)
<UsingTask TaskName="InDomainTask" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<Task><Code Type="Class" Language="cs"><![CDATA[
public class InDomainTask : Microsoft.Build.Utilities.Task
{
public override bool Execute()
{
Log.LogMessage("InDomainTask AppDomain.Id = " + System.AppDomain.CurrentDomain.Id);
return true;
}
}
]]></Code></Task>
</UsingTask>
<UsingTask TaskName="OutDomainTask" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
<Task><Code Type="Class" Language="cs"><![CDATA[
[Microsoft.Build.Framework.LoadInSeparateAppDomain]
public class OutDomainTask : Microsoft.Build.Utilities.AppDomainIsolatedTask
{
public override bool Execute()
{
Log.LogMessage("OutDomainTask AppDomain.Id = " + System.AppDomain.CurrentDomain.Id);
return true;
}
}
]]></Code></Task>
</UsingTask>
<Target Name="AfterBuild" AfterTargets="Compile">
<InDomainTask />
<OutDomainTask />
</Target>
这些的构建输出是:
1> InDomainTask AppDomain.Id = 1 1> OutDomainTask AppDomain.Id = 1
答案 0 :(得分:1)
不,这是不可能的。
内联任务使用CodeTaskFactory
创建任务,如果您take a look at the code on GitHub,您将看到在编译包含代码的程序集后,任务的实例正在使用{{1创建}}。这意味着它始终与MSBuild在同一AppDomain中创建。
将其与pre-compiled tasks进行比较,后者使用TaskLoader
类创建任务实例,Activator.CreateInstance
在任务类型上查找TaskLoader
并创建如果找到了单独的AppDomain中的实例。
最简单的解决方案是将内联代码转换为预编译任务。这很容易做到:
LoadInSeparateAppDomainAttribute
。Microsoft.Build.Utilities
元素替换为指定包含任务的程序集的元素。以下是一个例子:
包含任务的类库:
UsingTask
您的MSBuild文件:
public class InDomainTask : Microsoft.Build.Utilities.Task
{
public override bool Execute()
{
Log.LogMessage(System.AppDomain.CurrentDomain.FriendlyName);
return true;
}
}
public class OutDomainTask : Microsoft.Build.Utilities.AppDomainIsolatedTask
{
public override bool Execute()
{
Log.LogMessage(System.AppDomain.CurrentDomain.FriendlyName);
return true;
}
}
输出:
<UsingTask TaskName="InDomainTask" AssemblyFile="path\to\the\class\library.dll"/>
<UsingTask TaskName="OutDomainTask" AssemblyFile="path\to\the\class\library.dll"/>
<Target Name="AfterBuild" AfterTargets="Compile">
<InDomainTask/>
<OutDomainTask/>
</Target>
答案 1 :(得分:0)
或者,您可以使用Exec
命令在单独的MsBuild.exe进程中运行作业,并传入相同的当前目标文件和所需的属性。
<Project ToolsVersion="15.0" DefaultTargets="RunCodeFromTargetPath" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<RunCodeFromTargetPathAfterTargets>PrepareForRun</RunCodeFromTargetPathAfterTargets>
</PropertyGroup>
<Target Name="RunCodeFromTargetPath" AfterTargets="$(RunCodeFromTargetPathAfterTargets)">
<PropertyGroup Condition="'$(PlatformTarget)' == 'x64'">
<MsBuildFolderLocation >\amd64</MsBuildFolderLocation><!--support for x64 only assemblies -->
</PropertyGroup>
<Exec Command=""$(MSBuildBinPath)$(MsBuildFolderLocation)\MSBuild.exe" /target:RunCodeFromTargetPathInternal /p:TargetPath="$(TargetPath.Replace('\','\\'))" "$(MSBuildThisFileFullPath)"" />
</Target>
<Target Name="RunCodeFromTargetPathInternal">
<RunCodeFromTargetPathTask />
</Target>
<UsingTask
TaskName="RunCodeFromTargetPathTask"
TaskFactory="CodeTaskFactory"
AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.Core.dll">
<Task>
<Reference Include="$(TargetPath)" />
<Code Type="Fragment" Language="cs">
<![CDATA[
// your code here
]]>
</Code>
</Task>
</UsingTask>
</Project>