# License information goes here
# -*- coding: utf-8 -*-
"""Install SDSS-IV and SDSS-V software.
"""
from __future__ import absolute_import, division, print_function, unicode_literals
import datetime
import logging
from json import load
from os import chdir, environ, getcwd, getenv, makedirs, walk
from os.path import basename, dirname, exists, isdir, join
from shutil import copyfile, copytree, rmtree
from subprocess import PIPE, Popen
from sys import executable
from sdss_install.application import Argument
from sdss_install.install4 import Install4
from sdss_install.install5 import Install5
from .modules import Modules
try:
from ConfigParser import SafeConfigParser, RawConfigParser
except ImportError:
from configparser import SafeConfigParser, RawConfigParser
[docs]class Install:
'''
Main class which calls Install4 and Install5
for SVN and GitHub installations, respectively
'''
def __init__(self, options=None):
self.set_options(options=options)
self.set_logger(options=options)
self.initialize_data()
[docs] def set_logger(self, options=None):
'''Set the logger used by all classes in the package.'''
if options and logging:
debug = self.options.test or self.options.verbose or self.options.level == 'debug'
self.logger = logging.getLogger('sdss_install')
if self.logger:
if debug:
self.logger.setLevel(logging.DEBUG)
else:
self.logger.setLevel(logging.INFO)
handler = logging.StreamHandler()
if debug:
formatter = logging.Formatter("%(name)s - " +
"%(levelname)s - " +
"%(filename)s - " +
"line %(lineno)d - " +
"%(message)s")
else:
formatter = logging.Formatter("%(name)s - " +
"%(levelname)s - " +
"%(message)s")
handler.setFormatter(formatter)
self.logger.addHandler(handler)
else:
print('ERROR: Unable to set_logger')
else:
print('ERROR: Unable to set_logger. options=%r, logging=%r'
% (options, logging))
[docs] def set_options(self, options=None):
'''Set self.options wrapper'''
self.options = options if options else None
if not self.options:
print('ERROR - Unable to set_options')
[docs] def initialize_data(self):
'''Initialize class Install data.'''
self.ready = False
self.url = None
self.product = None
self.package = None
self.directory = None
self.svncommand = None
self.exists = None
self.modules = None
self.build_type = None
self.github_remote_url = None
self.external_product = None
[docs] def set_install4(self):
'''Set a class Install4 instance.'''
self.install4 = Install4(logger=self.logger, options=self.options)
if not self.install4:
self.logger.error('Unable to set self.install4')
[docs] def set_install5(self):
'''Set a class Install5 instance.'''
self.install5 = Install5(logger=self.logger, options=self.options)
if not self.install5:
self.logger.error('Unable to set self.install5')
[docs] def import_data(self):
'''Sync data from class Import4 or class Import5 to class Import.'''
if self.options.github:
if self.install5:
self.ready = self.install5.ready
self.product = self.install5.product
self.directory = self.install5.directory
else:
if self.install4:
self.ready = self.install4.ready
self.url = self.install4.url
self.product = self.install4.product
self.package = self.install4.package
self.directory = self.install4.directory
self.svncommand = self.install4.svncommand
self.exists = self.install4.exists
self.modules = self.install4.modules
self.build_type = self.install4.build_type
[docs] def export_data(self):
'''Sync class Import data to class Import4 or class Import5.'''
if self.options.github:
if self.install5:
self.install5.ready = self.ready
self.install5.product = self.product
self.install5.directory = self.directory
else:
self.logger.error('Unable to export_data to class Install5')
else:
if self.install4:
self.install4.ready = self.ready
self.install4.url = self.url
self.install4.product = self.product
self.install4.package = self.package
self.install4.directory = self.directory
self.install4.svncommand = self.svncommand
self.install4.exists = self.exists
self.install4.modules = self.modules
self.install4.build_type = self.build_type
else:
self.logger.error('Unable to export_data to class Install5')
[docs] def set_ready(self):
'''Call set_ready() of class Install4 or class Install5.'''
self.ready = self.logger and self.options
if self.ready:
if self.options.github:
self.set_install5()
if self.install5:
self.install5.set_ready()
else:
self.ready = False
else:
self.set_install4()
if self.install4:
self.install4.set_ready()
else:
self.ready = False
if self.ready:
self.import_data()
[docs] def set_product(self):
'''Call set_product() of class Install4 or class Install5.'''
if self.ready:
if self.options.github:
self.install5.set_product()
else:
self.install4.set_product()
self.import_data()
[docs] def set_directory(self):
'''
Initialize dict self.directory and set value for key 'original' to
current working directory.
'''
if self.ready:
self.directory = dict()
try:
self.directory['original'] = getcwd()
except OSError as ose:
self.logger.error("Check current directory: {0}"
.format(ose.strerror))
self.ready = False
self.export_data()
[docs] def set_directory_install(self):
'''Set dict self.directory values for keys 'root' and 'install'.'''
if self.ready:
self.import_data()
if self.options.root is None or not isdir(self.options.root):
if self.options.root is not None:
if not exists(self.options.root):
try:
makedirs(self.options.root)
self.logger.info("Creating {0}"
.format(self.options.root))
except OSError as ose:
self.logger.error("mkdir: " +
"cannot create directory '{0}': {1}"
.format(self.options.root, ose.strerror))
self.ready = False
else:
self.logger.error("Please set the --root keyword or " +
"SDSS_INSTALL_PRODUCT_ROOT environmental variable " +
"to a valid directory.")
self.ready = False
else:
self.logger.error("Please use the --root keyword " +
"or set a SDSS_INSTALL_PRODUCT_ROOT " +
"environmental variable.")
self.ready = False
if self.ready:
if self.options.root.endswith('/'):
self.options.root = dirname(self.options.root)
if self.options.root is not None:
environ['SDSS_INSTALL_PRODUCT_ROOT'] = self.options.root
if self.options.longpath is not None:
environ['SDSS4TOOLS_LONGPATH'] = 'True'
# set the GIT or SVN product ROOT; this overrides main PRODUCT_ROOT
if self.options.github:
sub_root = environ.get("SDSS_GIT_ROOT")
else:
sub_root = environ.get("SDSS_SVN_ROOT")
# set the root directory for the product
self.directory['root'] = (join(sub_root, self.product['root'])
if self.product['root'] else sub_root)
# check to use version sub-directories or not
verdir = self.product['version']
if self.options.github and self.options.skip_git_verdirs:
verdir = None
# set the install directory for the product
self.directory['install'] = join(self.directory['root'], self.product['name'])
if verdir:
self.directory['install'] = join(self.directory['install'], verdir)
self.export_data()
[docs] def set_directory_work(self):
'''
Set dict self.directory value for key 'work',
used to install product and/or module file.
Remove existing work directory.
'''
if self.ready:
self.import_data()
if self.options.module_only:
self.directory['work'] = self.directory['install']
else:
self.directory['work'] = join(self.directory['original'],
"%(name)s-%(version)s" %
self.product)
if isdir(self.directory['work']):
self.logger.info("Detected old working directory, " +
"%(work)s. Deleting..." % self.directory)
rmtree(self.directory['work'])
self.export_data()
[docs] def clean_directory_install(self, install_dir=None):
'''Remove existing install directory if exists and if option --force.'''
if self.ready:
self.import_data()
install_dir = install_dir if install_dir else self.directory['install']
if isdir(install_dir) and not self.options.test:
if self.options.force:
try:
cwd = getcwd()
except OSError as ose:
self.logger.error(
"Check current directory: {0}".format(ose.strerror))
self.ready = False
if self.ready:
if cwd.startswith(install_dir):
self.logger.error("Current working directory, {}, ".format(cwd) +
"is inside the install directory, {}, ".format(install_dir) +
"which will be deleted via the " +
"-F (or --force) option, so please cd to another " +
"working directory and try again!" % self.directory)
self.ready = False
else:
self.logger.info("Preparing to install in " +
"{} (overwriting due to force option)".format(install_dir))
rmtree(install_dir)
else:
self.logger.error("Install directory, %(install)s, already exists!"
% self.directory)
self.logger.info("Use the -F (or --force) option " +
"to overwrite.")
self.ready = False
else:
self.logger.info("Preparing to install in %(install)s"
% self.directory)
self.export_data()
[docs] def set_sdss_github_remote_url(self, use_public=None):
'''Set the set_sdss_github_remote_url() of class Install5'''
if self.ready and self.options.github and self.install5:
self.install5.set_sdss_github_remote_url(use_public=use_public)
self.import_data()
[docs] def set_svncommand(self):
'''Wrapper for method Install4.set_svncommand()'''
if self.ready and not self.options.github:
self.install4.set_svncommand()
[docs] def set_exists(self):
'''Call set_exists() of class Install4 or class Install5'''
if self.ready:
if not self.options.github:
self.install4.set_exists()
self.import_data()
[docs] def fetch(self):
'''Call set_fetch() of class Install4 or class Install5'''
if self.ready:
if self.options.github:
self.install5.fetch()
else:
self.install4.fetch()
self.import_data()
[docs] def install_external_dependencies(self):
'''Install external dependencies.'''
if self.ready:
if (self.options.external_dependencies and
isinstance(self.options.external_dependencies, dict)
):
for key in self.options.external_dependencies:
if self.ready:
self.external_product = dict()
external_dependency = self.options.external_dependencies[key]
install_product = (external_dependency['install_product']
if external_dependency
and 'install_product' in external_dependency
else None)
paths = (external_dependency['paths']
if external_dependency
and 'paths' in external_dependency
else None)
if install_product:
self.install_external_product(
install_product=install_product)
else:
self.logger.debug('No install_product found.')
# Needs to be called after self.install_external_product()
if paths:
self.set_external_paths(paths=paths)
else:
self.logger.debug('No PATHs found')
else:
self.ready = False
self.logger.error('Failed to install external dependencies. ' +
'self.options.external_dependencies: {}'
.format(self.options.external_dependencies) +
'isinstance(self.options.external_dependencies,dict): {}'
.format(isinstance(self.options.external_dependencies, dict))
)
[docs] def install_external_product(self, install_product=None):
'''Install external products'''
if self.ready:
if install_product and isinstance(install_product, dict):
url = (install_product['url']
if install_product and 'url' in install_product else None)
if url:
if 'github.com' in url:
self.install_external_github_product(
github_product=install_product)
elif 'svn.' in url:
self.install_external_svn_product(
svn_product=install_product)
else:
self.ready = False
self.logger.error('Encountered unsupported ' +
'install_product url: {}.'.format(url))
else:
self.ready = False
self.logger.error('Unable to install_external_product. ' +
'install_product: {}, '.format(install_product) +
'url: {}, '.format(url)
)
else:
self.ready = False
self.logger.error('Unable to install_external_product. ' +
'install_product: {}, '.format(install_product) +
'isinstance(install_product,dict): {}, '
.format(isinstance(install_product, dict))
)
[docs] def install_external_svn_product(self, svn_product=None):
'''Install external dependency from SVN'''
self.ready = False
self.logger.error('Installing external dependencies from svn is currently ' +
'unsupported. Please make a GitHub sdss_install issue (ticket) ' +
'to request that this feature be added.')
[docs] def install_external_github_product(self, github_product=None):
'''Install external dependency from GitHub'''
if self.ready:
if github_product and isinstance(github_product, dict):
url = (github_product['url']
if github_product and 'url' in github_product else None)
github_url = dirname(url) if url else None
product = basename(url) if url else None
version = (github_product['version']
if github_product and 'version' in github_product else None)
github_remote_url = join(github_url, product)
self.install5.validate_product_and_version(github_url=github_url,
product=product,
version=version)
self.ready = self.ready and self.install5.ready
if self.ready:
self.external_product['github_remote_url'] = github_remote_url
self.external_product['product'] = product
self.external_product['version'] = version
self.external_product['is_master'] = (version == 'master')
self.external_product['is_branch'] = (
True if self.external_product['is_master'] else
self.install5.is_type(type='branch',
github_url=github_url,
product=product,
version=version))
self.external_product['is_tag'] = (
False if self.external_product['is_branch'] else
self.install5.is_type(type='tag',
github_url=github_url,
product=product,
version=version))
self.ready = self.ready and self.install5.ready
if self.ready:
self.set_external_product_install_dir()
self.clean_directory_install(
install_dir=dirname(self.external_product['install_dir']))
self.install5.external_product = self.external_product
self.install5.clone()
self.ready = self.ready and self.install5.ready
self.install5.checkout()
else:
self.ready = False
self.logger.error('Unable to install_external_github_product. ' +
'github_product: {}, '.format(github_product) +
'isinstance(github_product,dict): {}, '
.format(isinstance(github_product, dict))
)
[docs] def set_external_product_install_dir(self):
'''Set the directory for external dependencies.'''
if self.ready:
if (self.external_product and
'product' in self.external_product and
'version' in self.external_product
):
install_dir = join(self.directory['root'],
'external',
self.external_product['product'],
self.external_product['version']
)
self.external_product['install_dir'] = install_dir
if not exists(install_dir):
try:
makedirs(install_dir)
self.logger.info("Creating {0}".format(install_dir))
except OSError as ose:
self.logger.error("mkdir: " +
"cannot create directory '{0}': {1}"
.format(install_dir, ose.strerror))
self.ready = False
else:
self.ready = False
self.logger.error('Unable to set_external_product_install_dir. ' +
'self.external_product: {}, '
.format(self.external_product)
)
[docs] def set_external_paths(self, paths=None):
'''Set external paths, like PATH, IDL_PATH, and PYTHONPATH'''
if self.ready:
if paths and isinstance(paths, dict):
idl_paths = paths['idl'] if paths and 'idl' in paths else None
shell_paths = paths['shell'] if paths and 'shell' in paths else None
python_paths = paths['python'] if paths and 'python' in paths else None
install_dir = (self.external_product['install_dir']
if self.external_product else None)
if idl_paths:
for idl_path in idl_paths:
if self.ready:
path = (join(install_dir, idl_path)
if install_dir and isinstance(idl_path, str)
else None)
self.set_external_path(
path=path, path_type='IDL_PATH')
elif shell_paths:
for shell_path in shell_paths:
if self.ready:
path = (join(install_dir, shell_path)
if install_dir and isinstance(shell_path, str)
else None)
self.set_external_path(path=path, path_type='PATH')
elif python_paths:
for python_path in python_paths:
if self.ready:
path = (join(install_dir, python_path)
if install_dir and isinstance(python_path, str)
else None)
self.set_external_path(
path=path, path_type='PYTHONPATH')
else:
self.ready = False
self.logger.error('Unable to set_external_paths. ' +
'Encountered unsupported key in paths: {}, '.format(paths) +
"Supported keys: ['idl','shell','python']"
)
else:
self.ready = False
self.logger.error('Unable to set_external_paths. ' +
'paths: {}, '.format(paths) +
'isinstance(paths,dict): {}, '
.format(isinstance(paths, dict))
)
[docs] def set_external_path(self, path=None, path_type=None):
'''Prepend the given path to the given path_type.'''
if self.ready:
if path and path_type:
if path.endswith('/'):
path = path.rstrip('/')
supported_path_types = ['PATH', 'IDL_PATH', 'PYTHONPATH']
if path_type in supported_path_types:
old_path = None
try:
self.logger.info(
'Loading current {}'.format(path_type))
old_path = environ[path_type]
if old_path and path not in old_path:
new_path = path + ':' + old_path
try:
environ[path_type] = new_path
self.logger.info(
'Updated {}'.format(path_type))
if self.options.level == 'debug':
self.logger.debug("environ['{0}']: {1}"
.format(path_type, environ[path_type]))
except:
self.logger.info(
'WARNING: Unable to update {}. Skipping.'.format(path_type))
except:
try:
environ[path_type] = path
self.logger.info('WARNING: Unable to set {}. '.format(path_type) +
'Setting it to {}.'.format(path))
except:
self.logger.info('WARNING: Unable to set or reset {}. Skipping.'
.format(path_type))
else:
self.logger.info('WARNING: Unable to set_external_path. ' +
'Unsupported path_type: {}. '.format(path_type) +
'Supported path types: {}. '.format(
supported_path_types)
)
else:
self.logger.info('WARNING: Unable to set_external_path. ' +
'path: {0}, path_type: {1}'.format(path, path_type))
[docs] def checkout(self):
'''Call Install5.checkout'''
if self.ready:
self.install5.checkout()
self.import_data()
[docs] def reset_options_from_config(self):
'''
Set absent command-line options from etc/config.ini file,
if it exists.
'''
# NOTE: Initially, config files only have the option [sdss4install].
# These options need to be duplicated for [sdss_install] in order
# to transition from sdss4install to sdss_install.
# Some products may contain an optional etc/config.ini file to
# determine the config self.options to build
if self.ready:
config_filename = join('etc', 'config.ini')
config_file = join(self.directory['work'], config_filename)
if exists(config_file):
config = SafeConfigParser()
try:
config.optionxform = unicode
except:
config.optionxform = str
if len(config.read(config_file)) == 1:
if config.has_section('sdss4install'):
self.process_install_section(config=config,
section='sdss4install')
if config.has_section('sdss_install'):
self.process_install_section(config=config,
section='sdss_install')
if config.has_section('envs'):
missing = [env for key, env in config.items('envs')
if not getenv(env, None)]
for env in missing:
self.logger.error("Required environment variable " +
"{0} must be set prior to sdss_install"
.format(env))
if missing:
self.ready = False
if config.has_section('external_dependencies'):
self.process_install_section(config=config,
section='external_dependencies')
def process_install_section(self, config=None, section=None):
if config and section:
if section == 'external_dependencies':
if 'json_filepath' in config.options(section):
json_filepath = join(self.directory['work'],
config.get(section, 'json_filepath'))
if exists(json_filepath):
try:
with open(json_filepath) as json_file:
self.options.external_dependencies = load(
json_file)
except:
self.logger.info(
'WARNING: Unable to open the file {}'.format(json_filepath))
else:
for option in config.options(section):
if option == 'no_build' and not self.options.no_build:
try:
self.options.no_build = config.getboolean(section,
option)
if self.options.no_build:
self.logger.info("Using {0} to set " +
"--no-build option"
.format(config_filename))
except:
pass
elif option == 'skip_module' and not self.options.skip_module:
try:
self.options.skip_module = config.getboolean(section,
option)
if self.options.skip_module:
self.logger.info("Using {0} to set " +
"--skip_module option".format(config_filename))
except:
pass
elif (option == 'no_python_package' and not
self.options.no_python_package):
try:
self.options.no_python_package = (
config.getboolean(section, option))
if self.options.no_python_package:
self.logger.info("Using {0} to set " +
"--no_python_package option"
.format(config_filename))
except:
pass
elif option == 'make_target' and not self.options.make_target:
try:
self.options.make_target = config.get(
section, option)
if self.options.make_target:
self.logger.info("Using {0} to set " +
"--make_target {1} option"
.format(config_filename,
self.options.make_target))
except:
pass
elif option == 'evilmake' and not self.options.evilmake:
try:
self.options.evilmake = config.getboolean(section,
option)
if self.options.evilmake:
self.logger.info("Using {0} to set " +
"--evilmake option".format(config_filename))
except:
pass
else:
self.logger.error('Unable to process_install_section. ' +
'config={0}, section={1}'.format(config, section))
[docs] def set_build_type(self):
'''Analyze the code to determine the build type'''
self.build_message = None
if self.ready:
self.build_type = list()
if self.options.no_build:
self.build_message = "Proceeding without build..."
else:
if (exists(join(self.directory['work'], 'Makefile'))
and self.options.evilmake):
self.build_message = "Installing via evilmake"
self.build_type.append('evilmake')
elif (exists(join(self.directory['work'], 'setup.py'))
and not self.options.force_build_type):
self.build_type.append('python')
if exists(join(self.directory['work'], 'Makefile')):
self.build_type.append('c')
elif exists(join(self.directory['work'], 'Makefile')):
self.build_type.append('c')
if not self.build_type:
self.build_message = ("Proceeding without a setup.py " +
"or Makefile...")
[docs] def logger_build_message(self):
'''Log the build message.'''
if self.build_message:
self.logger.info(self.build_message)
[docs] def make_directory_install(self):
'''Make install directory.'''
# If this is a trunk or branch install or nothing to build,
# this directory will be created by other means.
if self.ready:
if not (self.product['is_master_or_branch'] or
self.options.no_python_package or
self.options.evilmake or not
self.build_type or
self.options.test):
try:
makedirs(self.directory['install'])
except OSError as ose:
self.logger.error(ose.strerror)
self.ready = False
[docs] def set_modules(self):
'''Set a class Modules instance.'''
self.modules = Modules(options=self.options,
logger=self.logger,
product=self.product,
directory=self.directory,
build_type=self.build_type)
[docs] def set_environ(self):
'''Set environment variables WORKING_DIR and INSTALL_DIR.'''
if self.ready:
environ['WORKING_DIR'] = self.directory['work']
environ['INSTALL_DIR'] = self.directory['install']
[docs] def build(self):
'''Build the installed product.'''
if self.ready:
if (self.product['is_master_or_branch'] or
self.options.no_python_package or
self.options.evilmake or not
self.build_type):
if self.options.test:
self.logger.info("Skipping install in %(install)s " +
"(--test)" % self.directory)
else:
self.logger.info("Installing in %(install)s" %
self.directory)
copytree(self.directory['work'], self.directory['install'])
chdir(self.directory['install'])
if 'evilmake' in self.build_type:
if not self.options.skip_module:
self.modules.load(product=self.product['name'],
version=self.product['version'])
command = ['evilmake', 'clean']
self.logger.info('Running "{0}" in {1}'
.format(' '.join(command),
self.directory['install']))
(out, err, proc_returncode) = self.execute_command(
command=command)
self.logger.debug(out)
if proc_returncode != 0:
self.logger.error("Evilmake response:")
self.logger.error(err)
command = ['evilmake']
if self.options.make_target:
command += [self.options.make_target]
self.logger.info('Running "{0}" in {1}'
.format(' '.join(command),
self.directory['install']))
(out, err, proc_returncode) = self.execute_command(command=command,
argument='ignore')
self.logger.debug(out)
if proc_returncode != 0:
self.logger.error("Evilmake response:")
self.logger.error(err)
elif 'c' in self.build_type:
if not self.options.skip_module:
self.modules.load(product=self.product['name'],
version=self.product['version'])
command = (['make', '-C', 'src']
if exists(join(self.directory['work'], 'src'))
else ['make'])
if self.options.make_target:
command += [self.options.make_target]
self.logger.info('Running "{0}" in {1}'
.format(' '.join(command),
self.directory['install']))
(out, err, proc_returncode) = self.execute_command(
command=command)
self.logger.debug(out)
if proc_returncode != 0:
self.logger.error("Error during compile:")
self.logger.error(err)
self.ready = False
if self.options.documentation:
self.logger.info('WARNING: Documentation will not be built ' +
'for trunk or branch installs!')
else:
self.package = True
chdir(self.directory['work'])
if 'python' in self.build_type:
command = [executable,
'setup.py',
'install',
"--prefix=%(install)s" % self.directory]
self.logger.debug(' '.join(command))
if not self.options.test:
(out, err, proc_returncode) = self.execute_command(
command=command)
self.logger.debug(out)
if proc_returncode != 0:
self.logger.error("Error during installation:")
self.logger.error(err)
self.ready = False
#
# Copy additional files
#
md = None
cf = None
if isdir('etc'):
md = list()
cf = list()
for root, dirs, files in walk('etc'):
for d in dirs:
md.append(join(self.directory['install'], root, d))
for name in files:
if name.endswith('.module'):
continue
cf.append((join(root, name),
join(self.directory['install'],
root,
name)))
if md or cf:
etc_dir = join(self.directory['install'], 'etc')
self.logger.debug('Creating {0}'.format(etc_dir))
makedirs(etc_dir)
if md:
for name in md:
self.logger.debug('Creating {0}'.format(name))
if not self.options.test:
makedirs(name)
if cf:
for src, dst in cf:
self.logger.debug('Copying {0} -> {1}'
.format(src, dst))
if not self.options.test:
copyfile(src, dst)
[docs] def build_documentation(self):
'''Build the documentaion of the installed product.'''
if self.ready and self.options.documentation:
if 'python' in self.build_type:
if exists(join('doc', 'index.rst')):
#
# Assume Sphinx documentation.
#
self.logger.debug("Found Sphinx documentation.")
if not self.options.skip_module:
self.modules.load(product=self.product['name'],
version=self.product['version'])
sphinx_keywords = {
'name':
self.product['name'],
'release':
self.product['version'],
'version':
'.'.join(self.product['version'].split('.')[0:3]),
'year':
datetime.date.today().year}
for sd in ('_templates', '_build', '_static'):
if not isdir(join('doc', sd)):
try:
makedirs(join('doc', sd))
except OSError as ose:
self.logger.error(ose.strerror)
self.ready = False
if not exists(join('doc', 'Makefile')):
copyfile(join(getenv('sdss_install_DIR'),
'etc',
'doc',
'sphinx',
'Makefile'),
join('doc', 'Makefile'))
if not exists(join('doc', 'conf.py')):
with open(join(getenv('sdss_install_DIR'),
'etc',
'doc',
'sphinx',
'conf.py')) as conf:
newconf = conf.read().format(**sphinx_keywords)
with open(join('doc', 'conf.py'), 'w') as conf2:
conf2.write(newconf)
command = [executable, 'setup.py', 'build_sphinx']
self.logger.debug(' '.join(command))
if not self.options.test:
(out, err, proc_returncode) = self.execute_command(
command=command)
self.logger.debug(out)
if proc_returncode != 0:
self.logger.error(
"Error during documentation build:")
self.logger.error(err)
self.ready = False
if not self.options.test:
if isdir(join('build', 'sphinx', 'html')):
copytree(join('build', 'sphinx', 'html'),
join(self.directory['install'], 'doc'))
else:
self.logger.warning("Documentation build requested, " +
"but no documentation found.")
else:
#
# This is not a Python product, assume Doxygen documentation.
#
if isdir('doc'):
doxygen_keywords = {
'name':
self.product['name'],
'version':
self.product['version'],
'description':
("Documentation for %(name)s built by sdss_install."
% self.product)}
if not exists(join('doc', 'Makefile')):
copyfile(join(getenv('sdss_install_DIR'),
'etc',
'doc',
'doxygen',
'Makefile'),
join('doc', 'Makefile'))
if not exists(join('doc', 'Docyfile')):
with open(join(getenv('sdss_install_DIR'),
'etc',
'doc', 'doxygen', 'Doxyfile')) as conf:
newconf = conf.read().format(**doxygen_keywords)
with open(join('doc', 'Doxyfile'), 'w') as conf2:
conf2.write(newconf)
else:
self.logger.warning("Documentation build requested, " +
"but no documentation found.")
#
# At this point either we have already completed a Python installation
# or we still need to compile the C/C++ product (we had to construct
# doc/Makefile first).
#
[docs] def build_package(self):
'''Build the C/C++ product.'''
if self.ready and 'c' in self.build_type and self.package:
environ[self.product['name'].upper()+'_DIR'] = self.directory['work']
command = ['make', 'install']
self.logger.debug(' '.join(command))
if not self.options.test:
(out, err, proc_returncode) = self.execute_command(command=command)
self.logger.debug(out)
if proc_returncode != 0:
self.logger.error("Error during compile:")
self.logger.error(err)
self.ready = False
[docs] def clean(self):
'''Remove the work directory tree.'''
if self.ready:
try:
rmtree(self.directory['work'])
except:
pass
[docs] def finalize(self):
'''Log installation final result message.'''
# Don't put <if self.ready> here:
if self.directory and self.directory['original']:
chdir(self.directory['original'])
finalize = "Done" if self.ready else "Fail"
# if self.options.github and self.options.module_only:
# rmtree(join(self.product['name'],self.product['version']))
finalize_ps = None
if self.options.test:
finalize = "Test " + finalize
else:
finalize += "!"
if basename(self.options.product) == 'tree':
pass
elif self.options.skip_module:
finalize += " (skipped modules)"
elif self.modules and not self.modules.built:
finalize += " (failed modules)"
if self.modules.built == False:
finalize_ps = "Nonexistent template %r" % self.modules.file
elif self.modules and self.modules.built:
finalize_ps = ("Ready to load module %(name)s/%(version)s"
% self.product)
self.logger.info(finalize)
if finalize_ps:
self.logger.info(finalize_ps)
[docs] def execute_command(self, command=None, argument=None):
'''Execute the passed terminal command.'''
(out, err, proc_returncode) = (None, None, None)
if command:
proc = Popen(command, stdout=PIPE, stderr=PIPE)
if proc:
(out, err) = proc.communicate() if proc else (None, None)
proc_returncode = proc.returncode if proc else None
if argument:
out = out.decode(
"utf-8", argument) if isinstance(out, bytes) else out
err = err.decode(
"utf-8", argument) if isinstance(err, bytes) else err
else:
out = out.decode(
"utf-8") if isinstance(out, bytes) else out
err = err.decode(
"utf-8") if isinstance(err, bytes) else err
else:
self.ready = False
self.logger.error('Unable to execute_command. ' +
'proc: {}'.format(proc))
else:
self.ready = False
self.logger.error('Unable to execute_command. ' +
'command: {}'.format(command))
return (out, err, proc_returncode)