#!/usr/bin/env python # -*- Mode: python -*- # # Copyright (C) 1999-2006 The ViewCVS Group. All Rights Reserved. # # By using this file, you agree to the terms and conditions set forth in # the LICENSE.html file which can be found at the top level of the ViewVC # distribution or at http://viewvc.org/license-1.html. # # For more information, visit http://viewvc.org/ # # ----------------------------------------------------------------------- # # install script for viewvc -- temporary? # # ### this will eventually be replaced by autoconf plus tools. an # ### interactive front-end to ./configure may be provided. # # ----------------------------------------------------------------------- import os import sys import string import re import traceback import py_compile import getopt import StringIO # get access to our library modules sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), 'lib')) import compat import viewvc import compat_ndiff version = viewvc.__version__ ## installer text INFO_TEXT = """ This is the ViewVC %s installer. It will allow you to choose the install path for ViewVC. You will now be asked some installation questions. Defaults are given in square brackets. Just hit [Enter] if a default is okay. """ % version ## installer defaults DESTDIR = None ROOT_DIR = None ## list of files for installation ## tuple (source path, destination path, install mode, true/false flag for ## search-and-replace, flag or text for prompt before replace, ## compile_it) ## FILE_INFO_LIST = [ ("bin/cgi/viewvc.cgi", "bin/cgi/viewvc.cgi", 0755, 1, 0, 0), ("bin/cgi/query.cgi", "bin/cgi/query.cgi", 0755, 1, 0, 0), ("bin/mod_python/viewvc.py", "bin/mod_python/viewvc.py", 0755, 1, 0, 0), ("bin/mod_python/query.py", "bin/mod_python/query.py", 0755, 1, 0, 0), ("bin/mod_python/handler.py", "bin/mod_python/handler.py", 0755, 1, 0, 0), ("bin/mod_python/.htaccess", "bin/mod_python/.htaccess", 0755, 0, 0, 0), ("bin/standalone.py", "bin/standalone.py", 0755, 1, 0, 0), ("viewvc.conf.dist", "viewvc.conf", 0644, 0, """Note: If you are upgrading from viewcvs-0.7 or earlier: The section [text] has been removed from viewcvs.conf. The functionality went into the new files in subdirectory templates.""", 0), ("cvsgraph.conf.dist", "cvsgraph.conf", 0644, 0, 1, 0), ("bin/loginfo-handler", "bin/loginfo-handler", 0755, 1, 0, 0), ("bin/cvsdbadmin", "bin/cvsdbadmin", 0755, 1, 0, 0), ("bin/svndbadmin", "bin/svndbadmin", 0755, 1, 0, 0), ("bin/make-database", "bin/make-database", 0755, 1, 0, 0), ] if sys.platform == "win32": FILE_INFO_LIST.extend([ ("bin/asp/viewvc.asp", "bin/asp/viewvc.asp", 0755, 1, 0, 0), ("bin/asp/query.asp", "bin/asp/query.asp", 0755, 1, 0, 0), ]) TREE_LIST = [ ("lib", "lib", 0), ("templates", "templates", 1), ] # used to escape substitution strings passed to re.sub(). re.escape() is no # good because it blindly puts backslashes in front of anything that is not # a number or letter regardless of whether the resulting sequence will be # interpreted. def ReEscape(str): return string.replace(str, "\\", "\\\\") def Error(text, etype=None, evalue=None): print print "[ERROR] %s" % text if etype: print '[ERROR] ', traceback.print_exception(etype, evalue, None, file=sys.stdout) sys.exit(1) def MkDir(path): try: compat.makedirs(path) except os.error, e: if e[0] == 17: # EEXIST: file exists return if e[0] == 13: # EACCES: permission denied Error("You do not have permission to create directory %s" % path) Error("Unknown error creating directory %s" % path, OSError, e) def SetOnePath(contents, var, value): pattern = re.compile('^' + var + r'\s*=\s*.*$', re.MULTILINE) repl = '%s = r"%s"' % (var, os.path.join(ROOT_DIR, value)) return re.sub(pattern, ReEscape(repl), contents) def SetPythonPaths(contents): if contents[:2] == '#!': shbang = '#!' + sys.executable contents = re.sub('^#![^\n]*', ReEscape(shbang), contents) contents = SetOnePath(contents, 'LIBRARY_DIR', 'lib') contents = SetOnePath(contents, 'CONF_PATHNAME', 'viewvc.conf') return contents def InstallFile(src_path, dest_path, mode, set_python_paths, prompt_replace, compile_it): dest_path = os.path.join(ROOT_DIR, dest_path) if prompt_replace and os.path.exists(DESTDIR + dest_path): # Collect ndiff output from ndiff sys.stdout = StringIO.StringIO() compat_ndiff.main([DESTDIR + dest_path, src_path]) ndiff_output = sys.stdout.getvalue() # Return everything to normal sys.stdout = sys.__stdout__ # Collect the '+ ' and '- ' lines # total collects the difference lines to be printed later total = "" # I use flag to throw out match lines. flag = 1 for line in string.split(ndiff_output,'\n'): # Print line if it is a difference line if line[:2] == "+ " or line[:2] == "- " or line[:2] == "? ": total = total + line + "\n" flag = 1 else: # Compress lines that are the same to print one blank line if flag: total = total + "\n" flag = 0 if total == "\n": print " File %s exists,\n but there is no difference between target and source files.\n" % (DESTDIR + dest_path) return if type(prompt_replace) == type(""): print prompt_replace while 1: temp = raw_input(""" File %s exists and is different from source file. DO YOU WANT TO, overwrite [o] do not overwrite [d] view differences [v]: """ % (DESTDIR + dest_path)) print temp = string.lower(temp[0]) if temp == "d": return if temp == "v": if string.lower(src_path[-4:]) in [ '.gif', '.png', '.jpg' ]: print 'Can not print differences between binary files' else: print total print """ LEGEND A leading '- ' indicates line to remove from installed file A leading '+ ' indicates line to add to installed file A leading '? ' shows intraline differences.""" if temp == "o": ReplaceFile(src_path, dest_path, mode, set_python_paths, prompt_replace, compile_it) return else: ReplaceFile(src_path, dest_path, mode, set_python_paths, prompt_replace, compile_it) return def ReplaceFile(src_path, dest_path, mode, set_python_paths, prompt_replace, compile_it): try: contents = open(src_path, "rb").read() except IOError, e: Error(str(e)) if set_python_paths: contents = SetPythonPaths(contents) ## write the file to the destination location path, basename = os.path.split(DESTDIR + dest_path) MkDir(path) try: open(DESTDIR + dest_path, "wb").write(contents) except IOError, e: if e[0] == 13: # EACCES: permission denied Error("You do not have permission to write file %s" % dest_path) Error("Unknown error writing file %s" % dest_path, IOError, e) os.chmod(DESTDIR + dest_path, mode) if compile_it: py_compile.compile(DESTDIR + dest_path, DESTDIR + dest_path + "c" , dest_path) return def install_tree(src_path, dst_path, prompt_replace): files = os.listdir(src_path) files.sort() for fname in files: # eliminate some items which appear in a development area if fname == 'CVS' or fname == '.svn' or fname == '_svn' \ or fname[-4:] == '.pyc' or fname[-5:] == '.orig' \ or fname[-4:] == '.rej' or fname[0] == '.' \ or fname[-1] == '~': continue src = os.path.join(src_path, fname) dst = os.path.join(dst_path, fname) if os.path.isdir(src): install_tree(src, dst, prompt_replace) else: print " ", src set_paths = 0 compile_it = fname[-3:] == '.py' InstallFile(src, dst, 0644, set_paths, prompt_replace, compile_it) # prompt to delete all .py and .pyc files that don't belong in installation full_dst_path = os.path.join(DESTDIR + ROOT_DIR, dst_path) for fname in os.listdir(full_dst_path): if not os.path.isfile(os.path.join(full_dst_path, fname)) or \ not ((fname[-3:] == '.py' and fname not in files) or (fname[-4:] == '.pyc' and fname[:-1] not in files)): continue while 1: temp = raw_input(""" File %s does not belong in ViewVC %s. DO YOU WANT TO, delete [d] leave as is [l]: """ % (os.path.join(dst_path, fname), version)) print temp = string.lower(temp[0]) if temp == "l": break if temp == "d": os.unlink(os.path.join(full_dst_path, fname)) break ## MAIN if __name__ == "__main__": # option parsing try: optlist, args = getopt.getopt(sys.argv[1:], "", ['prefix=', 'destdir=']) except getopt.GetoptError, e: Error("Invalid option", getopt.GetoptError, e) for opt, arg in optlist: if opt == '--prefix': ROOT_DIR = arg if opt == '--destdir': DESTDIR = arg ## print greeting print INFO_TEXT ## prompt for ROOT_DIR if none provided if ROOT_DIR is None: if sys.platform == "win32": pf = os.getenv("ProgramFiles", "C:\\Program Files") default = os.path.join(pf, "viewvc-" + version) else: default = "/usr/local/viewvc-" + version temp = string.strip(raw_input("Installation path [%s]: " % default)) print if len(temp): ROOT_DIR = temp else: ROOT_DIR = default ## prompt for DESTDIR if none provided if DESTDIR is None: default = '' temp = string.strip(raw_input( "DESTDIR path (generally, only package maintainers will need " "to change\nthis) [%s]: " % default)) print if len(temp): DESTDIR = temp else: DESTDIR = default ## install the files print "Installing ViewVC to:", ROOT_DIR, if DESTDIR: print "(DESTDIR = %s)" % (DESTDIR) else: print for args in FILE_INFO_LIST: print " ", args[0] apply(InstallFile, args) for args in TREE_LIST: apply(install_tree, args) print """ ViewVC File Installation Complete Consult INSTALL for detailed information to finish the installation and configure ViewVC for your system. Overview of remaining steps: 1) Edit the %s file. 2) Configure an existing web server to run (or copy to cgi-bin) %s. OR Run the web server that comes with ViewVC at %s. """ % ( os.path.join(ROOT_DIR, 'viewvc.conf'), os.path.join(ROOT_DIR, 'bin', 'cgi', 'viewvc.cgi'), os.path.join(ROOT_DIR, 'standalone.py'))