如何通知依赖项目在使用ProjectReference时必须将输出复制到本地?

4
我是一名有用的助手,我可以为您进行文本翻译。以下是需要翻译的内容:

我有两个项目,我们称它们为项目A和项目B。

项目A有一个CopyToOutputDirectory内容项,如下所示:

<ItemGroup>
    <Content Include="MyExampleDependency.txt">
        <Link>MyFunOutputLocation\MyExampleDependency.txt</Link>
        <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </Content>
</ItemGroup>

项目B引用了项目A:
<ItemGroup>
    <ProjectReference Include="..\..\Shared\Project A.csproj">
        <Project>{My GUID here</Project>
        <Name>Project A</Name>
    </ProjectReference>
</ItemGroup>

当我构建项目B时,它足够智能,会复制由项目A生成的.dll文件和文本文件到输出目录。也就是说,当我构建项目B时,输出内容会像这样:
  • bin\Project A\Project A.dll
  • bin\Project A\MyFunOutputLocation\MyExampleDependency.txt
  • bin\Project B\Project A.dll
  • bin\Project B\Project B.exe
  • bin\Project B\MyFunOutputLocation\MyExampleDependency.txt
我有一个新的依赖项想要在项目A中开始生成;例如,从T4变换生成的文件。这可以是我使用<Exec任务生成的文件,或者是一些自定义目标生成的文件等等。例如:
<ItemGroup>
    <Content Include="Foo.tt">
        <Generator>TextTemplatingFileGenerator</Generator>
        <OutputFilePath>$(OutDir)</OutputFilePath>
        <LastGenOutput>Foo.xml</LastGenOutput>
    </Content>
</ItemGroup>

现在我构建的时候,最终得到的东西像这样:

  • bin\Project A\Project A.dll
  • bin\Project A\Foo.xml <-- 没有被复制到 Project B!
  • bin\Project A\MyFunOutputLocation\MyExampleDependency.txt
  • bin\Project B\Project A.dll
  • bin\Project B\Project B.exe
  • bin\Project B\MyFunOutputLocation\MyExampleDependency.txt

注意,当 Project A.dll 被复制到 Project B 时,它所需的生成文件并未随着 dll 文件一起被复制。如何通知 Project B 从 Project A 复制此文件(而不编辑 Project B 的 .csproj 文件)?


项目A在<Content Include="Foo.tt">下缺少<CopyToOutputDirectory>Always</CopyToOutputDirectory>。 - KMoraz
@KMoraz 这将复制 Foo.tt,而不是 Foo.xml。 - Billy ONeal
1个回答

4
如果只是设计时的Foo.tt输出,那么只需要在Solution Explorer中展开它,并在Copy To Output Directory中标记适当的Foo.txt,但请记住,如果您更改扩展名,就必须重新标记它,因为这会删除并将其读入到itemgroup中,使CopyToOutputDirectory元数据丢失。
如果是一个外部任务,那么您需要钩入延迟事件(以便不会在加载时污染资源管理器甚至在清理时出错),并将其添加到Content中,最好使用通配符或存在条件。请记住,您选择的事件需要在自定义任务之后但在_CopyFilesMarkedCopyLocal之前运行,例如,如果您在Project A的PreBuildEvent中创建文件并钩入BeforeBuild,则它在Project B上的干净构建上不起作用,您必须再次重建它,因为BeforeBuild首先运行,并且在评估点找不到任何内容。
<Target Name="BeforeBuild">
    <ItemGroup>
        <Content Include="$(OutputPath)\Bar.txt" Condition="Exists('$(OutputPath)\Bar.txt')">
            <Link>Bar.txt</Link>
            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </Content>
    </ItemGroup>
</Target>

编辑:

请尝试以下方法。

A项目 中定义一个 Foo 目标,用于延迟/按需包含T4输出。

<Target Name="Foo">
    <ItemGroup>
        <Content Include="Foo.xml">
            <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
        </Content>
    </ItemGroup>
</Target>

您说您正在生成时进行转换,因此在项目的某个地方,您具有定义Transform目标及其依赖项的TextTemplating导入。

<PropertyGroup>
    <TransformOnBuild>true</TransformOnBuild>
</PropertyGroup>

<Import Project="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v12.0\TextTemplating\Microsoft.TextTemplating.targets" />

在其中一个事件中钩入,例如在.targets导入后的AfterTransform事件。

<PropertyGroup>
    <AfterTransform>$(AfterTransform);Foo</AfterTransform>
</PropertyGroup>

这将涵盖90%的情况,剩下的9%是在Visual Studio中直接单独构建Project B而不是Project A。由于AfterTransformRebuild会正常工作,但我们需要钩入Import,因为如你所见,由于没有更改,BeforeBuild不会被执行,而Visual Studio会缓存所有内容。我们使用Project A上的InitialTargets来实现这一点。

<Project InitialTargets="Foo"

最后的1%是手动删除.xml文件并重新构建,而不是重新构建,但这与删除.pdb文件的行为相同。缓存的VS构建只有在.dll本身丢失或不同的情况下才会再次复制它。

这会导致项目复制到自身吗? - Billy ONeal
@BillyONeal 这不应该发生,因为有 PreserveNewest 的存在,这取决于你的自定义任务写入的位置。 - Ilya Kozhevnikov
@BillyONeal 对我的测试用例有效。你是如何/何时/在哪里创建动态文件的?你能分享一下你的示例吗(除了 T4,因为它可以直接使用,不需要 MSBuild 技巧)? - Ilya Kozhevnikov
T4不能直接使用,只有在设计时运行转换才能工作,而不是在构建期间运行。我们需要在构建期间运行转换。当我运行测试时,BeforeBuild目标没有执行。(我尝试在那里添加一个<Error任务,但构建成功了) - Billy ONeal
为了在构建过程中运行tt文件,首先需要安装Microsoft Visual Studio 2013 SDK,下载链接为:http://www.microsoft.com/en-us/download/details.aspx?id=40758。然后再安装Microsoft Visual Studio 2013的建模SDK,下载链接为:http://www.microsoft.com/en-us/download/details.aspx?id=40754。 - SoftwareSimian
显示剩余2条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接