Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Install as package part2 #845

Merged
merged 4 commits into from
Feb 19, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 115 additions & 46 deletions install.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
src_path = os.path.join(source_path, "src")
sys.path.insert(0, src_path)

# Note: The following imports are carefully selected, they will work even
# though rez is not yet built.
#
from rez.utils._version import _rez_version
from rez.cli._entry_points import get_specifications
from rez.backport.shutilwhich import which
Expand Down Expand Up @@ -98,26 +101,117 @@ def copy_completion_scripts(dest_dir):
return None


def install(dest_dir, print_welcome=False):
"""Install rez into the given directory.

Args:
dest_dir (str): Full path to the install directory.
"""
print("installing rez to %s..." % dest_dir)

# create the virtualenv
create_environment(dest_dir)

# install rez from source
install_rez_from_source(dest_dir)

# patch the rez binaries
patch_rez_binaries(dest_dir)

# copy completion scripts into venv
completion_path = copy_completion_scripts(dest_dir)

# mark venv as production rez install. Do not remove - rez uses this!
_, _, _, venv_bin_dir = path_locations(dest_dir)
dest_bin_dir = os.path.join(venv_bin_dir, "rez")
validation_file = os.path.join(dest_bin_dir, ".rez_production_install")
with open(validation_file, 'w') as f:
f.write(_rez_version)

# done
if print_welcome:
print()
print("SUCCESS! To activate Rez, add the following path to $PATH:")
print(dest_bin_dir)

if completion_path:
print('')
shell = os.getenv('SHELL')

if shell:
shell = os.path.basename(shell)
ext = "csh" if "csh" in shell else "sh" # Basic selection logic

print("You may also want to source the completion script (for %s):" % shell)
print("source {0}/complete.{1}".format(completion_path, ext))
else:
print("You may also want to source the relevant completion script from:")
print(completion_path)

print('')


def install_rez_from_source(dest_dir):
_, py_executable = get_py_venv_executable(dest_dir)

# install via pip
run_command([py_executable, "-m", "pip", "install", "."])


def install_as_rez_package(repo_path):
"""Installs rez as a rez package.

Note that this can be used to install new variants of rez into an existing
rez package (you may require multiple platform installations for example).

Args:
repo_path (str): Full path to the package repository to install into.
"""
from tempfile import mkdtemp

# do a temp production (venv-based) rez install
tmpdir = mkdtemp(prefix="rez-install-")
install(tmpdir)

try:
Comment on lines +161 to +176
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Maybe this temp install setup can be turned into a context so it can be re-used in the future:

temp_install context
import contextlib


@contextlib.contextmanager
def temp_install(cleanup=True, print_welcome=False):
    """Do a temp production (venv-based) rez install.
    
    Args:
        cleanup (bool): Cleanup temp install when exiting context.
    """
    from tempfile import mkdtemp

    tmpdir = mkdtemp(prefix="rez-install-")
    install(tmpdir, print_welcome=print_welcome)

    try:
        yield tmpdir
    finally:
        if cleanup:
            try:
                shutil.rmtree(tmpdir)
            except Exception:
                pass


def install_as_rez_package(repo_path):
    """Installs rez as a rez package.
    
    Note that this can be used to install new variants of rez into an existing
    rez package (you may require multiple platform installations for example).
    
    Args:
        repo_path (str): Full path to the package repository to install into.
    """
    with temp_install() as tmpdir:
        # This extracts a rez package from the installation. See
        # rez.utils.installer.install_as_rez_package for more details.
        #
        args = (
            os.path.join(tmpdir, "bin", "python"), "-E", "-c",
            r"from rez.utils.installer import install_as_rez_package;"
            r"install_as_rez_package('%s')" % repo_path
        )
        print(subprocess.check_output(args))

# This extracts a rez package from the installation. See
# rez.utils.installer.install_as_rez_package for more details.
#
args = (
os.path.join(tmpdir, "bin", "python"), "-E", "-c",
r"from rez.utils.installer import install_as_rez_package;"
r"install_as_rez_package('%s')" % repo_path
)
print(subprocess.check_output(args))

finally:
# cleanup temp install
try:
shutil.rmtree(tmpdir)
except:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
except:
except Exception:

pass


