We have a .targets file that implements a few custom build steps we
need for our projects. One of those tasks is to call a command
line tool to do some work with the assembly that was just compiled.
We store the .targets file and the commandline tool directly in our
source control (since they are both small) so that it is very easy for
developers to build. They just grab the latest source tree and
they have everything they need to build.
Here is today's problem: How can our .targets file find the
location of the command line tool Developers may have checked
out the source code anywhere. The commandline tools is in the
same directory as the .targets file, but I'm not sure how to even find
the location of the .targets file from within the .targets file
itself. The location of the .targets file relative to individual
projects that import it varies from project to project, so I really
need to find the command line tool by baseing it's location relative to
the .targets file. Follow me so far Since relative paths, and
reservered properties are all relative to the project doing the import,
I can't use things like $(MSBuildProjectDirectory).
We do have a DLL with custom tasks in that same folder. I could
write a task that exposed the location of the DLL (assuming MSBuild
does not copy the DLL elsewhere prior to using it). That seems a
bit hacky :)
I am a bit of an MSBuild novice, so maybe there is an easier/better way. Any other suggestions

How to find the location of a .targets file?
Aravindalochana
Thierry Tuo
Thanks for the informative reply. It would still be nice to be able to do this in the straight ItemGroup syntax, but this is certainly better than nothing!
Many thanks,
Taylor
garry_gill
Microsoft (R) Build Engine Version 2.0.50727.42
[Microsoft .NET Framework, Version 2.0.50727.42]
Copyright (C) Microsoft Corporation 2005. All rights reserved.
Build started 2/10/2006 1:40:13 PM.
__________________________________________________
Project "C:\temp\msbuildtest\One\Two\YourProject.proj" (default targets):
Target Print:
Command line location: C:\temp\msbuildtest\One\Two\utils\CommandLine.txt
Command line location: C:\temp\msbuildtest\One\Two\utils\CommandLine.txt
Build succeeded.
0 Warning(s)
0 Error(s)
Time Elapsed 00:00:00.01
I believe this is related to this statement in the MSBuild documentation (http://msdn2.microsoft.com/en-us/library/92x05xfs.aspx):
All relative paths in imported projects are interpreted relative to the directory of the importing project. Therefore, if a project file is imported into several project files in different locations, the relative paths in the imported project file will be interpreted differently for each importing project.
Robby Economides
I was wanting to do the same thing - determine the location of the very file I am in - not the importing file. I think your solution is clever, but I keep thinking there has to be a cleaner way than this! There is no equilvalent property for THIS very file or this very directory like MSBuildProjectDirectory Is there any internal MSBuild state that is exposed in the API that I could examine to determine which file is currently running Maybe then I could write a cleaner task that would tell me which file or directory I was really in
Many thanks,
Taylor
Anu Viswan
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace CurrentDirectory
{
public class CurrentDir : AppDomainIsolatedTask
{
private ITaskItem currentDir;
[Output]
public ITaskItem CurrentDirectory
{
get
{
return this.currentDir;
}
}
public override bool Execute()
{
System.IO.FileInfo projFile = new System.IO.FileInfo(base.BuildEngine.ProjectFileOfTaskNode);
this.currentDir = new TaskItem(projFile.Directory.FullName);
return true;
}
}
}
I've posted a blog related to this, and all the related files are available there as well if you'd like to download http://www.sedodream.com/PermaLink,guid,020fd1af-fb17-4fc9-8336-877c157eb2b4.aspx
Sayed Ibrahim Hashimi
www.sedodream.com
Chris Peeters
Wow, I don't think I've screwed up like that in quite a while, sorry for that. But I do have a workaround for you. Its kind of a trick.
First create this task:
using System;
using System.Collections.Generic;
using System.Text;
using Microsoft.Build.Framework;
using Microsoft.Build.Utilities;
namespace CurrentDirectory
{
public class CurrentDir : Task
{
private ITaskItem currentDir;
[Output]
public ITaskItem CurrentDirectory
{
get
{
return this.currentDir;
}
}
public override bool Execute()
{ System.IO.FileInfo assemblyFile = new System.IO.FileInfo(System.Reflection.Assembly.GetExecutingAssembly().Location);
this.currentDir = new TaskItem( assemblyFile.Directory.FullName );
return true;
}
}
}
Build this into an assembly, I called it CurrentDirectory.dll
Place this task into the directory that you need to get to. In the SharedTargets.targets file use it to get to the path.
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<!--
<ItemGroup>
<CommandLineUtil Include="utils\CommandLine.txt"/>
</ItemGroup>
-->
<UsingTask AssemblyFile="CurrentDirectory.dll" TaskName="CurrentDir"/>
<PropertyGroup>
<CommandLineFullPath>@(CommandLineUtil->'%(FullPath)')</CommandLineFullPath>
</PropertyGroup>
<Target Name="SharedTarget">
<CreateItem Include="utils\CommanLine.txt">
<Output ItemName="CommandLineUtil" TaskParameter="Include"/>
</CreateItem>
<CurrentDir>
<Output ItemName="CurrentDir" TaskParameter="CurrentDirectory" />
</CurrentDir>
<Message Text="Inside the shared target" Importance="high"/>
<Message Text="Location: @(CurrentDir->'%(Fullpath)')"/>
</Target>
</Project>
Here is the output:
__________________________________________________
C:\temp\MSBuildDirectoryExample\One\Two>msbuild YourProject.proj /t:sharedtarget
Microsoft (R) Build Engine Version 2.0.50727.42
[Microsoft .NET Framework, Version 2.0.50727.42]
Copyright (C) Microsoft Corporation 2005. All rights reserved.
Build started 2/10/2006 3:16:04 PM.
__________________________________________________
Project "C:\temp\MSBuildDirectoryExample\One\Two\YourProject.proj" (sharedtarget
target(s)):
Target SharedTarget:
Inside the shared target
Location: C:\temp\MSBuildDirectoryExample\Shared
Build succeeded.
0 Warning(s)
0 Error(s)
Obviously this is not the ideal resolution, and there may be better ways. Just threw this together, if this doesn't work for you let me know. I'll update that zip file later 2nite.
Sayed Ibrahim Hashimi
www.sedodream.com
Saikalyan
This post contained incorrect information
Alberto Pardo
Perhaps there will be a more intuitive method in the next release. I know that others have asked about this very same topic previously.
Sayed Ibrahim Hashimi
www.sedodream.com
Jogesh Grover
Not sure if this is what you're trying to do, but the problem I had that was similar was Trying to build several child projects from a main but the child projects referenced dll's using relative paths, and thos paths weren't valid from my main build file.
I had to manually edit the child project file and change the reference like so:
<ItemGroup>
<Reference Include="log4net">
<Name>log4net</Name>
<HintPath>$(MSBuildProjectDirectory)\..\..\..\SharedLibs\log4net\log4net.dll</HintPath>
</Reference>
<Reference Include="System">
<Name>System</Name>
</Reference>
<Reference Include="System.Data">
<Name>System.Data</Name>
</Reference>
<Reference Include="System.Xml">
<Name>System.XML</Name>
</Reference>
<ProjectReference Include="..\Castle.Services.Logging\Castle.Services.Logging-vs2005.csproj">
<Name>Castle.Services.Logging</Name>
<Project>{1AFAEC30-152C-43E2-B37C-E713419D20B8}</Project>
<Package>{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}</Package>
</ProjectReference>
</ItemGroup>
Now when I do my build the path will resolve correctly...it would be wonderful if vs would give you some measure of control as to how it adds the reference like letting you specify variables or something similar