Android应用程序中自动递增版本代码

发布于 2024-11-25 10:55:18 字数 648 浏览 2 评论 0原文

每次在 Eclipse 中构建 Android 应用程序时,有没有办法自动递增版本代码?

根据http://developer.android.com/guide/publishing/versioning.html,你有手动增加 AndroidManifest.xml 中的版本代码。

据我了解,您必须在每次构建之前运行一个脚本,例如解析 AndroidManifest.xml 文件、查找版本号、递增版本号并在构建本身开始之前保存文件。但是,我无法了解 Eclipse 如何以及是否支持在构建之前/之后运行脚本。

我找到了关于配置 ant builder 的 这篇文章,但这不是完全是关于 Android 的,我担心这会扰乱 Android 的预定义构建步骤?

应该是常见问题,请问你是怎么解决的?

好吧,人们可以手动执行此操作,但是一旦您忘记执行此操作,您就会得到具有相同编号的不同版本,并且整个版本控制就没有意义了。

is there a way to auto-increment the version code each time you build an Android application in Eclipse?

According to http://developer.android.com/guide/publishing/versioning.html, you have to manually increment your version code in AndroidManifest.xml.

I understand, you have to run a script before each build which would, e.g. parse AndroidManifest.xml file, find the version number, increment it and save the file before the build itself starts. However, i couldn't find out how and if Eclipse supports runnings scripts before/after builds.

I have found this article about configuring ant builder, but this is not exactly about Android and I fear this will mess up too much the predefined building steps for Android?

Should be a common problem, how did you solve it?

Well, one can do this manually, but as soon as you forget to do this chore, you get different versions with the same number and the whole versioning makes little sense.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(15

等你爱我 2024-12-02 10:55:18

我做到了这一点。以下是我为下一个人所做的事情(使用 Eclipse):

1) 创建一个外部控制台可执行文件,它将向 AndroidManifest.xml 写入新版本代码:(我的是 C# 语言)

using System.IO;
using System.Text.RegularExpressions;

namespace AndroidAutoIncrementVersionCode
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                string FILE = @"AndroidManifest.xml";
                string text = File.ReadAllText(FILE);
                Regex regex = new Regex(@"(?<A>android:versionCode="")(?<VER>\d+)(?<B>"")", RegexOptions.IgnoreCase);
                Match match = regex.Match(text);
                int verCode = int.Parse(match.Groups["VER"].Value) + 1;
                string newText = regex.Replace(text, "${A}" + verCode + "${B}", 1);

                File.WriteAllText(FILE, newText);
            }
            catch { }
        }
    }
}

放在一边:任何 c-sharp 编译器都可以构建此应用程序,您不需要 Visual Studio 甚至 Windows

  1. 如果您还没有),请安装 .NET 运行时(Mono 可以工作,链接) (链接到MS的.NET Framework 2.0,2.0是最小的下载,任何版本>=2.0都是很好
  2. 将此代码复制到 *.cs 文件(我将其命名为:AndroidAutoIncrementVersionCode.cs
  3. 打开命令提示符并导航到您创建的位置*.cs 文件
  4. 使用以下命令构建文件(在 Windows 上,与 Mono 类似,但更改编译器的路径):
    c:\Windows\Microsoft.NET\Framework\v2.0.50727\csc AndroidAutoIncrementVersionCode.cs
    (请参阅:.NETMono 了解更多信息)
  5. 恭喜,您刚刚构建了一个 C# 应用程序,没有任何工具,它应该已经生成AndroidAutoIncrementVersionCode.exe自动在同一目录

    **里程可能会有所不同,路径可能会有所不同,无需购买,在禁止的地方无效,我添加此内容是因为 C# 非常棒,人们错误地认为它具有 MS 锁定,您可以轻松地将其转换为另一个语言(但我不会为你这样做;)。顺便说一句,任何 .NET 编译器的任何版本都可以工作,我调整了最小公分母的代码...*

结束一边

2)在构建过程中运行可执行文件:
a) 转到项目属性

转到项目属性

b) 在属性中,转到“Builders” -> “新建...”

Eclipse 属性屏幕

c) 选择“程序”

选择程序

d) 在“主”选项卡中选择程序位置(我还将工作目录设置为安全)并根据需要为其命名。

编辑配置 - main

e) 在“刷新”选项卡中选择“完成后刷新资源”和“所选资源”选项 - 这将在我们编写清单后刷新清单。

编辑配置 - 刷新

f) 在“构建选项”选项卡中,您可以关闭“分配控制台”,因为您没有输入和输出,然后仅选择“手动构建期间”和“自动构建期间”,如果选中则取消选择“清理后”。然后选择“指定相关资源的工作集”并单击“指定资源...”按钮。在“编辑工作集”对话框中,找到“AndroidManifest.xml”文件并检查它,然后单击“完成”

“编辑配置-构建选项”
编辑工作集

