How to add project dependencies in a MSBuild solution programmatically with C# - c#

Hello I ran into a XY problem using Visual Studio 2017 and dotnet 6.0.100
My original problem is: "How to add project dependencies in a MSBuild solution programmatically with C#"
This is needed to force a modular application to compile unreferenced projects in a .sln during the build process to also compile all other projects in the solution.
This .sln file is built programmatically and should also do this step automatically during creation of the .sln to reduce possible problems for the user working with this .sln.
The .csproj of the projects however should not be touched since they are part of a git submodule, that should not include these changes to the .csproj but only the changes to the .csproj made by the user.
Solution
There is a 10 year old solution described here and here using the DTE Interface.
Derived problem: I failed to include the correct references for the EnvDTE.DTE or EnvDTE80.DTE2 objects to be available in code.
I tried adding the Microsoft.VisualStudio.Setup.Configuration.Interop NuGet package and searched in the references for EnvDTE or Microsoft.VisualStudio.Shell as seen e.g. in the example here without luck.
Original problem
Manually the project dependencies can be added as described here
I tried adding the project dependencies via the dotnet sln command but didn't find a corresponding command to add the dependencies.
dotnet sln -h only points to add, list and remove. The add command didn't show an option for dependencies.
Since I already use dotnet commands to create the solution and add projects this would be a good solution for me if there is a way.
The other thing I tried was using Microsoft.Build.Construction:
SolutionFile solutionFile = SolutionFile.Parse(PROJECT_SOLUTION_FILE.FullName);
var projectInSolution = solutionFile.ProjectsInOrder;
which does enable me to read the projects in the sln but seemingly not to manipulate it since its all readonly.
Summary
I need a way to tell the solution that all projects have to be compiled on run/compilation of the designated one.
To accomplish this I either need a way to add project dependencies to the .sln file via
dotnet shell commands
something like Microsoft.Build.Construction
or a good source explaining to me how I can get the EnvDTE code running in my MS Visual Studio 2017
or there is some awesome way to do this that I didn't stumble across yet
I really appreciate any help to solving my problem.
Edit:
To clarify project dependencies are different from project references in .csproj files!
The project dependencies I refer to are this section in the .sln:
Project("{GUID}") = "PROJECTNAME", "CSPROJECT_LOCATION", "{GUID}"
ProjectSection(ProjectDependencies) = postProject
{GUID} = {GUID}
{GUID} = {GUID}
...
EndProjectSection
EndProject

I found the solution to my Y problem, adding the dependencies using EnvDTE as described in the answer.
The references for EnvDTE and Microsoft.VisualStudio.Setup.Configuration.Interop did not show up because I was using a SDK-Style project to test them, since we do use them by default currently.
Installing the Microsoft.VisualStudio.Setup.Configuration.Interop NuGet did not remedy this either!
According to this question SDK-Style projects do not support working with the DTE Interface yet.
That is the reason, why I was unable to find the references described in this example.
The example also mentions creating a .NET Framework Console App, which I did not see as a requirement at the time.
Summary:
To work with the DTE Interface in 2021 better use the old project format and avoid the SDK-Style project format!

Related

ANTLR4 runtime problem in visual studio 2019

