MSBuild 4.5 is ignoring project dependencies - msbuild

I have a VS2010 solution file with over 20 projects in it, and some of the projects have dependencies on other projects from within the solution.
I also have multiple build configurations set up for different purposes, but I've trimmed down the projects to be built to just include the bare minimum number of projects.
For example, I have three libraries (A, B, and C), a website project, and a website deployment project (VS2010). The website has references to libraries A and B, and B in turn has a reference to C. My build configuration only has the website and the deployment project checked. When I check the project dependencies from the solution's properties, the website correctly lists the libraries, and B shows C, as expected.
When I run the build against my build config from within VS2010, it works completely fine, but when I run MSBuild on the solution specifying my configuration (as follows), it just results in a bunch of errors.
msbuild.exe mysolution.sln /t:Build /p:configuration=MyConfig
Here's an example of the errors I get:
Services\IService.cs(11,63): error CS0246: The type or namespace name 'Priority' could not be found (are you missing a using directive or an assembly reference?)
I noticed this happening on my build server (TeamCity v7.1.2), but I can reproduce it on multiple machines, and I narrowed it down to an issue with MSBuild, itself.
It only started happening after I installed .NET 4.5 (and 2 security patches), so I uninstalled it, reinstalled .NET 4.0 (with patches) since it was also removed, then tried the same command, and it worked just fine.
This leads me to believe that something was changed or broken in MSBuild with .NET 4.5, but nothing in their documentation seems to talk about this kind of change.
MSBuild 4.5 documentation: http://msdn.microsoft.com/en-us/library/hh162058.aspx
I've even tried passing BuildProjectDependencies=true to MSBuild, and it comes up stating that it skipped the other projects because they were not selected in the configuration manager, which is correct and intentional.
The only way I got it to work with MSBuild 4.5 was to go back and select the projects that were being skipped, but since the actual solution is a bit more complex with the dependency chain, I don't want to have to try to manage the configurations manually each time we update the solution with new projects or dependencies. It SHOULD be automatic.
Any ideas with what I'm doing?

If you still not resolved this issue, lets try some blind shots:
Msbuild have confirmed bug when it sometimes generates wrong build order based on followed dependencies. Try to build not entire solution but exact project you want to build - like msbuild.exe YourWebsiteProject.csproj /t:Clean;Build /p:configuration=MyConfig. Is the problem still persists ?
Ensure that YourWebsiteProject and your libs (B and C) have proper references - on project, not on dll in another project folder (simplest way to fix that - remove from B reference to C and re-add it again, just ensure that you are adding project reference and not browsing to bin\Debug for very C.dll). Is issue still there ?
If you could provide detailed or even diagnostic msbuild log (add to your msbuild command line following switches /ds /v:diag and then share teamcity full build log somewhere or pipe command line log to file) or some sample projects set where I can reproduce this behaviour - it could help a lot with issue resolving.

