使用msbuild在父文件夹中查找文件

时间:2011-11-23 11:31:49

标签: .net-3.5 msbuild

在MsBuild中,可以创建一个由build.proj.user构建文件解析的Microsoft.Common.Targets文件。

我希望有一个类似的系统,可以在文件夹的根目录中有一个.user文件,并让msbuild从这个文件中获取配置设置。

以这些路径为例:

c:\working\build.proj.user
c:\working\solution1\build.proj.user
c:\working\solution1\project1\
c:\working\solution1\project2\
c:\working\solution1\project3\build.proj.user

c:\working\solution2\
c:\working\solution2\project1\
c:\working\solution2\project2\

我想实现对于solution1 / project1,文件c:\working\solution1\build.proj.user被读取,而对于solution2 / project1,文件c:\working\build.proj.user

目的是允许根据解决方案和/或项目自定义集成测试connectionstring属性。

我看到的当前解决方案是:

  • 制作一个自定义的msbuild任务,该任务将查找此文件
  • 构造一个shell命令来查找文件。
  • 在父路径的父级和父级中进行硬编码查看

我不是任何一个解决方案的粉丝,并且想知道是否有更优雅的方式来实现我的目标(使用msbuild)。

2 个答案:

答案 0 :(得分:16)

MSBuild 4.0中存在此功能: $([MSBuild] :: GetDirectoryNameOfFileAbove(目录,文件名)

示例:在祖先目录中包含名为“Common.targets”的文件

<Import Project="$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Common.targets))\Common.targets" 
    Condition=" '$([MSBuild]::GetDirectoryNameOfFileAbove($(MSBuildThisFileDirectory), Common.targets))' != '' " />

有关详细信息,请参阅此博客文章:MSBuild Property Functions

答案 1 :(得分:7)

将其添加到项目文件中:

<Import Project="build.proj.user" Condition="Exists('build.proj.user')"/>
<Import Project="..\build.proj.user" Condition="!Exists('build.proj.user') and Exists('..\build.proj.user')"/>
<Import Project="..\..\build.proj.user" Condition="!Exists('build.proj.user') and !Exists('..\build.proj.user') and Exists('..\..\build.proj.user')"/>

编辑: 您也可以使用MsBuild inline task执行此操作。它有点慢,但更通用:) MsBuild 4.0支持内联任务

<Project xmlns='http://schemas.microsoft.com/developer/msbuild/2003' ToolsVersion="4.0">
  <UsingTask TaskName="FindUserFile" TaskFactory="CodeTaskFactory" AssemblyFile="$(MSBuildToolsPath)\Microsoft.Build.Tasks.v4.0.dll">
    <ParameterGroup>
      <CurrentDirName ParameterType="System.String" Required="true" />
      <FileToFind ParameterType="System.String" Required="true" />
      <UserFileName ParameterType="System.String" Output="true" />
    </ParameterGroup>
    <Task>
      <Using Namespace="System"/>
      <Using Namespace="System.IO"/>
      <Code Type="Fragment" Language="cs">
        <![CDATA[
          Log.LogMessage("FindUserFile parameters:");
          Log.LogMessage("CurrentDirName = " + CurrentDirName);
          Log.LogMessage("FileToFind = " + FileToFind);

          while(CurrentDirName != Directory.GetDirectoryRoot(CurrentDirName) && !File.Exists(CurrentDirName + Path.DirectorySeparatorChar + FileToFind))
             CurrentDirName = Directory.GetParent(CurrentDirName).FullName;
          if(File.Exists(CurrentDirName + Path.DirectorySeparatorChar + FileToFind)) 
             UserFileName = CurrentDirName + Path.DirectorySeparatorChar + FileToFind;

          Log.LogMessage("FindUserFile output properties:");
          Log.LogMessage("UserFileName = " + UserFileName);
        ]]>
      </Code>
    </Task>
  </UsingTask>

  <Target Name="FindUserFileTest" >
    <FindUserFile CurrentDirName="$(MSBuildThisFileDirectory)" FileToFind="build.proj.user">
     <Output PropertyName="UserFileName" TaskParameter="UserFileName" />
    </FindUserFile>

    <Message Text="UserFileName = $(UserFileName)"/>
    <Error Condition="!Exists('$(UserFileName)')" Text="File not found!"/>

  </Target>
</Project>

工作原理: FindUserFile是用C#语言编写的内联任务。它尝试查找FileToFind参数中指定的文件。然后迭代所有父文件夹,它返回UserFileName输出属性中第一次出现的FileToFind文件。如果找不到文件,则UserFileName输出属性为空字符串。