360 lines
11 KiB
Python
Executable File
360 lines
11 KiB
Python
Executable File
#!/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, 'bin', 'standalone.py'))
|