MSBuild - How to copy files based on condition - visual-studio

Using MSBuild how do i copy a file based on configuration of the build i.e. if the build configuration is Staging vs Release. I need to be able to copy FolderA\FileA if the build configuration is Staging and FolderB\FileB for production. Also the file when it gets copied should be at the root of the output directory instead in a folder as FolderA or FolderB. Using task is throwing errors during project load.

MSBuild - How to copy files based on condition
You can use Condition=" '$(Configuration)' == 'xxx' " for your copy task in the project file.
To accomplish this, unload your project. Then at the very end of the project, just before the end-tag </Project>, place below scripts:
<ItemGroup>
<MySourceFilesA Include="FolderA\FileA.txt" />
<MySourceFilesB Include="FolderB\FileB.txt" />
</ItemGroup>
<Target Name="CopyFiles" AfterTargets="Build">
<Copy SourceFiles="#(MySourceFilesA)" DestinationFolder="$(OutDir)" Condition=" '$(Configuration)' == 'Staging' " />
<Copy SourceFiles="#(MySourceFilesB)" DestinationFolder="$(OutDir)" Condition=" '$(Configuration)' == 'Release' " />
</Target>
You can modify the paht of SourceFiles and DestinationFolder according to your needs, just make sure the path is correct.
Hope this helps.

Related

Different `google-services.json/GoogleService-Info.plist` for iOS/Android project based on build configuration in Xamarin

So I have a requirement where my project should use different GoogleServices files for Android/iOS while using different configurations like for eg while I am using the debug configuration it should use the debug version of the file and in the release, it should use the release version.
Something similar to
Xamarin firebase different google-services,json for different build configurations
When I follow the accepted the answer I get a compile-time error saying
The command COPY /Y "$(ProjectDir)GoogleServices\google-services-development.json" "$(ProjectDir)google-services.json" exited with code 1.
I tried clean build and cleaning bin/obj nothing changed.
So I tried the other solution mentioned here and what happens is the GoogleServices files(all of them) are excluded from the project and nothing happens if I build and run. I am unsure if this is even working or not.
I have added the following lines in my csproj for release and debug respectively
<ItemGroup Condition="'$(Configuration)'=='Debug'">
<GoogleServicesJson Include="Dev\google-services.json">
<Link>google-services.json</Link>
</GoogleServicesJson>
</ItemGroup>
<ItemGroup Condition="'$(Configuration)'=='Release'">
<GoogleServicesJson Include="Prod\google-services.json">
<Link>google-services.json</Link>
</GoogleServicesJson>
</ItemGroup>
Where dev and prod are root folders in my native android project
Any suggestions are welcome.
You have to edit *.csproj file.
Using a solution to use multiple Info.plist (LogicalName tag) and Condition tag you can play with any other files all you want.
For Android I added two *.json files to Resources folder and added this snippet to my *.csproj file:
<ItemGroup Condition=" '$(Configuration)' != 'Release' ">
<GoogleServicesJson Include="Resources\dev-google-services.json">
<LogicalName>Resources\google-services.json</LogicalName>
</GoogleServicesJson>
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)' == 'Release' ">
<GoogleServicesJson Include="Resources\release-google-services.json">
<LogicalName>Resources\google-services.json</LogicalName>
</GoogleServicesJson>
</ItemGroup>
In this example I use release-google-services.json for the "Release" build configuration, and dev-google-services.json for any other configurations.
Same for iOS. I added two *.plist files to root folder and added this snippet to my *.csproj file:
<ItemGroup Condition=" '$(Configuration)' != 'AppStore' ">
<BundleResource Include="Dev-GoogleService-Info.plist">
<LogicalName>GoogleService-Info.plist</LogicalName>
</BundleResource>
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)' == 'AppStore' ">
<BundleResource Include="Release-GoogleService-Info.plist">
<LogicalName>GoogleService-Info.plist</LogicalName>
</BundleResource>
</ItemGroup>
This approach works for me. I guess it doesn't matter where you put these files and how you name them. Just use the LogicalName that you need.
Also, you can combine it with other variables to compose more complicated conditions. For example, in order to build two *.apk in Release configuration with different *.json files you can:
<ItemGroup Condition=" '$(Configuration)|$(DynamicConstants)' != 'Release|' ">
<GoogleServicesJson Include="Resources\dev-google-services.json">
<LogicalName>Resources\google-services.json</LogicalName>
</GoogleServicesJson>
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)|$(DynamicConstants)' == 'Release|' ">
<GoogleServicesJson Include="Resources\release-google-services.json">
<LogicalName>Resources\google-services.json</LogicalName>
</GoogleServicesJson>
</ItemGroup>
Build your project like this:
msbuild MobileApp.sln /p:Configuration=Release /p:DynamicConstants=DEBUG
When you use DEBUG parameter you build Release apk with dev-google-services.json.
When you omit DEBUG parameter you build Release apk with release-google-services.json.

