Welcome Guest! To enable all features please Login or Register.

Notification

Icon
Error

Conditional Project/DLL reference problem
Espresso
#1 Posted : Wednesday, February 27, 2013 7:16:14 PM(UTC)
Rank: Newbie

Groups: Registered
Joined: 2/27/2013(UTC)
Posts: 4
Location: United States of America

Thanks: 2 times
Our team of 8 developers all use NCrunch and we are having problems with some of our larger solutions with frequent NCrunch build/retest failures due to DLL references.

First off, I will mention that I have read and (think) I fully understood the Project Atomicity page (http://www.ncrunch.net/documentation/considerations-and-constraints_project-atomicity), and I've searched the forums and found this thread: http://forum.ncrunch.net...ssembly-References.aspx which might be relevant, but doesn't go very far.

While I understand why it's easier on NCrunch to use (and require) project references to determine inter-project dependencies, and that is uses msbuild so doesn't have access to solution-defined dependencies, requiring project references is not a useful way for us to reference projects within several of our solutions (at least not all the time). This is because we have several projects that are shared common "base" code across several applications, and those have their own solutions, their own code repositories, and their own separate CI builds that still have to run properly. On developer machines, these solutions are checked out in sibling folders under a parent dev folder. To attempt to illustrate, let's say we have the following:

"Base" solution 1 (versioned independently and has its own repository and CI build):
Base Project A
Base Project B (project references to A)
Base Project C (project references to A and B)

"Base" solution 2 (versioned independently and has its own repository and CI build):
[solution contains a Solution Folder which includes the A, B, and C projects, which are only built under configurations other than Debug and Release]
Base Project X
Base Project Y (project reference to X; dll reference to A)
Base Project Z (project references to X and Y; dll references to A, B, and C)

Product solution P (versioned independently and has its own repository and CI build):
[solution contains a Solution Folder which includes the A, B, C, X, Y, and Z projects, which are only built under configurations other than Debug and Release]
Product Project P (dll references to A, B, X, and Y)
Product Project P.Tests (project reference to P, dll references to A, B, C, X, Y, and Z)

For our purposes, we're talking about problems working in Product solution P.

It's probably obvious that this can work in NCrunch with proper references to required libraries everywhere, but that dependencies aren't clear to NCrunch. This means that rebuilding everything queues the builds in a fairly random-looking order, and the projects have to be manually re-compiled in the NCrunch Tests window in the proper order after some fail - sometimes with multiple tries - in order to get things rebuilt and re-tested properly. To resolve this, I tried making all references be project references for NCrunch. I created a new build configuration in each of the projects called "Local Debug" (copied from the Debug configuration), similar to this:

Code:
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Local Debug|AnyCPU' ">
    <DebugSymbols>true</DebugSymbols>
    <DebugType>full</DebugType>
    <Optimize>false</Optimize>
    <OutputPath>..\build\</OutputPath>
    <DefineConstants>DEBUG;TRACE</DefineConstants>
    <ErrorReport>prompt</ErrorReport>
    <WarningLevel>4</WarningLevel>
    <PlatformTarget>x86</PlatformTarget>
    <AllowUnsafeBlocks>false</AllowUnsafeBlocks>
  </PropertyGroup>


In order to allow NCrunch and local developer-machine builds to work, the project files were modified with conditional references. The old inter-project dll references were removed and conditions added in a separate block like this:

Code:

  <Choose>
    <!-- CI builds are done using Debug for testing builds, Release for less frequent RC builds, but this should also work locally when building in Debug also -->
    <When Condition="'$(Configuration)' == 'Debug' Or '$(Configuration)' == 'Release'">
      <!-- dll refs -->
      <ItemGroup>
          <Reference Include="Base.A">
		<SpecificVersion>False</SpecificVersion>
		<HintPath>..\..\Base.1\build\Base.A.dll</HintPath>
		<Private>False</Private>
	  </Reference>
	  <Reference Include="Base.B">
		<SpecificVersion>False</SpecificVersion>
		<HintPath>..\..\Base.1\build\Base.B.dll</HintPath>
		<Private>False</Private>
	  </Reference>
	  <Reference Include="Base.C">
		<SpecificVersion>False</SpecificVersion>
		<HintPath>..\..\Base.1\build\Base.C.dll</HintPath>
		<Private>False</Private>
	  </Reference>
      </ItemGroup>
    </When>
    <!-- typically used for Local Debug builds -->
    <Otherwise>
      <!-- project refs -->
      <ItemGroup>
           <ProjectReference Include="..\..\Base.1\Base.A\Base.A.fsproj">
		  <Project>{3B2DFDD4-9C93-4BAA-8778-848153C07E40}</Project>
		  <Name>Base.A</Name>
           </ProjectReference>
           <ProjectReference Include="..\..\Base.1\Base.B\Base.B.csproj">
		  <Project>{9A6AB529-04EC-427B-9F8C-35E36516DE8F}</Project>
		  <Name>Base.B</Name>
           </ProjectReference>
           <ProjectReference Include="..\..\Base.1\Base.C\Base.C.csproj">
		  <Project>{6D27F041-4110-4567-B5B4-6CB935F49402}</Project>
		  <Name>Base.C</Name>
           </ProjectReference>
	  </ItemGroup>
    </Otherwise>
  </Choose>
  <!-- unconditional project refs and other 3rd party dll refs here -->
  <ItemGroup>
      <ProjectReference Include="..\ProductP\ProductP.csproj">
	  <Project>{5517F38F-D6CD-431D-8163-F421EDFDBD14}</Project>
	  <Name>ProductP</Name>
      </ProjectReference>
  </ItemGroup>


The solution was modified so that a Local Debug solution configuration ran each project in its Local Debug configuration. The NCrunch configuration was then modified so that the projects that required conditional dll references had "Use build configuration" set to Local Debug. That resulted in errors like this:

Quote:
..\..\..\..\..\..\..\..\..\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets (1028)#0: The specified project reference metadata for the reference "..\..\Base.1\Base.A\Base.A.fsproj" is missing or has an invalid value: Project


After trying some different things, I wondered if NCrunch was just using the first references rather than using the conditions, so I tried inverting the condition and making the first block be the project references and the second block be the dll references. That resulted in the same error with "Use build configuration" set to Local Debug.

I also tried using a simpler version of the conditional blocks, using something like the following:

Code:

      <!-- dll refs -->
      <ItemGroup  Condition="'$(Configuration)' == 'Debug' Or '$(Configuration)' == 'Release'">
          <Reference Include="Base.A">
		<SpecificVersion>False</SpecificVersion>
		<HintPath>..\..\Base.1\build\Base.A.dll</HintPath>
		<Private>False</Private>
	  </Reference>
	  <Reference Include="Base.B">
		<SpecificVersion>False</SpecificVersion>
		<HintPath>..\..\Base.1\build\Base.B.dll</HintPath>
		<Private>False</Private>
	  </Reference>
	  <Reference Include="Base.C">
		<SpecificVersion>False</SpecificVersion>
		<HintPath>..\..\Base.1\build\Base.C.dll</HintPath>
		<Private>False</Private>
	  </Reference>
      </ItemGroup>

      <!-- project refs -->
      <ItemGroup  Condition="'$(Configuration)' != 'Debug' And '$(Configuration)' != 'Release'">
           <ProjectReference Include="..\..\Base.1\Base.A\Base.A.fsproj">
		  <Project>{3B2DFDD4-9C93-4BAA-8778-848153C07E40}</Project>
		  <Name>Base.A</Name>
           </ProjectReference>
           <ProjectReference Include="..\..\Base.1\Base.B\Base.B.csproj">
		  <Project>{9A6AB529-04EC-427B-9F8C-35E36516DE8F}</Project>
		  <Name>Base.B</Name>
           </ProjectReference>
           <ProjectReference Include="..\..\Base.1\Base.C\Base.C.csproj">
		  <Project>{6D27F041-4110-4567-B5B4-6CB935F49402}</Project>
		  <Name>Base.C</Name>
           </ProjectReference>
	</ItemGroup>


... which gives the same error also with "Use build configuration" set to Local Debug.

Ultimately, I could not get it working the way I want. I still have the conditionals in the project files, but I have the NCrunch configuration set with "Use build configuration" set to blank, which for our projects defaults to the Debug configuration. This seems to cause NCrunch to use the dll references, and seems to be preventing the "...specified project reference metadata..." error. It still doesn't build things in the right order, however, which leaves me back at square 1.

It's worth mentioning that for small solutions dll references aren't as much of a problem - or may be an infrequent one. I created a small sample solution to test this and NCrunch doesn't have as many problems with it (aside from the "...specified project reference metadata..." error when I configure the sample solution that way). In our actual Product P solution, however, 10 Base.1 projects are included in local builds of the Product P solution, and 4 of the Base.2 projects (which depend on Base.1 projects) are included in the solution.

This could likely be resolved by removing all of the Base.1 and Base.2 projects from the Product P solution and sticking with DLL references, but we include them because we tend to focus on a product at a time, and frequently modify code in Base.1 and Base.2 during development. Including them allows us to avoid multiple copies of VS open to rebuild Base then rebuild Product P, and to give better up-front visibility of the scope of changes among other things.

What I would like to know is whether the mis-handling of conditionals is expected in the current version of NCrunch, or am I using them in a way not handled by NCrunch that could be fixed with different syntax? Any other suggestions on how to address this without creating alternate copies of the projects would be appreciated.
Remco
#2 Posted : Wednesday, February 27, 2013 10:35:59 PM(UTC)
Rank: NCrunch Developer

Groups: Administrators
Joined: 4/16/2011(UTC)
Posts: 7,144

Thanks: 959 times
Was thanked: 1290 time(s) in 1196 post(s)
Hi,

Thanks for posting and for taking the time to explain such a detailed question. Assembly referencing has a tendency to be hard to describe in even the simplest of cases, and you've explained the issue very well.

The attempted solution you've described above (using the 'choose' tags) is a clever approach and is along the lines of what I would recommend in order to work around the problem. Unfortunately, NCrunch does also need to manipulate the project file in order to control which assemblies are being referenced at build time. This manipulation clashes with the choose element and gives unpredictable results during NCrunch builds (as you've experienced).