if __name__ == "__main__":
parser = argparse.ArgumentParser("Rez installer")
parser = argparse.ArgumentParser(
"Rez installer", description="Install rez in a production ready, "
"standalone Python virtual environment.")
parser.add_argument(
'-v', '--verbose', action='count', dest='verbose', default=0,
help="Increase verbosity.")
parser.add_argument(
'-s', '--keep-symlinks', action="store_true", default=False,
help="Don't run realpath on the passed DEST_DIR to resolve symlinks; "
help="Don't run realpath on the passed DIR to resolve symlinks; "
"ie, the baked script locations may still contain symlinks")
parser.add_argument(
"DIR", default="/opt/rez", nargs='?',
'-p', '--as-rez-package', action="store_true",
help="Install rez as a rez package. Note that this installs the API "
"only (no cli tools), and DIR is expected to be the path to a rez "
"package repository (and will default to ~/packages instead).")
parser.add_argument(
"DIR", nargs='?',
help="Destination directory. If '{version}' is present, it will be "
"expanded to the rez version. Default: %(default)s")
"expanded to the rez version. Default: /opt/rez")

opts = parser.parse_args()

Expand All @@ -128,49 +222,24 @@ def install_rez_from_source(dest_dir):
)

# determine install path
dest_dir = opts.DIR.format(version=_rez_version)
if opts.DIR:
path = opts.DIR
elif opts.as_rez_package:
path = "~/packages"
else:
path = "/opt/rez"

if opts.as_rez_package:
dest_dir = path
else:
dest_dir = path.format(version=_rez_version)

dest_dir = os.path.expanduser(dest_dir)
if not opts.keep_symlinks:
dest_dir = os.path.realpath(dest_dir)

print("installing rez to %s..." % dest_dir)

# create the virtualenv
create_environment(dest_dir)

# install rez from source
install_rez_from_source(dest_dir)

# patch the rez binaries
patch_rez_binaries(dest_dir)

# copy completion scripts into venv
completion_path = copy_completion_scripts(dest_dir)

# mark venv as production rez install. Do not remove - rez uses this!
_, _, _, venv_bin_dir = path_locations(dest_dir)
dest_bin_dir = os.path.join(venv_bin_dir, "rez")
validation_file = os.path.join(dest_bin_dir, ".rez_production_install")
with open(validation_file, 'w') as f:
f.write(_rez_version)

# done
print()
print("SUCCESS! To activate Rez, add the following path to $PATH:")
print(dest_bin_dir)

if completion_path:
print('')
shell = os.getenv('SHELL')

if shell:
shell = os.path.basename(shell)
ext = "csh" if "csh" in shell else "sh" # Basic selection logic

print("You may also want to source the completion script (for %s):" % shell)
print("source {0}/complete.{1}".format(completion_path, ext))
else:
print("You may also want to source the relevant completion script from:")
print(completion_path)

print('')
# perform the installation
if opts.as_rez_package:
install_as_rez_package(dest_dir)
else:
install(dest_dir, print_welcome=True)
41 changes: 41 additions & 0 deletions src/rez/utils/installer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
from __future__ import print_function

import rez
from rez.package_maker import make_package
from rez.system import system
import os.path
import sys
import shutil


def install_as_rez_package(repo_path):
"""Install the current rez installation as a rez package.

Note: This is very similar to 'rez-bind rez', however rez-bind is intended
for deprecation. Rez itself is a special case.

Args:
repo_path (str): Repository to install the rez package into.
"""
def commands():
env.PYTHONPATH.append('{this.root}')

def make_root(variant, root):
# copy source
rez_path = rez.__path__[0]
site_path = os.path.dirname(rez_path)
rezplugins_path = os.path.join(site_path, "rezplugins")

shutil.copytree(rez_path, os.path.join(root, "rez"))
shutil.copytree(rezplugins_path, os.path.join(root, "rezplugins"))

variant = system.variant
variant.append("python-{0.major}.{0.minor}".format(sys.version_info))

with make_package("rez", repo_path, make_root=make_root) as pkg:
pkg.version = rez.__version__
pkg.commands = commands
pkg.variants = [variant]

print('')
print("Success! Rez was installed to %s/rez/%s" % (repo_path, rez.__version__))