I'm trying to get antlr working in VS. I’ve not touched VS for a very long time. I've added the AntlrVSIX extension per the Antlr online docs, this created a sample calculator project for me but I can't compile it (I uninstalled and reinstalled AntlrVSIX but it made no difference to the following problem).
VS complains “The type or namespace name 'Antlr4' could not be found (are you missing a using directive or an assembly reference?)” and points to the line “using Antlr4.Runtime.Misc;” (and lots more similar).
If I look in menu Tools:NuGet Package Manager:Manage NuGetPackages for Solution… it clearly shows Antlr4.Runtime.Standard present and installed (showing version 4.8.0).
I guess I have to add a reference to this dll to my project so I I right click on References:Add Reference... in Solutions Explorer but I can’t find it in any list, however I search.
There is a Browse button which I assume I could use to link to the DLL directly, so I’ve searched the disk, found the dll and linked to that. This now works in that all those errors go away, but this can’t possibly be the right way.
So how do you add a reference to it properly, using References:Add Reference...? VS knows it’s there, it displays it, but doesn’t let it be referenced like that because it won’t show it within VS via References:Add Reference...
On your specific question, yes, you don't want to add a reference to the dll. Instead, you need to add a "<PackageReference>" in the csproj for these dependencies. All this lives in Nuget.org. So, in VS2019, right click on a project in the Solution Manager, then look for "Manage Nuget packages" to add Antlr4BuildTasks and Antlr4.Runtime.Standard.
Note, I've been updating the Antlrvsix extension, but haven't made a release for 2 months, longer than my usual schedule, because the next version has a huge number of changes. I will be cutting version 8 in a week. The template in VS2019 was removed because it is old and out of date. It uses an ancient version of the Antlr4BuildTasks. Instead, use the Antlr4BuildTasks.Templates v8.1 to create a C# project from scratch. Please follow the directions here. You don't need to download the Antlr tool kit, Java, or set any environmental variables. You also don't even need to use VS2019. You only need Net Core 3.1 and to install Antlr4BuildTasks.Templates. Then, type "mkdir foo; cd foo; dotnet new antlr; dotnet restore; dotnet build; dotnet run" at a command-line shell to create a C# application with Antlr4. Once you create the application, you can modify that to what you need. If you start from a Net Standard or Net Core project that doesn't have Antlr yet, you will need to add in the project reference for Antlr4.Runtime.Standard 4.8 and Antlr4BuildTasks 8.1, add in all grammars, and driver to set up and call the parser--harder to do, but it can be done through VS, or you can edit the CSPROJ file more easily and faster. The Antlr4BuildTasks looks at your CSPROJ file and will see that Antlr4.Runtime.Standard 4.8 is referenced, then will use the correct version of the Antlr JAR file to generate the parser and lexer. Any questions, let me know.
I suggest you using Antlr4.CodeGenerator NuGet package, it's gonna generated all necessary files (visitors, listeners) to work with your grammar in antlr. Check out my article for the details, there is a link to Github repo with the solution that works in VS 2019.

Developing and debugging projects in mutli-repository in Visual Studio