I thought I'd update my previous answer, as I've spent a lot of time and effort creating my own workaround to this problem. The work around is a bit more comprehensive than simply living with the problem, but I've attempted to both eliminate the issue and insulate ourselves against future shocks like this.
MSBuild has been demoted from working with solutions, configurations or otherwise. MSBuild is just asked to compile projects in isolation. The order this happens is calculated by a Powershell script that parses ours solutions and projects to work out the best Just-In-Time build execution plan.
Key to this (and I think you might find helpful) are the following snippets:
Identifying my solutions
I have a list of all the solutions in my platform, and I essentially iterate over each of these.
$buildPlan = (
#{
solutions = (
#{
name = "DataStorage"
namespace = "Platform.Databases"
},
#{
name = "CoreFramework"
},
#{
namespace = "Platform.Server"
name = "Application1"
},
#{
namespace = "Platform.Server"
name = "Application2"
},
#{
namespace = "Platform.Client"
name = "Application1"
}
)
})
I have some logic that helps to translate this into actual physical paths, but its very bespoke to our needs, so I won't list it here. Sufficed to say, from this list, I can find the .sln file I need to parse.
Parsing the solution file for projects
With each solution, I read the .sln file and attempt to extract all the projects contained within that I will need to build later.
So firstly, identify all projects in my
$solutionContent = Get-Content $solutionFile
$buildConfigurations += Get-Content $solutionFile | Select-String "{([a-fA-F0-9]{8}-([a-fA-F0-9]{4}-){3}[a-fA-F0-9]{12})}\.(Release.*)\|Any CPU\.Build" | % {
New-Object PSObject -Property #{
Name = $_.matches[0].groups[3].value.replace("Release ","");
Guid = $_.matches[0].groups[1].value
}
} | Sort-Object Name,Guid -unique
And then translate this into a nice list of projects that I can iterate over later.
$projectDefinitions = $solutionContent |
Select-String 'Project\(' |
ForEach-Object {
$projectParts = $_ -Split '[,=]' | ForEach-Object { $_.Trim('[ "{}]') };
$configs = ($buildConfigurations | where {$_.Guid -eq $projectParts[3]} | Select-Object Name)
foreach ($config in $configs)
{
$santisiedConfig = if ([string]::IsNullOrEmpty($config.Name)){"Release"}else{$config.Name}
if ($projectParts[1] -match "OurCompanyPrefix.")
{
New-Object PSObject -Property #{
Name = $projectParts[1];
File = $projectParts[2];
Guid = $projectParts[3];
Config = $santisiedConfig
}
}
}
}
Load the Visual Studio project
From my parsing of the solution file, I now have a list of projects per solution, which crucially contains the relative File Path from the solution root to find the project.
$projectDefinition = [xml](Get-Content $csProjectFileName)
$ns = #{ e = "http://schemas.microsoft.com/developer/msbuild/2003" }
$references = #();
1) Identifying external project references
$references += Select-Xml -Xml $projectDefinition -XPath "//e:Project/e:ItemGroup/e:Reference" -Namespace $ns | % {$_.Node} | where {$_.Include -match "OurCompanyPrefix" -and $_.HintPath -notmatch "packages"} | % {$_.Include}
2) Identifying internal project references
$references += Select-Xml -Xml $projectDefinition -XPath "//e:Project/e:ItemGroup/e:ProjectReference" -Namespace $ns | % { $_.Node.Name }
3) Following "Post-Build" events as external references
$references += Select-Xml -Xml $projectDefinition -XPath "//e:Project/e:PropertyGroup/e:PostBuildEvent" -Namespace $ns | where {(!([String]::IsNullOrEmpty($_.Node.InnerText)))} | % {
$postBuildEvents = $_.Node.InnerText.Split("`n")
$projectsReferencedInPostBuildEvents = $postBuildEvents | Select-String "\(SolutionDir\)((\w|\.)*)" | % {$_.Matches[0].Groups[1].Value}
if ($projectsReferencedInPostBuildEvents -ne $null)
{
Write-Output $projectsReferencedInPostBuildEvents | % { $matchedProject = $_; ($releaseConfiguation | ? {$_.File -match $matchedProject}).Name }
}
}
And, since we're at it, get some basic output information too
This is handy when it comes to iterating my list of projects to build, as know where to push the output, or where to find the output of a dependent.
$assemblyName = (Select-Xml -Xml $projectDefinition -XPath "//e:Project/e:PropertyGroup/e:AssemblyName" -Namespace $ns).Node.InnerText
$outputPath = (Select-Xml -Xml $projectDefinition -XPath "//e:Project/e:PropertyGroup[contains(#Condition,'Release|')]/e:OutputPath" -Namespace $ns).Node.InnerText
And at the end of it all
We just need to make sure we don't have any duplicates, so I record just the distinct dependencies of this particular code project:
$dependendents = #();
if ($references -ne $null)
{
$buildAction.project.dependencies += $references | where {(!([string]::IsNullOrEmpty($_))) -and ($_ -match "OurCompanyPrefix\.(.*)")} | % { $_.ToLower()} | Select -unique
}
I would hope this provides you with enough information for parsing your SLN and PROJ files. How you would choose to capture and store this information I think would depend entirely up to you.
I'm in the middle of writing quite an in-depth blog post about this, which will contain all the trimmings and framework I've eluded to above. The post isn't ready yet, but I will be linking to it from an earlier post : http://automagik.piximo.me/2013/02/just-in-time-compilation.html - Since this change by Microsoft nearly derailed this work!
Cheers.

