Skip to content

Instantly share code, notes, and snippets.

@trojanfoe
Created March 15, 2016 10:22
Show Gist options
  • Save trojanfoe/3e094086830fd426b30c to your computer and use it in GitHub Desktop.
Save trojanfoe/3e094086830fd426b30c to your computer and use it in GitHub Desktop.
#!/usr/bin/python
#
# Bump build number if source files have changed, writing the version and build
# number to source files as well as updating one or more info.plist files.
#
# usage: bump_build_number.py version-file version-impl-file version-header-file plist-file [ ... plist-file ]
#
import sys, os, subprocess, re
def read_version(vername):
version = None
build = None
verfile = open(vername, "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_version(vername, version, build):
verfile = open(vername, "w")
verfile.write("version {0}\n".format(version))
verfile.write("build {0}\n".format(build))
verfile.close()
return True
def get_process_output(cmdline):
if sys.version_info[0] < 3:
output = subprocess.Popen(cmdline, stdout=subprocess.PIPE, shell=True).stdout.read().strip()
else:
output = subprocess.getoutput(cmdline)
return output
def set_plist_version(plistname, version, build):
if not os.path.exists(plistname):
sys.stderr.write("{0} does not exist\n".format(plistname))
return False
plistbuddy = '/usr/libexec/Plistbuddy'
if not os.path.exists(plistbuddy):
sys.stderr.write("{0} does not exist\n".format(plistbuddy))
return False
cmdline = [plistbuddy,
"-c", "Set CFBundleShortVersionString {0}".format(version),
"-c", "Set CFBundleVersion {0}".format(build),
plistname]
if subprocess.call(cmdline):
sys.stderr.write("Failed to update {0}\n".format(plistname))
return False
print("Updated {0} with v{1} ({2})".format(plistname, version, build))
return True
def should_ignore_file(filename, ignorefiles):
for ignorefile in ignorefiles:
if os.path.isfile(ignorefile) and os.stat(filename) == os.stat(ignorefile):
return True
return False
#
# Determine if any files within dirname are newer than vername, ignoring
# any files in ignorefiles and including any files with extensions in
# includeexts
#
def should_bump(vername, dirname, ignorefiles, extensions):
verstat = os.stat(vername)
# Assumes mname is in the top-level source directory:
allnames = []
for dirname, dirnames, filenames in os.walk(dirname):
for subdirname in dirnames:
allnames.append(os.path.join(dirname, subdirname))
for filename in filenames:
allnames.append(os.path.join(dirname, filename))
for filename in allnames:
if should_ignore_file(filename, ignorefiles):
continue
extension = os.path.splitext(filename)[1]
if not extension:
continue
if extension not in extensions:
continue
filestat = os.stat(filename)
if filestat.st_mtime > verstat.st_mtime:
sys.stderr.write("{0} is newer than {1}\n".format(filename, vername))
return True
return False
def write_source(implname, hdrname, version, build, commit):
implroot, implext = os.path.splitext(implname)
implfile = open(implname, "w")
if implext == ".c":
implfile.write('#include "{0}"\n'.format(os.path.basename(hdrname)))
implfile.write('const char *version = "{0}";\n'.format(version))
implfile.write('unsigned buildnum = {0};\n'.format(build))
implfile.write('const char *commit = "{0}";\n'.format(commit))
elif implext == ".m":
implfile.write('#import "{0}"\n'.format(os.path.basename(hdrname)))
implfile.write('NSString * const version = @"{0}";\n'.format(version))
implfile.write('unsigned buildnum = {0};\n'.format(build))
implfile.write('NSString * const commit = @"{0}";\n'.format(commit))
elif implext == ".cpp" or implext == ".cxx":
implfile.write('#include "{0}"\n'.format(os.path.basename(hdrname)))
implfile.write('const std::string version = "{0}";\n'.format(version))
implfile.write('unsigned buildnum = {0};\n'.format(build))
implfile.write('const std::string commmit = "{0}";\n'.format(commit))
implfile.close()
sys.stdout.write("Written {0}\n".format(implname))
hdrfile = open(hdrname, "w")
if implext == ".c":
hdrfile.write('#pragma once\n')
hdrfile.write('extern const char *version;\n')
hdrfile.write('extern unsigned buildnum;\n')
hdrfile.write('extern const char *commit;\n');
if implext == ".m":
hdrfile.write('#import <Foundation/Foundation.h>\n')
hdrfile.write('extern NSString * const version;\n')
hdrfile.write('extern unsigned buildnum;\n')
hdrfile.write('extern NSString * const commit;\n');
elif implext == ".cpp" or implext == ".cxx":
hdrfile.write('#pragma once\n')
hdrfile.write('#include <string>\n')
hdrfile.write('extern const std::string version;\n')
hdrfile.write('extern unsigned buildnum;\n')
hdrfile.write('extern const std::string commit;\n');
hdrfile.close()
sys.stdout.write("Written {0}\n".format(hdrname))
return True
def upver(vername, implname, hdrname):
(version, build) = read_version(vername)
if version == None or build == None:
sys.stderr.write("Failed to read version/build from {0}\n".format(vername))
return False
# Bump the version number if any source files or asset files have changed
srcdir = os.path.normpath(os.path.join(os.path.dirname(__file__), ".."))
implroot, implext = os.path.splitext(implname)
hdrroot, hdrext = os.path.splitext(hdrname)
bump = should_bump(vername, srcdir, [implname, hdrname], [implext, hdrext])
if bump:
build += 1
sys.stdout.write("Incremented to build {0}\n".format(build))
write_version(vername, version, build)
sys.stdout.write("Written {0}\n".format(vername))
else:
sys.stdout.write("Staying at build {0}\n".format(build))
gitdirty = get_process_output("git ls-files -m")
if len(gitdirty) > 0:
commit = "0000000000000000000000000000000000000000"
sys.stdout.write("Git is dirty, so using dummy commit SHA-1\n")
else:
commit = get_process_output("git rev-parse HEAD")
if not write_source(implname, hdrname, version, build, commit):
version = None
build = None
commit = None
return (version, build, commit)
if __name__ == "__main__":
if len(sys.argv) < 5:
sys.stderr.write("Usage: bump_build_number.py infile.ver version-impl-file version-header-file info.plist [... info.plist]\n")
sys.exit(1)
vername = sys.argv[1]
implname = sys.argv[2]
hdrname = sys.argv[3]
(version, build, commit) = upver(vername, implname, hdrname)
if version == None or build == None or commit == None:
sys.exit(2)
for i in range(4, len(sys.argv)):
plistname = sys.argv[i]
set_plist_version(plistname, version, build)
sys.exit(0)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment