Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Understanding .NET Core csproj actions

I'm trying to have the contents of a folder copied to build output. Ideally under the same path relative to the output/publish folder. I have:

<!-- front-end files to serve -->
<ItemGroup>
  <Folder Include="Frontend\Content">
    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
  </Folder>
</ItemGroup>

However, when I use dotnet publish, no file is copied.

This works for the config file I want - this was generated by VS:

<ItemGroup>
  <None Remove="my-config.json" />
</ItemGroup>
<ItemGroup>
  <Content Include="my-config.json">
    <CopyToOutputDirectory>Always</CopyToOutputDirectory>
  </Content>
</ItemGroup>

So my question is - what is this craziness? Semantically, an element called None with a Remove attribute - what does that even mean?

My vague intuition is that the element is equivalent to a "set", the contents of which will be actioned based on the name of that element/set, and that Include, Exclude, Remove somehow(?) interact to define and refine the contents of that set.

But then - what's "Folder"? What's "Content"? What happens when things double-up: why is the "None/Remove" combo needed? If it is at all - that was generated by VS!

Documentation-wise, I found:

https://docs.microsoft.com/en-us/dotnet/core/tools/csproj#how-to-see-the-whole-project-as-msbuild-sees-it, which talks about Compile, EmbeddedResource and None default globs (Include, Exclude and Remove globs). I know what a glob is, I know what those actions mean, but what are those types of globs? How do they interact?

It also points to MSBuild documentation at https://docs.microsoft.com/en-us/visualstudio/msbuild/msbuild-project-file-schema-reference?view=vs-2017, but that seems useless for this: it doesn't document None, EmbeddedResource, or Compile elements shown, nor does it talk about the Content and Folder elements I've found in other examples (and generated by VS).

In fact, nothing I've found yet talks about what this all means.

Where can I find documentation for this?

like image 683
Kieren Johnstone Avatar asked Jan 15 '19 09:01

Kieren Johnstone


People also ask

How do Csproj files work?

A CSPROJ file is a C# (C Sharp) programming project file created by Microsoft Visual Studio. It contains XML-formatted text that lists a project's included files and compilation options. Developers compile CSPROJ files using MSBuild (the Microsoft Build Engine).

What is Csproj file in asp net core?

csproj file tells dotnet how to build the ASP.NET application. It's one of the most important files in an ASP.NET project. An ASP.NET project can depend on third-party libraries developed by other developers. Usually, these libraries are installed as Nuget packages using Nuget package manager.

Do I need to check in Csproj?

I think what i have concluded is that the CSPROJ and SLN files do not get checked into TFS and must be manually passed to each developer when they initially get latest from TFS.

What is an ItemGroup in Csproj?

In addition to the generic Item element, ItemGroup allows child elements that represent types of items, such as Reference , ProjectReference , Compile , and others as listed at Common MSBuild project items.


1 Answers

  1. Folder items

<Folder> is used inside VS just shows a node in solution explorer, e.g. to get you a point to right click "Add > New …". wwwroot is a classic example for ASP.NET Core applications but if you use the VS' "Add > New Folder" feature or delete all of the files in an existing folder, it will add a node to keep it in the solution explorer.

The <Folder> items aren't used during the build. Copying items is determined based on the metadata specified on known item types (None,Content,...) and not based on Folder items in the filesystem hierarchy.

If you want to specify metadata for a whole hierarchy, you could do a

<Content Include="my/content/**/*" CopyToPublishDirectory="True" … />
  1. Include/Remove combo

Inside the static content of a project file (not within a <Target> - a few different rules apply in there), MSBuild allows three operations on collections of items:

  • Include: add an items to the ""collection"" of items of the specified item type
  • Update: change the metadata on items of the specified item type
  • Remove: remove items from the ""collection""

This means that if you do

<None Include="foo.txt" />
<Content Include="foo.txt" />

Then there both the @(None) and @(Content) collections will contain foo.txt. This isn't really a problem for the build system (unless you specify you want both of them copied to the output - this leads to errors with conflicting output paths), but for tooling that shows you the files in your project and their properties this can create tricky situations.

If foo.txt is listed as None, Content and EmbeddedResource, then the IDE (VS, Rider, VS/Mac, …) tooling has to make a tough decision on what to show you for that file.

To mitigate this problem, it is good to make sure that a file is listed as only one of these "Build Action" item types so that IDEs won't be confused (and transitively also the user).

So why do have to remove files from None?

The new "SDK-style" projects add a lot of defaults. They basically contain something like (simplified!!):

<None Include="**/*" />

So if you want to change a file to be an embedded resource or a content item, you should remove the file from the @(None) collection.

If you don't need to change the item type, you can also use Update:

<None Update="some/content/**/*" CopyToOutputDirectory="True" />
  1. So what do those things under <ItemGroup> do?

Those definitions do nothing on their own. They just fill / change a collection of "items". You could even create your own and use them later:

<MyCoolFiles Include="cool/files/**/*;other/cool/files/**/*" />
<None Remove="@(MyCoolFiles)" />
<Content Include="@(MyCoolFiles)" CopyToPublishDirectory="True" />

<Target Name="PrintCoolFiles" BeforeTargets="BeforeBuild">
  <Message Importance="high" Text="These are my cool files: @(MyCoolFiles)" />
</Target>

The key point is that some parts of the build logic look for items they know a about and use them.

The part that calculates the input to the compiler will look at Compile and EmbeddedResource items, the part that determines which files to copy to the output directory will look for various items (None, Content, EmbeddedResource and a few others IIRC) with specific metadata set.

New project types may define their own item types to be listed in VS' solution explorer via extensibility points.

But VS will only show items in the solution explorer for item types that are configured in this way. And for these item types it makes sense to not have a file listed in multiple of them.

like image 166
Martin Ullrich Avatar answered Oct 16 '22 01:10

Martin Ullrich