使用MSBuild构建解决方案列表时如何将所有输出复制到另一个文件夹?

时间:2013-09-05 13:43:50

标签: msbuild

我们在解决方案和项目的大文件夹树中有大量遗留代码,其中许多我们不感兴趣(但遗憾的是现在无法移动)

我们目前正在使用MSBuild编译我们感兴趣的解决方案,如下所示:

<ItemGroup>
  <Solutions Include="some_path\solution1.sln"
  <Solutions Include="some_other_path\solution2.sln"
  <Solutions Include="yet_another_path\solution3.sln"
</ItemGroup>

<Target Name="Rebuild">
  <MSBuild Projects=@(Solutions) Targets="Rebuild" />
</Target>

这可以很好地构建,但我们想要做的是将最终存在于各个项目bin\Release文件夹中的所有文件转换为具有相同相对文件夹但具有不同根目录的不同树结构。 / p>

我们怎样才能没有只查找特定的文件模式(例如所有dll或exe文件),因为我们的文件结构包含许多我们不感兴趣的文件。

所以基本上有没有办法找到我们的构建脚本正在构建的所有文件,并将它们移动到另一个保留其相对路径的位置?

3 个答案:

答案 0 :(得分:0)

您是否尝试覆盖输出路径?

<MSBuild Projects="@(Solutions)"              Properties="OutputPath=$(ParentDir)\Output\%(Solutions.Filename)"/>

这将把每个解决方案的输出放在$(ParentDir)\ Output。

的树中

答案 1 :(得分:0)

以下是2个想法:

<?xml version="1.0" encoding="utf-8"?>
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="AllTargetsWrapped">

    <!--
        <UsingTask AssemblyFile="$(ProgramFiles)\MSBuild\MSBuild.Community.Tasks.dll" TaskName="Version"/>
        -->
    <Import Project="$(MSBuildExtensionsPath32)\MSBuildCommunityTasks\MSBuild.Community.Tasks.Targets" />

    <PropertyGroup>
        <TPath Condition="Exists('$(MSBuildExtensionsPath32)\ExtensionPack\4.0\MSBuild.ExtensionPack.tasks')">$(MSBuildExtensionsPath32)\ExtensionPack\4.0\MSBuild.ExtensionPack.tasks</TPath>
    </PropertyGroup>
    <Import Project="$(TPath)"/>

    <PropertyGroup>
        <!-- Always declare some kind of "base directory" and then work off of that in the majority of cases  -->
        <WorkingCheckout>.</WorkingCheckout>
    </PropertyGroup>

    <Target Name="AllTargetsWrapped">
        <CallTarget Targets="ShowVariables" />
        <CallTarget Targets="ShowMeTheFiles" />

    </Target>

    <Target Name="ShowVariables">
        <Message Text=" $_WorkingCheckout_ = '$(WorkingCheckout)' " />

        <Message Text=" $_MSBuildExtensionsPath32_ = '$(MSBuildExtensionsPath32)' " />
        <Message Text=" $_MSBuildProjectDirectory_ = '$(MSBuildProjectDirectory)' " />

    </Target>

    <Target Name="ShowMeTheFiles">

        <ItemGroup>
            <AllFiles Include="$(WorkingCheckout)\**\*.*" />
        </ItemGroup>
        <MSBuild.Community.Tasks.RegexMatch Expression="\d+" Input="%(AllFiles.FullPath)">
            <Output TaskParameter="Output" ItemName="MatchedFiles" />
        </MSBuild.Community.Tasks.RegexMatch>       


        <Message Text="rootdir + directory + filename + extension: @(MatchedFiles->'%(rootdir)%(directory)%(filename)%(extension)')"/>

       <Copy
            SourceFiles="@(MatchedFiles)"
            DestinationFiles="@(MatchedFiles->'C:\WUTEMP\DestinationFolder\01\%(RecursiveDir)%(Filename)%(Extension)')"
        />

        <MSBuild.ExtensionPack.FileSystem.FindUnder TaskAction="FindFiles" Path="$(WorkingCheckout)\" SearchPattern="F*">
            <Output ItemName="AllFilesStartingWithF" TaskParameter="FoundItems"/>
        </MSBuild.ExtensionPack.FileSystem.FindUnder>       

        <Message Text="rootdir + directory + filename + extension: @(AllFilesStartingWithF->'%(rootdir)%(directory)%(filename)%(extension)')"/>

        <Copy
            SourceFiles="@(AllFilesStartingWithF)"
            DestinationFiles="@(AllFilesStartingWithF->'C:\WUTEMP\DestinationFolder\02\%(RecursiveDir)%(Filename)%(Extension)')"
        />      


    </Target>


</Project>

但我认为RecursiveDir在转换中受到了影响:&lt;

答案 2 :(得分:0)

这是一个存根自定义MSBuild任务。 您可以将“RunMyCustomKeeperLogic”逻辑更改为您想要的任何内容。 我把它称为“保留每个第5个文件”作为一个愚蠢的例子。