f) 现在在“编辑配置对话框”中点击“确定”,然后在应用程序的属性中选择新创建的构建器,然后继续单击“向上”,直到它位于列表顶部,这样自动增量首先运行,并且不会触发意外的不同步状态或重建。一旦您创建的新构建器位于列表顶部,单击“确定”即可完成。

编辑配置 - 点击确定
在此处输入图像描述

I accomplished this. And here's how I did it for the next guy (using Eclipse):

1) Create an external console executable that is going to write a new version code to the AndroidManifest.xml: (mine is in C#)

using System.IO;
using System.Text.RegularExpressions;

namespace AndroidAutoIncrementVersionCode
{
    class Program
    {
        static void Main(string[] args)
        {
            try
            {
                string FILE = @"AndroidManifest.xml";
                string text = File.ReadAllText(FILE);
                Regex regex = new Regex(@"(?<A>android:versionCode="")(?<VER>\d+)(?<B>"")", RegexOptions.IgnoreCase);
                Match match = regex.Match(text);
                int verCode = int.Parse(match.Groups["VER"].Value) + 1;
                string newText = regex.Replace(text, "${A}" + verCode + "${B}", 1);

                File.WriteAllText(FILE, newText);
            }
            catch { }
        }
    }
}

aside: any c-sharp compiler can build this app, you don't need Visual Studio or even Windows

  1. if you don't have it already, install .NET runtime (Mono will work, link) (link to MS's .NET framework 2.0, 2.0 is the smallest download, any version >= 2.0 is fine)
  2. copy this code to a *.cs file (i named mine: AndroidAutoIncrementVersionCode.cs)
  3. open a command prompt and navigate over to where you made your *.cs file
  4. build the file using this command (on Windows, similar for Mono but change path to compiler):
    c:\Windows\Microsoft.NET\Framework\v2.0.50727\csc AndroidAutoIncrementVersionCode.cs
    (see: .NET or Mono for more info)
  5. congrats, you just built a C# app without any tools, it should have generated AndroidAutoIncrementVersionCode.exe in the same directory automatically

    **mileage may vary, paths might be different, no purchase required, void where prohibited, i added this because C# is awesome, and people mistakenly think it has MS lock-in, you could just as easily translate this to another language (but i'm not going to do that for you ;). incidentally any version of any .NET compiler will work, i adapted the code for the least common denominator...*

end aside

2) Run the executable during the build process:
a) Go to the project properties

go to project properties

b) In the properties, Go to "Builders" -> "New..."

Eclipse properties screen

c) Choose "Program"

choose program

d) In the "Main" tab select the program location (I also set the working directory to be safe) and give it a name if you wish.

edit configuration - main

e) In the "Refresh" tab select the "Refresh resources upon completion" and "The selected resource" option - this will refresh the manifest after we write it.

edit configuration - refresh

f) In the "Build Options" tab you can turn off "Allocate Console" as you have no input and output and then select only "During manual builds" and "During auto builds" deselect "After a Clean" if it is checked. Then select "Specify a working set of relevant resources" and click the "Specify Resources..." button. In the "Edit Working Set" dialog, locate your "AndroidManifest.xml" file in the dialog and check it, then hit "Finish"

edit configuration - build options
edit working set

f) Now hit "OK" inside the "Edit Configuration Dialog" and in the properties for your App, select the newly created builder, and keep clicking "Up" until it is at the top of the list, this way the auto increment runs first, and doesn't trigger accidental out-of-sync states or rebuilds. Once the new builder you made is at the top of the list, click "OK" and you're finished.

edit configuration - hit ok
enter image description here

腻橙味 2024-12-02 10:55:18

此 shell 脚本适用于 *nix 系统,将 versionCode 和 versionName 的最后一个组成部分设置为当前的 subversion 修订版。我将 Netbeans 与 NBAndroid 结合使用,并从 custom_rules.xml 中的目标预编译中调用此脚本。

将此脚本保存在与 AndroidManifest.xml 相同的目录中名为 incVersion 的文件中,使其可执行: chmod +x incVersion

manf=AndroidManifest.xml
newverfull=`svnversion`
newvers=`echo $newverfull | sed 's/[^0-9].*$//'`
vers=`sed -n '/versionCode=/s/.*"\([0-9][0-9]*\)".*/\1/p' $manf`
vername=`sed -n '/versionName=/s/.*"\([^"]*\)".*/\1/p' $manf`
verbase=`echo $vername | sed 's/\(.*\.\)\([0-9][0-9]*\).*$/\1/'`
newvername=$verbase$newverfull
sed /versionCode=/s/'"'$vers'"'/'"'$newvers'"'/ $manf | sed /versionName=/s/'"'$vername'"'/'"'$newvername'"'/  >new$manf && cp new$manf $manf && rm -f new$manf
echo versionCode=$newvers versionName=$newvername

创建或编辑 custom_rules.xml 并添加以下内容:

<?xml version="1.0" encoding="UTF-8"?>
<project name="custom_rules">
    <xmlproperty file="AndroidManifest.xml" prefix="mymanifest" collapseAttributes="true"/>
    <target name="-pre-compile">
        <exec executable="./incVersion" failonerror="true"/>
    </target>
