Msbuild 单个TFS 2010构建定义能否用于多个分支?

Msbuild 单个TFS 2010构建定义能否用于多个分支?,msbuild,build,Msbuild,Build,我负责我们产品的构建,并被要求提出一种定制现有构建定义的方法,以便在需要时构建不同的分支 此产品的生成过程已经有多个自定义步骤和操作,并且该产品有大量已生成的项目文件,因此为创建的每个新分支设置新的生成定义是无效的 生成定义设置为从主分支生成。其目的是输入一个特定的分支(使用一个工作流参数,该参数可以在生成排队时输入),然后生成该分支而不是默认的主分支,而无需编辑生成定义 我有一个单独的测试程序,用于测试所有自定义构建活动和过程。在这个构建定义的工作流中,我添加了一些用于日志记录的构建消息,以便

我负责我们产品的构建,并被要求提出一种定制现有构建定义的方法,以便在需要时构建不同的分支

此产品的生成过程已经有多个自定义步骤和操作,并且该产品有大量已生成的项目文件,因此为创建的每个新分支设置新的生成定义是无效的

生成定义设置为从主分支生成。其目的是输入一个特定的分支(使用一个工作流参数,该参数可以在生成排队时输入),然后生成该分支而不是默认的主分支,而无需编辑生成定义

我有一个单独的测试程序,用于测试所有自定义构建活动和过程。在这个构建定义的工作流中,我添加了一些用于日志记录的构建消息,以便可以查看构建过程中使用的变量的值

我还基于这个测试程序创建了一个分支,用于测试可用于构建多个分支的构建定义

首先,我从原始分支为原始测试解决方案的项目文件运行了一个构建,然后更改了构建定义,以便使用新分支执行同样的操作,然后运行另一个构建。当比较这两个分支之间的构建日志时,它们之间只有一些细微的差异。(日志详细度设置为诊断)

第一个区别-我查看了一个工作区变量,构建的Folders属性引用了它们各自的分支,特别是Folders属性的ServerItem属性

第二个区别-正在生成的项目文件(BuildSettings.ProjectsToBuild)来自各自的分支

除此之外,我没有看到这两个构建日志之间的任何其他差异

这里的主要问题是:

是否有一种标准方法可以将正在生成的分支交换为单个生成定义

如果没有,在对生成进行排队时,是否可以将自定义工作流模板(在Workspace and BuildSettings.ProjectsToBuild中)中对默认主分支的所有引用都更改为输入的分支

一如既往,提前感谢您提供的所有帮助

简言之:否和是:-)

在这些答案的基础上,TFS系统默认模板更倾向于为要构建的每个分支使用一个构建定义,或者指定需要一次性构建的所有分支,但要使用一个构建定义。在“队列新建”对话框中,没有标准方法从可用分支中进行选择

下一个答案是肯定的,因为您确实可以自定义构建模板,我知道您已经开始这样做了。根据您的分支结构,向模板中添加一个属性(显示在“队列新建”对话框中)应该相当容易,并具有一个(例如)半列分隔字符串,其中包含您希望在运行期间生成的所有分支,对于该列表中的每个元素,在模板实际开始迭代该列表之前,在工作流过程中添加要构建的解决方案


这应该可以完成工作,但是,我很感兴趣的是,你最终是如何想要这种能力的?您没有使用VS解决方案文件吗?