I've done some experimentation here and I believe I've managed to come up with a way that can make this work by using conditions on the reference and projectreference tags, for example:

<ProjectReference Include="..\ClassLibrary1\ClassLibrary1.csproj" Condition="$(NCrunch) == '1'">
<Project>{6811e408-1602-4ab1-8b02-757aa35d51fb}</Project>
<Name>ClassLibrary1</Name>
</ProjectReference>

<Reference Include="ClassLibrary1" Condition="$(NCrunch) != '1'">
<HintPath>..\ClassLibrary1\bin\debug\ClassLibrary1.dll</HintPath>
</Reference>

When building using Visual Studio, the Reference tag is used because the $(NCrunch) tag != 1. During NCrunch builds, the project reference is used instead. You could also use your custom build configuration (i.e. $(Configuration)) instead of $(NCrunch) depending upon your situation. The effect should be the same as what you have above, although because the structure of the project file isn't necessarily different, NCrunch is able to manipulate it without blowing up.

Give it a try and see if it does the trick.


Cheers,

Remco
1 user thanked Remco for this useful post.
Espresso on 2/28/2013(UTC)
Espresso
#3 Posted : Thursday, February 28, 2013 3:37:49 AM(UTC)
Rank: Newbie

Groups: Registered
Joined: 2/27/2013(UTC)
Posts: 4
Location: United States of America

