Add folder from solution to C# .NET project Content output - msbuild

I want to include files from the solution directory, regardless of where the files are. I added this to my .csproj file:
<ItemGroup>
<Content Include="$(SolutionDir)\web\**\*" />
</ItemGroup>
But the files do not appear in the solution explorer. And they do not appear in the publish output either.
How to do this correctly?

You still need to tell MSBuild to copy the content, for example
<ItemGroup>
<Content Include="$(SolutionDir)\web\**\*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>

Related

csproj include (recursive) folders based on a list (batching)

I want to add some files recursively to a single project that are located in folders inside my solution root.
Right now, I found a working solution to add those files by specifying each folder manually:
<ItemGroup>
<Content Include="..\Folder1\**\*">
<Link>Folder1\%(RecursiveDir)\%(Filename)%(Extension)</Link>
</Content>
<Content Include="..\Folder2\**\*">
<Link>Folder2\%(RecursiveDir)\%(Filename)%(Extension)</Link>
</Content>
<Content Include="..\Folder3\**\*">
<Link>Folder3\%(RecursiveDir)\%(Filename)%(Extension)</Link>
</Content>
</ItemGroup>
As those can be a handfull folders and they can be different depending on my solution, I want to have an easy list to define the folder names.
Something like this:
<PropertyGroup>
<DefinedResourceFolders>Folder1;Folder2;Folder3</DefinedResourceFolders>
</PropertyGroup>
This would also allow me to input this as a property directly when calling msbuild to extend the files that are going to the build output, maybe.
I tried to look into the MSBuild Batching documentation and several other online sources, but I could not figure it out. Most batching examples I found were working with Targets, not Includes into the solution items.
Is this possible? How would I define the <ItemGroup> for the content then?
P.S.: I don't care about wildcard issues with .csproj files when new files are added, etc. This will either be a single "Resources" project only containing those displayed files, or I am using my external .props file that I am importing into each .csproj file anyway.
Suppose you want to include all files in folders root/Folder1, root/Folder2 and root/Folder3 recursively.
This is how the builds (both VS and CLI) do what you want. However, VS will not show the files as part of the project.
<Project Sdk="Microsoft.NET.Sdk" InitialTargets="__AddBatchedContent;$(InitialTargets)">
<!-- the usual properties -->
<!-- You could of course define the folder array directly in an Item,
but this is what you wanted :-) -->
<PropertyGroup>
<Lookups>Folder1;Folder2;Folder3</Lookups>
</PropertyGroup>
<ItemGroup>
<LookupDir Include="$(Lookups)" />
</ItemGroup>
<Target Name="__AddBatchedContent">
<ItemGroup>
<!-- save the original source folder of the globbed file name in custom
metadata "Folder" so we can use it later as its output base folder -->
<__BatchedFiles Include="..\%(LookupDir.Identity)\**\*" Folder="%(LookupDir.Identity)" />
<Content Include="#(__BatchedFiles)" Link="%(Folder)\%(RecursiveDir)\%(Filename)%(Extension)" CopyToOutputDirectory="Always" />
</ItemGroup>
</Target>
</Project>
Note that if you want to put this in a .targets file to use this technique in multiple projects, you should replace the ..\ with $(MSBuildThisFileDirectory)..\ so that the path is relative to the (known) location of the .targets file instead of the (unknown) location of the importing project.
Thanks to #Ilya Kozhevnikov for the basis of this answer.
You are not the only one who would like this to be simpler :-): https://github.com/dotnet/msbuild/issues/3274

ASP.NET Core 6: copy external directory to the publish folder

How to copy the content of external directory to the publish folder when I do dotnet publish ?
I tried the following in the project config, but unfortunately this copies the content of documents-templates directory to the root of the publish directory, but I want the whole folder documents-templates to be copied with its contents.
Is there any way to set the destination folder name too ?
<ItemGroup>
<Content Include="D:\Workspace\OtherProject\documents-templates\**">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
</ItemGroup>
I have tested in my local, and it works for me.
<ItemGroup>
<Parent Include="C:\Users\Admin\Desktop\Testimages\*.*" />
<EmbeddedResource Include="#(Parent)">
<Link>Resources\%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>

NuGet Package Packing - Is it possible to copy files to a custom directory?

I'm trying to package a few files into a NuGet package, but the issue is that all of the files are sent to the "content" folder within the NuGet package by default when packaged. Normally this is okay, but for the JSON files I have in "ABCJsons" I'd like them to be sent to "content/NewFolderName".
In my example below, the first block is my AbcToolTester, which has all of its project files files being successfully sent to the content directory in the NuGet package. The second block, is where I attempted to copy all the json files with ABCLibrary (ABCLibrary has subfolders where the actual Jsons are located) to the destination folder "ABCJsons". I thought this would do the trick, but unfortunately the ABCJson files just get sent to the content folder along with all the other files.
<ItemGroup>
<Content Include="..\AbcToolTester\bin\Debug\netcoreapp3.1\**" Exclude="..\AbcToolTester\bin\Debug\netcoreapp3.1\*.pdb">
<IncludeInPackage>true</IncludeInPackage>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</Content>
<ItemGroup>
<Target Name="CopyABCLibrary" AfterTargets="AfterBuild">
<ItemGroup>
<ABCJsonsInclude="..\..\tests\ABCLibrary\**\*.*"/>
</ItemGroup>
<Copy SourceFiles="#(ABCJsons)" DestinationFolder="$(TargetDir)\ABCJsons" SkipUnchangedFiles="true" />
</Target>
It's hard to tell if the NuGet package you are creating is actually dependent on AbcToolTester project because there are easier ways to package that. That's another question though.
For your actual issue, you can simplify the copying process while also telling it where to pack the files. Replace your CopyABCLibrary target with this:
<ItemGroup>
<Content Include="..\..\tests\ABCLibrary\**\*.*">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
<Pack>true</Pack>
<PackagePath>ABCJsons\%(RecursiveDir)</PackagePath>
<!-- This line hides the items from showing in the solution explorer -->
<Visible>false</Visible>
</Content>
</ItemGroup>
This will put all those files into the root of the nuget package into the ABCJsons folder and preserve the directory structure. Change the path accordingly to put it somewhere else.

