Создать варианты номера версии для 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)

2 ответа

Решение

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

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

Вам нужно создать 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)

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

  • CFBundleShortVersionString

    Строка, описывающая выпущенную версию приложения с использованием семантического контроля версий. Эта строка будет отображаться в описании App Store.

  • CFBundleVersion

    Строка, указывающая версию сборки (выпущена или не выпущена). Это строка, но Apple рекомендует использовать цифры.

  • CFBundleGetInfoString

    Кажется устаревшим, так как он больше не указан в справочнике ключей списка свойств информации.

Во время разработки 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 также.

Другие вопросы по тегам