Thanks: 2 times
TL;DR -- NCrunch appears to have a problem with 'use build configuration' values that contain white space... and perhaps some others


I tried your change on one of the failing projects, replacing the choose block with something like this:

Code:

      <ItemGroup>
          <Reference Include="Base.A" Condition="'$(Configuration)' == 'Debug' Or '$(Configuration)' == 'Release'">
		<SpecificVersion>False</SpecificVersion>
		<HintPath>..\..\Base.1\build\Base.A.dll</HintPath>
		<Private>False</Private>
	  </Reference>
	  <Reference Include="Base.B" Condition="'$(Configuration)' == 'Debug' Or '$(Configuration)' == 'Release'">
		<SpecificVersion>False</SpecificVersion>
		<HintPath>..\..\Base.1\build\Base.B.dll</HintPath>
		<Private>False</Private>
	  </Reference>
	  <Reference Include="Base.C" Condition="'$(Configuration)' == 'Debug' Or '$(Configuration)' == 'Release'">
		<SpecificVersion>False</SpecificVersion>
		<HintPath>..\..\Base.1\build\Base.C.dll</HintPath>
		<Private>False</Private>
	  </Reference>
           <ProjectReference Include="..\..\Base.1\Base.A\Base.A.fsproj" Condition="'$(Configuration)' != 'Debug' And '$(Configuration)' != 'Release'">
		  <Project>{3B2DFDD4-9C93-4BAA-8778-848153C07E40}</Project>
		  <Name>Base.A</Name>
           </ProjectReference>
           <ProjectReference Include="..\..\Base.1\Base.B\Base.B.csproj" Condition="'$(Configuration)' != 'Debug' And '$(Configuration)' != 'Release'">
		  <Project>{9A6AB529-04EC-427B-9F8C-35E36516DE8F}</Project>
		  <Name>Base.B</Name>
           </ProjectReference>
           <ProjectReference Include="..\..\Base.1\Base.C\Base.C.csproj" Condition="'$(Configuration)' != 'Debug' And '$(Configuration)' != 'Release'">
		  <Project>{6D27F041-4110-4567-B5B4-6CB935F49402}</Project>
		  <Name>Base.C</Name>
           </ProjectReference>
	</ItemGroup>


