使用#define和clang为info.plist创建版本号变体?

时间:2016-03-15 01:38:12

标签: c++ ios xcode macos clang

多年前,在使用GCC进行编译时,可以预先处理#include .h文件中的以下定义,以便在info.plist中使用:

#define MAJORVERSION 2
#define MINORVERSION 6 
#define MAINTVERSION 4

<key>CFBundleShortVersionString</key> <string>MAJORVERSION.MINORVERSION.MAINTVERSION</string>

......这将变成&#34; 2.6.4&#34;。这是有效的,因为GCC支持&#34; -traditional&#34;旗。 (参见Tech Note TN2175 Info.plist files in Xcode Using the C Preprocessor,&#34;消除宏扩展过程中令牌之间的空格&#34;)

然而,快进到2016年和Clang 7.0.2(Xcode 7.2.1)显然不支持&#34; -traditional&#34;或&#34; -traditional-cpp&#34; (或正确支持),产生这个字符串:

"2 . 6 . 4"

(见Bug 12035 - Preprocessor inserts spaces in macro expansions, comment 4

因为有很多不同的变体(CFBundleShortVersionString,CFBundleVersion,CFBundleGetInfoString),所以解决这个clang问题会很好,并定义一次,并将这些部分连接/串联起来。现在这种做法普遍被接受的模式是什么? (我目前正在MacOS上构建,但相同的模式适用于IOS)

2 个答案:

答案 0 :(得分:1)

这是我用来增加构建号的Python脚本,每当检测到源代码更改时,都会更新项目中的一个或多个Info.plist文件。

它是为解决this question中提出的问题而创建的。我问过一段时间。

您需要在源树中创建如下所示的buildnum.ver文件:

version 1.0
build 1

(当达到某些项目里程碑时,您需要手动增加version,但buildnum会自动递增。

注意 .ver文件的位置必须位于源树的根目录中(请参阅下面的SourceDir),因为此脚本将在此处查找已修改的文件目录。如果找到任何内容,则会增加内部版本号。修改意味着上次更新.ver文件后源文件发生了更改。

然后创建一个新的Xcode目标来运行外部构建工具并运行类似:

tools/bump_buildnum.py SourceDir/buildnum.ver SourceDir/Info.plist

(让它在${PROJECT_DIR}中运行)

然后使所有实际的Xcode目标依赖于此目标,因此它在构建任何目标之前运行。

#!/usr/bin/env python
#
# Bump build number in Info.plist files if a source file have changed.
#
# usage: bump_buildnum.py buildnum.ver Info.plist [ ... Info.plist ]
#
# andy@trojanfoe.com, 2014.
#

import sys, os, subprocess, re

def read_verfile(name):
    version = None
    build = None
    verfile = open(name, "r")
    for line in verfile:
        match = re.match(r"^version\s+(\S+)", line)
        if match:
            version = match.group(1).rstrip()
        match = re.match(r"^build\s+(\S+)", line)
        if match:
            build = int(match.group(1).rstrip())
    verfile.close()
    return (version, build)

def write_verfile(name, version, build):
    verfile = open(name, "w")
    verfile.write("version {0}\n".format(version))
    verfile.write("build {0}\n".format(build))
    verfile.close()
    return True

def set_plist_version(plistname, version, build):
    if not os.path.exists(plistname):
        print("{0} does not exist".format(plistname))
        return False

    plistbuddy = '/usr/libexec/Plistbuddy'
    if not os.path.exists(plistbuddy):
        print("{0} does not exist".format(plistbuddy))
        return False

    cmdline = [plistbuddy,
        "-c", "Set CFBundleShortVersionString {0}".format(version),
        "-c", "Set CFBundleVersion {0}".format(build),
        plistname]
    if subprocess.call(cmdline) != 0:
        print("Failed to update {0}".format(plistname))
        return False

    print("Updated {0} with v{1} ({2})".format(plistname, version, build))
    return True

def should_bump(vername, dirname):
    verstat = os.stat(vername)
    allnames = []
    for dirname, dirnames, filenames in os.walk(dirname):
        for filename in filenames:
            allnames.append(os.path.join(dirname, filename))

    for filename in allnames:
        filestat = os.stat(filename)
        if filestat.st_mtime > verstat.st_mtime:
            print("{0} is newer than {1}".format(filename, vername))
            return True

    return False

def upver(vername):
    (version, build) = read_verfile(vername)
    if version == None or build == None:
        print("Failed to read version/build from {0}".format(vername))
        return False

    # Bump the version number if any files in the same directory as the version file
    # have changed, including sub-directories.
    srcdir = os.path.dirname(vername)
    bump = should_bump(vername, srcdir)

    if bump:
        build += 1
        print("Incremented to build {0}".format(build))
        write_verfile(vername, version, build)
        print("Written {0}".format(vername))
    else:
        print("Staying at build {0}".format(build))

    return (version, build)

if __name__ == "__main__":
    if os.environ.has_key('ACTION') and os.environ['ACTION'] == 'clean':
        print("{0}: Not running while cleaning".format(sys.argv[0]))
        sys.exit(0)

    if len(sys.argv) < 3:
        print("Usage: {0} buildnum.ver Info.plist [... Info.plist]".format(sys.argv[0]))
        sys.exit(1)
    vername = sys.argv[1]

    (version, build) = upver(vername)
    if version == None or build == None:
        sys.exit(2)

    for i in range(2, len(sys.argv)):
        plistname = sys.argv[i]
        set_plist_version(plistname, version, build)        

    sys.exit(0)

答案 1 :(得分:0)

首先,我想澄清每个密钥的用途:

  • CFBundleShortVersionString

    使用semantic versioning描述应用发布版本的字符串。此字符串将显示在App Store说明中。

  • CFBundleVersion

    指定构建版本(已发布或未发布)的字符串。它是一个字符串,但Apple建议使用数字。

  • CFBundleGetInfoString

    似乎已弃用,因为Information Property List Key Reference中已不再列出。

在开发过程中,CFBundleShortVersionString经常不会更改,我通常会在Xcode中手动设置CFBundleShortVersionString。我定期更改的唯一字符串是CFBundleVersion,因为如果CFBundleVersion未更改,则无法向iTunes Connect / TestFlight提交新版本。

要更改值,我使用带PlistBuddy的Rake任务将时间戳(年,月,日,小时和分钟)写入CFBundleVersion

desc "Bump bundle version"
task :bump_bundle_version do
  bundle_version = Time.now.strftime "%Y%m%d%H%M"
  sh %Q{/usr/libexec/PlistBuddy -c "Set CFBundleVersion #{bundle_version}" "DemoApp/DemoApp-Info.plist"}
end

如果您还需要自动化CFBundleShortVersionString,也可以使用PlistBuddy。