</project>

因此,如果我当前的 svn 修订版是 82 ,我最终在 AndroidManifest.xml 中得到了这样的结果:

android:versionCode="82"
android:versionName="2.1.82">

当我想发布新版本时,我通常会更新 versionName 的第一部分,但即使我忘记了,versionName 的最后一部分(公开的)在我的“关于”活动中)总是会告诉我它是从哪个 svn 版本构建的。另外,如果我没有签入更改,则修订号将为 82M,版本名称将类似于 2.1.82M。

与每次构建完成时简单地增加版本号相比,其优点是版本号始终处于控制之下,并且可以直接与特定的 svn 修订版相关。在调查最新版本以外的错误时非常有用。

This shell script, suitable for *nix systems, sets the versionCode and the last component of versionName to the current subversion revision. I'm using Netbeans with NBAndroid and I call this script from the target -pre-compile in custom_rules.xml.

Save this script in a file called incVersion in the same directory as AndroidManifest.xml, make it executable: chmod +x incVersion

manf=AndroidManifest.xml
newverfull=`svnversion`
newvers=`echo $newverfull | sed 's/[^0-9].*$//'`
vers=`sed -n '/versionCode=/s/.*"\([0-9][0-9]*\)".*/\1/p' $manf`
vername=`sed -n '/versionName=/s/.*"\([^"]*\)".*/\1/p' $manf`
verbase=`echo $vername | sed 's/\(.*\.\)\([0-9][0-9]*\).*$/\1/'`
newvername=$verbase$newverfull
sed /versionCode=/s/'"'$vers'"'/'"'$newvers'"'/ $manf | sed /versionName=/s/'"'$vername'"'/'"'$newvername'"'/  >new$manf && cp new$manf $manf && rm -f new$manf
echo versionCode=$newvers versionName=$newvername

Create or edit custom_rules.xml and add this:

<?xml version="1.0" encoding="UTF-8"?>
<project name="custom_rules">
    <xmlproperty file="AndroidManifest.xml" prefix="mymanifest" collapseAttributes="true"/>
    <target name="-pre-compile">
        <exec executable="./incVersion" failonerror="true"/>
    </target>
</project>

So if my current svn revision is 82, I end up with this in AndroidManifest.xml:

android:versionCode="82"
android:versionName="2.1.82">

When I want to release a new version I'll typically update the first parts of versionName, but even if I forget, the last part of versionName (which is exposed in my About activity) will always tell me what svn revision it was built from. Also, if I have not checked in changes, the revision number will be 82M and versionName will be something like 2.1.82M.

The advantage over simply incrementing the version number each time a build is done is that the number stays under control, and can be directly related to a specific svn revision. Very helpful when investigating bugs in other than the latest release.

写下不归期 2024-12-02 10:55:18

FWIW,我能够用六行 python 更新构建版本值:

#!/bin/env python
import os
from xml.dom.minidom import parse
dom1 = parse("AndroidManifest.xml")
dom1.documentElement.setAttribute("android:versionName","%build.number%")
f = os.open("AndroidManifest.xml", os.O_RDWR)
os.write( f, dom1.toxml() )

FWIW, I was able to update the build version value in six lines of python:

#!/bin/env python
import os
from xml.dom.minidom import parse
dom1 = parse("AndroidManifest.xml")
dom1.documentElement.setAttribute("android:versionName","%build.number%")
f = os.open("AndroidManifest.xml", os.O_RDWR)
os.write( f, dom1.toxml() )
待"谢繁草 2024-12-02 10:55:18

Charles 的回答的基础上,以下内容增加了现有的构建版本:

#!/usr/bin/python
from xml.dom.minidom import parse

dom1 = parse("AndroidManifest.xml")
oldVersion = dom1.documentElement.getAttribute("android:versionName")
versionNumbers = oldVersion.split('.')

versionNumbers[-1] = unicode(int(versionNumbers[-1]) + 1)
dom1.documentElement.setAttribute("android:versionName", u'.'.join(versionNumbers))

with open("AndroidManifest.xml", 'wb') as f:
    for line in dom1.toxml("utf-8"):
        f.write(line)

Building on Charles' answer, the following increments the existing build version:

#!/usr/bin/python
from xml.dom.minidom import parse

dom1 = parse("AndroidManifest.xml")
oldVersion = dom1.documentElement.getAttribute("android:versionName")
versionNumbers = oldVersion.split('.')

versionNumbers[-1] = unicode(int(versionNumbers[-1]) + 1)
dom1.documentElement.setAttribute("android:versionName", u'.'.join(versionNumbers))

with open("AndroidManifest.xml", 'wb') as f:
    for line in dom1.toxml("utf-8"):
        f.write(line)
画离情绘悲伤 2024-12-02 10:55:18

所以,我是这样看的:

根据您提出的文章,使用 ant 来完成此任务(目标?)。

  1. 解析清单(解析XML)
  2. 从清单中获取旧版本并增加它/从存储
  3. 库获取版本在清单构建Android应用程序中存储新版本

