jws305
jws305

Reputation: 115

How can I copy files between output directories with MSBuild?

I have a C# library which has a complex way of generating its output on build and I can not get the correct output to be copied to projects referencing it.

Solution structure

MySolution
├─ MyConsoleApp           (c# console)
│  └─ Program.cs
└─ MyNodeServices         (c# library)
   ├─ node_modules**
   ├─ NodeProgram
   │  ├─ build**
   │  │  ├─ tsCode.js**
   │  │  └─ tsCode.map**
   │  └─ src
   │     └─ tsCode.ts     (TypeScriptCompile)
   ├─ package.json
   ├─ tsconfig.json
   └─ CodeThatReferencesNodeProgram.cs

** indicates files are not included in project

On build, I want MyNodeServices to copy the NodeProgram folder entirely to the output directory (src as well as build) and then copy node_modules into the NodeProgram. This is going to be my execution context when using NodeServices.

MyConsoleApp references MyNodeServices and I want the NodeProgram directory from MyNodeServices to be copied to the output directory of MyConsoleApp on build.

Constraints

What I have tried

Include NodeProgram as content

<TypeScriptCompile Include="NodeProgram\src\tsCode.ts">
<Content Include="$(ProjectDir)NodeProgram\**" />

This works for including the code in the output of MyConsoleApp but the .ts files are displayed twice in solution explorer. This also does not address node_modules.

After build copy

<Target Name="AfterBuild">
  <ItemGroup>
    <ProgramCode Include="$(ProjectDir)NodeProgram\**" />
    <NodeModules Include="$(ProjectDir)node_modules\**" />
  </ItemGroup>
  <MakeDir Directories="$(TargetDir)\NodeProgram" Condition=" !Exists('$(TargetDir\NodeProgram') " />
  <MakeDir Directories="$(TargetDir)\NodeProgram\node_modules" Condition=" !Exists('$(TargetDir\NodeProgram\node_modules') " />
  <Copy SourceFiles="@(ProgramCode)" DestinationFiles="@(ProgramCode->'$(TargetDir)\NodeProgram\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="true" />
  <Copy SourceFiles="@(NodeModules)" DestinationFiles="@(NodeModules->'$(TargetDir)\NodeProgram\node_modules\%(RecursiveDir)%(Filename)%(Extension)')" SkipUnchangedFiles="true" />
</Target>

This keeps my solution clean and copies the correct files to the output directory but the files seem to be ignored after that. If I clean the solution the copied folder remains in the output directory and the contents are not copied to referencing projects.

Questions

Upvotes: 3

Views: 1447

Answers (1)

jws305
jws305

Reputation: 115

I came up with a solution that seems to solve my problems. I have added the following to the project file of MyNodeServices

<!-- Specify the files to be included in the output directory -->
<ItemGroup>
  <NodeFile Include="NodePrograms\**">
    <InProject>false</InProject>
  </NodeFile>

  <NodeModule Include="node_modules\**">
    <InProject>false</InProject>
  </NodeModule>
</ItemGroup>


<!-- Copy files from the project directory into the output directory -->
<Target Name="BeforeBuild"
        Inputs="@(NodeFile);@(NodeModules)"
        Outputs="@(NodeFile->'$(OutputPath)%(RelativeDir)%(Filename)%(Extension)');@(NodeModule->'$(OutputPath)NodePrograms\%(RelativeDir)%(Filename)%(Extension)')">

  <Copy SourceFiles="@(NodeFile)"
        DestinationFiles="$(OutputPath)%(NodeFile.RelativeDir)%(NodeFile.Filename)%(NodeFile.Extension)" />

  <Copy SourceFiles="@(NodeModule)"
        DestinationFiles="$(OutputPath)NodePrograms\%(NodeModule.RelativeDir)%(NodeModule.Filename)%(NodeModule.Extension)" />

  <ItemGroup>
    <!-- Specify files that were written to the output directory so the clean task knows to remove them -->
    <FileWrites Include="@(NodeFile->'$(OutputPath)%(RelativeDir)%(Filename)%(Extension)')"/>
    <FileWrites Include="@(NodeModule->'$(OutputPath)NodePrograms\%(RelativeDir)%(Filename)%(Extension)')"/>
  </ItemGroup>

</Target>


<!-- Include the files from the output directory when other projects reference this -->
<Target Name="IncludeNodeFiles"
        BeforeTargets="GetCopyToOutputDirectoryItems">

  <ItemGroup>
    <OutputNodeFiles Include="@(NodeFile->'$(OutputPath)%(RelativeDir)%(Filename)%(Extension)')">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <TargetPath>%(RelativeDir)%(FileName)%(Extension)</TargetPath>
    </OutputNodeFiles>

    <OutputNodeModules Include="@(NodeModule->'$(OutputPath)NodePrograms\%(RelativeDir)%(Filename)%(Extension)')">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
      <TargetPath>NodePrograms\%(RelativeDir)%(FileName)%(Extension)</TargetPath>
    </OutputNodeModules>

    <AllItemsFullPathWithTargetPath Include="@(OutputNodeFiles->'%(FullPath)')"  />
    <AllItemsFullPathWithTargetPath Include="@(OutputNodeModules->'%(FullPath)')"  />
  </ItemGroup>
</Target>

Upvotes: 3

Related Questions