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

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>

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

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.

The .cs files are not under the .xaml file

I am Newbie self learning Xamarin, I was making small projects Until I encountered this problem. every time I start new project, I get an Error:
"Could not restore Packages".
App.xaml.cs used to be under App.xaml and main.xaml.cs used to be under main.xaml. Now they 2 independent files.
1) How can I restore packages?
2) How can I associate .cs file to .xaml file?
Here is an Image of my problem
Thanks in advance
A project I am working on had the issue for awhile and this was the fix for it.
Place this XML grouping inside of your Test.csproj file.
<ItemGroup>
<!-- https://bugzilla.xamarin.com/show_bug.cgi?id=55591 -->
<None Remove="**\*.xaml" />
<Compile Update="**\*.xaml.cs" DependentUpon="%(Filename)" />
<EmbeddedResource Include="**\*.xaml" SubType="Designer" Generator="MSBuild:UpdateDesignTimeXaml" />
</ItemGroup>
Please note that in a future update, and forgive me because I don't 100% remember, an update of Visual Studio or .NetStandard fixed the issue. Our project no longer needs this fix.
Edit your ".projitems" file located on the same directory of your .xaml files of your Xamarin project, there is the asociation between the .cs files and .xaml files in this way
<Compile Include="$(MSBuildThisFileDirectory)MyPage.xaml.cs">
<DependentUpon>MyPage.xaml</DependentUpon>
<SubType>Code</SubType>
</Compile>
<ItemGroup>
<EmbeddedResource Include="$(MSBuildThisFileDirectory)MyPage.xaml">
<SubType>Designer</SubType>
<Generator>MSBuild:Compile</Generator>
</EmbeddedResource>
</ItemGroup>
<Compile Update="C:\Projects\MyPage.xaml.cs">
<DependentUpon>MyPage.xaml</DependentUpon>
</Compile>
I just noticed the issue on some of my former solution file and in the csproj file for one project I had one view not displaying properly.
It had this code only for the view that had not proper nesting.
<ItemGroup>
<Compile Update="Views\NotProperNesting.xaml.cs">
<DependentUpon>NotProperNesting.xaml</DependentUpon>
</Compile>
</ItemGroup>
I don't know why it was there in the 1st place, but just removed it and it worked.

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

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>

MSBuild - ItemGroup of all bin directories within subdirectories

My Solution has multiple projects (and therefore subdirectories), and there is a 'bin' folder in each project folder.
I'm trying to create an ItemGroup in my MSBuild script that includes all these directories.
I thought this would be sufficient, but it doesn't contain anything:
<ItemGroup>
<BinDirs Include="**\bin" />
</ItemGroup>
I'm not sure why this doesn't work. Can anyone point me in the right direction to achieve what I'm trying to do?
Regards,
Nick
Since this hasn't got an answer, yet comes high on the list of Google results:
The link provided by Alexey has several answers to work around this problem, but it's not obvious why the example you've given doesn't work.
MSBuild ItemGroup collections don't seem to like wildcard Transforms when targeting directories.
You can use explicit paths, e.g.
<ItemGroup>
<BinDirs Include="C:\MyProject\bin" />
</ItemGroup>
Or paths relative to where your build script is running, e.g.
<ItemGroup>
<BinDirs Include="..\MyProject\bin" />
</ItemGroup>
However it does not transform your wildcards unless you are targeting files, e.g.
<ItemGroup>
<ThisWorks Include="..\**\bin\*" />
<ThisDoesnt Include="..\**\bin" />
</ItemGroup>
That post contains several ways to select folders using wildcards, the one I tend to use is:
<ItemGroup>
<GetAllFiles Include="..\**\bin\*.*" />
<GetFolders Include="#(GetAllFiles->'%(RootDir)%(Directory)'->Distinct())" />
</ItemGroup>
As noted on the post, it's not perfect at selecting the root folders, as it has to find where there are files. Using bin*.* would only get the bin folder if files were located in it.
If your build is anything like a standard VS output, you will probably find your bin folder has no files, instead having directories based on your configuration names, e.g. bin\Debug, in which case targeting bin\**\* will result in your item group containing those folders.
E.g.
<ItemGroup>
<GetAllFiles Include="..\**\bin\**\*" />
<GetFolders Include="#(GetAllFiles->'%(RootDir)%(Directory)'->Distinct())" />
</ItemGroup>
Would get:
..\Proj1\bin\Debug
..\Proj1\bin\Release
..\Proj2\bin\Debug
..\Proj2\bin\Release
I don't know of a wildcard way to get bin folders without files in... yet. If anybody finds one, please post as it would be useful.
Hope this helps someone save some time.
In MSBuild 4.0 this is possible:
<Folders Include="$([System.IO.Directory]::GetDirectories(".","Bin", SearchOption.AllDirectories))" />