exclude certain projects from using Directory.Build.props - c#

i have a new visual studio solution that has around 350 projects. it takes visual studio a lot of time to compile the .sln file so i implemented Directory.Build.props to avoid copying of references that are not needed to copy to the local directory so the build can be made quicker. below is the code that im using inside the Directory.Build.props file under the root folder.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<ItemDefinitionGroup>
<Reference>
<Private>False</Private>
</Reference>
<ProjectReference>
<Private>False</Private>
</ProjectReference>
</ItemDefinitionGroup>
</Project>
since i placed Directory.Build.props under root folder it is being applied for all projects.
Question::
how can i exclude few projects from applying Directory.Build.props so that the references can be copied to the local.
in short i want the Directory.Build.props to be applied to only 300 projects under the solution file remaining 50 projects need to be excluded from this
how/where can i write a condition in the above code that will exclude certain projects being affected by this code

For others dealing with the same problem, there is another trick that can be used to exclude certain project from using the Directory.Build.props file found at root level.
If you add a dummy Directory.Build.props file in the project you want to exclude, then the Directory.Build.props from the root will not be used. This is because MSBuild walks the directory structure upwards from the location of your project, until it locates the first Directory.Build.props. That will be used. This behavior is documented on the Customize your build page under Search scope at the Microsoft docs.
Sample of the dummy Directory.Build.props:
<Project>
<!-- Only here so that the default Directory.Build.props will not be used. -->
</Project>
I found this to be a convenient way to solve this issue. Especially when dealing with only a few projects that need to be excluded.

I had to work around this in a bit of a hacky way.
In my example, there was a custom analyzer project I wrote that I did not want included in another set of projects. I ended up writing something like this in my Directory.Build.props:
<Project>
...
<Choose>
<When Condition="$(MSBuildProjectName)!='Analyzer' AND ...">
<ItemGroup>
<ProjectReference Include="..\Analyzer\Analyzer.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
<OutputItemType>Analyzer</OutputItemType>
</ProjectReference>
</ItemGroup>
</When>
<Otherwise>
...
</Otherwise>
</Choose>
...
</Project>
Where I filled in ... with the projects I wanted it to skip.
I understand this may not be the exact answer you were looking for, but I did a ton of research and was also unable to find any way to do it the way you described. The stuff I have posted was the only way I was able to achieve the ability to exclude certain things from being applied to specific projects by filtering via name. I know that this is hacky and sucks, but it's the only thing that was able to work for me.
Also note that <Otherwise></Otherwise> may be turned into <Otherwise /> possibly, and may even be optional altogether. I left it there so that you could place stuff inside of it if needed.

Related

Can share global namespaces across .net 6 projects?

In .Net 6, we gained Global Usings. I have several projects that are similar and I'd like to share the same GlobalNamespaces.cs file I've created across the projects.
I tried to add the file as a link in Visual Studio, but after removing the usings of that file, I'm getting build errors.
Is there a way to share it across projects?
An example:
c:\myproject\src\GlobalUsings.cs
c:\myproject\src\Project1\ (*.csproj, cs files)
c:\myproject\src\Project2\ (*.csproj, cs files)
I want to use GlobalUsings in both.
Thanks to #gsferreira's article and a link to an example from his tweet
I was able to add a Directory.Build.props file next to my .sln and it works!
<Project>
<PropertyGroup>
<LangVersion>latest</LangVersion>
</PropertyGroup>
<ItemGroup Condition="'$(MSBuildProjectExtension)' == '.csproj' AND '$(MSBuildProjectName)' != 'My.Other.Project'">
<Using Include="Newtonsoft.Json" />
</ItemGroup>
</Project>
This works now!
// using Newtonsoft.Json;
namespace Project2;
public class Class1
{
JToken token = new JObject();
}
This works well for global using, but I found it was more complicated than I expected since there are some project that don't have nuget packages or other projects referenced. So the Global usings isn't really global, but more specific to what the project has referenced. If the similar projects were in a same sub folder, maybe that would work better. I haven't tried nesting Directory.Build.props files

