I have a Visual Studio C# solution which consists of some projects. One of the projects needs to reference another project which is not part of the solution.
At the beginning I was referencing dlls:
<ItemGroup>
<Reference Include="ExternalProj1, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<HintPath>..\..\Proj1\ExternalProj1.dll</HintPath>
</Reference>
</ItemGroup>
However I must reference projects so that those will generate their dlls. In fact If I reference dlls and they have not been created, I need to build those projects separately.
However when referencing projects:
<ItemGroup>
<ProjectReference Include="..\..\Proj1\ExternalProj1">
<Project>{3341b552-a569-4313-aabc-34452fff60ac}</Project>
<Name>ExternalProj1</Name>
</ProjectReference>
</ItemGroup>
However when building the compiler cannot find those assemblies. The strange thing is that building process is reported as completed successfully but the error window reports one warning:
The referenced component ExternalProj could not be found.
So, what am I doing wrong? Thank you
I see you are using ProjectReference, which is what I'm familiar with in plain (non-NET) C++ projects. The Include attribute needs to name the file, not just the base name; e.g.
<ProjectReference Include="..\..\Proj1\ExternalProj1.vcxproj">
That is, ProjectReference is not Reference. See Common MSBuild Project Items
Also, the metadata that determines whether to link the LIB automatically is determined via the supplied props files if it is not specified for that item. Is a managed project even producing a LIB? So this should (with the filename correct) cause the nominated project to be built also as a dependent, doing something with its products is another issue altogether.
Try building from MSBuild.exe command line, not the IDE, to see the pure behavior before the IDE messes things up or adds more issues to figure out. And, feed it the specific proj file you are wanting, not the "solution" file. The .sln file is a strange beast and not only is it possible to have project references not present in the sln, there is no inherent concept of a sln file at all. Other than a list of projects to show in the IDE, it is a magic file converted into a master proj on the fly that lets you name various targets individually without having to know which proj file (or the path to it) which is handy enough, but mainly there for compatibility with VSBuild according to The Books. So avoid it, at least to simplify things to get the behavior you want during the exploration stage. Then add any complications back in if you still want them :) .
Related
I want to release a source generator package, and I want to include a private project dependency on the source generator project.
For example, assume that my projects are A.SourceGenerator and A.CodeAnalysis.Core. I want A.SourceGenerator to depend on A.CodeAnalysis.Core, but use ProjectReference instead of PackageReference.
This means that I want the following line or something similar in my A.SourceGenerator.csproj file:
<None Include="$(OutputPath)\A.CodeAnalysis.Core.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
The issue is that this, or some similar variations, won't work and will produce a source generation error, under the CS8785 compiler warning, with the following error message:
Generator BenchmarkSourceGenerator failed to generate source. It will not contribute to the output and compilation errors may occur as a result.
Exception was of type FileNotFoundException with message:
Could not load file or assembly A.CodeAnalysis.Core, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null or one of its dependencies. The system cannot find the file specified.
The above error is shown because the project's contents need to be locally copied on the directory that the source generator lies on. There is a trick for that when using PackageReference, as shown here, but it clearly does not apply for the case of ProjectReference.
I would prefer to avoid creating a brand new NuGet package that the source generator depends on, just to include the dependencies. The other solution is to not abstract that away and only have one source generator project including all source generators.
I don't have much knowledge about project files, but at least i can share some contents of my project file.
I had to reference other projects like this:
<ProjectReference Include="..\OtherProject.csproj" OutputItemType="Analyzer" />
I don't remember where i found this, but i hope it helps.
Update:
I had someone explain this to me and I hope I'll get it right:
Basically in order to not interfere with the rest of the Package, OutputItemType="Analyzer" causes C# Analyzers and SGs to be packed under analyzers/dotnet/cs.
But there it won't be able to locate it's normal dependencies.
So you add PackagePath="analyzers/dotnet/cs" to pack your package dependencies alongside your Analyzer/SG.
And to get your project dependencies packed to the right path, you can add OutputItemType="Analyzer" even though they might not be analyzers.
For our new project we decided to use .NET 5 for compatibility and we chose to split the project into different libraries to maintain it easily.
The problems come out now that we are trying to put them together.
We would like to have a hierarchy of libraries where the middle level reference the low level ones and then in the main project reference the middle ones and automatically gains the references to the low levels. But this is not working as expected.
We started using direct reference to .dll differentiating by Debug and Release version for debugging as follows:
<ItemGroup Condition=" '$(Configuration)' == 'Debug' ">
<Reference Include="Model.Core">
<HintPath>..\..\Model.Core\Model.Core\bin\Debug\net5.0\Model.Core.dll</HintPath>
</Reference>
<Reference Include="Services.Logging">
<HintPath>..\..\..\Services\Services.Logging\Services.Logging\bin\Debug\net5.0\Services.Logging.dll</HintPath>
</Reference>
</ItemGroup>
<ItemGroup Condition=" '$(Configuration)' == 'Release' ">
<Reference Include="Model.Core">
<HintPath>..\..\Model.Core\Model.Core\bin\Release\net5.0\Model.Core.dll</HintPath>
</Reference>
<Reference Include="Services.Logging">
<HintPath>..\..\..\Services\Services.Logging\Services.Logging\bin\Release\net5.0\Services.Logging.dll</HintPath>
</Reference>
</ItemGroup>
The problem with this approach is we don't get indirect references resolved (and them are not included inside the middle library) so when running a program we got a FileNotFoundException about the low level library.
Low-level libraries: Logger (wrapper for Serilog NuGet package), Model.Core
Middle-level library: Model.FileSystem
Running program
This is a call example:
Running program -> Model.FileSystem.ReadConfiguration() -> Logger.Log()
At this point we get the FileNotFoundException.
I know we can solve the problem by manually coping the necessary dlls directly inside the bin folder of running program but we would like to have it done automatically.
An alternative way can be to pack all libraries inside NuGet packages and then refer to them. This way references are automatically resolved but it seems over-complicated for our needs. I'm surely missing something but now this seems the procedure and it's quite painful while debugging:
Create the package with Debug configuration
Move the package to the local or private repository
Compile the project referencing the package
Debug
Once the debug ends, repeat 1, 2 and 3 with Release configuration version.
Imho this way there are too many manually parts needed each time we change something and could lead to problems mixing Debug and Release packages when forgetting about changing the package (can happen while compiling many many times).
I liked the direct reference where you can target Release and Debug versions of the same library according to main configuration but it doesn't work.
Sorry for being so verbose but it's quite complicated to explain this problem and I'm not sure I was clear. I asked this question even here How to reference private library in debug/release mode.
Thanks in advance to anyone tries to help me.
I have chosen to include in each solution the project needed for references. This way there are no missing libraries while running.
Not really what I wanted but it's the easiest way.
When trying to add references to a VSIX, it normally pulls it from the references in the .csproj. However, if the references are not in the .csproj, because they now are in a project.json file, then they don't get pulled to the vsix. The solution then may compile, but then the extension fails with "file not found" errors when installed into Visual Studio (since the assemblies where not copied to the VSIX).
I tried with the section of the manifest like so:
<Asset Type="Microsoft.VisualStudio.Assembly" d:Source="Project" d:ProjectName="*PROJECTNAME*" Path="|*ASSEMBLYNAME*|" AssemblyName="|*ASSEMBLYNAME*;AssemblyName|" />
But it does not work, as it does not recognize the package references.
After some research I saw a similar issue with a PCL, however, without an answer and not the same type of problem: MEF With Portable Class library using Microsoft Composition MEF2 throws file not found exception
In the same note, this seems like an acceptable workaround: VSIX with Project Templates and NuGet Packages however, as far as I understood, it implies using the package during the installation. Besides that, it doesn't work for our case as they need to specify the package version and we are using project.json so we can use floating versions (ie: 2.0.*)
Is there a way to reference this project.json references that we are missing? Maybe a workaround? The solutions I have found seem to all require to "paste" de DLL somewhere, which for floating versions is not that convenient.
Thanks in advance for any help or input.
Edit/Update: Since VSIX automatically pushes any assembly referenced in the CSPROJ (and not the project itself), trying to get the DLLs at a project level seems unlikely. After many tries, I think that a valid workaround would be to get the assemblies from the Output Folder. However, to my knowledge, VSIX does not have a way of doing this, or does it?
I'm not sure I'm understanding your question correctly, but if you're trying to install a Project Template via a VSIX and you want the project template to include all it's nuget packages when you use it you could do something like this.
Edit your Project Template's xproj file and add the following lines:
<ItemGroup>
<None Include="project.json"/>
<None Include="project.lock.json"/>
</ItemGroup>
Edit your Project Template's vstemplate file and add the following lines in the Project node:
<ProjectItem ReplaceParameters="true" TargetFileName="project.json">project.json</ProjectItem>
<ProjectItem ReplaceParameters="true" TargetFileName="project.lock.json">project.lock.json</ProjectItem>
That should be all you need to do. Now when you install the project template, then create a new project using that template it should include all the nuget packages that were in the project.json file.
I have a solution with 20+ projects. Pretty much every project references two third party assemblies called foo.v.4.5.dll. I got a new version of the third party application today called foo.v.5.0.dll. I have to visit every project and change the references to the new assemblies. If they don't work well then I have to revisit and undo the change. This is time consuming and a pain.
Is there an easier way to do this? Are there any tools that can help me change the project references globally or is this the only way?
Each project has a .csproj file.
These files contains the references to the assemblies.
You can create a code to open all those files, search for the current assembly text and substitute for the new assembly text.
There goes an example:
<Reference Include="Autodesk.AutoCAD.Interop.Common, Version=18.1.0.0, Culture=neutral, PublicKeyToken=eed84259d7cbf30b, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<EmbedInteropTypes>True</EmbedInteropTypes>
<HintPath>..\..\..\..\..\..\ObjectARX 2011\inc-win32\Autodesk.AutoCAD.Interop.Common.dll</HintPath>
</Reference>
So a XML parsing would be probably the best way
I'm working with a large (270+ project) VS.Net solution. Yes, I know this is pushing the friendship with VS but it's inherited and blah blah. Anyway, to speed up the solution load and compile time I've removed all projects that I'm not currently working on... which in turn has removed those project references from the projects I want to retain. So now I'm going through a mind numbing process of adding binary references to the retained projects so that the referenced Types can be found.
Here's how I'm working at present;
Attempt to compile, get thousands of
errors, 'type or namespace missing'
Copy the first line of the error
list to the clipboard
Using a perl script hooked up to a
hotkey (AHK) I extract the type name from
the error message and store it in the windows clipboard
I paste the type name into source
insight symbol browser and note the
assembly containing the Type
I go back to VS and add that
assembly as a binary reference to
the relevant project
So now, after about 30 mins I'm thinking there's just got to be a quicker way...
These solutions come to my mind:
You can try to use Dependency Walker or similar program to analyze dependecies.
Parse MSBuild files (*.csproject) to get list of dependencies
EDIT:
Just found 2 cool tools Dependency Visualizer & Dependency Finder on codeplex I think they can help you greatly.
EDIT:
#edg, I totally misread your question, since you lose references from csproj files you have to use static analysis tool like NDepend or try to analyze dependencies in run time.
No, there currently isn't a built-in quicker way.
I would suggest not modifying the existing solution and create a new solution with new projects that duplicate (e.g. rename and edit) the projects you want to work on. If you find that the solution with the hundreds of projects is an issue for you then you'll likely just need to work on a subset. Start with a couple of new projects, add the binary (not project) reference and go from there.
One thing you can try is opening up the old .csproj file in notepad and replacing the ProjectReference tags with Reference tags. If you can write a parser, feel free to share. :)
Entry in .csproj file if it is a project reference
<ItemGroup>
<ProjectReference Include="..\WindowsApplication2\WindowsApplication2.csproj">
<Project>{7CE93073-D1E3-49B0-949E-89C73F3EC282}</Project>
<Name>WindowsApplication2</Name>
</ProjectReference>
</ItemGroup>
Entry in .csproj file if it is an assembly reference
<ItemGroup>
<Reference Include="WindowsApplication2, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<SpecificVersion>False</SpecificVersion>
<ExecutableExtension>.dll</ExecutableExtension>
<HintPath>..\WindowsApplication2\bin\Release\WindowsApplication2.dll</HintPath>
</Reference> </ItemGroup>
Instead of removing the project files from the solution, you could unload the projects you aren't working on (right-click the project and select Unload Project). As long as the unloaded project has been built once, any other project with a reference to it will be able to find the assembly in the project's output directory and build with it.