当前位置: 首页 > 面试题库 >

Java项目的构建和版本编号(ant,cvs,hudson)

轩辕季同
2023-03-14
问题内容

Java项目中系统内部版本号和版本号管理的当前最佳做法是什么?特别:

  • 如何在分布式开发环境中系统地管理内部版本号

  • 如何在源代码中维护版本号/可用于运行时应用程序

  • 如何与源存储库正确集成

  • 如何更自动地管理版本号和存储库标签

  • 如何与持续构建基础架构集成

有很多可用的工具,并且ant(我们正在使用的构建系统)的任务将维护一个构建号,但是目前尚不清楚如何使用CVS,svn或类似的方法与多个并发开发人员一起管理该构建号。

[编辑]

下面出现了几个很好的有用的部分或特定答案,因此我将总结其中的一些。在我看来,这并不是真正强大的“最佳实践”,而是一些相互重叠的想法。在下面,找到我的摘要以及人们可能会尝试跟进的一些问题。[stackoverflow的新功能…如果我做错了,请提供评论。]

  • 如果您使用的是SVN,则特定的结帐版本会随之增加。内部编号可以利用此编号来创建唯一的内部编号,以标识特定的签出/修订。[由于遗留原因,我们使用的CVS并不能提供如此高的洞察力…使用标签进行人工干预可以使您分一杯there。]

  • 如果将maven用作构建系统,则支持从SCM生成版本号,以及用于自动生成发行版的发行模块。[出于各种原因,我们不能使用Maven,但这可以帮助那些可以使用Maven的人。

  • 如果您使用ant作为构建系统,则以下任务描述可以帮助生成捕获构建信息的Java .properties文件,然后可以通过多种方式将其折叠到构建中,我们将这个想法扩展到包括来自哈德逊的。

  • Ant和Maven(以及Hudson和Cruise Control)提供了将构建号获取到.properties文件或.txt / .html文件的简便方法。这个“安全”足以防止它被有意或无意地篡改吗?在构建时将其编译为“版本”类更好吗?

  • 断言:应在诸如hudson的连续集成系统中定义/制定内部版本号。 ]我们采纳了这个建议,但是它确实解决了发布工程问题:发布如何发生?版本中是否有多个内部版本号?不同发行版的内部版本号之间是否存在有意义的关系?

  • 问题:内部编号背后的目标是什么?是否用于质量检查?怎么样?开发人员主要将其用于在开发期间消除多个版本之间的歧义,还是让质量检查人员进一步确定最终用户获得的版本?如果目标是可复制性,那么从理论上讲,这就是发行版本号应提供的内容-为什么不提供?(请在下面的回答中作答,这将有助于阐明您所做的/建议的选择…)

  • 问题:手动构建中是否有内部版本号?这是否有问题,以至每个人都应该使用CI解决方案?

  • 问题:是否应将内部版本号签到SCM?如果目标是可靠,明确地确定特定的构建,则如何应对可能会崩溃/重新启动等的各种连续或手动构建系统。

  • 问题:内部版本号短而甜美(即,单调递增的整数),以便易于粘贴到文件名中进行存档,易于在通信中引用等……还是应该长且充满用户名,日期戳,机器名称等?

  • 问题:请提供有关内部编号分配如何适合较大的自动发布过程的详细信息。是的,行家爱好者,我们知道这已经做完了,但并不是我们所有人都喝醉了库尔援助。

我真的很想将其充实为一个完整的答案,至少对于我们的cvs / ant /hudson设置的具体示例而言,以便有人可以根据此问题制定完整的策略。可以将这种特殊情况的详细描述(包括cvs标记方案,相关的CI配置项以及将内部版本号折叠到版本中以便以编程方式折叠的发布过程)标记为“答案”的人如果您要询问/回答其他特定配置(例如svn/ maven / cruise控件),我将在此处链接到该问题。--JA

[编辑2009年10月23日]我接受了投票最多的答案,因为我认为这是一个合理的解决方案,而其他几个答案也都包含了好主意。如果有人想破解使用marty-
lamb
合成其中的某些内容,我将考虑接受另一种。我对marty-lamb唯一的担心是,它不会产生可靠的序列化内部版本号-
它依赖于构建者系统上的本地时钟来提供明确的内部版本号,这并不好。

[编辑7月10日]

现在,我们包括一个如下所示的类。这样可以将版本号编译到最终可执行文件中。在日志数据,长期归档的输出产品中会发出不同形式的版本信息,并用于将我们(有时是几年后)对输出产品的分析追溯到特定的版本。

public final class AppVersion
{
   // SVN should fill this out with the latest tag when it's checked out.

   private static final String APP_SVNURL_RAW = 
     "$HeadURL: svn+ssh://user@host/svnroot/app/trunk/src/AppVersion.java $";
   private static final String APP_SVN_REVISION_RAW = "$Revision: 325 $";

   private static final Pattern SVNBRANCH_PAT = 
     Pattern.compile("(branches|trunk|releases)\\/([\\w\\.\\-]+)\\/.*");
   private static final String APP_SVNTAIL = 
     APP_SVNURL_RAW.replaceFirst(".*\\/svnroot\\/app\\/", "");

  private static final String APP_BRANCHTAG;
  private static final String APP_BRANCHTAG_NAME;
  private static final String APP_SVNREVISION = 
    APP_SVN_REVISION_RAW.replaceAll("\\$Revision:\\s*","").replaceAll("\\s*\\$", "");