VS2017 Directory.Build.props doesn't load for whole solution

OK here is my problem I have 2 library and 2 project that include their .csproj (1 for Dev, 1 for Client Delivery).
I need a Defined constant to set accessible most of my class when we are with the Dev purpose (internal -> public).
I used a Directory.Build.props in my dev project directory that defined a variable and my Libraries .csproj define a constant if this variable exists.
<PropertyGroup Condition ="$(ActiveIHMMode)=='true'">
<DefineConstants>$(DefineConstants);DEV_IHM_MODE</DefineConstants>
</PropertyGroup>
I can see everything work well for my dev proj but it doesn't for my Libraries (they don't see my .props variable)
I assume there is a simple reason for it, it's because of dependencies compile order.
My directory Hierarchie is the following :
LibA
LibB
ProjectDelivery
ProjectDev
My LibA is compiled first and doesn't find any Directory.Build.props because my file is in my ProjectDev Directory, but my ProjectDev as the last element to compile finds it, but it's too late for my Lib.
First time using .props and I can't see a way to resolve it. Thanks for your future help.
First of all, to clarify a possible confusion inferred from your title, automatically importing Directory.Build.props is not a Visual Studio 2017 feature, but a MSBuild 15 feature (which is included with VS2019).
With my nitpicking out of the way, let's get technical:
The problem is not your build order. The reason Directory.Build.props is only picked up in your ProjectDev project, is because MSBuild looks in the directory of the .csproj for a file called Directory.Build.props and imports it if it finds it. If it is not found, the file is searched in the parent directory. And it keeps looking for the Directory.Build.props in the parent directory until it reaches the root, or it actually finds that file and then stops, so it only automatically imports the first Directory.Build.props found.
Your project structure, as described above, looks like this:
/LibA/
/LibA/LibA.csproj
/LibB/
/LibB/LibB.csproj
/ProjectDelivery/
/ProjectDelivery/ProjectDelivery.csproj
/ProjectDev/
/ProjectDev/ProjectDev.csproj
/ProjectDev/Directory.Build.props
Only ProjectDev gets the Directory.Build.props automatically imported; none of the other projects have a Directory.Build.props neither in their directory nor in any of their parent directories.
To fix your issue you can either move the Directory.Build.props one folder up, so that it gets automatically imported by all of your projects,
or you may import the Directory.Build.props manually by adding an Import element to your .csproj:
<Project>
<Import Project="..\ProjectDev\Directory.Build.props" />
</Project>
You can read up on more details about Directory.Build.props in the documentation.

How are preprocessors linked to different target frameworks?

I'm following along with a talk by Immo Landwerth, in a .NET standard project, I saw him switching between projects (which are not typically projects under the solution) from the top left drop-down menu, here is a gif: https://image.ibb.co/mmjoHU/pre.gif
To reproduce the same thing I created a class library in .NET Framework to see the goings, but the project failed to load (with a modified csproj file to be the same as the demo's csproj), then I created a .NET standard library and modified the .csproj file to this (the same as the demo's csproj):
<Project Sdk="MSBuild.Sdk.Extras">
<PropertyGroup>
<TargetFrameworks>netstandard1.4;net461;uap10.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="System.ValueTuple" Version="4.3.0" />
</ItemGroup>
<ItemGroup Condition=" '$(TargetFramework)' == 'net461' ">
<Reference Include="System.Device" />
</ItemGroup>
</Project>
I could see the three target frameworks:netstandard1.4;net461;uap10.0. but really couldn't understand how they got mapped to the preprocessors: NET461, WINDOWS_UWP. which work on the Immo's project, but didn't work with my modified .NET standard library and the three targets didn't appear.
Looks like conditional compilation symbols with linked .cs files to me.
In the Properties > Build tab, you should see a textbox labelled "Conditional compilation symbols". Any strings you put in this textbox can be used to conditionally execute code with #if and #elif. There are also symbols that the build system is already aware of (some are listed here).
I'm assuming these projects also share the same .cs files, which explains the #if/#elif/#else conditions "flipping" within the same file (when you add a file to a project, choose add it as a link to an existing file, then a copy is not made).
I have done this exact thing on multiple projects for different configurations and the behavior matches the .gif you posted.

