#!/usr/bin/env python """\ @file update_version_files.py @brief Update all of the various files in the repository to a new version number, instead of having to figure it out by hand $LicenseInfo:firstyear=2010&license=viewerlgpl$ Second Life Viewer Source Code Copyright (C) 2010-2011, Linden Research, Inc. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; version 2.1 of the License only. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA Linden Research, Inc., 945 Battery Street, San Francisco, CA 94111 USA $/LicenseInfo$ """ import sys import os.path # Look for indra/lib/python in all possible parent directories ... # This is an improvement over the setup-path.py method used previously: # * the script may blocated anywhere inside the source tree # * it doesn't depend on the current directory # * it doesn't depend on another file being present. def add_indra_lib_path(): root = os.path.realpath(__file__) # always insert the directory of the script in the search path dir = os.path.dirname(root) if dir not in sys.path: sys.path.insert(0, dir) # Now go look for indra/lib/python in the parent dies while root != os.path.sep: root = os.path.dirname(root) dir = os.path.join(root, 'indra', 'lib', 'python') if os.path.isdir(dir): if dir not in sys.path: sys.path.insert(0, dir) break else: print >>sys.stderr, "This script is not inside a valid installation." sys.exit(1) add_indra_lib_path() import getopt, os, re, commands from indra.util import llversion def usage(): print "Usage:" print sys.argv[0] + """ [options] Options: --version Specify the version string to replace current version. --revision Specify the revision to replace the last digit of the version. By default, revision is computed from the version control system. --skip-on-branch Specify a regular expression against which the current branch is matched. If it matches, then leave version strings alone. Use this to avoid changing version strings on release candidate builds. --server Update llversionserver.h only with new version --viewer Update llversionviewer.h only with new version --channel Specify the viewer channel string to replace current channel. --server_channel Specify the server channel string to replace current channel. --verbose --help Print this message and exit. Common Uses: # Update server and viewer build numbers to the current hg revision: update_version_files.py # Update build numbers unless we are on a release branch: update_version_files.py --skip-on-branch='^Branch_' # Update server and viewer version numbers explicitly: update_version_files.py --version=1.18.1.6 # Update just the viewer version number explicitly: update_version_files.py --viewer --version=1.18.1.6 # Update just the server build number to the current hg revision: update_version_files.py --server # Update the viewer channel update_version_files.py --channel="First Look Puppeteering" # Update the server channel update_version_files.py --server_channel="Het Grid" """ def _getstatusoutput(cmd): """Return Win32 (status, output) of executing cmd in a shell.""" if os.path.sep != "/": # stupid #%#$$ windows cmd = 'cmd.exe /c "'+cmd+'"' pipe = os.popen(cmd, 'r') text = pipe.read() sts = pipe.close() if sts is None: sts = 0 if text[-1:] == '\n': text = text[:-1] return sts, text re_map = {} #re_map['filename'] = (('pattern', 'replacement'), # ('pattern', 'replacement') re_map['indra/llcommon/llversionviewer.h'] = \ (('const S32 LL_VERSION_MAJOR = (\d+);', 'const S32 LL_VERSION_MAJOR = %(VER_MAJOR)s;'), ('const S32 LL_VERSION_MINOR = (\d+);', 'const S32 LL_VERSION_MINOR = %(VER_MINOR)s;'), ('const S32 LL_VERSION_PATCH = (\d+);', 'const S32 LL_VERSION_PATCH = %(VER_PATCH)s;'), ('const S32 LL_VERSION_BUILD = (\d+);', 'const S32 LL_VERSION_BUILD = %(VER_BUILD)s;'), ('const char \* const LL_CHANNEL = "(.+)";', 'const char * const LL_CHANNEL = "%(VIEWER_CHANNEL)s";')) re_map['indra/llcommon/llversionserver.h'] = \ (('const S32 LL_VERSION_MAJOR = (\d+);', 'const S32 LL_VERSION_MAJOR = %(SERVER_VER_MAJOR)s;'), ('const S32 LL_VERSION_MINOR = (\d+);', 'const S32 LL_VERSION_MINOR = %(SERVER_VER_MINOR)s;'), ('const S32 LL_VERSION_PATCH = (\d+);', 'const S32 LL_VERSION_PATCH = %(SERVER_VER_PATCH)s;'), ('const S32 LL_VERSION_BUILD = (\d+);', 'const S32 LL_VERSION_BUILD = %(SERVER_VER_BUILD)s;'), ('const char \* const LL_CHANNEL = "(.+)";', 'const char * const LL_CHANNEL = "%(SERVER_CHANNEL)s";')) re_map['indra/newview/res/viewerRes.rc'] = \ (('FILEVERSION [0-9,]+', 'FILEVERSION %(VER_MAJOR)s,%(VER_MINOR)s,%(VER_PATCH)s,%(VER_BUILD)s'), ('PRODUCTVERSION [0-9,]+', 'PRODUCTVERSION %(VER_MAJOR)s,%(VER_MINOR)s,%(VER_PATCH)s,%(VER_BUILD)s'), ('VALUE "FileVersion", "[0-9.]+"', 'VALUE "FileVersion", "%(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s"'), ('VALUE "ProductVersion", "[0-9.]+"', 'VALUE "ProductVersion", "%(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s"')) # Trailing ',' in top level tuple is special form to avoid parsing issues with one element tuple re_map['indra/newview/Info-SecondLife.plist'] = \ (('<key>CFBundleVersion</key>\n\t<string>[0-9.]+</string>', '<key>CFBundleVersion</key>\n\t<string>%(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s</string>'),) # This will probably only work as long as InfoPlist.strings is NOT UTF16, which is should be... re_map['indra/newview/English.lproj/InfoPlist.strings'] = \ (('CFBundleShortVersionString = "Second Life version [0-9.]+";', 'CFBundleShortVersionString = "Second Life version %(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s";'), ('CFBundleGetInfoString = "Second Life version [0-9.]+', 'CFBundleGetInfoString = "Second Life version %(VER_MAJOR)s.%(VER_MINOR)s.%(VER_PATCH)s.%(VER_BUILD)s')) version_re = re.compile('(\d+).(\d+).(\d+).(\d+)') def main(): script_path = os.path.dirname(__file__) src_root = script_path + "/../" verbose = False opts, args = getopt.getopt(sys.argv[1:], "", ['version=', 'revision=', 'channel=', 'server_channel=', 'skip-on-branch=', 'verbose', 'server', 'viewer', 'help']) update_server = False update_viewer = False new_version = None new_revision = None new_viewer_channel = None new_server_channel = None skip_on_branch_re = None for o,a in opts: if o in ('--version'): new_version = a if o in ('--revision'): new_revision = a if o in ('--skip-on-branch'): skip_on_branch_re = re.compile(a) if o in ('--channel'): new_viewer_channel = a if o in ('--server_channel'): new_server_channel = a if o in ('--verbose'): verbose = True if o in ('--server'): update_server = True if o in ('--viewer'): update_viewer = True if o in ('--help'): usage() return 0 if not(update_server or update_viewer): update_server = True update_viewer = True # Get current channel/version from llversion*.h try: viewer_channel = llversion.get_viewer_channel() viewer_version = llversion.get_viewer_version() except IOError: print "Viewer version file not present, skipping..." viewer_channel = None viewer_version = None update_viewer = False try: server_channel = llversion.get_server_channel() server_version = llversion.get_server_version() except IOError: print "Server version file not present, skipping..." server_channel = None server_version = None update_server = False if verbose: print "Source Path:", src_root if viewer_channel != None: print "Current viewer channel/version: '%(viewer_channel)s' / '%(viewer_version)s'" % locals() if server_channel != None: print "Current server channel/version: '%(server_channel)s' / '%(server_version)s'" % locals() print # Determine new channel(s) if new_viewer_channel != None and len(new_viewer_channel) > 0: viewer_channel = new_viewer_channel if new_server_channel != None and len(new_server_channel) > 0: server_channel = new_server_channel # Determine new version(s) if new_version: m = version_re.match(new_version) if not m: print "Invalid version string specified!" return -1 if update_viewer: viewer_version = new_version if update_server: server_version = new_version else: if llversion.using_hg(): if new_revision: revision = new_revision else: revision = llversion.get_hg_changeset() branch = llversion.get_hg_repo() elif new_revision: revision = new_revision branch = "unknown" else: print >>sys.stderr, "ERROR: could not determine revision and branch" return -1 if skip_on_branch_re and skip_on_branch_re.match(branch): print "Release Candidate Build, leaving version files untouched." return 0 if update_viewer: m = version_re.match(viewer_version) viewer_version = m.group(1)+"."+m.group(2)+"."+m.group(3)+"."+revision if update_server: m = version_re.match(server_version) server_version = m.group(1)+"."+m.group(2)+"."+m.group(3)+"."+revision if verbose: if update_viewer: print "Setting viewer channel/version: '%(viewer_channel)s' / '%(viewer_version)s'" % locals() if update_server: print "Setting server channel/version: '%(server_channel)s' / '%(server_version)s'" % locals() print # split out version parts if viewer_version != None: m = version_re.match(viewer_version) VER_MAJOR = m.group(1) VER_MINOR = m.group(2) VER_PATCH = m.group(3) VER_BUILD = m.group(4) if server_version != None: m = version_re.match(server_version) SERVER_VER_MAJOR = m.group(1) SERVER_VER_MINOR = m.group(2) SERVER_VER_PATCH = m.group(3) SERVER_VER_BUILD = m.group(4) # For readability and symmetry with version strings: VIEWER_CHANNEL = viewer_channel SERVER_CHANNEL = server_channel # Iterate through all of the files in the map, and apply the # substitution filters for filename in re_map.keys(): try: # Read the entire file into a string full_fn = src_root + '/' + filename file = open(full_fn,"r") file_str = file.read() file.close() if verbose: print "Processing file:",filename for rule in re_map[filename]: repl = rule[1] % locals() file_str = re.sub(rule[0], repl, file_str) file = open(full_fn,"w") file.write(file_str) file.close() except IOError: print "File %(filename)s not present, skipping..." % locals() return 0 if __name__ == '__main__': sys.exit(main())