TFS mapping root path

I'm having the next problem:
I am trying to invoke a custom msbuild task from a .csproj file and I need to set a parameter (something like $(ProjectDir) ) indicating the TFS path that the project has mapped.
I don't know if it's possible or not; I couldn't find any macro in this link:
http://msdn.microsoft.com/en-us/library/c02as0cs(v=vs.80).aspx
Just in case, this is the invocation from the .csproj file to my custom task:
<UsingTask TaskName="MyTask"
AssemblyFile="MyTask.dll" />
<Target Name="AfterBuild" Condition=" '$(Configuration)' == 'Test' ">
<DbBuildTask ConfigFile="$(ProjectDir)config\properties.config"
XPath="/configuration/properties/nhConnectionString"
PathTfs="........" />
</Target>
Any ideas?
Thanks in advance!

Copy DLL files to bin directory after one-click publishing in VS2010

I have a web application in VS2010 which has a number of DLLs that need to be copied into the bin directory after doing a publish in VS2010.
I've tried putting the following into my .csproj file (which sits in the root folder of the web applications) but it doesn't seem to work:
<Target Name="AfterBuild">
<ItemGroup>
<_CircularDependencies Include="DLLs\Circular\Dependencies\*.dll" />
</ItemGroup>
<Copy
SourceFiles="#(_CircularDependencies)"
DestinationFiles="#(_CircularDependencies->'bin\%(Filename)%(Extension)')"
SkipUnchangedFiles="true" />
</Target>
For bonus points, I have another set of DLLs copied to be copied post-publish, but I want to use one set when doing a debug publish (for Win32) and a different set when doing a release publish (x86).
Thanks!
OK, I've managed to get this working fully. Thanks to the answers provided above, I've been able to add some MS Build commands to the .csproj file to copy the appropriate DLLs from various folders into the bin folder based on the current build configuration. However as these are unmanaged DLLs (i.e. not .NET) I can't create normal references to them and they fail to be copied during the publish. I got around this by dynamically adding the files to the project as 'content'.
The solution came in three parts. Firstly, create an item group for the files near the top of the .csproj file (I've tried to use generic filenames here to make it clearer), with conditions based on the current build configuration:
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
<_UnmanagedDLLs Include="Win32DLLs\*.dll" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<_UnmanagedDLLs Include="x64DLLs\*.dll" />
</ItemGroup>
Then add another item group to include these DLLs (as content, not references) in the build:
<ItemGroup>
<Content Include="#(_UnmanagedDLLs->'bin\%(Filename)%(Extension)')" />
</ItemGroup>
Finally, at the bottom of the .csproj file, I do the copy on the AfterBuild target:
<Target Name="AfterBuild">
<Copy SourceFiles="#(_UnmanagedDLLs)" DestinationFiles="#(_UnmanagedDLLs->'bin\%(Filename)%(Extension)')" SkipUnchangedFiles="true" />
</Target>
It means I can do a debug publish for my windows 32 staging box and a release publish for my x64 production box while keeping my bin folder out of SVN.
Once you get the copy working, separate sets for debug/release is easy with a condition:
<ItemGroup Condition="'$(Configuration)' == 'Release'">
<_CircularDependencies Include="DLLs\Circular\Dependencies\*.dll" />
</ItemGroup>
<ItemGroup Condition="'$(Configuration)' == 'Debug'">
<_CircularDependencies Include="DebugDLLs\Circular\Dependencies\*.dll" />
<_CircularDependencies Include="DebugDLLs\Circular\Dependencies\*.pdb" />
</ItemGroup>
If you want your copy to happen after publish, not after build you need to change your target from:
<Target Name="AfterBuild">
to
<Target Name="AfterPublish">

Copy built assemblies (including PDB, .config and XML comment files) to folder post build

Is there a generic way I can get a post-build event to copy the built assembly, and any .config and any .xml comments files to a folder (usually solution relative) without having to write a post-build event on each project in a solution?
The goal is to have a folder that contains the last successful build of an entire solution.
It would be nice to use the same build solution over multiple solutions too, possibly enabling/ disabling certain projects (so don't copy unit tests etc).
Thanks,
Kieron
You can set common OutputPath to build all projects in Sln in one temp dir and copy required files to the latest build folder. In copy action you can set a filter to copy all dlls without "test" in its name.
msbuild.exe 1.sln /p:Configuration=Release;Platform=AnyCPU;OutputPath=..\latest-temp
There exists more complicated and more flexible solution. You can setup a hook for build process using CustomAfterMicrosoftCommonTargets. See this post for example.
Sample targets file can be like that:
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<BuildDependsOn>
$(BuildDependsOn);
PublishToLatest
</BuildDependsOn>
</PropertyGroup>
<Target Name="PreparePublishingToLatest">
<PropertyGroup>
<TargetAssembly>$(TargetPath)</TargetAssembly>
<TargetAssemblyPdb>$(TargetDir)$(TargetName).pdb</TargetAssemblyPdb>
<TargetAssemblyXml>$(TargetDir)$(TargetName).xml</TargetAssemblyXml>
<TargetAssemblyConfig>$(TargetDir)$(TargetName).config</TargetAssemblyConfig>
<TargetAssemblyManifest>$(TargetDir)$(TargetName).manifest</TargetAssemblyManifest>
<IsTestAssembly>$(TargetName.ToUpper().Contains("TEST"))</IsTestAssembly>
</PropertyGroup>
<ItemGroup>
<PublishToLatestFiles Include="$(TargetAssembly)" Condition="Exists('$(TargetAssembly)')" />
<PublishToLatestFiles Include="$(TargetAssemblyPdb)" Condition="Exists('$(TargetAssemblyPdb)')" />
<PublishToLatestFiles Include="$(TargetAssemblyXml)" Condition="Exists('$(TargetAssemblyXml)')" />
<PublishToLatestFiles Include="$(TargetAssemblyConfig)" Condition="Exists('$(TargetAssemblyConfig)')" />
<PublishToLatestFiles Include="$(TargetAssemblyManifest)" Condition="Exists('$(TargetAssemblyManifest)')" />
</ItemGroup>
</Target>
<Target Name="PublishToLatest"
Condition="Exists('$(LatestDir)') AND '$(IsTestAssembly)' == 'False' AND '#(PublishToLatestFiles)' != ''"
DependsOnTargets="PreparePublishingToLatest">
<Copy SourceFiles="#(PublishToLatestFiles)" DestinationFolder="$(LatestDir)" SkipUnchangedFiles="true" />
</Target>
</Project>
In that targets file you can specify any actions you want.
You can place it here "C:\Program Files\MSBuild\v4.0\Custom.After.Microsoft.Common.targets" or here "C:\Program Files\MSBuild\4.0\Microsoft.Common.targets\ImportAfter\PublishToLatest.targets".
And third variant is to add to every project you want to publish import of custom targets. See How to: Use the Same Target in Multiple Project Files

Is there such a thing as a "content/data only project" in visual studio

I have a bunch of ancillary XML and XSLT files that I want to edit and manage in visual studio.
The files do not logically belong under any code project in my solution and so in order to group them neatly, I have created a "dummy" C# dll project in visual studio and disabled it from building in Debug / release builds).
I wondered if there was a nicer way of achieving the same result (i.e. having all the files visible in solution explorer). What I think really want is a visual studio project type of "content only" but such a thing does not exist (or have I not looked hard enough?).
I have toyed with the idea of adding the files as solution items but then they seem harder to manage because creating a new "solution item folder" does not actually create a folder on disk.
Any one have any ideas?
Visual Studio 2015 has a project type called "Shared Project" which is essentially a content only project with no targets. It's listed under Visual C# but it can be used for any files.
A work colleague has come up with a solution.
He has suggested hand editing the project to remove the DefaultTargets from the Project (and delete a load of now unused properties).
MSBuild complains if there are no targets in the project so he has added three empty targets.
The final project looks something like this
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="3.5" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProductVersion>9.0.30729</ProductVersion>
<SchemaVersion>2.0</SchemaVersion>
<ProjectGuid>{541463A7-7CFA-4F62-B839-6367178B16BD}</ProjectGuid>
</PropertyGroup>
<ItemGroup>
... files ...
</ItemGroup>
<ItemGroup>
... files ...
</ItemGroup>
<Target Name="Build"/>
<Target Name="Rebuild"/>
<Target Name="Clean"/>
</Project>
Admittedly, this solution requires more fiddling that I would have liked but seems to achieve what I was after: namely a project that does not aattempt to produce any build output.
Andy posted a link with a solution that's mostly worked for me; basically delete the following line from the project file:
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
and add the following lines:
<Target Name="Build">
<Copy
SourceFiles="#(Content)"
DestinationFiles="#(Content->'$(OutputPath)%(RelativeDir)%(Filename)%(Extension)')" />
</Target>
<Target Name="Clean">
<Exec Command="rd /s /q $(OutputPath)" Condition="Exists($(OutputPath))" />
</Target>
<Target Name="Rebuild" DependsOnTargets="Clean;Build">
</Target>
I also found that disabling the project Debug property "Enable the Visual Studio hosting process" (for each configuration) prevented the MyProject.vshost.exe file from being generated.
As David I. McIntosh pointed out in a comment on this answer, if your project is part of a solution with multiple projects and any other projects use the same output path as the content-only project, the above Clean target will delete all of the files in the output path, i.e. the build output of other projects, and would thus only be correct if the content-only project is the first project built (among those sharing the same build output path). The following is a safer and friendlier Clean target for this scenario:
<Target Name="Clean">
<Delete Files="#(Content->'$(OutputPath)%(RelativeDir)%(Filename)%(E‌​xtension)')"/>
</Target>
Then, try creating a Blank solution. Create Empty project. Have your files in respective folders with in the solution folder. From property window, use the Show all files, include those folders into the project. There is no better solution other then this. I hope.
This answer is just a convenient consolidation of the answers above given by Chris Fewtrell and Kenny Evitt, along with the slight modification in my comments above, and a bit more detail on what the declaration of the content items should/could look like:
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="12.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{541463A7-7CFA-4F62-B839-6367178B16BD}</ProjectGuid>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == '64-bit|AnyCPU'">
<PlatformTarget>x64</PlatformTarget>
<OutputPath>..\builds\$(Configuration)\</OutputPath>
<IntermediateOutputPath>..\builds\$(Configuration)\Intermediate\YourProjectName\</IntermediateOutputPath>
</PropertyGroup>
<ItemGroup>
<Content Include="fileInProjectFolder.csv" />
<Content Include="SubDir\fileInSubdir.txt" />
<Content Include="..\actualSourceDirectoryOfFile\app.log.basic.config">
<Link>targetSubdirInOutputDir\app.log.basic.config</Link>
</Content>
<Content Include="..\actualSourceDirectoryOfFile\yetAnotherFile.config">
<Link>yetAnotherFile.config</Link>
</Content>
... more files ...
</ItemGroup>
<Target Name="Build">
<Copy
SourceFiles="#(Content)"
DestinationFiles="#(Content->'$(OutputPath)%(RelativeDir)%(Filename)%(Extension)')" />
</Target>
<Target Name="Clean">
<Delete Files="#(Content->'$(OutputPath)%(RelativeDir)%(Filename)%(E‌​xtension)')"/>
</Target>
<Target Name="Rebuild" DependsOnTargets="Clean;Build">
</Target>
</Project>
Note that this always copies all the "content" files to the output directory - the options "Copy If Newer", "Copy Always" and "Do Not Copy", as presented in the visual studio GUI ( appears as, for example, <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory> in the .csproj file) are ignored.
In my situation, I needed to have a set of configuration files that would be common to many projects. To simply achieve this, I performed the following steps:
Create a Class Library project named "Configuration"
Delete all *.cs files from Configuration project
Put configuration files in a "Configuration" folder in the Configuration project
Copy configuration files to required projects in the post-build event. In Configuration project's Properties > Build Events > Post-build event:
xcopy "$(TargetDir)Configuration\*" "$(SolutionDir)TARGET_PROJECT\$(OutDir)" /i /v /q /s /y
In the above, replace TARGET_PROJECT with your actual project
This will copy all the files in the Configurations folder to the output directory of the project that needs the configuration files (eg. MyProject/bin/Debug, etc).

Resources