我已设法修改了生成的工作流模板,以便可以为解决方案的多个分支生成。为了实现这一点,我必须更改工作流中的某些内容,并为运行构建的服务帐户创建一些额外的工作区

  • 我在工作流中添加了一个参数,以便在构建排队时可以输入所需的分支

  • 我更改了构建的放置位置,使其与输入的分支相关(可选)

  • 在生成计算机上为分支生成创建了单独的源目录

  • 初始化工作空间名称和源目录,以便它们与新的工作空间名称和源目录文件夹匹配

  • 我创建了一个自定义活动来更改BuildSettings.ProjectsToBuild中的项目/解决方案列表,以便它们从输入的分支引用它们

  • 在测试阶段,我注意到,尽管我为输入的分支创建的新工作区最初设置为使用输入的分支作为其ServerItem,但它仍然会使用在构建定义中输入的ServerItem创建工作区。为了解决这个问题,我创建了另一个自定义活动来重新映射分支工作区,以便ServerItem指向正确的分支


  • 在我的工作流程中,我还涉及到一些其他的事情,但它们是我的开发人员要求的更多的调整,上面的步骤导致了单个构建定义的多个分支构建

    我还希望在我的构建定义中有更多的分支灵活性,因此我实现了一个代码活动来更改ProjectsToBuild(如上面的害虫步骤5)。以下是代码(删除了特定于项目的内容):

    [BuildActivity(HostenEnvironmentOption.All)]
    公共密封类转换项目根据牧场:代码活动
    {
    公共静态字符串s_MainBranch=“$/MyTeamProject/Main”;
    公共InArgument BuildDetail{get;set;}
    公共InArgument BuildingSettingsOriginal{get;set;}
    public OutArgument BuildingSettings转换为{get;set;}
    受保护的覆盖无效执行(CodeActivityContext上下文)
    {
    Logger.Instance.Init(上下文);
    IBuildDetail buildDetail=buildDetail.Get(上下文);
    BuildSettingsConverted.Set(上下文,ConvertProject(BuildSettingsOriginal.Get(上下文),buildDetail.BuildDefinition));
    }
    ///返回一个BuildSettings,其中ProjectsToBuild根据生成定义的工作区进行转换
    P
    
    [BuildActivity(HostEnvironmentOption.All)]
    public sealed class ConvertProjectsAccordingToBranch : CodeActivity
    {
        public static string s_MainBranch = "$/MyTeamProject/Main";
    
        public InArgument<IBuildDetail> BuildDetail { get; set; }
        public InArgument<BuildSettings> BuildSettingsOriginal { get; set; }
        public OutArgument<BuildSettings> BuildSettingsConverted { get; set; }
    
        protected override void Execute(CodeActivityContext context)
        {
            Logger.Instance.Init(context);
            IBuildDetail buildDetail = BuildDetail.Get(context);
            BuildSettingsConverted.Set(context, ConvertProjects(BuildSettingsOriginal.Get(context), buildDetail.BuildDefinition));
        }
    
        /// <summary>Returns a BuildSettings with ProjectsToBuild converted according to the build definition's workspace</summary>
        public BuildSettings ConvertProjects(BuildSettings settingsOriginal, IBuildDefinition buildDefinition)
        {
            var mappings = buildDefinition.Workspace.Mappings;
            if (mappings.Count() != 1)
            {
                throw new BuildProcessException(string.Format(CultureInfo.InvariantCulture,
                    "Build definition must have exactly one workspace mapping. Build definition ID:{0} has {1} mappings",
                    buildDefinition.Id,   // IBuildDefinition doesn't have any Name property, seriously!
                    mappings.Count()));
            }
    
            string definitionBranch = mappings.First().ServerItem;
            var settingsConverted = new BuildSettings()
            {
                PlatformConfigurations = settingsOriginal.PlatformConfigurations,
            };
    
            foreach (string projectOriginal in settingsOriginal.ProjectsToBuild)
            {
                var projectConverted = projectOriginal.Replace(s_MainBranch, definitionBranch);
                if (!projectConverted.StartsWith(definitionBranch))
                {
                    throw new BuildProcessException(string.Format(CultureInfo.InvariantCulture,
                        "Project {0} is not under main branch {1},  Definition branch: {2}",
                        projectOriginal, s_MainBranch, definitionBranch));
                }
    
                settingsConverted.ProjectsToBuild.Add(projectConverted);
                Logger.Instance.Log("Converted ProjectToBuild: {0},  original: {1}", projectConverted, projectOriginal);
            }
    
            return settingsConverted;
        }
    }