81

I'm packaging example code for an SDK distribution. In the distribution, the relative path from code to the SDK assemblies is different from the build machine. For example:

Distribution

csharp/bin/assembly.dll
example/ex1/ex1.csproj

Build Machine

foo/sdk/csharp/bin/assembly.dll
bar/baz/quux/ex1/ex1.csproj

Assume that I can't move anything. Is there a way I can instruct ex1.csproj to look in both

../../csharp/bin/ and ../../../../foo/sdk/csharp/bin/ for assembly.dll?

In C++ I'd put the dependency path in a standalone property sheet and distribute a different version with the SDK. But C# doesn't have property sheets, and I don't want to maintain two versions of the full project.

I've seen this question which states that I can't use multiple <HintPath> tags, so I'm looking for another way to approximate the same behavior.

Community
  • 1
  • 1
japreiss
  • 11,111
  • 2
  • 40
  • 77
  • 1
    Just add a property to the .csproj file and use it in the HintPath. You can override its value on the build machine with the msbuild.exe /p option. – Hans Passant Mar 27 '13 at 19:17

6 Answers6

131

The simplest way since only ONE HintPath can be used is to use the all-so-nice Condition attribute like this:

<Reference Include="TheAssembly">
    <HintPath Condition="Exists('..\My\Assembly\Path')">..\My\Assembly\Path\TheAssembly.dll</HintPath>
    <HintPath Condition="Exists('..\..\My\Assembly\Path')">..\..\My\Assembly\Path\TheAssembly.dll</HintPath>
    <HintPath Condition="Exists('..\..\..\My\Assembly\Path')">..\..\..\My\Assembly\Path\TheAssembly.dll</HintPath>
    <HintPath Condition="Exists('..\..\..\..\My\Assembly\Path')">..\..\..\..\My\Assembly\Path\TheAssembly.dll</HintPath>
    <HintPath Condition="Exists('..\..\..\..\..\My\Assembly\Path')">..\..\..\..\..\My\Assembly\Path\TheAssembly.dll</HintPath>
    <HintPath Condition="Exists('..\..\..\..\..\..\My\Assembly\Path')">..\..\..\..\..\..\My\Assembly\Path\TheAssembly.dll</HintPath>
    <HintPath Condition="Exists('..\..\..\..\..\..\..\My\Assembly\Path')">..\..\..\..\..\..\..\My\Assembly\Path\TheAssembly.dll</HintPath>
    etc...
</Reference>

So the answer to the question would be this:

<Reference Include="assembly">
    <HintPath Condition="Exists('..\..\csharp\bin')">..\..\csharp\bin\assembly.dll</HintPath>
    <HintPath Condition="Exists('..\..\..\..\foo\sdk\csharp\bin')">..\..\..\..\foo\sdk\csharp\bin\assembly.dll</HintPath>
</Reference>

If multiple conditions matches, the last one will be used.