将文件恢复到“.msbuild”世界后,您可以随意复制/移动任何内容。

namespace GranadaCoder.Framework.CrossDomain.MSBuild.Tasks.IO
{
    using System.Collections.Generic;
    using Microsoft.Build.Utilities;

    public abstract class FileBasedTaskBase : Task
    {
        /// <summary>
        /// Converts the delimited source file string to and IList.
        /// </summary>
        /// <param name="sourceFilesString">The source files string.</param>
        /// <returns></returns>
        protected IList<string> ConvertSourceFileStringToList(string sourceFilesString)
        {
            IList<string> returnList = sourceFilesString.Split(';');
            return returnList;
        }

        /// <summary>
        /// Task Entry Point.
        /// </summary>
        /// <returns></returns>
        public override bool Execute()
        {
            AbstractExecute();
            return !Log.HasLoggedErrors;
        }

        protected abstract bool AbstractExecute();
    }
}




namespace GranadaCoder.Framework.CrossDomain.MSBuild.Tasks.IO//.CustomFileKeepTask
{
    using System;
    using System.Collections.Generic;
    using System.Collections;
    using System.Linq;
    using System.IO;
    using System.Security;

    using Microsoft.Build.Framework;
    using Microsoft.Build.Utilities;

    public class CustomFileKeepTask : FileBasedTaskBase
    {
        private static readonly string ROOT_DIRECTORY = "myrootdir";
        private static readonly string FULL_PATH = "myfullpath";
        private static readonly string FILE_NAME = "myfilename";
        private static readonly string DIRECTORY = "mydirectory";
        private static readonly string EXTENSION = "myextension";
        private static readonly string KEEPER = "mykeeper";

        /// <summary>
        /// Gets or sets the source files.
        /// </summary>
        /// <value>The source files.</value>
        [Required]
        public string SourceFiles { get; set; }

        /// <summary>
        /// Gets the file versions as a Task Output property.
        /// </summary>
        /// <value>The file versions.</value>
        [Output]
        public ITaskItem[] FilteredFiles
        { get; private set; }

        /// <summary>
        /// Task Entry Point.
        /// </summary>
        /// <returns></returns>
        protected override bool AbstractExecute()
        {
            InternalExecute();
            return !Log.HasLoggedErrors;
        }

        /// <summary>
        /// Internal Execute Wrapper.
        /// </summary>
        private void InternalExecute()
        {
            IList<string> files = null;

            if (String.IsNullOrEmpty(this.SourceFiles))
            {
                Log.LogWarning("No SourceFiles specified");
                return;
            }

            if (!String.IsNullOrEmpty(this.SourceFiles))
            {
                Console.WriteLine(this.SourceFiles);
                files = base.ConvertSourceFileStringToList(this.SourceFiles);
            }

            List<FileInfoWrapper> fiws = new List<FileInfoWrapper>();

            foreach (string f in files)
            {
                FileInfoWrapper fiw = null;
                fiw = this.DetermineExtraInformation(f);
                fiws.Add(fiw);
            }

            fiws = RunMyCustomKeeperLogic(fiws);

            ArrayList itemsAsStringArray = new ArrayList();

            foreach (var fiw in fiws.Where(x => x.IsAKeeper == true))
            {

                IDictionary currentMetaData = new System.Collections.Hashtable();
                currentMetaData.Add(ROOT_DIRECTORY, fiw.RootDirectory);
                currentMetaData.Add(FULL_PATH, fiw.FullPath);
                currentMetaData.Add(FILE_NAME, fiw.FileName);
                currentMetaData.Add(DIRECTORY, fiw.Directory);
                currentMetaData.Add(EXTENSION, fiw.Extension);

                string trueOrFalse = fiw.IsAKeeper.ToString();
                currentMetaData.Add(KEEPER, trueOrFalse);

                itemsAsStringArray.Add(new TaskItem(trueOrFalse, currentMetaData));
            }


            this.FilteredFiles = (ITaskItem[])itemsAsStringArray.ToArray(typeof(ITaskItem));
        }


        private List<FileInfoWrapper> RunMyCustomKeeperLogic(List<FileInfoWrapper> fiws)
        {
            int counter = 0;
            foreach (var fiw in fiws)
            {
                if(counter++ % 5 == 0)
                {
                    fiw.IsAKeeper = true;
                }
            }
            return fiws;
        }