但就我而言,当我部署或分发应用程序时,我通常会根据标签的修订版按值填充此字段。

So, I see it like this:

Depending on article that you present, use ant for this tasks (targets?).

  1. parse Manifest (parse XML)
  2. get old version form manifest and increase it/get version from repo
  3. store new version in manifest
  4. build android app.

But im my case I usually fill this field by value based on Tag's revision when I deploy or distribute application.

夜访吸血鬼 2024-12-02 10:55:18

收据

要在每次运行构建时自动将 AndroidManifest.xml 中的清单元素的 android:versionCode 属性设置为当前时间(以秒为单位的纪元,从 unix shell 获取),请将其添加到您的-custom_rules.xml Android 文件中的预构建目标。

<target name="-pre-build">
  <exec executable="date" outputproperty="CURRENT_TIMESTAMP">
    <arg value="+%s"/>
  </exec>
  <replaceregex file="AndroidMainfest.xml" match="android:versionCode=.*"
    replace='android:versionCode="${CURRENT_TIMESTAMP}"' />
</target>

确认测试

使用 Android 项目目录中的以下 shell 命令获取生成的 apk 文件的 versionCode 属性:

$ANDROID_SDK/build-tools/20.0.0/aapt dump badging bin/<YourProjectName>.apk | grep versionCode

并将其与 shell 命令返回的当前日期进行比较:date +% s
差异应等于上述两个确认步骤之间的时间段(以秒为单位)。

这种方法的优点:

  1. 无论构建是从命令行还是从 Eclipse 启动,都会更新 versionCode。
  2. versionCode 保证是唯一的,并且每次构建都会增加
  3. 如果需要的话,versionCode 可以被逆向工程为近似的构建时间
  4. 上面的脚本替换了 versionCode 的任何当前值,甚至是 0,并且不需要宏占位符(例如-build_id-)。
  5. 由于该值在 AndroidManifest.xml 文件中更新,因此您可以将其签入版本控制,它将保留实际值,而不是某些宏(例如 -build_id-)。

Receipe:

To automatically have the android:versionCode attribute of manifest element in AndroidManifest.xml set to the current time (from epoch in seconds, obtained from unix shell) everytime you run a build, add this to your -pre-build target in custom_rules.xml Android file.

<target name="-pre-build">
  <exec executable="date" outputproperty="CURRENT_TIMESTAMP">
    <arg value="+%s"/>
  </exec>
  <replaceregex file="AndroidMainfest.xml" match="android:versionCode=.*"
    replace='android:versionCode="${CURRENT_TIMESTAMP}"' />
</target>

Confirmation Test:

Obtain the versionCode attribute of the generated apk file, using the following shell command from your Android project directory :

$ANDROID_SDK/build-tools/20.0.0/aapt dump badging bin/<YourProjectName>.apk | grep versionCode

and compare it to the current date returned from the shell command: date +%s
The difference should equal the period of time in seconds between the two confirmation steps above.

Advantages of this approach:

  1. Regardless of whether the build is started from command line or Eclipse, it will update the versionCode.
  2. The versionCode is guaranteed to be unique and increasing for each build
  3. The versionCode can be reverse-engineered into an approximate build time if you need it
  4. The above script replaces any present value of versionCode, even 0 and doesn't require a macro place holder (such as -build_id-).
  5. Because the value is updated in the AndroidManifest.xml file, you can check it in to version control and it will retain the actual value, not some macro (such as -build_id-).
对你再特殊 2024-12-02 10:55:18

建立在 Rocky 的答案 的基础上,我稍微增强了 python 脚本以增加 versionCode,在 Eclipse 上适用于我(按照 < a href="https://stackoverflow.com/a/8156809/823934">ckozl 很棒的教程) Mac OSX

#!/usr/bin/python
from xml.dom.minidom import parse

dom1 = parse("AndroidManifest.xml")
oldVersion = dom1.documentElement.getAttribute("android:versionName")
oldVersionCode = dom1.documentElement.getAttribute("android:versionCode")
versionNumbers = oldVersion.split('.')

versionNumbers[-1] = unicode(int(versionNumbers[-1]) + 1)
dom1.documentElement.setAttribute("android:versionName", u'.'.join(versionNumbers))
dom1.documentElement.setAttribute("android:versionCode", str(int(oldVersionCode)+1))
with open("AndroidManifest.xml", 'wb') as f:
    for line in dom1.toxml("utf-8"):
        f.write(line)

也不要忘记 chmod +x autoincrement.py 并确保第一行有正确的 python 路径(取决于您的环境),如 sulai 指出

Building on Rocky's answer I enhanced that python script a bit to increase also versionCode, works for me on Eclipse (integrated as per ckozl great tutorial) & Mac OSX

#!/usr/bin/python
from xml.dom.minidom import parse