In .NET 4.5, the default value of the OnlyReferenceAndBuildProjectsEnabledInSolutionConfiguration property in C:\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets was changed from false to true. As its name implies, this property causes MSBuild to ignore references to projects that are excluded from the build configuration.
(It's possible that Microsoft made this change in response to a Connect bug I filed against MSBuild 4.0, even though I warned them that the change breaks the build.)
The workaround is simple: Set the property back to false in the first <PropertyGroup> section of each of your projects:
<PropertyGroup>
...
<OnlyReferenceAndBuildProjectsEnabledInSolutionConfiguration>false</OnlyReferenceAndBuildProjectsEnabledInSolutionConfiguration>
</PropertyGroup>

I've encountered the exact same problem. The two work-around's I've managed to find are both unacceptable for the long term, but they do overcome the initial issue of getting old our build processes working with the new 4.5 stack.
Swap the project references with file references
Create compound build configurations
I've opted for #2, as file references mean the developers would lose out on real-time intellisense etc.
The compound configurations are simply this:
Release Server -> All Server Projects
Release Consumer -> "Release Server" + Client Projects
The problem seems to be, that if a project is not included in the current/active build configuration, that it will not include it as a referenced dependency. So, by adding the dependencies into the configuration, the projects will at least compile.
Both ugly, but at least it get's me out of a tight spot.
Matt

I get the sense this issue has multiple causes. I tried most of the solutions here. I cannot change our build server to use a PS script so that solution was out. Nothing I could try worked.
Finally, I deleted my solution and started a new one. The new solution worked. After diffing the broken solution with the working solution, I found the original solution was was missing lines. Each dependency that would not compile was missing this line:
{A93FB559-F0DB-4F4D-9569-E676F59D6168}.Release|Any CPU.Build.0 = Release|Any CPU
Note 1: The GUID will change from dependency to dependency.
Note 2: You can find lines like this one under the "GlobalSection(ProjectConfigurationPlatforms) = postSolution" part of the solution file.
My build.proj file says build "Release" using the "Any CPU" platform. Because MSBuild could not find this line it did not build this dependency. This results in the "error CS0246: The type or namespace could not be found" message.
If you are curious, someone had set this solution to the "x86" platform (which is wrong for us). I changed it to "Any CPU" (along with several other changes). Visual Studio did not add the corresponding lines to the solution file. Everything built fine in the IDE, but MSBuild started throwing errors.

I found that MSBuild is building projects from a solution in order in which they are declared in the .sln file. So if you reorder them with text editor you can fix the order for MSBuild.

Related

cmake - Troubleshooting LINK_DIRECTORIES

My cmake file contains this statement:
link_directories("C:/path/to/lib")
In the properties of the generated Visual Studio solution I see:
/LIBPATH:"C:/path/to/lib"
/LIBPATH:"C:/path/to/lib/Release"
I am not sure where that second LIBPATH is coming from.
Within the cmake file I would like to print out the LINK_DIRECTORIES property, how would I do that? I have tried various things along the lines of...
cmake_print_properties(DIRECTORIES xxx PROPERTIES LINK_DIRECTORIES)
...but I am not sure what parameters to pass.
Edit OK this worked:
cmake_print_properties(DIRECTORIES . PROPERTIES LINK_DIRECTORIES)
It prints out:
Properties for DIRECTORY .:
..LINK_DIRECTORIES = "C:/path/to/lib"
So where is that second LIBPATH coming from? I don't know whether it is generated by cmake somehow, or added later by Visual Studio.

how to customize nix package builder script

The root problem is that nix uses autoconf to build libxml2-2.9.14 instead of cmake, and a consequence of this is that the cmake-configuration is missing (details like version number, platform specific dependencies like ws2_32 etc which are needed by my project cmake scripts). libxml2-2.9.14 already comes with cmake configuration and works nicely, except that nix does not use it (I guess they have their own reasons).
Therefore I would like to reuse the libxml2-2.9.14 nix package and override the builder script with my own (which is a trivial cmake dance).
Here is my attempt:
defaultPackage = forAllSystems (system:
let
pkgs = nixpkgsFor.${system};
cmakeLibxml = pkgs.libxml2.overrideAttrs( o: rec {
PROJECT_ROOT = builtins.getEnv "PWD";
builder = "${PROJECT_ROOT}/nix-libxml2-builder.sh";
});
in
Where nix-libxml2-builder.sh is my script calling cmake with all the options I need. It fails like this:
last 1 log lines:
> bash: /nix-libxml2-builder.sh: No such file or directory
For full logs, run 'nix log /nix/store/andvld0jy9zxrscxyk96psal631awp01-libxml2-2.9.14.drv'.
As you can see the issue is that PROJECT_ROOT does not get set (ignored) and I do not know how to feed my builder script.
What am I doing wrong?
Guessing from the use of defaultPackage in your snippet, you use flakes. Flakes are evaluated in pure evaluation mode, which means there is no way to influence the build from outside. Hence, getEnv always returns an empty string (unfortunately, this is not properly documented).
There is no need to refer to the builder script via $PWD. The whole flake is copied to the nix store so you can use your files directly. For example:
builder = ./nix-libxml2-builder.sh;
That said, the build will probably still fail, because cmake will not be available in the build environment. You would have to override nativeBuildInputs attribute to add cmake there.

OpenCover: Unable to run coverage for more than 2 assemblies

I am trying to use OpenCover With XUnit and MSBuild for our project and it works fine with one or two assemblies. But when the number of assemblies goes more than 2 it throws the below error:
EXEC : error : unknown command line option: MyProj.UnitTest.dll
[C:\CMR\Source\trunk\Build\Script\CMR.msbuild]
Committing...
No results, this could be for a number of reasons. The most common reasons are:
1) missing PDBs for the assemblies that match the filter please review the output
file and refer to the Usage guide (Usage.rtf) about filters.
2) the profiler may not be registered correctly,
please refer to the Usage guide and the -register switch.
Thought the problem would be with the 3rd assembly i added, so ran it individually again it worked fine. Below is the script I used:
<Exec Command='$(OpenCoverPath)\OpenCover.Console.exe "-target: $(XUnitPath)\xunit.console.exe" "-targetargs:C:\MyPath\UnitTest1.dll C:\MyPath\UnitTest2.dll C:\MyPath\UnitTest3.dll /noshadow" "-output:c:\OpenCoverReport\coverage.xml"'/>
And this is my assumption, for the purpose of posting here i had put paths of dll as C:\MyPath\UnitTest.dll but indeed the path is so huge and there are multiple assemblies with huge path. Does it has anything to do with this error?
try the -targetdir option of OpenCover
e.g.
<Exec Command='$(OpenCoverPath)\OpenCover.Console.exe -targetdir:"C:\MyPath" "-target: $(XUnitPath)\xunit.console.exe" "-targetargs:UnitTest1.dll UnitTest2.dll UnitTest3.dll /noshadow" "-output:c:\OpenCoverReport\coverage.xml" '/>