        /// <summary>
        /// Determines the file version.
        /// </summary>
        /// <param name="fileName">Name of the file.</param>
        /// <returns>File version or 0.0.0.0 if value cannot be determined</returns>
        private FileInfoWrapper DetermineExtraInformation(string fileName)
        {
            FileInfoWrapper fiw = new FileInfoWrapper();
            fiw.Directory = string.Empty;
            fiw.Extension = string.Empty;
            fiw.FileName = string.Empty;
            fiw.FullPath = string.Empty;
            fiw.RootDirectory = string.Empty;
            fiw.IsAKeeper = false;
            try
            {
                if (System.IO.File.Exists(fileName))
                {
                    fiw.Extension = System.IO.Path.GetExtension(fileName);
                    fiw.FileName = System.IO.Path.GetFileNameWithoutExtension(fileName);
                    fiw.FullPath = fileName;// System.IO.Path.GetFileName(fileName);
                    fiw.RootDirectory = System.IO.Path.GetPathRoot(fileName);

                    //Take the full path and remove the root directory to mimic the DotNet default behavior of '%filename'
                    fiw.Directory = System.IO.Path.GetDirectoryName(fileName).Remove(0, fiw.RootDirectory.Length);

                }
            }
            catch (Exception ex)
            {
                if (ex is IOException
                    || ex is UnauthorizedAccessException
                    || ex is PathTooLongException
                    || ex is DirectoryNotFoundException
                    || ex is SecurityException)
                {
                    Log.LogWarning("Error trying to determine file version " + fileName + ". " + ex.Message);
                }
                else
                {
                    Log.LogErrorFromException(ex);
                    throw;
                }
            }
            return fiw;
        }




        /// <summary>
        /// Internal wrapper class to hold file properties of interest.
        /// </summary>
        internal sealed class FileInfoWrapper
        {
            public string Directory { get; set; }
            public string Extension { get; set; }
            public string FileName { get; set; }
            public string FullPath { get; set; }
            public string RootDirectory { get; set; }
            public bool IsAKeeper { get; set; }
        }
    }
}

这里是一个示例.msbuild(或.proj或.xml)文件来调用上面的内容。

<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="AllTargetsWrapper" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">

  <UsingTask AssemblyFile="GranadaCoder.Framework.CrossDomain.MSBuild.dll" TaskName="CustomFileKeepTask"/>




  <Target Name="AllTargetsWrapper">
    <CallTarget Targets="CustomFileKeepTask1" />
    <CallTarget Targets="CustomFileKeepTask2" />
  </Target>


  <PropertyGroup>
    <WorkingCheckout>c:\Program Files\MSBuild</WorkingCheckout>
  </PropertyGroup>



  <ItemGroup>
    <MyTask1IncludeFiles Include="$(WorkingCheckout)\**\*.*" />
  </ItemGroup>

  <Target Name="CustomFileKeepTask1">
    <CustomFileKeepTask SourceFiles="@(MyTask1IncludeFiles)" >

      <Output TaskParameter="FilteredFiles"  ItemName="MyFilteredFileItemNames"/>

    </CustomFileKeepTask>


    <Message Text=" MyFilteredFileItemNames MetaData  "/>
    <Message Text="  ------------------------------- "/>
    <Message Text="   "/>


    <Message Text="directory: "/>
    <Message Text="@(MyFilteredFileItemNames->'%(mydirectory)')"/>
    <Message Text="   "/>
    <Message Text="   "/>
    <Message Text="extension: "/>
    <Message Text="@(MyFilteredFileItemNames->'%(myextension)')"/>
    <Message Text="   "/>
    <Message Text="   "/>
    <Message Text="filename: "/>
    <Message Text="@(MyFilteredFileItemNames->'%(myfilename)')"/>
    <Message Text="   "/>
    <Message Text="   "/>
    <Message Text="fullpath: "/>
    <Message Text="@(MyFilteredFileItemNames->'%(myfullpath)')"/>
    <Message Text="   "/>
    <Message Text="   "/>
    <Message Text="rootdir: "/>
    <Message Text="@(MyFilteredFileItemNames->'%(myrootdir)')"/>
    <Message Text="   "/>
    <Message Text="   "/>

    <Message Text="FilteredFile: "/>
    <Message Text="@(MyFilteredFileItemNames->'%(mykeeper)')"/>
    <Message Text="   "/>
    <Message Text="   "/>


    <Message Text="   "/>
    <Message Text="   "/>
    <Message Text="rootdir + directory + filename + extension: "/>
    <Message Text="@(MyFilteredFileItemNames->'%(myrootdir)%(mydirectory)%(myfilename)%(myextension)')"/>
    <Message Text="   "/>
    <Message Text="   "/>



    <Message Text="List of files using special characters (carriage return)"/>
    <Message Text="@(MyFilteredFileItemNames->'&quot;%(myfullpath)&quot;' , '%0D%0A')"/>
    <Message Text="   "/>
    <Message Text="   "/>



  </Target>



  <ItemGroup>
    <MyTask2IncludeFiles Include="c:\windows\notepad.exe"  />
  </ItemGroup>

  <Target Name="CustomFileKeepTask2">
    <CustomFileKeepTask SourceFiles="@(MyTask2IncludeFiles)" >
      <Output TaskParameter="FilteredFiles" PropertyName="SingleFileFilteredFile"/>
    </CustomFileKeepTask>

    <Message Text="SingleFileFilteredFile = $(SingleFileFilteredFile)   "/>

  </Target>


</Project>