dom1 = parse("AndroidManifest.xml")
oldVersion = dom1.documentElement.getAttribute("android:versionName")
oldVersionCode = dom1.documentElement.getAttribute("android:versionCode")
versionNumbers = oldVersion.split('.')

versionNumbers[-1] = unicode(int(versionNumbers[-1]) + 1)
dom1.documentElement.setAttribute("android:versionName", u'.'.join(versionNumbers))
dom1.documentElement.setAttribute("android:versionCode", str(int(oldVersionCode)+1))
with open("AndroidManifest.xml", 'wb') as f:
    for line in dom1.toxml("utf-8"):
        f.write(line)

also don't forget to chmod +x autoincrement.py and make sure you have correct path to python on the first line (depending on your environment) as sulai pointed out

鸵鸟症 2024-12-02 10:55:18

我已经做了类似的事情,但将其编写为桌面 AIR 应用程序,而不是一些外部 C#(不觉得安装另一个构建系统)。构建此 Flex/ActionScript 应用程序并更改文件的路径,将其构建为独立的桌面应用程序。它会重写文件的 1.2.3 部分。

    <?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                       xmlns:s="library://ns.adobe.com/flex/spark"
                       xmlns:mx="library://ns.adobe.com/flex/mx"
                       width="371" height="255" applicationComplete="Init();">
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>

    <fx:Script>
        <![CDATA[

            public function Init():void
            {
                import flash.filesystem.File;
                import flash.filesystem.FileMode;
                import flash.filesystem.FileStream;

                var myFile:File = new File("D:\\Dropbox\\Projects\\My App\\src\\Main-app.xml");

                var fileStream:FileStream = new FileStream();
                fileStream.open(myFile, FileMode.READ);

                var fileContents:String = fileStream.readUTFBytes(fileStream.bytesAvailable);

                var startIndex:Number = fileContents.indexOf("<versionNumber>");
                var numberIndex:Number = startIndex + 15;
                var endIndex:Number = fileContents.indexOf("</versionNumber>");

                if (startIndex == -1 || endIndex == -1)
                    return;

                var versionNumber:String = fileContents.substr(numberIndex, endIndex - numberIndex);
                var versionArr:Array = versionNumber.split(".");
                var newSub:Number = Number(versionArr[2]);
                newSub++;
                versionArr[2] = newSub.toString();
                versionNumber = versionArr.join(".");

                var newContents:String = fileContents.substr(0, startIndex) + "<versionNumber>" + versionNumber + "</versionNumber>" +
                                fileContents.substr(endIndex + 16);
                fileStream.close(); 


                fileStream = new FileStream();
                fileStream.open(myFile, FileMode.WRITE);
                fileStream.writeUTFBytes(newContents);
                fileStream.close(); 

                close();
            }
        ]]>
    </fx:Script>
    <s:Label x="10" y="116" width="351" height="20" fontSize="17"
             text="Updating My App Version Number" textAlign="center"/>

</s:WindowedApplication>

I've done something similar but written it as a Desktop AIR app instead of some external C# (didn't feel installing another build system). Build this Flex/ActionScript app and change the path to your file, the build it as a standalone desktop app. It rewrites the 1.2.3 part of your file.

    <?xml version="1.0" encoding="utf-8"?>
<s:WindowedApplication xmlns:fx="http://ns.adobe.com/mxml/2009"
                       xmlns:s="library://ns.adobe.com/flex/spark"
                       xmlns:mx="library://ns.adobe.com/flex/mx"
                       width="371" height="255" applicationComplete="Init();">
    <fx:Declarations>
        <!-- Place non-visual elements (e.g., services, value objects) here -->
    </fx:Declarations>

    <fx:Script>
        <![CDATA[

            public function Init():void
            {
                import flash.filesystem.File;
                import flash.filesystem.FileMode;
                import flash.filesystem.FileStream;

                var myFile:File = new File("D:\\Dropbox\\Projects\\My App\\src\\Main-app.xml");

                var fileStream:FileStream = new FileStream();
                fileStream.open(myFile, FileMode.READ);

                var fileContents:String = fileStream.readUTFBytes(fileStream.bytesAvailable);

                var startIndex:Number = fileContents.indexOf("<versionNumber>");
                var numberIndex:Number = startIndex + 15;
                var endIndex:Number = fileContents.indexOf("</versionNumber>");

                if (startIndex == -1 || endIndex == -1)
                    return;

                var versionNumber:String = fileContents.substr(numberIndex, endIndex - numberIndex);
                var versionArr:Array = versionNumber.split(".");
                var newSub:Number = Number(versionArr[2]);
                newSub++;
                versionArr[2] = newSub.toString();
                versionNumber = versionArr.join(".");

                var newContents:String = fileContents.substr(0, startIndex) + "<versionNumber>" + versionNumber + "</versionNumber>" +
                                fileContents.substr(endIndex + 16);
                fileStream.close(); 


                fileStream = new FileStream();
                fileStream.open(myFile, FileMode.WRITE);
                fileStream.writeUTFBytes(newContents);
                fileStream.close(); 

                close();
            }
        ]]>
    </fx:Script>
    <s:Label x="10" y="116" width="351" height="20" fontSize="17"
             text="Updating My App Version Number" textAlign="center"/>