This is the basics of the problem I am having (of course it is oversimplified for the sake of the question):
I have 2 projects in a solution stored in mono-repository:
Project1 - outputs a library
Project2 - outputs an executable
When Project2 references Project1.
I would like to move to multi-repository where Project1 will be stored in different Repository from Project2.
Project1 will output a nuget package and Project2 will reference it instead the project itself.
The issue I am facing: in current situation (mono-repository) when during development I introduce a feature in
Project2 that also requires a change in Project1 it is not a problem. If there are problems I can discover it
during development time.
In a new way (multi-repository) I first need to make a change to Project1, create nuget and push it to nuget
store, then update reference in Project2. If I would have problems in Project1, I must go back to Project1,
fix the issue and push it again, update reference in Project2 and so on. Also, losing the benefit of debugging
both projects.
Is there a solution to this approach? To focus my question: if I have source codes of both projects on my dev machine,
is it possible somehow to instruct Visual Studio to use source code instead of referenced Nuget for debugging?
Hope I explained it right and clear as possible...
If you have two different projects and both of them are in different repositories, you could add the library as a submodule of the first project. I'm assuming you're using git. You could do this simply by:
git submodule add <link for the other repo>
In visual studio you just add the project to the same solution and then reference library from the executable. I'm not sure if that would do the trick for you, but I hope that works.
We have the exact same problem in our company.
This is 2 part problem.
First part is to have both repositories near each other... There are more options, we were deciding between these 2:
git submodule
meta repository (it is a compromise between mono-repository and multi-repository) you have 2 repositories you want to connect, so you create 3rd repository as a meta-repository via https://github.com/mateodelnorte/meta
Second part is how to connect those 2 repositories so that they are debuggable, but still apart each other... We though of these 3 ways:
new .sln in meta repository that will reference both projects (this didn't cut it, because we already been in a position when we had multiple .sln files and maintaining them is not that easy, because when you add some project into one, you have to add it to the other solutions and this goes sideways really quick)
using Reference and PackageReference with Condition - locally for debugging when .dll is build in ProjA then the ProjB would use "Reference", if not the ProjB would use PackageReference => this was our main solution to our problem until we did it like that... (Because we have more than 2 projects ProjA -> ProjB -> ProjC.. The problem here was when it was built locally, referenced via Reference DLL, then ProjA was not visible from ProjC, but when built via CI and referenced via PackageReference, then ProjA was visible from ProjC)
using only Nugets - Every build of ProjA it will create nuget locally (pre-release) and in ProjB, we would reference that via wild-cards. This works, until you make a second change into ProjA, because ProjB will cache that nuget in C:/Users//.nuget/packages :( so when building ProjA and packing the nuget we clear the newly built nuget from this packages folder. You have to restore ProjB every time you make change into ProjA, but this is where we landed as a final solution for now.
So the final solution for us is:
meta-repository for like 5 other repositories
for debugging we use locally built nugets with constant version of "major.minor.patch.65534-local"
for CI we use the same nuget packaging but we override the local version with
feature branches "major.minor.patch.build_number-branch_name" (having -something after the version makes that nuget pre-release)
master/main branch "major.minor.path.build_number"

Create a Visual Studio Project Template that pulls NuGet references from online feed

I'm creating a Visual Studio Project Template and bundling it inside of a VS Extension. I need Projects created from the Template to reference ~20 NuGet packages.
Is it possible to have the references resolved from nuget.org rather than having to include all of the references inside the VSIX?
The NuGet documentation on Visual Studio Templates provides instructions on how to add packages inside the VSIX, but it requires the file be stored locally on disk and the .nupkg is bundles inside the vsix:
Add your nupkg files as custom extension content in your source.extension.vsixmanifest file. If you're using the 2.0 schema it should look like this:
<Asset Type="Moq.4.0.10827.nupkg" d:Source="File"
Path="Packages\Moq.4.0.10827.nupkg" d:VsixSubPath="Packages" />
Question already asked
I know a similar question was asked (Creating a Visual Studio Project Template that already includes a Nuget Package Reference?) and answered (not possible), but this was asked in 2011.
5 years later, is it still not possible?
Since there is still no Built-In functionality to Install/Upgrade packages from online Repo, here is a small workaround wich might help:
Prerequisites
First, install the NuGet.VisualStudio nuget package into your project.
You get that from here
When installed, the package will automatically set the Embed Interop Types property of the assembly reference to True. The reason it does so is to make your code resilient against version changes when users update to newer versions of NuGet.
For the same reason, you must NOT use any other types besides the above interfaces in your code. You must NOT reference any other NuGet assemblies either, including NuGet.Core.dll.
After setting up all that stuff, you can do the following in your RunFinished-Method:
var componentModel = (IComponentModel) Package.GetGlobalService(typeof(SComponentModel));
IVsPackageInstallerServices installerServices =
componentModel.GetService<IVsPackageInstallerServices>();
if (!installerServices.IsPackageInstalled(project, "Newtonsoft.Json")) {
var installer = componentModel.GetService<IVsPackageInstaller>();
installer.InstallPackage(
"All",
project,
"Newtonsoft.Json",
(System.Version) null,
false);
}
Note
That example shows based on Newtonsoft.Json how you can install a package.
For sure you can choose the projects targeting the installation. Also you can determine the Version to be installed.
It seems a bit uncomfortable, but unfortunately there is no other way around.
Usings
using Microsoft.VisualStudio.ComponentModelHost;
using Microsoft.VisualStudio.Shell;
using NuGet.VisualStudio;
Let me know if that helps!
Yes, you can create a nuget package and add those other packages as its dependencies. Then when you download that package it will get all its dependencies and add to your project.

MSBuild .NET project that will output existing assembly as it's result

I need to create csproj file that will be usable as project reference in VS2013 and will output prebuilt binary as it's "Build" result.
We use referenced projects for build, however company policy doesn't allow access to some of that projects for everyone. As a result projects need to be updated manually to make them build. This is really a major inconvenience when switching branches and when making edits to project files, so I want to create dummy project that will be bound to pre-built binaries as their "output" and will be placed instead of real projects.
EDIT: Moving that assembly to Nuget package is not an option for now since Nuget has some issues with dev flow (when you need to debug/test/develop package). I saw some VS extension that implements switching between Nuget package and local project which might solve this issue, but I'm not sure if it will be accepted and want to explore other options.
To be clear - the thing I want to avoid is editing project in any way, so that project can be built cleanly after pulling it from Git, and I don't have to clean it every time before commit.
I haven't properly tested it, but the solution seems really simple (if I understand the question properly).
Just add this to the existing .csproj, overriding the Build target to just give the path to the pre-built assembly.
<Target
Name="Build"
Returns="$(TargetPath)" />
This assumes the TargetPath property already defined, and it should automatically be if you're modifying the original .csproj. Otherwise just define it yourself in a <PropertyGroup> before the Build task.
Note that having TargetPath defined is important for the ProjectReferences in your own project to resolve.
How about having those restricted (binary only) projects reside in an internal Nuget package feed, so that Nuget can install the packages as needed, on build?

Nu-Get & issue with project level dependences for projects referenced by multiple solutions

I'm trying to figure out what the best way to handle this scenario is.
Let's say I have a library that's referenced by multiple different non-related solutions, let's call it WebServiceInterface.dll. This library has a dependency on JSON.NET.
Before NuGet
The JSON.NET binary was referenced via a SVN external in the WebServiceInterface project. Other solutions which had a dependency on WebServiceInterface referenced the project (also as an SVN external) and as a result pulled both the project, and it's dependencies.
With NuGet
I haven't figured out how to force the JSON.NET reference to be stored under the WebServiceInterface project (as opposed to the RandomSolution\packages location). I found reference # nu-get to project-level and solution-level pacakges, but I can't seem to find out how to specify this when I add a dependency via nu-get.
The goal here is that when someone checks out WebServiceInterface and adds it to a new solution that it builds (instead of having broken references to JSON.NET which point to the packages directory under whatever the last solution was that checked in).
When I went to find out if Chris B had created a NuGet issue for this, I couldn't find one. EDIT: He did, see his comment below. But I did find a semi-documented feature of NuGet that I used to solve this problem: Allow specifying the folder where packages are installed
Let me break this question into 2 issues:
getting NuGet to allow for multiple solutions to use the same packages location
getting the NuGet packages to automagically fetch from source control when you include a project that has NuGet packages
Problem 1:
By default NuGet stores packages in a packages folder in the solution's folder. To change that location, create a nuget.config file in the solution's root folder with the following contents:
<settings>
<repositoryPath>..\..\..\Utilities\Library\nuget.packages</repositoryPath>
</settings>
<repositoryPath> is relative to your solution; so obviously make it whatever you want. Make each solution have it's own relative path to the same packages folder.
As far as NuGet's flow, from that point, the paths in repositories.config are relative to the folder containing repositories.config, not the solution, so now all projects/packages are managed independent of the solution location.
This allows multiple solutions to use the same packages in source control, and if those solutions use the same projects (that use NuGet packages), those solutions/projects will all be kept in sync no matter which solution updates the package.
Problem 1 completely solved.
Problem 2:
Let me address this from 2 perspectives. This applies to Visual Studio and TFS -- I'll leave SVN for someone else to address.
First: if you have no source code on your drive and do a get of a solution (not a project), I prefer to make it so that you get everything that solution needs to build. There shouldn't be any missing references to go manually grab. That much we can do by adding the package files as solution items. Yes, in each solution. A bit of work, yes, but when it's done the package files will fetch/update from source control automagically.
Second: In a new solution, when you include an existing source control project that has NuGet packages, you have to manually fetch the packages from source control and add them as solution items. At least anyone else getting your solution in the future will automagically get everything they need to successfully build. At least with VS/TFS, this is just the way it is, AFAIK. If projB depends on projA, and you add projB to a new solution, VS/TFS won't automatically grab projA from TFS. You have to do that manually. So then the same goes for dll references (like NuGet packages).
Summary of my solution:
Only one copy of packages in source control for all solutions
Any solution can update packages and all the other solutions will be kept in sync*
* Once one solution updates packages to new paths or file names, they will appear as missing references to the other solutions and you'll have to manually clean that up. But at least you know right where the packages are in source control "(as opposed to the RandomSolution\packages location)."
The packages are always stored at the solution level, so if you install a package into multiple projects, they came from the same place. I don't believe you can configure it so that each project has its own packages folder.
I'm not sure there's a nice way to do what you're trying. You could maybe have a build step on the project that fetches the package, but I don't know how well that will suit you.
I'd recommend posting in the NuGet Issue Tracker to get a discussion going. The people working on it seem pretty active, so it might be something they can add support for in a future version :-)

Categories