Trying to include a transformed App.config in a XAP file

I have a Silverlight project with multiple configuration files, and am using the transformation approach shown here:
App.Config Transformation for projects which are not Web Projects in Visual Studio 2010?
This approach doesn't work as-is for Silverlight projects though. I've re-written the MSBuild project to look like this:
<ItemGroup>
<None Include="App.config" />
<None Include="App.QABuild.config">
<DependentUpon>App.config</DependentUpon>
</None>
</ItemGroup>
....
<UsingTask TaskName="TransformXml" AssemblyFile="$(MSBuildExtensionsPath)\Microsoft\VisualStudio\v10.0\Web\Microsoft.Web.Publishing.Tasks.dll" />
<Target Name="BeforeCompile" Condition="Exists('App.$(Configuration).config')">
<!-- Generate transformed app config in the output directory -->
<Message Importance="high" Text="Transforming 'App.$(Configuration).config' to output config file..." />
<TransformXml Source="App.config" Destination="$(OutputPath)App.config" Transform="App.$(Configuration).config" />
<ItemGroup>
<Content Include="$(OutputPath)App.config" />
</ItemGroup>
</Target>
<Target Name="BeforeCompile" Condition="!Exists('App.$(Configuration).config')">
<Message Importance="high" Text="Using default 'App.config' as output config file..." />
<Copy SourceFiles="App.config" DestinationFiles="$(OutputPath)App.config" />
<ItemGroup>
<Content Include="$(OutputPath)App.config" />
</ItemGroup>
</Target>
This code generates the correct output file for the correct configuration, however it is never included in the XAP file, even though I am putting the output config into the Content item group. All I need to happen is for the output config to get included in the output XAP but I can't get this to happen.
Can anyone tell me what I'm doing wrong? I'm not an MSBuild expert by any means!
Found the solution by digging into the Silverlight 4 targets. Turns out the XAP packager target actually takes an item called XapFilesInputCollection, which is where the input files come from. The Content item looks likes it is copied to this item before my target runs, so modifying the Content item afterwards is the wrong approach.
All I did was add the transformed files directly to the XapFilesInputCollection item and it worked as I expected.

Can I use both wildcard and Link element inside the Compile element?

In the .csrpoj file, If I have
<Compile Include="c:\path\File1.cs">
<Link>Dir1\File1.cs</Link>
</Compile>
Then Visual Studio shows that file as a shortcut under Dir1 folder in the Solution Explorer.
If I have
<Compile Include="c:\path\*.cs"></Compile>
Then all .cs files show up as shortcuts in Solution Explorer at top level:
Is there a way to include all files in some folder and make then show up under a sub-folder? Omitting the filename in Link element does not work:
<Compile Include="c:\path\*.cs">
<Link>Dir1\</Link>
</Compile>
The files still show up at top level.
How do I include all files in a folder and still use the Link element? The reason I need this is, I need to include files from multiple folders and some of them have the same name. Two files at top level cannot have the same name.
Any other way to achieve this?
Others have suggested the using the Link attribute with placeholders, which indeed works. However, Microsoft has implemented a new attribute (which isn't in any of my code completion suggestions), named LinkBase, shown below.
<ItemGroup>
<Compile Include="..\SomeDirectory\*.cs" LinkBase="SomeDirectoryOfYourChoosing" />
</ItemGroup>
Sources:
https://github.com/Microsoft/msbuild/issues/2795
https://github.com/dotnet/sdk/pull/1246
Link Additional Files in Visual Studio
<Content Include="..\..\SomeDirectory\**\*.xml">
<Link>SomeLinkDirectoryOfYourChoosing\%(Filename)%(Extension)</Link>
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</Content>
For the sake of others, here's the answer plus the comment from Dean and Miserable Variable, which I found useful:
I have two projects, and I need to include *.xsd from one in the other, without copying the files or having to update the referencing csproj file every time a new XSD is added to the first.
The solution was to add the following to the csproj file
<Content Include="..\BusinessLayer\Schemas\*.xsd">
<Link>Contract\Schemas\xxx.xsd</Link>
</Content>
Note xxx.xsd, you have to give a dummy filename in the Link element. It just gets replaced.
Also, you can include all sub folders with:
<Content Include="..\BusinessLayer\Schemas\**\*.xsd">
<Link>Contract\Schemas\ThisTextDoesntMatter</Link>
</Content>
And files of all type (useful for pulling in CSS/JS/Style folders from 3rd parties) with:
<Content Include="..\PresentationLayer\CustomerStyle\**\*.*">
<Link>CustomerStyle\placeHolder</Link>
</Content>
To include subfolders:
<ItemGroup>
<Compile Include="..\SomeExternalFolder\**\*.cs" LinkBase="YourProjectFolder" />
</ItemGroup>