#!/usr/bin/env python # -*- Mode: python -*- # # Copyright (C) 2000-2002 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 ViewCVS # distribution or at http://viewcvs.sourceforge.net/license-1.html. # # Contact information: # Greg Stein, PO Box 760, Palo Alto, CA, 94302 # gstein@lyra.org, http://viewcvs.sourceforge.net/ # # ----------------------------------------------------------------------- # # install script for viewcvs -- 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 StringIO # get access to our library modules sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), 'lib')) import compat import viewcvs import ndiff version = viewcvs.__version__ ## installer text INFO_TEXT = """\ This is the ViewCVS %s installer. It will allow you to choose the install path for ViewCVS. 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 if sys.platform == "win32": pf = os.getenv("ProgramFiles", "C:\\Program Files") ROOT_DIR = os.path.join(pf, "viewcvs-" + version) else: ROOT_DIR = "/usr/local/viewcvs-" + version ## 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 = [ ("cgi/viewcvs.cgi", "cgi/viewcvs.cgi", 0755, 1, 0, 0), ("cgi/query.cgi", "cgi/query.cgi", 0755, 1, 0, 0), ("standalone.py", "standalone.py", 0755, 1, 0, 0), ("cgi/viewcvs.conf.dist", "viewcvs.conf", 0644, 1, """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), ("cgi/cvsgraph.conf.dist", "cvsgraph.conf", 0644, 0, 1, 0), ("tools/loginfo-handler", "loginfo-handler", 0755, 1, 0, 0), ("tools/cvsdbadmin", "cvsdbadmin", 0755, 1, 0, 0), ("tools/make-database", "make-database", 0755, 1, 0, 0), ] if sys.platform == "win32": FILE_INFO_LIST.extend([ ("windows/viewcvs.asp", "windows/viewcvs.asp", 0755, 1, 0, 0), ("windows/query.asp", "windows/query.asp", 0755, 1, 0, 0), ("windows/viewcvs.py", "windows/viewcvs.py", 0755, 1, 0, 0), ("windows/query.py", "windows/query.py", 0755, 1, 0, 0), ("windows/htaccess.mod_python", "windows/htaccess.mod_python", 0755, 1, 0, 0) ]) TREE_LIST = [ ("lib", "lib", 0), ("website", "doc", 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, "\\", "\\\\") # Used to escape backslashes and quotes in apache options def ApacheEscape(str): return re.sub(_re_apache, r"\\\g<0>", str) _re_apache = re.compile("[\\\\\"]") 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 = re.sub("", ReEscape(ROOT_DIR), contents) apacheDir = ApacheEscape(os.path.join(ROOT_DIR, 'lib')) contents = re.sub("", ReEscape(apacheDir), contents) contents = SetOnePath(contents, 'LIBRARY_DIR', 'lib') contents = SetOnePath(contents, 'CONF_PATHNAME', 'viewcvs.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(dest_path): # Collect ndiff output from ndiff sys.stdout = StringIO.StringIO() ndiff.main([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" % (dest_path) return if type(prompt_replace) == type(""): print prompt_replace while 1: temp = raw_input("\n File %s\n exists and is different from source file.\n DO YOU WANT TO,\n overwrite [o]\n do not overwrite [d]\n view differences [v]: " % (dest_path)) print temp = string.lower(temp[0]) if temp == "d": return if temp == "v": print total print "\nLEGEND\n A leading '- ' indicates line to remove from installed file\n A leading '+ ' indicates line to add to installed file\n 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(dest_path) MkDir(path) try: open(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(dest_path, mode) if compile_it: py_compile.compile(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[-4:] == '.pyc' or fname[-5:] == '.orig' \ or fname[-4:] == '.rej' or fname[0] == '.': 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 compile_it = fname[-3:] == '.py' # set the paths in all Python files -- it doesn't hurt to do a # search/replace on these files even if they don't have the # special symbols. set_paths = compile_it 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(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("\n File %s does not belong in ViewCVS %s.\n DO YOU WANT TO,\n delete [d]\n 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__": print INFO_TEXT ## get the install path temp = raw_input("Installation Path [%s]: " % ROOT_DIR) temp = string.strip(temp) if len(temp): ROOT_DIR = temp ## install the files print print "Installing ViewCVS to:", ROOT_DIR for args in FILE_INFO_LIST: print " ", args[0] apply(InstallFile, args) for args in TREE_LIST: apply(install_tree, args) print """ ViewCVS File Installation Complete Consult INSTALL for detailed information to finish the installation and configure ViewCVS 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 ViewCVS at %s. """ % ( os.path.join(ROOT_DIR, 'viewcvs.conf'), os.path.join(ROOT_DIR, 'cgi', 'viewcvs.cgi'), os.path.join(ROOT_DIR, 'standalone.py'))