Why won't MSBuild build a project with a dot in the name?

The Story So Far
I've got a nice solution with a desktop application project, a few library projects, and a couple of development tools projects (also desktop applications). At the moment, my build server outputs all of the code into one OutputPath. So we end up with
drop-x.y.z\
Company.MainApplication.exe <-- main application
Company.MainApplicationCore.dll <-- libraries
Helper.exe <-- developer tools
Grapher.exe
Parser.exe
... <-- the rest of the output
But, we're growing up and people outside of our team want access to our tools. So I want to organize the output. I decided that what we would want is a different OutputPath per executable project
drop-x.y.z\
Company.MainApplication\
Company.MainApplication.exe <-- main application
Company.MainApplicationCore.dll <-- libraries
... <-- application specific output
Helper\
Helper.exe <-- developer tools
... <-- tool specific output
Grapher\
Grapher.exe
...
Parser\
Parser.exe
...
What I Did
I found this simple command. I like it because it retains all the Solution working-dir context that makes msbuild a pain.
msbuild /target:<ProjectName>
For example, from my solution root as a working directory, I would call
PS> msbuild /target:Helper /property:OutputPath="$pwd\out\Helper"
I'm testing this from PowerShell, so that $pwd resolves to the full path to my working directory, or the Solution root in this case. I get the output I desire.
However, when I run this command
PS> msbuild /target:Company.MainApplication /property:OutputPath="$pwd\out\Company.MainApplication"
I get the following error output (there's no more information, I ran with /verbosity:diagnostic)
The target "Company.MainApplication" does not exist in the project.
What I Need
The command fails on any project with a dot or dots in the name. I tried with many combinations of working directories and properties. I tried several ways of escaping the property values. I also tried running the command from a <Task> in a targets file.
I need to know either
A) How to fix this command to work property
B) How to achieve the same output with minimal friction
Try using an underscore as an escape character for the dot in the target parameter, e.g.
msbuild /target:Company_MainApplication /property:OutputPath="$pwd\out\Company.MainApplication"
Specify the target after the -target: switch in the format :. If the project name contains any of the characters %, $, #, ;, ., (, ), or ', replace them with an _ in the specified target name.
https://learn.microsoft.com/en-us/visualstudio/msbuild/how-to-build-specific-targets-in-solutions-by-using-msbuild-exe?view=vs-2019
Dan Nolan's answer and comments are correct. Just want to supplement the Microsoft documentation.
The /targets: switch is to identify a <Target to run in the project file. You need to supply your .csproj file as a an argument that is not prefixed by a /xx option marker.
You might also want to work based on the .sln file. In that case, you still dont specify the project in the .sln to build in this manner. I'll leave you to search up the correct syntax in case that's what you end up doing.