Wolf5
  • 16,600
  • 12
  • 59
  • 58
  • how about this http://www.codeproject.com/Articles/184718/Take-advantage-of-Reference-Paths-in-Visual-Studio – Gurpreet Jun 16 '15 at 16:03
  • Yup. I think that would work as well. Same answer as Filip has written. – Wolf5 Jun 17 '15 at 08:50
  • I think this breaks as soon as more than one condition is truthy. That is, when both `csharp\bin` and `..\..\foo\sdk\csharp\bin` exist. – Steven Liekens Jun 17 '15 at 13:02
  • 3
    I does not break if that was to happen, as Visual Studio takes the first or last. It ignores that you have multiple as if the extras did not exist. – Wolf5 Jun 18 '15 at 09:24
  • HI , excellent i was looking for the same solution , i need one more info like can we check if a variable value is true in some text file then load the one dll and if false the other dll ? – ansar Dec 03 '15 at 05:55
  • 1
    To do that you would probably need to create a function in your .csproj using the tag that ouputs is true or false. Then run it it before the task and check on its output in the Condition attribute. (example of usingtask: http://stackoverflow.com/questions/13548851/msbuild-usingtask-resolve-references) – Wolf5 Dec 03 '15 at 08:28
  • @Wolf5 can you please have a look at http://stackoverflow.com/questions/34059733/multiple-hint-paths-in-csproj-file-from-an-text-file-variable – ansar Dec 21 '15 at 09:15
  • 1
    Amazing. Been having so much trouble with a project that was shared between multiple solutions. Thanks. – Fishcake Aug 09 '16 at 11:20
  • 1
    I think it is also worth mentioning that in case more than one such conditions are evaluated as true the LAST true condition in the list of HintPaths will be used by Visual Studio. It looks like that true conditions are overriding each other while they are being processed one by one. I haven't tried it with MSBuild yet but I assume it's the same. – Peter Bulyaki Sep 11 '17 at 10:28
  • Here is a Regex find+replace I used to accommodate NuGet references in projects that are shared in two solutions, where the second solution is nested in a subfolder of the first solution: --- find: ^(.*?)(.*?)packages(.*?)$ --- replace: $1..\packages$3\n$1..\..\packages$3 – Al Albers Sep 26 '20 at 01:32
25

I found a hacky solution that works for my case, where the parent directory is guaranteed to be different somewhere up the tree:

<Choose>
  <When Condition="Exists('$(MSBuildProjectDirectory)\..\..\example')">
    <ItemGroup>
      <Reference Include="Assembly ...">
        <HintPath>..\..\csharp\bin\assembly.dll</HintPath>
      </Reference>
    </ItemGroup>
  </When>
  <Otherwise>
    <ItemGroup>
      <Reference Include="Assembly ...">
         <HintPath>..\..\..\..\..\foo\sdk\csharp\bin\assembly.dll</HintPath>
      </Reference>
    </ItemGroup>
  </Otherwise>
</Choose>
japreiss
  • 11,111
  • 2
  • 40
  • 77
  • 5
    `` from Wolf5 answer below is betterer. – berkus Jul 29 '15 at 07:38
  • @berkus I think this answer is better for larger projects with a lot of references. You can add and remove references without having to add the directory condition each time and it looks cleaner imo. – Karl Essinger Jan 03 '22 at 15:33
  • This answer is better for my scenario that some fundamental library-projects (eg text-processing libraries) are referenced by both main-level project (eg webapp) and second-level projects (eg data-layer libraries) – Joe Lau Mar 12 '22 at 11:07
9

I am using this solution without any problem:

<Reference Include="log4net">
  <HintPath>
     $(SolutionDir)packages\log4net.2.0.8\lib\net45-full\log4net.dll
  </HintPath>
</Reference>

Got it from here: use hint paths relative answer from LeonidVasilyev

JReis
  • 99
  • 1
  • 3
7

Add the secondary path as follows to the general property group. in the csproj file

<PropertyGroup>
    <ReferencePath>..\..\..\..\..\foo\sdk\csharp\bin\</ReferencePath>
    ...
</PropertyGroup>

The ReferencePath property is intended to be specified when executing MsBuild, but it will work fine like this.

Filip De Vos
  • 11,568
  • 1
  • 48
  • 60
0

You could subst the /csharp/bin folder into a drive (differently on each machine), for example X: and then reference X:\ or X:\bin on both machines, as the path will now be the same.

Axarydax
  • 16,353
  • 21
  • 92
  • 151
  • This is too complicated. The SDK user must be able to unzip the SDK, open the example project, and compile it with no extra steps. – japreiss Mar 27 '13 at 18:31
  • I would still go along the same way - using user's SDK path as reference and do some folder links trickery on build machine to make it run there too – Axarydax Mar 27 '13 at 18:34
  • That means I'd create a directory link ..\..\csharp to point to ..\..\..\..\foo\sdk, so the ex1.csproj would find the assembly in ..\..\csharp – Axarydax Mar 27 '13 at 18:35
-2

Simply add the build server location of the DLLs as a Reference path on the project. Seems to do the trick nicely and is very simple. Works only if you know the build server's folder of the DLLs.

enter image description here

Ian Vink
  • 66,960
  • 104
  • 341
  • 555
  • I'm using Visual Studio 2012 and it's storing the reference path details in the csproj.user file not csproj What version of Studio are you using or is there a setting somewhere to get Studio to put the reference path details in the csproj file? – John Dyer Jul 25 '14 at 17:18
  • Down-voting as this only updates the csproj.user and the path is expected to exist on the developers machine when using GUI shown. – rob Jun 23 '15 at 08:57