  static {
    Matcher m = SVNBRANCH_PAT.matcher(APP_SVNTAIL);
    if (!m.matches()) {
      APP_BRANCHTAG = "[Broken SVN Info]";
      APP_BRANCHTAG_NAME = "[Broken SVN Info]";
    } else {
      APP_BRANCHTAG = m.group(1);
      if (APP_BRANCHTAG.equals("trunk")) {
        // this isn't necessary in this SO example, but it 
        // is since we don't call it trunk in the real case
        APP_BRANCHTAG_NAME = "trunk";
      } else {
        APP_BRANCHTAG_NAME = m.group(2);
      }
    }
  }

  public static String tagOrBranchName()
  { return APP_BRANCHTAG_NAME; }

  /** Answers a formatter String descriptor for the app version.
   * @return version string */
  public static String longStringVersion()
  { return "app "+tagOrBranchName()+" ("+
    tagOrBranchName()+", svn revision="+svnRevision()+")"; }

  public static String shortStringVersion()
  { return tagOrBranchName(); }

  public static String svnVersion()
  { return APP_SVNURL_RAW; }

  public static String svnRevision()
  { return APP_SVNREVISION; }

  public static String svnBranchId()
  { return APP_BRANCHTAG + "/" + APP_BRANCHTAG_NAME; }

  public static final String banner()
  {
    StringBuilder sb = new StringBuilder();
    sb.append("\n----------------------------------------------------------------");
    sb.append("\nApplication -- ");
    sb.append(longStringVersion());
    sb.append("\n----------------------------------------------------------------\n");
    return sb.toString();
  }
}

如果这值得成为Wiki讨论,请发表评论。


问题答案:

对于我的几个项目,我捕获了Subversion的修订号,时间,运行版本的用户以及一些系统信息,将它们填充到.properties文件中,该文件包含在应用程序jar中,并在运行时读取该jar。

蚂蚁代码如下:

<!-- software revision number -->
<property name="version" value="1.23"/>

<target name="buildinfo">
    <tstamp>
        <format property="builtat" pattern="MM/dd/yyyy hh:mm aa" timezone="America/New_York"/>
    </tstamp>        
    <exec executable="svnversion" outputproperty="svnversion"/>
    <exec executable="whoami" outputproperty="whoami"/>
    <exec executable="uname" outputproperty="buildsystem"><arg value="-a"/></exec>

    <propertyfile file="path/to/project.properties"
        comment="This file is automatically generated - DO NOT EDIT">        
        <entry key="buildtime" value="${builtat}"/>
        <entry key="build" value="${svnversion}"/>
        <entry key="builder" value="${whoami}"/>
        <entry key="version" value="${version}"/>
        <entry key="system" value="${buildsystem}"/>
    </propertyfile>
</target>

将其扩展为包含您可能想要添加的任何信息很简单。



 类似资料:
  • 我有一个具有以下结构的maven项目 即Project有它的pom.xml(作为pom的包)并有模块。每个模块都有一个节中,可以将其设置为“project”的工件(在根pom.xml中定义),也可以将其设置为任何其他project2/project3/projectn工件。 现在,由于这个原因,我看看如果Project1的根pom.xml设置为0.0.1-snapshot,并且如果我想创建一个版本

  • (该章需加入更多内容。。。原稿写的太简单了)我们同时也加入了一个发行版本, 将会送到客户端: Example 7.14. 多项目构建 - 发行文件 api/build.gradle task dist(type: Zip) { dependsOn spiJar from 'src/dist' into('libs') { from spiJar.archiv

  • 我正在将我们的构建从Ant转换为Gradle。我们的第一步是添加Gradle构建文件,这样我们就可以开始使用Gradle作为构建工具。这让我们可以使用现有的构建脚本使用Ant进行构建,并将其转换为随时间推移的渐变。我只想让Gradle调用现有的Ant构建文件。我们的项目都是NetBeans项目,都有build.xml和nbproject/build-implt.xml文件。有些项目需要NetBea

  • 在第七章 Java构建入门那部分我们使用了 apply() 方法,这个方法是从哪里来的呢? 我们之前说过Gradle在构建脚本中定义了一个项目. 对于构建脚本中每个项目,Gradle 都创建了一个 Project 类型的对象用来关联此项目. 当构建脚本执行时,它会去配置所关联的工程对象. 构建脚本中每个被调用的方法(这些方法并未在构建脚本中定义)都被委托给当前工程对象(使用工程对象引用方法)。 构

  • 本文向大家介绍如何为Java Eclipse项目构建ant文件?,包括了如何为Java Eclipse项目构建ant文件?的使用技巧和注意事项,需要的朋友参考一下 请按照下面给出的步骤将Ant集成到Eclipse中。 确保build.xml是Java项目的一部分,并且不位于项目外部的位置。 通过遵循窗口>显示视图>其他>ant>ant启用ant视图。 打开Project Explorer,将bui

  • 上一节我们简要介绍了如何编写一个单机的To Do应用,接下来要打包部署成可执行的应用,我们需要编译源代码,生成的class文件需要打包到JAR文件中。JDK提供了javac 和jar工具帮助你实现这些任务,但是你也不想每次源代码发生变化时你都手动去执行这些任务吧。 Gradle插件能够自动化完成这些任务,插件引入了一些领域特有的观念,其中一个Gradle插件就是java插件,Java插件不仅仅只有