diff options
-rw-r--r-- | lib/spack/docs/packaging_guide.rst | 121 | ||||
-rw-r--r-- | lib/spack/spack/hooks/licensing.py | 136 | ||||
-rw-r--r-- | lib/spack/spack/package.py | 33 | ||||
-rw-r--r-- | var/spack/repos/builtin/packages/allinea-forge/package.py | 28 | ||||
-rw-r--r-- | var/spack/repos/builtin/packages/allinea-reports/package.py | 28 | ||||
-rw-r--r-- | var/spack/repos/builtin/packages/nag/package.py | 32 | ||||
-rw-r--r-- | var/spack/repos/builtin/packages/pgi/package.py | 72 |
7 files changed, 446 insertions, 4 deletions
diff --git a/lib/spack/docs/packaging_guide.rst b/lib/spack/docs/packaging_guide.rst index 1b7941ab24..650e0ee3b2 100644 --- a/lib/spack/docs/packaging_guide.rst +++ b/lib/spack/docs/packaging_guide.rst @@ -703,6 +703,127 @@ Fetching a revision Subversion branches are handled as part of the directory structure, so you can check out a branch or tag by changing the ``url``. + +.. _license: + +Licensed software +------------------------------------------ + +In order to install licensed software, Spack needs to know a few more +details about a package. The following class attributes should be defined. + +``license_required`` +~~~~~~~~~~~~~~~~~~~~~ + +Boolean. If set to ``True``, this software requires a license. If set to +``False``, all of the following attributes will be ignored. Defaults to +``False``. + +``license_comment`` +~~~~~~~~~~~~~~~~~~~~~ + +String. Contains the symbol used by the license manager to denote a comment. +Defaults to ``#``. + +``license_files`` +~~~~~~~~~~~~~~~~~~~~~ + +List of strings. These are files that the software searches for when +looking for a license. All file paths must be relative to the installation +directory. More complex packages like Intel may require multiple +licenses for individual components. Defaults to the empty list. + +``license_vars`` +~~~~~~~~~~~~~~~~~~~~~ + +List of strings. Environment variables that can be set to tell the software +where to look for a license if it is not in the usual location. Defaults +to the empty list. + +``license_url`` +~~~~~~~~~~~~~~~~~~~~~ + +String. A URL pointing to license setup instructions for the software. +Defaults to the empty string. + +For example, let's take a look at the package for the PGI compilers. + +.. code-block:: python + + # Licensing + license_required = True + license_comment = '#' + license_files = ['license.dat'] + license_vars = ['PGROUPD_LICENSE_FILE', 'LM_LICENSE_FILE'] + license_url = 'http://www.pgroup.com/doc/pgiinstall.pdf' + +As you can see, PGI requires a license. Its license manager, FlexNet, uses +the ``#`` symbol to denote a comment. It expects the license file to be +named ``license.dat`` and to be located directly in the installation prefix. +If you would like the installation file to be located elsewhere, simply set +``PGROUPD_LICENSE_FILE`` or ``LM_LICENSE_FILE`` after installation. For +further instructions on installation and licensing, see the URL provided. + +Let's walk through a sample PGI installation to see exactly what Spack is +and isn't capable of. Since PGI does not provide a download URL, it must +be downloaded manually. It can either be added to a mirror or located in +the current directory when ``spack install pgi`` is run. See :ref:`mirrors` +for instructions on setting up a mirror. + +After running ``spack install pgi``, the first thing that will happen is +Spack will create a global license file located at +``$SPACK_ROOT/etc/spack/licenses/pgi/license.dat``. It will then open up the +file using the editor set in ``$EDITOR``, or vi if unset. It will look like +this: + +.. code-block:: + + # A license is required to use pgi. + # + # The recommended solution is to store your license key in this global + # license file. After installation, the following symlink(s) will be + # added to point to this file (relative to the installation prefix): + # + # license.dat + # + # Alternatively, use one of the following environment variable(s): + # + # PGROUPD_LICENSE_FILE + # LM_LICENSE_FILE + # + # If you choose to store your license in a non-standard location, you may + # set one of these variable(s) to the full pathname to the license file, or + # port@host if you store your license keys on a dedicated license server. + # You will likely want to set this variable in a module file so that it + # gets loaded every time someone tries to use pgi. + # + # For further information on how to acquire a license, please refer to: + # + # http://www.pgroup.com/doc/pgiinstall.pdf + # + # You may enter your license below. + +You can add your license directly to this file, or tell FlexNet to use a +license stored on a separate license server. Here is an example that +points to a license server called licman1: + +.. code-block:: + + SERVER licman1.mcs.anl.gov 00163eb7fba5 27200 + USE_SERVER + +If your package requires the license to install, you can reference the +location of this global license using ``self.global_license_file``. +After installation, symlinks for all of the files given in +``license_files`` will be created, pointing to this global license. +If you install a different version or variant of the package, Spack +will automatically detect and reuse the already existing global license. + +If the software you are trying to package doesn't rely on license files, +Spack will print a warning message, letting the user know that they +need to set an environment variable or pointing them to installation +documentation. + .. _patching: Patches diff --git a/lib/spack/spack/hooks/licensing.py b/lib/spack/spack/hooks/licensing.py new file mode 100644 index 0000000000..31f15cc3e9 --- /dev/null +++ b/lib/spack/spack/hooks/licensing.py @@ -0,0 +1,136 @@ +import os + +import spack +import llnl.util.tty as tty +from llnl.util.filesystem import join_path + + +def pre_install(pkg): + """This hook handles global license setup for licensed software.""" + if pkg.license_required: + set_up_license(pkg) + + +def set_up_license(pkg): + """Prompt the user, letting them know that a license is required. + + For packages that rely on license files, a global license file is + created and opened for editing. + + For packages that rely on environment variables to point to a + license, a warning message is printed. + + For all other packages, documentation on how to set up a license + is printed.""" + + # If the license can be stored in a file, create one + if pkg.license_files: + license_path = pkg.global_license_file + if not os.path.exists(license_path): + # Create a new license file + write_license_file(pkg, license_path) + # Open up file in user's favorite $EDITOR for editing + spack.editor(license_path) + tty.msg("Added global license file %s" % license_path) + else: + # Use already existing license file + tty.msg("Found already existing license %s" % license_path) + + # If not a file, what about an environment variable? + elif pkg.license_vars: + tty.warn("A license is required to use %s. Please set %s to the " + "full pathname to the license file, or port@host if you" + " store your license keys on a dedicated license server" % + (pkg.name, ' or '.join(pkg.license_vars))) + + # If not a file or variable, suggest a website for further info + elif pkg.license_url: + tty.warn("A license is required to use %s. See %s for details" % + (pkg.name, pkg.license_url)) + + # If all else fails, you're on your own + else: + tty.warn("A license is required to use %s" % pkg.name) + + +def write_license_file(pkg, license_path): + """Writes empty license file. + + Comments give suggestions on alternative methods of + installing a license.""" + + comment = pkg.license_comment + + # Global license directory may not already exist + if not os.path.exists(os.path.dirname(license_path)): + os.makedirs(os.path.dirname(license_path)) + license = open(license_path, 'w') + + # License files + license.write("""\ +{0} A license is required to use {1}. +{0} +{0} The recommended solution is to store your license key in this global +{0} license file. After installation, the following symlink(s) will be +{0} added to point to this file (relative to the installation prefix): +{0} +""".format(comment, pkg.name)) + + for filename in pkg.license_files: + license.write("{0}\t{1}\n".format(comment, filename)) + + license.write("{0}\n".format(comment)) + + # Environment variables + if pkg.license_vars: + license.write("""\ +{0} Alternatively, use one of the following environment variable(s): +{0} +""".format(comment)) + + for var in pkg.license_vars: + license.write("{0}\t{1}\n".format(comment, var)) + + license.write("""\ +{0} +{0} If you choose to store your license in a non-standard location, you may +{0} set one of these variable(s) to the full pathname to the license file, or +{0} port@host if you store your license keys on a dedicated license server. +{0} You will likely want to set this variable in a module file so that it +{0} gets loaded every time someone tries to use {1}. +{0} +""".format(comment, pkg.name)) + + # Documentation + if pkg.license_url: + license.write("""\ +{0} For further information on how to acquire a license, please refer to: +{0} +{0}\t{1} +{0} +""".format(comment, pkg.license_url)) + + license.write("""\ +{0} You may enter your license below. + +""".format(comment)) + + license.close() + + +def post_install(pkg): + """This hook symlinks local licenses to the global license for + licensed software.""" + if pkg.license_required: + symlink_license(pkg) + + +def symlink_license(pkg): + """Create local symlinks that point to the global license file.""" + target = pkg.global_license_file + for filename in pkg.license_files: + link_name = join_path(pkg.prefix, filename) + if os.path.exists(target): + os.symlink(target, link_name) + tty.msg("Added local symlink %s to global license file" % + link_name) diff --git a/lib/spack/spack/package.py b/lib/spack/spack/package.py index 8e6cf32954..8f8eb6db64 100644 --- a/lib/spack/spack/package.py +++ b/lib/spack/spack/package.py @@ -374,6 +374,22 @@ class Package(object): if not hasattr(self, 'list_depth'): self.list_depth = 1 + # Set default licensing information + if not hasattr(self, 'license_required'): + self.license_required = False + + if not hasattr(self, 'license_comment'): + self.license_comment = '#' + + if not hasattr(self, 'license_files'): + self.license_files = [] + + if not hasattr(self, 'license_vars'): + self.license_vars = [] + + if not hasattr(self, 'license_url'): + self.license_url = None + # Set up some internal variables for timing. self._fetch_time = 0.0 self._total_time = 0.0 @@ -382,6 +398,16 @@ class Package(object): spack.repo.get(self.extendee_spec)._check_extendable() @property + def global_license_file(self): + """Returns the path where a global license file should be stored.""" + if not self.license_files: + return + spack_root = ancestor(__file__, 4) + global_license_dir = join_path(spack_root, 'etc', 'spack', 'licenses') + return join_path(global_license_dir, self.name, + os.path.basename(self.license_files[0])) + + @property def version(self): if not self.spec.versions.concrete: raise ValueError("Can only get of package with concrete version.") @@ -882,6 +908,7 @@ class Package(object): def build_process(): """Forked for each build. Has its own process and python module space set up by build_environment.fork().""" + start_time = time.time() if not fake: if not skip_patch: @@ -1320,11 +1347,9 @@ class Package(object): def rpath(self): """Get the rpath this package links with, as a list of paths.""" rpaths = [self.prefix.lib, self.prefix.lib64] - rpaths.extend(d.prefix.lib - for d in self.spec.traverse(root=False) + rpaths.extend(d.prefix.lib for d in self.spec.traverse(root=False) if os.path.isdir(d.prefix.lib)) - rpaths.extend(d.prefix.lib64 - for d in self.spec.traverse(root=False) + rpaths.extend(d.prefix.lib64 for d in self.spec.traverse(root=False) if os.path.isdir(d.prefix.lib64)) return rpaths diff --git a/var/spack/repos/builtin/packages/allinea-forge/package.py b/var/spack/repos/builtin/packages/allinea-forge/package.py new file mode 100644 index 0000000000..e5bd62dd70 --- /dev/null +++ b/var/spack/repos/builtin/packages/allinea-forge/package.py @@ -0,0 +1,28 @@ +from spack import * + + +class AllineaForge(Package): + """Allinea Forge is the complete toolsuite for software development - with + everything needed to debug, profile, optimize, edit and build C, C++ and + Fortran applications on Linux for high performance - from single threads + through to complex parallel HPC codes with MPI, OpenMP, threads or CUDA.""" + + homepage = "http://www.allinea.com/products/develop-allinea-forge" + + version('6.0.4', 'df7f769975048477a36f208d0cd57d7e') + + # Licensing + license_required = True + license_comment = '#' + license_files = ['licences/Licence'] + license_vars = ['ALLINEA_LICENCE_FILE', 'ALLINEA_LICENSE_FILE'] + license_url = 'http://www.allinea.com/user-guide/forge/Installation.html' + + def url_for_version(self, version): + # TODO: add support for other architectures/distributions + url = "http://content.allinea.com/downloads/" + return url + "allinea-forge-%s-Redhat-6.0-x86_64.tar" % version + + def install(self, spec, prefix): + textinstall = which('textinstall.sh') + textinstall('--accept-licence', prefix) diff --git a/var/spack/repos/builtin/packages/allinea-reports/package.py b/var/spack/repos/builtin/packages/allinea-reports/package.py new file mode 100644 index 0000000000..7b11e681f5 --- /dev/null +++ b/var/spack/repos/builtin/packages/allinea-reports/package.py @@ -0,0 +1,28 @@ +from spack import * + + +class AllineaReports(Package): + """Allinea Performance Reports are the most effective way to characterize + and understand the performance of HPC application runs. One single-page + HTML report elegantly answers a range of vital questions for any HPC site + """ + + homepage = "http://www.allinea.com/products/allinea-performance-reports" + + version('6.0.4', '3f13b08a32682737bc05246fbb2fcc77') + + # Licensing + license_required = True + license_comment = '#' + license_files = ['licences/Licence'] + license_vars = ['ALLINEA_LICENCE_FILE', 'ALLINEA_LICENSE_FILE'] + license_url = 'http://www.allinea.com/user-guide/reports/Installation.html' + + def url_for_version(self, version): + # TODO: add support for other architectures/distributions + url = "http://content.allinea.com/downloads/" + return url + "allinea-reports-%s-Redhat-6.0-x86_64.tar" % version + + def install(self, spec, prefix): + textinstall = which('textinstall.sh') + textinstall('--accept-licence', prefix) diff --git a/var/spack/repos/builtin/packages/nag/package.py b/var/spack/repos/builtin/packages/nag/package.py new file mode 100644 index 0000000000..af893d3db0 --- /dev/null +++ b/var/spack/repos/builtin/packages/nag/package.py @@ -0,0 +1,32 @@ +from spack import * +import os + + +class Nag(Package): + """The NAG Fortran Compiler.""" + homepage = "http://www.nag.com/nagware/np.asp" + + version('6.1', '1e29d9d435b7ccc2842a320150b28ba4') + version('6.0', '3fa1e7f7b51ef8a23e6c687cdcad9f96') + + # Licensing + license_required = True + license_comment = '!' + license_files = ['lib/nag.key'] + license_vars = ['NAG_KUSARI_FILE'] + license_url = 'http://www.nag.com/doc/inun/np61/lin-mac/klicence.txt' + + def url_for_version(self, version): + # TODO: url and checksum are architecture dependent + # TODO: We currently only support x86_64 + return 'http://www.nag.com/downloads/impl/npl6a%sna_amd64.tgz' % \ + str(version).replace('.', '') + + def install(self, spec, prefix): + # Set installation directories + os.environ['INSTALL_TO_BINDIR'] = prefix.bin + os.environ['INSTALL_TO_LIBDIR'] = prefix.lib + os.environ['INSTALL_TO_MANDIR'] = prefix + '/share/man/man' + + # Run install script + os.system('./INSTALLU.sh') diff --git a/var/spack/repos/builtin/packages/pgi/package.py b/var/spack/repos/builtin/packages/pgi/package.py new file mode 100644 index 0000000000..8dc6453469 --- /dev/null +++ b/var/spack/repos/builtin/packages/pgi/package.py @@ -0,0 +1,72 @@ +from spack import * +import os + + +class Pgi(Package): + """PGI optimizing multi-core x64 compilers for Linux, MacOS & Windows + with support for debugging and profiling of local MPI processes. + + Note: The PGI compilers are licensed software. You will need to create + an account on the PGI homepage and download PGI yourself. Once the download + finishes, rename the file (which may contain information such as the + architecture) to the format: pgi-<version>.tar.gz. Spack will search your + current directory for a file of this format. Alternatively, add this + file to a mirror so that Spack can find it. For instructions on how to + set up a mirror, see http://software.llnl.gov/spack/mirrors.html""" + + homepage = "http://www.pgroup.com/" + url = "file://%s/pgi-16.3.tar.gz" % os.getcwd() + + version('16.3', '618cb7ddbc57d4e4ed1f21a0ab25f427') + + variant('network', default=True, + description="Perform a network install") + variant('single', default=False, + description="Perform a single system install") + variant('nvidia', default=False, + description="Enable installation of optional NVIDIA components") + variant('amd', default=False, + description="Enable installation of optional AMD components") + variant('java', default=False, + description="Enable installation of Java Runtime Environment") + variant('mpi', default=False, + description="Enable installation of Open MPI") + + # Licensing + license_required = True + license_comment = '#' + license_files = ['license.dat'] + license_vars = ['PGROUPD_LICENSE_FILE', 'LM_LICENSE_FILE'] + license_url = 'http://www.pgroup.com/doc/pgiinstall.pdf' + + def install(self, spec, prefix): + # Enable the silent installation feature + os.environ['PGI_SILENT'] = "true" + os.environ['PGI_ACCEPT_EULA'] = "accept" + os.environ['PGI_INSTALL_DIR'] = prefix + + if '+network' in spec and '~single' in spec: + os.environ['PGI_INSTALL_TYPE'] = "network" + os.environ['PGI_INSTALL_LOCAL_DIR'] = "%s/%s/share_objects" % \ + (prefix, self.version) + elif '+single' in spec and '~network' in spec: + os.environ['PGI_INSTALL_TYPE'] = "single" + else: + msg = 'You must choose either a network install or a single ' + msg += 'system install.\nYou cannot choose both.' + raise RuntimeError(msg) + + if '+nvidia' in spec: + os.environ['PGI_INSTALL_NVIDIA'] = "true" + + if '+amd' in spec: + os.environ['PGI_INSTALL_AMD'] = "true" + + if '+java' in spec: + os.environ['PGI_INSTALL_JAVA'] = "true" + + if '+mpi' in spec: + os.environ['PGI_INSTALL_MPI'] = "true" + + # Run install script + os.system("./install") |