</s:WindowedApplication>
本王不退位尔等都是臣 2024-12-02 10:55:18

我能够根据所提供的信息制定出自己的解决方案。如果它对某人有用,这里是我的 bash 脚本,用于在 Linux 上使用 GIT VCS 时更新 versionCode 和 versionName 属性。

我编辑 AndroidManifest.xml 文件的脚本如下所示:

#/bin/bash

CODE=`git tag | grep -c ^v`
NAME=`git describe --dirty`
COMMITS=`echo ${NAME} | sed -e 's/v[0-9\.]*//'`

if [ "x${COMMITS}x" = "xx" ] ; then
    VERSION="${NAME}"
else
    BRANCH=" (`git branch | grep "^\*" | sed -e 's/^..//'`)"
    VERSION="${NAME}${BRANCH}"
fi

cat AndroidManifest.template.xml \\
    | sed -e "s/__CODE__/${CODE}/" \\
          -e   "s/__VERSION__/${VERSION}/" > AndroidManifest.xml

exit 0

它解析模板文件 (AndroidManifest.template.xml) 并用更合适的值替换字符串“__VERSION__”和“__CODE__”:

  • “__CODE__”被替换为计数Git 存储库中的标签数量,以单个小写 V 开头,后跟一系列数字和点。这看起来像大多数版本字符串,例如:“v0.5”、“v1.1.4”等。
  • “__VERSION__”被替换为“gitdescribe”命令的输出的组合,如果不是“干净”的构建,则替换为构建它的分支。

我所说的“干净”构建是指所有组件都处于版本控制之下并且它们的最新提交都被标记的构建。 “git describe --dirty”将根据当前分支上最新提交中最后一个可到达的带注释的标签来报告版本号。如果自该标记以来存在提交,则会报告这些提交的计数,就像上次提交的缩写对象名称一样。如果版本控制下的任何文件被修改,“--dirty”选项将在上述信息中附加“-dirty”。

因此 AndroidManifest.xml 不应再受版本控制,您应该只编辑 AndroidManifest.template.xml 文件。 AndroidManifest.template.xml 文件的开头看起来像这样:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.packagename"
    android:versionCode="__CODE__"
    android:versionName="__VERSION__" >

希望这对某人有用

I was able to work out my own solution from the information given. In case it is useful for someone here is my bash script for updating the versionCode and versionName attributes when using the GIT VCS on Linux.

My script to edit the AndroidManifest.xml file looks like this:

#/bin/bash

CODE=`git tag | grep -c ^v`
NAME=`git describe --dirty`
COMMITS=`echo ${NAME} | sed -e 's/v[0-9\.]*//'`

if [ "x${COMMITS}x" = "xx" ] ; then
    VERSION="${NAME}"
else
    BRANCH=" (`git branch | grep "^\*" | sed -e 's/^..//'`)"
    VERSION="${NAME}${BRANCH}"
fi

cat AndroidManifest.template.xml \\
    | sed -e "s/__CODE__/${CODE}/" \\
          -e   "s/__VERSION__/${VERSION}/" > AndroidManifest.xml

exit 0

It parses the template file (AndroidManifest.template.xml) and replaces the strings "__VERSION__" and "__CODE__" with more appropriate values:

  • "__CODE__" is replaced with a count of the number of tags in the Git repo which starts with a single lowercase V and is followed by a sequence of digits and dots. This looks like most version string like: "v0.5", "v1.1.4" and so on.
  • "__VERSION__" is replaced with a combination of the output from the "git describe" command and, if not a "clean" build, the branch on which it was built.

By a "clean" build I mean one where all the components are under version control and their is latest commit is tagged. "git describe --dirty" will report a version number based upon the last reachable annotated tag in your latest commit on the current branch. If there are commits since that tag a count of those commits is reported as is the abbreviated object name of your last commit. The "--dirty" option will append "-dirty" to the above information if any files are modified that are under version control have been modified.

So AndroidManifest.xml should not be under version control any more, and you should only edit the AndroidManifest.template.xml file. The start of your AndroidManifest.template.xml file looks something like this:

<manifest xmlns:android="http://schemas.android.com/apk/res/android"
    package="com.example.packagename"
    android:versionCode="__CODE__"
    android:versionName="__VERSION__" >

Hope this is useful to someone

丶情人眼里出诗心の 2024-12-02 10:55:18

所有功劳都归功于 ckoz,但我用 C# 编写了自己的实现。我认为它更快一点并且不会出错,因为如果出现问题,可能是配置错误,我应该知道它。

namespace AndroidVersionCodeAutoIncrement
{
    using System.IO;
    using System.Text.RegularExpressions;

    public class Program
    {
        private static readonly Regex VersionCodeRegex = new Regex("android:versionCode=\"(?<version>.*)\"", RegexOptions.Compiled);