What's the best way to change the namespace of a highly referenced class?

I am attempting to move a highly referenced class from one namespace to another. Simply moving the file into the new project which has a different root namespace results in over 1100 errors throughout my solution.
Some references to the class involve fully qualified namescape referencing and others involve the importing of the namespace.
I have tried using a refactoring tool (Refactor Pro) to rename the namespace, in the hope all references to the class would change, but this resulted in the aforementioned problem.
Anyone have ideas of how to tackle this challenge without needing to drill into every file manually and changing the fully qualified namespace or importing the new one if it doesn't exist already?
Thanks.
Try to use Resharper. I have used it in the past for refactoring highly referenced namespaces both fully qualified and imported with no problems at all.
Here's a Powershell script that I have used to accomplish a painful namespace rename. In my situation there were about 5000 VB.Net and C# source files making heavy use of a common library (CSLA). For reasons not worth discussing, I needed to rename the namespace Csla to CslaLegacy. With minimal effort, you should be able to adapt this script to your specific needs. The script recursively searches the source tree looking for .vb and .cs files. The $repValues hash table contains the strings that need to be replaced. A variation of this script can also be used to update project references, should your rename include an assembly name change. You can add a call to your source control tool to checkout the file before the modification. I originally did this for TFS, but found it slow to execute tf.exe. In the end it was much faster to simply checkout the entire source tree before running the script. I use PowerGUI script editor for debugging and running powershell scripts.
$root = "c:/path/to/your/source"
cd $root
$files = Get-ChildItem -Path $root -Recurse -include *.cs,*.vb
$repValues =
#{
'using Csla;' = 'using CslaLegacy;';
'using Csla.Validation;' = 'using CslaLegacy.Validation;';
'using Csla.Data;' = 'using CslaLegacy.Data;';
'Imports Csla' = 'Imports CslaLegacy';
'Imports Csla.Validation' = 'Imports CslaLegacy.Validation';
}
$stmtsToReplace = #()
foreach ($key in $repValues.Keys) { $stmtsToReplace += $null }
$repValues.Keys.CopyTo($stmtsToReplace, 0)
foreach ($file in $files)
{
$path = [IO.Path]::Combine($file.DirectoryName, $file.Name)
$sel = Select-String -Pattern $stmtsToReplace -Path $path -l
if ($sel -ne $null)
{
write "Modifying file $path"
(Get-Content -Encoding Ascii $path) |
ForEach-Object {
$containsStmt = $false
foreach ($key in $repValues.Keys)
{
if ($_.Contains($key))
{
$_.Replace($key, $repValues[$key])
$containsStmt = $true
break
}
}
if (!$containsStmt) { $_ }
} |
Set-Content -Encoding Ascii $path
}
}
I don't if it's going to be helpful in your case.
Modify the namespace, VS IDE will show you little red rectangular at the end of the namespace.
Press Ctrl+. and select the option you like most.
If you're using tool like ReSharper, click on the namespace and press Ctrl+R. Follow instruction in Rename Namespace dialog box.
It seems like you shouldn't run into too much trouble doing a global search and replace on the fully qualified name. So do a search-for-all on oldspace::myclass and replace it with newspace::myclass. Adding lines to the top of a file isn't terribly hard either, and you could probably replace
using oldspace;
with
using oldspace;
using newspace;
There are some risks with an approach like this, of course. It's quite easy to cause yourself subtle problems.
Another reason to switch to c sharp. Refactoring of source code built in for free in VS 2008.