and setting the project to run using configuration Local Debug in NCrunch config, which still results in this:

Code:
..\..\..\..\..\..\..\..\..\Windows\Microsoft.NET\Framework\v4.0.30319\Microsoft.Common.targets (1028)#0: 
The specified project reference metadata for the reference "..\..\Base.1\Base.A\Base.A.fsproj" is missing or has an invalid value: Project


BUT, changing it to this:

Code:

      <ItemGroup>
          <Reference Include="Base.A" Condition="$(NCrunch) != '1'">
		<SpecificVersion>False</SpecificVersion>
		<HintPath>..\..\Base.1\build\Base.A.dll</HintPath>
		<Private>False</Private>
	  </Reference>
	  <Reference Include="Base.B" Condition="$(NCrunch) != '1'">
		<SpecificVersion>False</SpecificVersion>
		<HintPath>..\..\Base.1\build\Base.B.dll</HintPath>
		<Private>False</Private>
	  </Reference>
	  <Reference Include="Base.C" Condition="$(NCrunch) != '1'">
		<SpecificVersion>False</SpecificVersion>
		<HintPath>..\..\Base.1\build\Base.C.dll</HintPath>
		<Private>False</Private>
	  </Reference>
           <ProjectReference Include="..\..\Base.1\Base.A\Base.A.fsproj" Condition="$(NCrunch) == '1'">
		  <Project>{3B2DFDD4-9C93-4BAA-8778-848153C07E40}</Project>
		  <Name>Base.A</Name>
           </ProjectReference>
           <ProjectReference Include="..\..\Base.1\Base.B\Base.B.csproj" Condition="$(NCrunch) == '1'">
		  <Project>{9A6AB529-04EC-427B-9F8C-35E36516DE8F}</Project>
		  <Name>Base.B</Name>
           </ProjectReference>
           <ProjectReference Include="..\..\Base.1\Base.C\Base.C.csproj" Condition="$(NCrunch) == '1'">
		  <Project>{6D27F041-4110-4567-B5B4-6CB935F49402}</Project>
		  <Name>Base.C</Name>
           </ProjectReference>
	</ItemGroup>