        public static void Main()
        {
            using (var manifestFileStream = File.Open("AndroidManifest.xml", FileMode.Open, FileAccess.ReadWrite))
            using (var streamReader = new StreamReader(manifestFileStream))
            {
                var manifestFileText = streamReader.ReadToEnd();

                var firstMatch = VersionCodeRegex.Match(manifestFileText);
                if (firstMatch.Success)
                {
                    int versionCode;
                    var versionCodeValue = firstMatch.Groups["version"].Value;
                    if (int.TryParse(versionCodeValue, out versionCode))
                    {
                        manifestFileText = VersionCodeRegex.Replace(manifestFileText, "android:versionCode=\"" + (versionCode + 1) + "\"", 1);

                        using (var streamWriter = new StreamWriter(manifestFileStream))
                        {
                            manifestFileStream.Seek(0, SeekOrigin.Begin);
                            streamWriter.Write(manifestFileText);
                            manifestFileStream.SetLength(manifestFileText.Length);
                        }
                    }
                }
            }
        }
    }
}

All credit goes to ckoz, but I writed my own implementation in c#. I think it's a little faster and doesn't eat errors because If something goes wrong probably something is wrongly configured and I should know about it.

namespace AndroidVersionCodeAutoIncrement
{
    using System.IO;
    using System.Text.RegularExpressions;

    public class Program
    {
        private static readonly Regex VersionCodeRegex = new Regex("android:versionCode=\"(?<version>.*)\"", RegexOptions.Compiled);

        public static void Main()
        {
            using (var manifestFileStream = File.Open("AndroidManifest.xml", FileMode.Open, FileAccess.ReadWrite))
            using (var streamReader = new StreamReader(manifestFileStream))
            {
                var manifestFileText = streamReader.ReadToEnd();

                var firstMatch = VersionCodeRegex.Match(manifestFileText);
                if (firstMatch.Success)
                {
                    int versionCode;
                    var versionCodeValue = firstMatch.Groups["version"].Value;
                    if (int.TryParse(versionCodeValue, out versionCode))
                    {
                        manifestFileText = VersionCodeRegex.Replace(manifestFileText, "android:versionCode=\"" + (versionCode + 1) + "\"", 1);

                        using (var streamWriter = new StreamWriter(manifestFileStream))
                        {
                            manifestFileStream.Seek(0, SeekOrigin.Begin);
                            streamWriter.Write(manifestFileText);
                            manifestFileStream.SetLength(manifestFileText.Length);
                        }
                    }
                }
            }
        }
    }
}
寄居者 2024-12-02 10:55:18

对于那些在 OSX 上并且想要使用 Python,但又不想丢失 XML 格式的人,当 python XML 解析器完成解析时会发生这种情况,这里有一个 python 脚本,它将基于正则表达式进行增量,从而保留格式:

#!/usr/bin/python
import re

f = open('AndroidManifest.xml', 'r+')
text = f.read()

result = re.search(r'(?P<groupA>android:versionName=")(?P<version>.*)(?P<groupB>")',text)
version = str(float(result.group("version")) + 0.01)
newVersionString = result.group("groupA") + version + result.group("groupB")
newText = re.sub(r'android:versionName=".*"', newVersionString, text);
f.seek(0)
f.write(newText)
f.truncate()
f.close()

该代码基于 @ckozl 答案,只是用 python 完成的,因此您不需要为此创建可执行文件。
只需将脚本命名为autoincrement.py,将其与manifest.xml 文件放在同一文件夹中,然后执行ckozl 上面描述的步骤即可!

For those that are on OSX and want to use Python, but not loose the XML formatting which when parsing is done by the python XML parser happens, here is a python script that will do the incremental based on regular expression, which keeps the formatting:

#!/usr/bin/python
import re

f = open('AndroidManifest.xml', 'r+')
text = f.read()

result = re.search(r'(?P<groupA>android:versionName=")(?P<version>.*)(?P<groupB>")',text)
version = str(float(result.group("version")) + 0.01)
newVersionString = result.group("groupA") + version + result.group("groupB")
newText = re.sub(r'android:versionName=".*"', newVersionString, text);
f.seek(0)
f.write(newText)
f.truncate()
f.close()

The code was based on @ckozl answer, just was done in python so you don't need to create an executable for this.
Just name the script autoincrement.py, place it in the same folder with the manifest.xml file and then do the steps that ckozl did describe above!

皓月长歌 2024-12-02 10:55:18

如果您想更新 AndroidManifest.xml 以使用特定版本号(可能来自构建系统),那么您可以使用我刚刚推送到 GitHub 的项目: https://github.com/bluebirdtech/AndroidManifestVersioner

这是一个基本的.NET命令行应用程序,用法:

AndroidManifestVersioner <path> <versionCode> <versionName>.

感谢其他发帖者提供的代码。

If you want to update the AndroidManifest.xml to use a specific version number, perhaps from a build system, then you can use the project I just pushed to GitHub: https://github.com/bluebirdtech/AndroidManifestVersioner

It's a basic .NET command line app, usage:

