Summary:
Projects build in wrong order with visual studio and managed C++ and C# projects
Description:
I have a massive (100+ projects) solution file that is building a few projects in the wrong order. The solution file contains the following types of projects:
native C/C++
Managed C++
Managed C#
The solution contains all the proper dependencies between the different types of projects. Ok, so when I build from the command line (Using MSBuild), there is a problem. The dependencies for the managed projects (both C++ and C#) get built in the wrong order. For instance a project will fail to build because a managed dependency is missing. For instance a managed C++ file will have a using declaration that will fail:
#using <foo.dll>
since foo.dll doesn't exist yet.
Which means that foo.dll should have been built before, but wasn't.
Like I mentioned earlier, the dependencies are properly set up in the solution file. For instance, if foo depends on baz, I have this in the solution file...
Project("{C4ABA494-43D0-400A-9917-20E167A12CFD}") = "Foo", "...\Foo.vcxproj", "{5A42640E-9E0A-442B-8A40-AA91AD5444AC}"
ProjectSection(ProjectDependencies) = postProject
...
{2CE32AE0-B129-40BA-B06E-A628FA149AB3} = {2CE32AE0-B129-40BA-B06E-A628FA149AB3}
EndProjectSection
EndProject
...
Project("{C4ABA494-43D0-400A-9917-20E167A12CFD}") = "baz", "...\baz.csproj", "{2CE32AE0-B129-40BA-B06E-A628FA149AB3}"
ProjectSection(ProjectDependencies) = postProject
...
EndProjectSection
EndProject
So the solution file correctly has the dependency. But the dependency in the Foo.vcxproj project is only expressed by the #using directive.
I've read on the visual studio blog that there is a known bug in ordering projects in msbuild.
http://blogs.msdn.com/b/visualstudio/archive/2010/12/21/incorrect-solution-build-ordering-when-using-msbuild-exe.aspx
Their work around is to add an item called to my projects, like this:
<ProjectReference Include="... foo.csproj">
<ReferenceOutputAssembly>false</ReferenceOutputAssembly>
</ProjectReference>
Anyways, my question is: do I need to do this ONLY for my managed C++ projects? Or do I do this for Managed C++ AND C# projects?
(I kind of believe I don't need to do this for C# projects since their dependencies are explicit)
Note: I have tried putting this on ALL projects in my build, and it didn't work so hot, as I got lots of strange build errors in my native projects...
Thanks for any response to this.
I had the same issue, but with C# projects only.
It seems like MsBuild is NOT using solution file dependencies. It is using project references inside the project files to create build order. Try to update all your ProjectReferences to get correct build order. In your case you have to add managed project reference (dependency) into your C++ project file.
The answer to your question is: Yes, you have to do it for both Managed C++ AND C# projects. Setting dependencies inside sln file is not enough if you are building with MSBuild.
Related
I'm trying to use MSBuild in a powershell script to build many projects and solutions in a full application suite. I set the parameter for OutDir to point to a single binaries directory and from an output perspective that works.
However the documentation states that OutDir is included in AssemblySearchPaths. But looking at the logs MSBuild is clearly stuck using the hintpath from the csproj file. I've tried setting AdditionalLibPaths as well with no success. This appears to be an issue with building from Visual Studio 2019 as well. My hintpaths point to a common debug directory. A release build still looks in the debug directory. This used to work in older versions of Studio in the .NET Framework days. It worked in older TFS XAML builds setting Output Location to "SingleFolder"
I've also played around with OutDir path ending various quantities of back slashes. I suspect that this old issue is fixed.
How can I get MSBuild to use an alternate directory for the dependencies?
https://learn.microsoft.com/en-us/visualstudio/msbuild/common-msbuild-project-properties?view=vs-2019
EDIT:
As per the accepted answer, adding OutDir to the AssemblySearchPaths does the trick. For me, I've created a proj file that I've added to each .NET Core csproj files. My thought is that when this gets fixed I can remove the tweak in one place.
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|AnyCPU'">
<AssemblySearchPaths>$(AssemblySearchPaths);$(OutDir)</AssemblySearchPaths>
</PropertyGroup>
My hintpaths point to a common debug directory. A release build still
looks in the debug directory.
The outdir is always the output folder which does not distinguish between Release and Debug. So you have to use <OutDir>C:\ttt\$(Configuration)\</OutDir> to distinguish between them.
Actually, the system msbuild properties are read earlier than the start of build task. You have to set the properties before the start of build process.
Simply modifying the system properties outdir in csproj will only take effect during the build process, but the system properties are still read before the build starts, also AssemblySearchPaths property read the previous outdir property. So you always take the default values before the modification.
You have to use Directory.Build.props file, it set the values earlier than msbuild start.
1) create a file called Directory.Build.props under your project folder.
2) add the outdir property like these into the file.
<Project>
<PropertyGroup>
<OutDir>C:\ttt\$(Configuration)\</OutDir>
</PropertyGroup>
</Project>
3) restart VS to enable it.
However, I note that it works well in non-sdk net framework projects but it does not list under new-sdk net core projects.
non-sdk net framework projects
new-sdk net core projects
Not sure it is an issue or the Team has forgotten it. Anyway, I have reported it to our DC Forum. You can vote it or add any comments if I did not described it in detail.
As a workaround, you could try to set the new value for AssemblySearchPaths property.
In order not to lose the original value of AssemblySearchPaths, you must add it to csporj file rather than Directory.Build.props file.
Just add these into csproj file:
<PropertyGroup>
<AssemblySearchPaths>$(AssemblySearchPaths);$(OutDir)</AssemblySearchPaths>
</PropertyGroup>
Update 1
I think it is an issue for net core projects.
What I said before is for VS IDE build. Now for MSBuild Command Line, it is another situation.
For non-sdk net framework projects
When I used msbuild xxx\xxx.csproj -p:outdir=c:\ttt -v:diagnostic, it shows this:
Well. It works perfect as we wished.
However, when we used the same command line for new-sdk net core projects, it does nothing. So I think it is quite an issue for net core projects.
And you should note that AdditionalLibPaths cannot be recognized by AssemblySearchPaths.
When I used this under :
msbuild xxx\xxx.csproj -p:AdditionalLibPaths=c:\ttt -v:diagnostic
And you should note that there is no property for AdditionalLibPaths under the list of AssemblySearchPaths property. And it also does not work for net core projects.
In short, it is quite an issue for net core projects no doubt. I also modify the DC ticket.
Now for new-sdk net core projects,
Since you used msbuild command line to set properties, so there is no need to use Directly.Build.props file. MSbuild command line property assignment is actually the same effect of the file.
Also, AssemblySearchPaths is not ready-only. You could modify it. And actually, all msbuild properties can be overwritten and that is a flexible feature of MSBuild.
In summary, you still have to use AssemblySearchPaths.
Solution
Since The Team has some problems with this detail in the net core project, we can use the flexibility of MSbuild to manually modify to get what we want:
1) abandon using Directly.Build.props file and also keep adding these on the net core csproj file:
<PropertyGroup>
<AssemblySearchPaths>$(AssemblySearchPaths);$(OutDir)</AssemblySearchPaths>
</PropertyGroup>
2) use the following command line for net core projects:
msbuild xxx\xxx.csproj -p:Outdir=c:\ttt -v:diagnostic
I have a project file that should build another solution. I tried using the MSBuild task for this, like this:
<MSBuild Projects="MySolution.sln" Properties="Configuration=$(Configuration)"/>
I tried a lot of variants, like supplying hardcoded configuration, target Rebuild and so on. Building reports an error when I pass a wrong solution name, non-existing configuration or target and so on, so it definitely loads the solution and the project files. It exits relatively fast though and produces no output. According to documentation and examples, this should work though. I also tried passing an ItemGroup for the project, including project-specific properties as suggested by examples or in MSBuild - How to build a .NET solution file (in an XML task script) from pre-written command line commands , but that does not work either. It runs without error but no output.
When passing a list of project files instead (or a single project file), it builds correctly, but the problem is that dependencies between the projects are not properly resolved. At the end, I have to supply all project files in the solution and add them to the list, which is what I want to avoid.
So, why does solution building not work, even though it should? What is wrong here?
Is this a .net project? If so you probably need to pass in a platform as well as a configuration.
<MSBuild
Projects="MySolution.sln"
Targets="build"
Properties="Configuration=$(Configuration);Platform=$(Platform)" />
If the project just contains web sites and libraries then the platform should be Any CPU if your solution just contains executables then x86 or x64 or if it's a mixture of different types of platform then you can use mixed platforms
To check what are available open the solution in Visual Studio, right click on the solution in solution explorer and select "Configuration Manager" you'll then have a drop down for "Active Solution Platforms"
I have a solution with the following projects:
MySolution.sln
- MySolution.Client.csproj
- MySolution.Service.csproj
- MySolution.Models.csproj
- MySolution.Server.xproj
MySolution.Models is a simple class library which contains shared code that is referenced by MySolution.Client and MySolution.Service - and I would like to reference it in MySolution.Server.
The GUI in VS 2015 RC1 lets me add the reference by right clicking References -> Add Reference. I then see all my projects under Projects -> Solution.
I select MySolution.Models and click Ok, after which I receive the following error in the output log:
Errors in ...PathToSolution\MySolution.Server\project.json
Unable to locate MySolution.Models >= 1.0.0-*
It really feels like this should work, since the GUI allows me to add the reference without any hiccups.
So the first thing to understand is DNX projects have no understanding of traditional .net projects. They don't read or parse csproj files. This is done to keep them cross platform and cross IDE compatible (csproj is a distinctly windows and VS specific thing).
When you add a reference to a "legacy" (I use legacy to mean a .net 4.x csproj based project) behind the scenes the IDE will run dnu wrap but it looks like in your case something broke.
The following should be done automatically.
In solution root global.json a folder "wrap" should be added to the
projects property.
A folder off the root named "wrap" will be created if it doesn't exist.
A /wrap/project.json will be created/updated with a path to the assembly (dll).
Add a reference to the assembly and version to the referencing project's project.json file.
So first thing to check is make sure you have a "wrap" folder and wrap reference in projects property of solution.json. If you don't then likely something "broke". Try removing the reference rebuilding and adding the reference back. Check the build output window for any errors (VS is still RC so there are something error which probably should be halting that are not).
Look for a project.json in the wrap folder. It should look something like this:
{
"version": "1.0.0-*",
"frameworks": {
"net452": {
"wrappedProject": "../../LegacyClassLibrary/LegacyClassLibrary.csproj",
"bin": {
"assembly": "../../LegacyClassLibrary/obj/{configuration}/LegacyClassLibrary.dll",
"pdb": "../../LegacyClassLibrary/obj/{configuration}/LegacyClassLibrary.pdb"
}
}
}
}
Note the framework version. If there is a mismatch then it will fail resolving the dependencies. For example if your MySolution.Models targets .Net 4.6 and thus when wrapped has a dnx46 framework reference but your MySolution.Server project has a reference to dnx452 (in the project.json for MySolution.Server) then it will fail when resolving the dependency to MySolution.Models.
The you quoted could probably be improved. It means that it could not resolve the dependency due to one of the following reasons
It could not find a MySolution.Models assembly (either source code or compiled dll) based on the paths it uses (starting from projects parameter in global.json).
It found a MySolution.Models assembly (either source code or compiled) BUT it was an invalid version. Check version in Models project vs the reference in Server project.json.
It found a MySolution.Models assembly but it can't resolve framework dependencies (i.e. Models requires dnx46 but Server only targets dnx452).
In my experience the third one if the most common. For the DNX templates in VS 2015 RC the default full framework being targeted is dnx452 (or is it dnx451?). New csproj projects will be 4.6 (dnx46) by default and existing projects could be just about anything.
An alternative solution:
I have found the following alternative to result in easier dependency management. If MySolution.Models will only be used by DNX projects then just convert it to a DNX project move it into the source folder and reference it directly. It will be part of the source compilation and you gain the benefits of dynamic compilation.
If MySolution.Models will be referenced by both DNX and legacy (csproj) projects then you can create a side-by-side xproj and project.json files for Models. They will be ignored by the legacy project. In essence you have both a legacy and DNX project using the same source files. You can then just like above reference it directly. Keep in mind the folder structure if the models folder is not under /src (and it probably isn't if this was an existing project) then you will either need to move it or add a reference to the folder in global.json. That sounded more confusing that it really is. Just keep in mind for a DNX project the global.json defines the relative paths to where DNX can find source code. The DNX also can resolve dependencies by nuget or searching the GAC but that is beyond what you are trying to do.
I am trying to make a build script for a .NET solution which consists of several c# projects and one custom project. The custom project can be build by devenv but msbuild chokes on it.
I would like MSBuild to ignore the custom project because I'm already building it with an Exec task. I actually need MSBuild to not even open the custom .proj file because it's in JSON format and thus causes MSBuild to crash out.
The /BuildProjectReferences=false switch doesn't work. MSBuild still tries to read the custom project file. Is there any way around this?
This question stemmed from a project which had a SilverFrost Fortran project alongside several c# class libraries. The solution would only compile using devenv. Msbuild would throw an error on the Fortran project because it doesn't use the standard .csproj format.
Even with /BuildProjectReferences=false, msbuild would try to read the Fortran project and throw an error. The workaround I discovered was to wrap the msbuild task in an nant task which does the following:
Invokes the Fortran command line compiler
Removes all references to the Fortran project from other .csproj projects using the xmlpoke task
Replaces said references with a direct dll reference to the Fortran compiler output
Invokes Msbuild on the modified solution
Instead of building your solution once by MSBuild, try to build each project one by one. In this case, you can ignore the desired project. You can also define your own "Exec"-based build in this new script.
In your project that uses the custom project, can you right click on the Project Dependencies and remove the custom project from the list? You can refer to the custom project's binary output instead.
I have a project which uses C++/CLI to implement a GUI and some background processing to talk to a sensor. I've got that all working and a lot of the comms stuff which we use to communicate the the sensor sits in a .dll. The problem is that I'd like to combine the library into the main executable to avoid having to worry about distributing .dlls.
I've got a demo project which works fine using a .lib but when I try and switch the mani code body to produce a .lib instead of .dll I get the following error:
1>------ Build started: Project: MyTool, Configuration: Debug Win32 ------
1>Compiling...
1>stdafx.cpp
1>.\stdafx.cpp : fatal error C1113: #using failed on 'c:\projects\MyTool\debug\MyLib.lib'
A bit of googling suggests this happens when you've not got the MSIL switch applied, but it's definitely in there in the library project.
I have a mixture of managed and unmanaged code in both my demo project and the real thing so I'm really struggling to see what the problem is here.
Any suggestions would be very gratefully received!
I am guessing a bit, but I suspect the "MyTool" project has the "MyLib" project as one of its "references" ("Project" menu >> Properties >> Common Properties >> References).
When you change the type of the MyLib project to a LIB instead of a DLL, you need to remove "MyLib" from the project references. You then update the project dependencies of the solution ("Project" menu >> "Project Dependencies...") so that MyTool depends on MyLib.
If you are linking to a mixed mode (managed/native) DLL you may get this error. Which you shouldn't if the project uses CLR even if one of the source files doesn't. But anyway, if that is the case, then try removing the reference from Project|Properties|Common Properties|References and then re-adding it.
I also ran into this. The reason it was failing was because I was compiling my native/managed C++ DLL to target .NET 4.0. And the DLL I was #using was a .NET 2.0 DLL. As such it was failing, even though the paths and file names lined up perfectly. In this case the error message was absolutely of no help at all.
I solve it by updating the independent DLL to .NET 4.0. So that both assemblies were using the same .NET framework.