Add as link to within a project folder to copy always to root of output

I'm doing some interop with unmanaged .dlls in a standard c# project. I cannot add these .dlls as simple references and have to use P/Invoke to actually use them. They don't include manifests so doing any clever reflection stuff to load these dynamically (And thus solve the problem of including them as explicit, separate files) is simply out of the question.
I am not concerned with installer releases, I can tell WiX(Or whatever installer platform I choose) what files to exactly put where on a target system.
The problem is in terms of debugging output, I need these .DLLs side-by-side with my own project's executable, but I don't want them cluttering up my actual managed code.
So my situation is this;
I add several .dll's into a project's folder as links.
I attempt to change the .csproj to output the linked files at the root of the debugging output.
It looks something like this:
<None Include="..\..\externals\MyLibraries\ADll.dll">
<Link>TidyFolder\ADll.dll</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
Now, normally you'd change the <Link> tag to where you want the output to go, but doing this:
<None Include="..\..\externals\MyLibraries\ADll.dll">
<Link>ADll.dll</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</None>
Changes the folder the linked .dlls are residing in within my project explorer; it does output correctly, but the clutter is undesirable and the reason why I wanted to throw them into a folder in the first place.
TL;DR: How can you simultaneously control where a linked item is visible within a project, as well as its output location on debug.
Consider adding them as embedded resource:
<ItemGroup>
<EmbeddedResource Include="..\..\externals\MyLibraries\ADll.dll">
<Link>TidyFolder\ADll.dll</Link>
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
</EmbeddedResource>
</ItemGroup>

Using the same assembly references in csproj with different folder structures

I have a C# project, MyProject.csproj that sits in a solution with the following folder structure and references Dependency.dll:
Libs
Dependency.dll
Projects
MyProject
MyProject.csproj
Thus the reference to Dependency.dll in MyProject.csproj has a HintPath of something like this:
..\..\Libs\Dependency.dll
Now I'd like to use MyProject in a different solution in a different project structure, without modifications, as source. This is because MyProject sits in its own source control repository and I'm using it in different solutions as Mercurial subrepositories/Git submodules. (The problem might be solved on the source control level...) Such a diffreent solution would look like this:
Libs
Dependency.dll
MyProject
MyProject.csproj
Note that the MyProject folder is now on the same level as the Libs folder. Thus the original HintPath is now invalid (since it should be ..\Libs\Dependency.dll) and I get build errors.
Is there a way to fix this but keep the same csproj across the different solutions?
I found the following possible solutions which are great but require the modification of the csproj. This is mostly possible in my case but sometimes there are external components where I can't request such modifications, so I'd look for some solution-level override if possible.
Conditional HintPath based on file existence check: .csproj multiple hint paths for an assembly This might work as for the majority of cases the solution structure is well-known here.
Specifying multiple assembly search locations: https://stackoverflow.com/a/15816779/220230
Thank you.
For now, I solved the issue using the technique outlined in this blog post.
<ItemGroup>
<LibReferenceSearchPathFiles Include="..\..\Libs\**\*.dll">
<InProject>false</InProject>
</LibReferenceSearchPathFiles>
</ItemGroup>
<Target Name="BeforeResolveReferences">
<RemoveDuplicates Inputs="#(LibReferenceSearchPathFiles->'%(RootDir)%(Directory)')">
<Output TaskParameter="Filtered" ItemName="LibReferenceSearchPath" />
</RemoveDuplicates>
<CreateProperty Value="#(LibReferenceSearchPath);$(AssemblySearchPaths)">
<Output TaskParameter="Value" PropertyName="AssemblySearchPaths" />
</CreateProperty>
</Target>
This enables dlls from subfolders of Libs to be loaded. If all the dlls would be in the root of the Libs folder, then the first wildcard can be removed from the Include value.

Categories