AndroidManifestVersioner <path> <versionCode> <versionName>.

Thanks to other posters for their code.

葬心 2024-12-02 10:55:18

这是 Java 版本的价值所在。还处理多个清单。

String directory = "d:\\Android\\workspace\\";

String[] manifests = new String[] 
{
        "app-free\\AndroidManifest.xml",
        "app-donate\\AndroidManifest.xml",
};

public static void main(String[] args)
{
    new version_code().run();
}

public void run()
{
    int I = manifests.length;
    for(int i = 0; i < I; i++)
    {
        String path = directory + manifests[i];

        String content = readFile(path);
        Pattern         versionPattern = Pattern.compile( "(.*android:versionCode=\")([0-9]+)(\".*)", Pattern.DOTALL );
        Matcher m = versionPattern.matcher(content);

        if (m.matches())
        {
            int code = Integer.parseInt( m.group(2) ) + 1;

            System.out.println("Updating manifest " + path + " with versionCode=" + code);

            String newContent = m.replaceFirst("$1" + code + "$3");

            writeFile(path + ".original.txt", content);
            writeFile(path, newContent);
        }
        else
        {
            System.out.println("No match to update manifest " + path);
        }
    }
}

Here is the Java version for what it's worth. Also handling multiple manifests.

String directory = "d:\\Android\\workspace\\";

String[] manifests = new String[] 
{
        "app-free\\AndroidManifest.xml",
        "app-donate\\AndroidManifest.xml",
};

public static void main(String[] args)
{
    new version_code().run();
}

public void run()
{
    int I = manifests.length;
    for(int i = 0; i < I; i++)
    {
        String path = directory + manifests[i];

        String content = readFile(path);
        Pattern         versionPattern = Pattern.compile( "(.*android:versionCode=\")([0-9]+)(\".*)", Pattern.DOTALL );
        Matcher m = versionPattern.matcher(content);

        if (m.matches())
        {
            int code = Integer.parseInt( m.group(2) ) + 1;

            System.out.println("Updating manifest " + path + " with versionCode=" + code);

            String newContent = m.replaceFirst("$1" + code + "$3");

            writeFile(path + ".original.txt", content);
            writeFile(path, newContent);
        }
        else
        {
            System.out.println("No match to update manifest " + path);
        }
    }
}
や莫失莫忘 2024-12-02 10:55:18

如果您使用 gradle,那么您可以在 build.gradle 中非常轻松地指定 versionNameversionCode。您可以使用 git commit count 作为递增的数字来识别构建。

您还可以使用此库:https://github.com/rockerhieu/Versionberg

If you're using gradle then you can specific versionName and versionCode very easy in build.gradle. You can use git commit count as an increasing number to identify the build.

You can also use this library: https://github.com/rockerhieu/Versionberg.

叶落知秋 2024-12-02 10:55:18

我非常喜欢两种解决方案。第一个取决于 Play 商店,另一个取决于 Git。

使用 Play 商店,您可以通过查看最高的可用上传版本代码来增加版本代码。此解决方案的好处是 APK 上传永远不会失败,因为您的版本代码始终比 Play 商店中的版本代码高 1。缺点是在 Play 商店之外分发 APK 变得更加困难。您可以按照 快速入门指南 并告诉插件自动解析版本代码

plugins {
    id 'com.android.application'
    id 'com.github.triplet.play' version 'x.x.x'
}

android {
    ...
}

play {
    serviceAccountCredentials = file("your-credentials.json")
    resolutionStrategy = "auto"
}

使用 Git,您可以根据存储库拥有的提交和标签数量来增加版本代码。这样做的好处是您的输出是可重现的,并且不依赖于存储库之外的任何内容。缺点是您必须进行新的提交或标记才能更改版本代码。您可以通过添加 Version Master Gradle 插件:

plugins {
    id 'com.android.application'
    id 'com.supercilex.gradle.versions' version 'x.x.x'
}

android {
    ...
}

There are two solutions I really like. The first depends on the Play Store and the other depends on Git.

Using the Play Store, you can increment the version code by looking at the highest available uploaded version code. The benefit of this solution is that an APK upload will never fail since your version code is always one higher than whatever is on the Play Store. The downside is that distributing your APK outside of the Play Store becomes more difficult. You can set this up using Gradle Play Publisher by following the quickstart guide and telling the plugin to resolve version codes automatically:

plugins {
    id 'com.android.application'
    id 'com.github.triplet.play' version 'x.x.x'
}

android {
    ...
}

play {
    serviceAccountCredentials = file("your-credentials.json")
    resolutionStrategy = "auto"
}

Using Git, you can increment the version code based on how many commits and tags your repository has. The benefit here is that your output is reproducible and doesn't depend on anything outside your repo. The downside is that you have to make a new commit or tag to bump your version code. You can set this up by adding the Version Master Gradle plugin:

plugins {
    id 'com.android.application'
    id 'com.supercilex.gradle.versions' version 'x.x.x'
}

android {
    ...
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文