"Fossies" - the Fresh Open Source Software Archive

Member "viewvc-1.2.1/viewvc-install" (26 Mar 2020, 18029 Bytes) of package /linux/misc/viewvc-1.2.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. See also the latest Fossies "Diffs" side-by-side code changes report for "viewvc-install": 1.1.28_vs_1.2.1.

    1 #!/usr/bin/env python
    2 # -*- Mode: python -*-
    3 #
    4 # Copyright (C) 1999-2020 The ViewCVS Group. All Rights Reserved.
    5 #
    6 # By using this file, you agree to the terms and conditions set forth in
    7 # the LICENSE.html file which can be found at the top level of the ViewVC
    8 # distribution or at http://viewvc.org/license-1.html.
    9 #
   10 # For more information, visit http://viewvc.org/
   11 #
   12 # -----------------------------------------------------------------------
   13 #
   14 # Install script for ViewVC
   15 #
   16 # -----------------------------------------------------------------------
   17 
   18 import os
   19 import sys
   20 import re
   21 import traceback
   22 import py_compile
   23 import getopt
   24 import StringIO
   25 
   26 # Get access to our library modules.
   27 sys.path.insert(0, os.path.join(os.path.dirname(sys.argv[0]), 'lib'))
   28 
   29 import viewvc
   30 import compat_ndiff
   31 version = viewvc.__version__
   32 
   33 
   34 ## Installer defaults.
   35 DESTDIR = None
   36 ROOT_DIR = None
   37 CLEAN_MODE = None
   38 
   39 
   40 ## List of files for installation.
   41 ##    tuple (source path,
   42 ##           destination path,
   43 ##           mode,
   44 ##           boolean -- search-and-replace?
   45 ##           boolean -- prompt before replacing?
   46 ##           boolean -- compile?)
   47 FILE_INFO_LIST = [
   48     ("bin/cgi/viewvc.cgi",        "bin/cgi/viewvc.cgi",        0755, 1, 0, 0),
   49     ("bin/wsgi/viewvc.wsgi",      "bin/wsgi/viewvc.wsgi",      0755, 1, 0, 0),
   50     ("bin/wsgi/viewvc.fcgi",      "bin/wsgi/viewvc.fcgi",      0755, 1, 0, 0),
   51     ("bin/mod_python/viewvc.py",  "bin/mod_python/viewvc.py",  0755, 1, 0, 0),
   52     ("bin/mod_python/handler.py", "bin/mod_python/handler.py", 0755, 1, 0, 0),
   53     ("bin/mod_python/.htaccess",  "bin/mod_python/.htaccess",  0755, 0, 0, 0),
   54     ("bin/standalone.py",         "bin/standalone.py",         0755, 1, 0, 0),
   55     ("bin/loginfo-handler",       "bin/loginfo-handler",       0755, 1, 0, 0),
   56     ("bin/cvsdbadmin",            "bin/cvsdbadmin",            0755, 1, 0, 0),
   57     ("bin/svndbadmin",            "bin/svndbadmin",            0755, 1, 0, 0),
   58     ("bin/make-database",         "bin/make-database",         0755, 1, 0, 0),
   59     ("conf/viewvc.conf.dist",     "viewvc.conf.dist",          0644, 0, 0, 0),
   60     ("conf/viewvc.conf.dist",     "viewvc.conf",               0644, 0, 1, 0),
   61     ("conf/cvsgraph.conf.dist",   "cvsgraph.conf.dist",        0644, 0, 0, 0),
   62     ("conf/cvsgraph.conf.dist",   "cvsgraph.conf",             0644, 0, 1, 0),
   63     ("conf/mimetypes.conf.dist",  "mimetypes.conf.dist",       0644, 0, 0, 0),
   64     ("conf/mimetypes.conf.dist",  "mimetypes.conf",            0644, 0, 1, 0),
   65     ]
   66 if sys.platform == "win32":
   67   FILE_INFO_LIST.extend([
   68     ("bin/asp/viewvc.asp",        "bin/asp/viewvc.asp",        0755, 1, 0, 0),
   69   ])
   70 
   71 
   72 ## List of directories for installation.
   73 ##    type (source path,
   74 ##          destination path,
   75 ##          boolean -- optional item?,
   76 ##          boolean -- prompt before replacing?)
   77 TREE_LIST = [
   78     ("lib",                       "lib",                       0, 0),
   79     ("templates",                 "templates",                 0, 1),
   80     ]
   81 
   82 
   83 ## List of file extensions we can't show diffs for.
   84 BINARY_FILE_EXTS = [
   85     '.png',
   86     '.gif',
   87     '.jpg',
   88     ]
   89 
   90 
   91 def _escape(str):
   92     """Callback function for re.sub().
   93 
   94     re.escape() is no good because it blindly puts backslashes in
   95     front of anything that is not a number or letter regardless of
   96     whether the resulting sequence will be interpreted."""
   97     return str.replace("\\", "\\\\")
   98 
   99 
  100 def _actual_src_path(path):
  101     """Return the real on-disk location of PATH, which is relative to
  102     the ViewVC source directory."""
  103     return os.path.join(os.path.dirname(sys.argv[0]),
  104                         path.replace('/', os.sep))
  105                  
  106     
  107 def error(text, etype=None, evalue=None):
  108     """Print error TEXT to stderr, pretty printing the optional
  109     exception type and value (ETYPE and EVALUE, respective), and then
  110     exit the program with an errorful code."""
  111     sys.stderr.write("\n[ERROR] %s\n" % (text))
  112     if etype:
  113         traceback.print_exception(etype, evalue, None, file=sys.stderr)
  114     sys.exit(1)
  115     
  116 
  117 def replace_var(contents, var, value):
  118     """Replace instances of the variable VAR as found in file CONTENTS
  119     with VALUE."""
  120     pattern = re.compile('^' + var + r'\s*=\s*.*$', re.MULTILINE)
  121     repl = '%s = r"%s"' % (var, os.path.join(ROOT_DIR, value))
  122     return re.sub(pattern, _escape(repl), contents)
  123 
  124 
  125 def replace_paths(contents):
  126     """Replace all ViewVC path placeholders found in file CONTENTS."""
  127     if contents[:2] == '#!':
  128         shbang = '#!' + sys.executable
  129         contents = re.sub('^#![^\n]*', _escape(shbang), contents)
  130     contents = replace_var(contents, 'LIBRARY_DIR', 'lib')
  131     contents = replace_var(contents, 'CONF_PATHNAME', 'viewvc.conf')
  132     return contents
  133 
  134 
  135 def install_file(src_path, dst_path, mode, subst_path_vars,
  136                  prompt_replace, compile_it):
  137     """Install a single file whose source is at SRC_PATH (which is
  138     relative to the ViewVC source directory) into the location
  139     DST_PATH (which is relative both to the global ROOT_DIR and
  140     DESTDIR settings), and set the file's MODE.  If SUBST_PATH_VARS is
  141     set, substitute path variables in the file's contents.  If
  142     PROMPT_REPLACE is set (and is not overridden by global setting
  143     CLEAN_MODE), prompt the user for how to deal with already existing
  144     files that differ from the to-be-installed version.  If COMPILE_IT
  145     is set, compile the file as a Python module."""
  146     
  147     src_path = _actual_src_path(src_path)
  148     dst_path = os.path.join(ROOT_DIR, dst_path.replace('/', os.sep))
  149     destdir_path = DESTDIR + dst_path
  150 
  151     overwrite = None
  152     if not (prompt_replace and os.path.exists(destdir_path)):
  153         # If the file doesn't already exist, or we've been instructed to
  154         # replace it without prompting, then drop in the new file and get
  155         # outta here.
  156         overwrite = 1
  157     else:
  158         # If we're here, then the file already exists, and we've possibly
  159         # got to prompt the user for what to do about that.
  160   
  161         # Collect ndiff output from ndiff
  162         sys.stdout = StringIO.StringIO()
  163         compat_ndiff.main([destdir_path, src_path])
  164         ndiff_output = sys.stdout.getvalue()
  165 
  166         # Return everything to normal
  167         sys.stdout = sys.__stdout__
  168 
  169         # Collect the '+ ' and '- ' lines.
  170         diff_lines = []
  171         looking_at_diff_lines = 0
  172         for line in ndiff_output.split('\n'):
  173             # Print line if it is a difference line
  174             if line[:2] == "+ " or line[:2] == "- " or line[:2] == "? ":
  175                 diff_lines.append(line)
  176                 looking_at_diff_lines = 1
  177             else:
  178                 # Compress lines that are the same to print one blank line
  179                 if looking_at_diff_lines:
  180                     diff_lines.append("")
  181                     looking_at_diff_lines = 0
  182 
  183         # If there are no differences, we're done here.
  184         if not diff_lines:
  185             overwrite = 1
  186         else:
  187             # If we get here, there are differences.
  188             if CLEAN_MODE == 'true':
  189                 overwrite = 1
  190             elif CLEAN_MODE == 'false':
  191                 overwrite = 0
  192             else:
  193                 print "File %s exists and is different from source file." \
  194                       % (destdir_path)
  195                 while 1:
  196                     name, ext = os.path.splitext(src_path)
  197                     if ext in BINARY_FILE_EXTS:
  198                         temp = raw_input("Do you want to [O]verwrite or "
  199                                          "[D]o not overwrite? ")
  200                     else:
  201                         temp = raw_input("Do you want to [O]verwrite, [D]o "
  202                                          "not overwrite, or [V]iew "
  203                                          "differences? ")
  204                     if len(temp) == 0:
  205                         continue
  206                     temp = temp[0].lower()
  207                     if temp == "v" and ext not in BINARY_FILE_EXTS:
  208                         print """
  209 ---------------------------------------------------------------------------"""
  210                         print '\n'.join(diff_lines) + '\n'
  211                         print """
  212 LEGEND
  213    A leading '- ' indicates line to remove from installed file
  214    A leading '+ ' indicates line to add to installed file
  215    A leading '? ' shows intraline differences.
  216 ---------------------------------------------------------------------------"""
  217                     elif temp == "d":
  218                         overwrite = 0
  219                     elif temp == "o":
  220                         overwrite = 1
  221 
  222                     if overwrite is not None:
  223                         break
  224 
  225     assert overwrite is not None
  226     if not overwrite:
  227         print "   preserved %s" % (dst_path)
  228         return
  229 
  230     ### If we get here, we're creating or overwriting the existing file.
  231 
  232     # Read the source file's contents.
  233     try:
  234         contents = open(src_path, "rb").read()
  235     except IOError, e:
  236         error(str(e))
  237 
  238     # (Optionally) substitute ViewVC path variables.
  239     if subst_path_vars:
  240         contents = replace_paths(contents)
  241 
  242     # Ensure the existence of the containing directories.
  243     dst_parent = os.path.dirname(destdir_path)
  244     if not os.path.exists(dst_parent):
  245         try:
  246             os.makedirs(dst_parent)
  247             print "   created   %s%s" % (dst_parent, os.sep)
  248         except os.error, e:
  249             if e.errno == 17: # EEXIST: file exists
  250                 return
  251             if e.errno == 13: # EACCES: permission denied
  252                 error("You do not have permission to create directory %s" \
  253                       % (dst_parent))
  254             error("Unknown error creating directory %s" \
  255                   % (dst_parent, OSError, e))
  256 
  257     # Now, write the file contents to their destination.
  258     try:
  259         exists = os.path.exists(destdir_path)
  260         open(destdir_path, "wb").write(contents)
  261         print "   %s %s" \
  262               % (exists and 'replaced ' or 'installed', dst_path)
  263     except IOError, e:
  264         if e.errno == 13:
  265             # EACCES: permission denied
  266             error("You do not have permission to write file %s" % (dst_path))
  267         error("Unknown error writing file %s" % (dst_path, IOError, e))
  268 
  269     # Set the files's mode.
  270     os.chmod(destdir_path, mode)
  271 
  272     # (Optionally) compile the file.
  273     if compile_it:
  274         py_compile.compile(destdir_path, destdir_path + "c" , dst_path)
  275 
  276 
  277 def install_tree(src_path, dst_path, is_optional, prompt_replace):
  278     """Install a tree whose source is at SRC_PATH (which is relative
  279     to the ViewVC source directory) into the location DST_PATH (which
  280     is relative both to the global ROOT_DIR and DESTDIR settings).  If
  281     PROMPT_REPLACE is set (and is not overridden by global setting
  282     CLEAN_MODE), prompt the user for how to deal with already existing
  283     files that differ from the to-be-installed version.  If
  284     IS_OPTIONAL is set, don't fuss about a missing source item."""
  285 
  286     orig_src_path = src_path
  287     orig_dst_path = dst_path
  288     src_path = _actual_src_path(src_path)
  289     dst_path = os.path.join(ROOT_DIR, dst_path.replace('/', os.sep))
  290     if not os.path.isdir(src_path):
  291         print "   skipping  %s" % (dst_path)
  292         return
  293     destdir_path = os.path.join(DESTDIR + dst_path)
  294 
  295     # Get a list of items in the directory.
  296     files = os.listdir(src_path)
  297     files.sort()
  298     for fname in files:
  299         # Ignore some stuff found in development directories, but not
  300         # intended for installation.
  301         if fname == 'CVS' or fname == '.svn' or fname == '_svn' \
  302              or fname[-4:] == '.pyc' or fname[-5:] == '.orig' \
  303              or fname[-4:] == '.rej' or fname[0] == '.' \
  304              or fname[-1] ==    '~':
  305             continue
  306 
  307         orig_src_child = orig_src_path + '/' + fname
  308         orig_dst_child = orig_dst_path + '/' + fname
  309 
  310         # If the item is a subdirectory, recurse.  Otherwise, install the file.
  311         if os.path.isdir(os.path.join(src_path, fname)):
  312             install_tree(orig_src_child, orig_dst_child, 0, prompt_replace)
  313         else:
  314             set_paths = 0
  315             compile_it = fname[-3:] == '.py'
  316             install_file(orig_src_child, orig_dst_child, 0644,
  317                          set_paths, prompt_replace, compile_it)
  318 
  319     # Check for .py and .pyc files that don't belong in installation.
  320     # (Of course, if we didn't end up actually creating/populating
  321     # destdir_path, we can skip this altogether.)
  322     if not os.path.exists(destdir_path):
  323         return
  324     for fname in os.listdir(destdir_path):
  325         if not os.path.isfile(os.path.join(destdir_path, fname)) or \
  326              not ((fname[-3:] == '.py' and fname not in files) or
  327                         (fname[-4:] == '.pyc' and fname[:-1] not in files)):
  328             continue
  329         
  330         # If we get here, there's cruft.
  331         delete = None
  332         if CLEAN_MODE == 'true':
  333             delete = 1
  334         elif CLEAN_MODE == 'false':
  335             delete = 0
  336         else:
  337             print "File %s does not belong in ViewVC %s." \
  338                   % (dst_path, version)
  339             while 1:
  340                 temp = raw_input("Do you want to [D]elete it, or [L]eave "
  341                                  "it as is? ")
  342                 temp = temp[0].lower()
  343                 if temp == "l":
  344                     delete = 0
  345                 elif temp == "d":
  346                     delete = 1
  347 
  348                 if delete is not None:
  349                     break
  350 
  351         assert delete is not None
  352         if delete:
  353             print "   deleted   %s" % (os.path.join(dst_path, fname))
  354             os.unlink(os.path.join(destdir_path, fname))
  355         else:
  356             print "   preserved %s" % (os.path.join(dst_path, fname))
  357       
  358 
  359 
  360 def usage_and_exit(errstr=None):
  361     stream = errstr and sys.stderr or sys.stdout
  362     stream.write("""Usage: %s [OPTIONS]
  363 
  364 Installs the ViewVC web-based version control repository browser.
  365 
  366 Options:
  367 
  368     --help, -h, -?    Show this usage message and exit.
  369 
  370     --prefix=DIR      Install ViewVC into the directory DIR.  If not provided,
  371                       the script will prompt for this information.
  372 
  373     --destdir=DIR     Use DIR as the DESTDIR.  This is generally only used
  374                       by package maintainers.  If not provided, the script will
  375                       prompt for this information. 
  376 
  377     --clean-mode=     If 'true', overwrite existing ViewVC configuration files
  378                       found in the target directory, and purge Python modules
  379                       from the target directory that aren't part of the ViewVC
  380                       distribution.  If 'false', do not overwrite configuration
  381                       files, and do not purge any files from the target
  382                       directory.  If not specified, the script will prompt
  383                       for the appropriate action on a per-file basis.
  384 
  385 """ % (os.path.basename(sys.argv[0])))
  386     if errstr:
  387         stream.write("ERROR: %s\n\n" % (errstr))
  388         sys.exit(1)
  389     else:
  390         sys.exit(0)
  391         
  392 
  393 if __name__ == "__main__":
  394     # Option parsing.
  395     try:
  396         optlist, args = getopt.getopt(sys.argv[1:], "h?",
  397                                       ['prefix=',
  398                                        'destdir=',
  399                                        'clean-mode=',
  400                                        'help'])
  401     except getopt.GetoptError, e:
  402         usage_and_exit(str(e))
  403     for opt, arg in optlist:
  404         if opt == '--help' or opt == '-h' or opt == '-?':
  405             usage_and_exit()
  406         if opt == '--prefix':
  407             ROOT_DIR = arg
  408         if opt == '--destdir':
  409             DESTDIR = arg
  410         if opt == '--clean-mode':
  411             arg = arg.lower()
  412             if arg not in ('true', 'false'):
  413                 usage_and_exit("Invalid value for --clean-mode parameter.")
  414             CLEAN_MODE = arg
  415 
  416     # Print the header greeting.
  417     print """This is the ViewVC %s installer.
  418 
  419 It will allow you to choose the install path for ViewVC.  You will now
  420 be asked some installation questions.  Defaults are given in square brackets.
  421 Just hit [Enter] if a default is okay.
  422 """ % version
  423 
  424     # Prompt for ROOT_DIR if none provided.
  425     if ROOT_DIR is None:
  426         if sys.platform == "win32":
  427             pf = os.getenv("ProgramFiles", "C:\\Program Files")
  428             default = os.path.join(pf, "viewvc-" + version)    
  429         else:
  430             default = "/usr/local/viewvc-" + version
  431         temp = raw_input("Installation path [%s]: " % (default)).strip()
  432         print
  433         if len(temp):
  434             ROOT_DIR = temp
  435         else:
  436             ROOT_DIR = default
  437             
  438     # Prompt for DESTDIR if none provided.
  439     if DESTDIR is None:
  440         default = ''
  441         temp = raw_input("DESTDIR path (generally only used by package "
  442                          "maintainers) [%s]: " % (default)).strip()
  443         print
  444         if len(temp):
  445             DESTDIR = temp
  446         else:
  447             DESTDIR = default
  448                 
  449     # Install the files.
  450     print "Installing ViewVC to %s%s:" \
  451           % (ROOT_DIR, DESTDIR and " (DESTDIR = %s)" % (DESTDIR) or "")
  452     for args in FILE_INFO_LIST:
  453         apply(install_file, args)
  454     for args in TREE_LIST:
  455         apply(install_tree, args)
  456         
  457     # Print some final thoughts.
  458     print """
  459 
  460 ViewVC file installation complete.
  461 
  462 Consult the INSTALL document for detailed information on completing the
  463 installation and configuration of ViewVC on your system.  Here's a brief
  464 overview of the remaining steps:
  465 
  466   1) Edit the %s file.
  467 
  468   2) Either configure an existing web server to run
  469      %s.
  470      
  471      Or, copy %s to an
  472      already-configured cgi-bin directory.
  473      
  474      Or, use the standalone server provided by this distribution at
  475      %s.
  476 """ % (os.path.join(ROOT_DIR, 'viewvc.conf'),
  477        os.path.join(ROOT_DIR, 'bin', 'cgi', 'viewvc.cgi'),
  478        os.path.join(ROOT_DIR, 'bin', 'cgi', 'viewvc.cgi'),
  479        os.path.join(ROOT_DIR, 'bin', 'standalone.py'))