works!

Given that success, I went back to using Choose, but with a check on $(NCrunch) instead of $(Configuration), and that also works:

Code:
  <Choose>
    <When Condition="'$(NCrunch)' != '1'">
      <!-- dll refs -->
      <ItemGroup>
        ...
      </ItemGroup>
    </When>
    <Otherwise>
      <!-- project refs -->
      <ItemGroup>
         ...
      </ItemGroup>
    </Otherwise>
  </Choose>


This is somewhat preferable to me just because it more cleanly separates the sets of references.

In the spirit of testing the boundaries of the failure a little bit, I tried a simpler condition, using

Code:
    <When Condition="'$(Configuration)' != 'Local Debug'">


... which failed again.

I checked the log again from the last failure, and found that the project build failure log shows this:
Code:
[19:07:48.347-BuildTask-72] Project configuration is:


What? The build configuration to use as defined in the NCrunch config for this project is Local Debug. Suspecting a white space problem, I changed this to 'Local Debug', which gave a different error, and "Local Debug", which gave yet another error.

After changing the name of the build configuration in the project file to remove whitespace and changing the 'use build configuration' value to match, it still failed. Trying alternate build configuration names like Foo in NCrunch config and csproj file worked.

/shrug

FWIW, each time, success or failure, the log reported
Code:
Project configuration is:
even when it seemed to clearly use the config named in 'use build configuration'.


For now I will either change the rest of the projects to be conditional on $(NCrunch) instead of the build configuration, or try finding a build configuration name that makes sense and works. Using $(NCrunch) has the downside that in developer environments VS is back to using dll references and relying on project dependency information from the solution settings instead of being able to use the alternate configuration to make each project use project level dependencies.

I will report back if I have problems with it.
Espresso
#4 Posted : Thursday, February 28, 2013 5:57:53 AM(UTC)
Rank: Newbie

Groups: Registered
Joined: 2/27/2013(UTC)
Posts: 4
Location: United States of America

Thanks: 2 times
Just for posterity, I'm following up with the version that seems to be working and doing the things I want:

Code:
<Choose>
    <When Condition="'$(NCrunch)' != '1' And '$(Configuration)' != 'Local Debug'">
      <!-- dll refs -->
      <ItemGroup>
      ...
      </ItemGroup>
    </When>
    <Otherwise>
      <!-- project refs -->
      <ItemGroup>
      ...
      </ItemGroup>
    </Otherwise>
  </Choose>


Remco
#5 Posted : Thursday, February 28, 2013 8:09:21 AM(UTC)
Rank: NCrunch Developer

Groups: Administrators
Joined: 4/16/2011(UTC)
Posts: 7,144

Thanks: 959 times
Was thanked: 1290 time(s) in 1196 post(s)
I'm glad you managed to get this to work, and I'm also pleasantly surprised that the Choose element does work with the NCrunch build manipulation - it must be more resilient than I give it credit for :)

Although I do think that there is something else going on here. I've just done some testing around configuration names that contain spaces and there doesn't seem to be any issues here (NCrunch doesn't parse this or segment the string in any way). Note that the 'Project configuration is:' is an internal message that actually just heads up a dump of the NCrunch project configuration (it doesn't describe the contents of the $(Configuration) property).

With the number of moving parts, I'm wondering if something might be going wrong in the injection or specification of the configuration value itself. It could be worth trying the MSBuild <Message> task to troubleshoot where the build is going wrong, but in all honesty as you now have the problem effectively solved any interest around this would be rather academic :)

Nice work!
Users browsing this topic
Guest
Forum Jump  
You cannot post new topics in this forum.
You cannot reply to topics in this forum.
You cannot delete your posts in this forum.
You cannot edit your posts in this forum.
You cannot create polls in this forum.
You cannot vote in polls in this forum.

YAF | YAF © 2003-2011, Yet Another Forum.NET
This page was generated in 0.092 seconds.
Trial NCrunch
Take NCrunch for a spin
Do your fingers a favour and supercharge your testing workflow
Free Download