Создайте варианты версий версии для info.plist, используя #define и clang?

Несколько лет назад, при компиляции с GCC, в файле #include.h можно было предварительно обработать для использования в info.plist:

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

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

..., который превратился бы в "2.6.4". Это сработало, потому что GCC поддерживал флаг "-traditional". (см. Техническое примечание TN2175 Файлы Info.plist в Xcode Использование препроцессора C в разделе "Устранение пробелов между токенами в процессе макрообработки" )

Однако ускоренная перемотка вперед до 2016 года и Clang 7.0.2 (Xcode 7.2.1), по-видимому, не поддерживает "-traditional" или "-traditional-cpp" (или поддерживает ее правильно), давая эту строку:

"2 . 6 . 4"

(см. Ошибка 12035 - Препроцессор вставляет пробелы в расширения макросов, комментарий 4)

Поскольку существует так много разных вариантов (CFBundleShortVersionString, CFBundleVersion, CFBundleGetInfoString), было бы неплохо обойти эту проблему с кланом и определить их один раз, а также объединить/объединить фрагменты. Какова общепринятая модель для этого? (Я сейчас строю на MacOS, но тот же шаблон будет работать для IOS)

Ответ 1

Вот Python script, который я использую для увеличения моего номера сборки при каждом обнаружении исходного кода и обновлении одного или нескольких файлов Info.plist в проекте.

Он был создан для решения проблемы, поднятой в этом вопросе. Я спросил некоторое время назад.

Вам нужно создать файл buildnum.ver в исходном дереве, который выглядит так:

version 1.0
build 1

(вам нужно будет вручную увеличить version при достижении определенных этапов проекта, но buildnum будет автоматически увеличиваться).

ПРИМЕЧАНИЕ местоположение файла .ver должно находиться в корневом каталоге исходного дерева (см. SourceDir ниже), так как этот script будет искать измененные файлы в этом каталоге. Если они найдены, номер сборки увеличивается. Изменено означает, что исходные файлы изменяются после последнего обновления файла .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 ]
#
# [email protected], 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)

Ответ 2

Во-первых, я хотел бы уточнить, что должен делать каждый ключ:

Во время разработки CFBundleShortVersionString часто не изменяется, и я обычно устанавливаю CFBundleShortVersionString вручную в Xcode. Единственная строка, которую я меняю регулярно, - CFBundleVersion, потому что вы не можете отправить новую сборку в iTunes Connect/TestFlight, если CFBundleVersion не был изменен.

Чтобы изменить значение, я использую задачу Rake с PlistBuddy для записи метки времени (год, месяц, день, час и минута) до 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

Вы можете использовать PlistBuddy, если вам также нужно автоматизировать CFBundleShortVersionString.