bitshift
bitshift

Reputation: 6852

How to copy and rename file to output folder as part of build

I believe this is more of an msbuild-related question. Have a .net core app and I need to conditionally publish a file and based on the build config selected in Visual Studio 2019, the file should be renamed before publishing to the target.

So Im looking at modifying the csproj file (which is nothing but an msbuild file itself) I dont see a condition option on the copy task https://learn.microsoft.com/en-us/visualstudio/msbuild/copy-task?view=vs-2019

The goal Im after, is if I have 3 different files
tester-notes.dev.json tester-notes.debug.json tester-notes.prod.json

If prod is selected as a build config, I want the file published to be tester-notes.prod.json, but renamed to tester-notes.json

Upvotes: 6

Views: 7479

Answers (3)

Timothy Bruce
Timothy Bruce

Reputation: 21

I have something a bit better here. There are conditions where running a "ren" in a PostBuildEvent can error out, and thus break your build for reasons which shouldn't break your build. So there is an improved solution which is 100% msbuild, w/o shelling out to any console commands.

We set up a pair of tasks. The first executes after a Clean operation and the second executes after a Build operation. For the post-Clean we gun down our renamed file. This is because it won't be eliminated by default, because it's not considered part of the output by msbuild. The post-Build event is what renames the file. Both of these are cross-platform and 100% msbuild.

Two important things to notice here is we skip these when the $(OutDir) is empty. There is an execution where the $(OutDir) is empty, and that will cause both the post-Clean and post-Build to break. But we can't simply ContinueOnError="true" because that swallow all errors. We would prefer the build does break if there is actually something wrong. So there's a condition on each, set to skip the step if $(OutDir) is empty.

This works on all platforms and, as far as I've been able to demonstrate in my own experiments, is perfectly reliable in all build cases, scenarios, and operations.

<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
    <ItemGroup>
        <Content Include="tester-notes.$(Configuration).json"
            CopyToOutputDirectory="Always"
        />
    </ItemGroup>

    <Target Name="RmTesterNotes"
        AfterTargets="Clean"
        Condition=" '$(OutDir)' != '' "
    >
        <Delete ContinueOnError="false"
            Files="$(OutDir)\tester-notes.json"
        />
    </Target>
    <Target Name="MvTesterNotes"
        AfterTargets="Build"
        Condition=" '$(OutDir)' != '' "
    >
        <Move ContinueOnError="false"
            SourceFiles="$(OutDir)\tester-notes.$(Configuration).json"
            DestinationFiles="$(OutDir)\tester-notes.json"
        />
    </Target>
</Project>

Enjoy!

Upvotes: 0

Timothy Bruce
Timothy Bruce

Reputation: 21

You'll have to manually edit your .*proj file. It works best if you rename the files to match the build Configuration (ex: tester-notes.Debug.json, tester-notes.Release.json), for reasons which are about to become obvious. You can use the $(Configuration) pseudovariable in the .*proj file to reference a specific resource, such as the .json of interest. You can declare it a Content file and make sure you always copy it to the output (build) directory. Once there, you can use a post-build event to rename it.

The following is just the bare-bones of a .*proj file which includes a tester json file dependent on the build configuration and renames it to the universal/generic name.

<?xml version="1.0" encoding="utf-8"?>
<Project Sdk="Microsoft.NET.Sdk">
    <ItemGroup>
        <Content Include="tester-notes.$(Configuration).json" CopyToOutputDirectory="Always" />
    </ItemGroup>
    <PropertyGroup>
        <PreBuildEvent>
        </PreBuildEvent>
        <PostBuildEvent>
            ren tester-notes.$(Configuration).json tester-notes.json
        </PostBuildEvent>
    </PropertyGroup>
</Project>

Enjoy!

Upvotes: 1

LoLance
LoLance

Reputation: 28196

Assuming you have three files(build action = None) in Solution Explorer when developing:

enter image description here

You can use something similar to this script to rename and copy to publish folder if you're using FileSystem publish mode:

<ItemGroup Condition="$(Configuration)=='Dev'">
    <FileToRename Include="$(ProjectDir)\tester-notes.dev.json" />
  </ItemGroup>
  <ItemGroup Condition="$(Configuration)=='Debug'">
    <FileToRename Include="$(ProjectDir)\tester-notes.debug.json" />
  </ItemGroup>
  <ItemGroup Condition="$(Configuration)=='Prof'">
    <FileToRename Include="$(ProjectDir)\tester-notes.prof.json" />
  </ItemGroup>

  <Target Name="DoSthAfterPublish1" AfterTargets="Publish" Condition="$(Configuration)=='Dev'">
    <Copy SourceFiles="@(FileToRename)" DestinationFiles="@(FileToRename->Replace('.dev.json','.json'))"/>
    <Move SourceFiles="$(ProjectDir)\tester-notes.json" DestinationFolder="$(PublishDir)" OverwriteReadOnlyFiles="true"/>
  </Target>

  <Target Name="DoSthAfterPublish2" AfterTargets="Publish" Condition="$(Configuration)=='Debug'">
    <Copy SourceFiles="@(FileToRename)" DestinationFiles="@(FileToRename->Replace('.debug.json','.json'))"/>
    <Move SourceFiles="$(ProjectDir)\tester-notes.json" DestinationFolder="$(PublishDir)" OverwriteReadOnlyFiles="true"/>
  </Target>

  <Target Name="DoSthAfterPublish3" AfterTargets="Publish" Condition="$(Configuration)=='Prof'">
    <Copy SourceFiles="@(FileToRename)" DestinationFiles="@(FileToRename->Replace('.prof.json','.json'))"/>
    <Move SourceFiles="$(ProjectDir)\tester-notes.json" DestinationFolder="$(PublishDir)" OverwriteReadOnlyFiles="true"/>
  </Target>

And if you can reset tester-notes.debug.json to tester-notes.Debug.json,, then we may combine the three targets into one by using DestinationFiles="@(FileToRename->Replace('.$(Configuration).json','.json'))". Hope it makes some help :)

In addition:

According to the Intellisense we can find the Copy task supports Condition:

enter image description here

Upvotes: 9

Related Questions