Skip to content
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
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,9 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Changed

* Changed `compas_rhino.uninstall` to also remove broken symlinks if no specific packages are provided for un-installation.
* Changed `compas_rhino.install` to also remove broken symlinks.

### Removed


Expand Down
86 changes: 69 additions & 17 deletions src/compas_rhino/install.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,21 @@
]


def install(version=None, packages=None):
def install(version=None, packages=None, clean=False):
"""Install COMPAS for Rhino.

Parameters
----------
version : {'5.0', '6.0', '7.0'}, optional
version : {'5.0', '6.0', '7.0', '8.0'}, optional
The version number of Rhino.
Default is ``'6.0'``.
packages : list of str, optional
List of packages to install or None to use default package list.
Default is ``['compas', 'compas_rhino', 'compas_ghpython']``.
Default is the result of ``installable_rhino_packages``,
which collects all installable packages in the current environment.
clean : bool, optional
If ``True``, this will clean up the entire scripts folder and remove
also existing symlinks that are not importable in the current environment.

Examples
--------
Expand All @@ -44,28 +48,62 @@ def install(version=None, packages=None):

"""

if version not in ('5.0', '6.0', '7.0'):
if version not in ('5.0', '6.0', '7.0', '8.0'):
version = '6.0'

packages = _filter_installable_packages(version, packages)

ipylib_path = compas_rhino._get_ironpython_lib_path(version)
# We install COMPAS packages in the scripts folder
# instead of directly as IPy module.
scripts_path = compas_rhino._get_scripts_path(version)

print('Installing COMPAS packages to Rhino {0} scripts folder:'.format(version))
print('{}\n'.format(scripts_path))
# This is for old installs
ipylib_path = compas_rhino._get_ironpython_lib_path(version)

packages = _filter_installable_packages(version, packages)

results = []
symlinks_to_install = []
symlinks_to_uninstall = []
exit_code = 0

# check all installable packages
# add the packages that can't be imported from the current env to the list of symlinks to uninstall
# and remove the package name from the list of installable packages
# make a copy of the list to avoid problems with removing items
# note: perhaps this should already happen in the filter function...
Copy link
Member

Choose a reason for hiding this comment

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

This makes sense to me.

for name in packages[:]:
try:
importlib.import_module(name)
except ImportError:
path = os.path.join(scripts_path, name)
symlinks_to_uninstall.append(dict(name=name, link=path))
packages.remove(name)

# Also remove all broken symlinks from the scripts folder
# because ... they're broken!
# If it is an actual folder or a file, leave it alone
# because probably someone put it there on purpose.
for name in os.listdir(scripts_path):
path = os.path.join(scripts_path, name)
if os.path.islink(path):
if not os.path.exists(path):
symlinks_to_uninstall.append(dict(name=name, link=path))
else:
if clean:
try:
importlib.import_module(name)
except ImportError:
path = os.path.join(scripts_path, name)
symlinks_to_uninstall.append(dict(name=name, link=path))
else:
if name not in packages:
packages.append(name)

# add all of the packages in the list of installable packages
# to the list of symlinks to uninstall
# and to the list of symlinks to install
for package in packages:
symlink_path = os.path.join(scripts_path, package)
if os.path.exists(symlink_path):
symlinks_to_uninstall.append(dict(name=package, link=symlink_path))
symlinks_to_uninstall.append(dict(name=package, link=symlink_path))

package_path = compas_rhino._get_package_path(importlib.import_module(package))
symlinks_to_install.append(dict(name=package, source_path=package_path, link=symlink_path))
Expand All @@ -92,10 +130,21 @@ def install(version=None, packages=None):
if not compas_rhino._try_remove_bootstrapper(ipylib_path):
results.append(('compas_bootstrapper', 'ERROR: Cannot remove legacy compas_bootstrapper, try to run as administrator.'))

# -------------------------
# Ready to start installing
# -------------------------

# create new symlinks and register the results
symlinks = [(link['source_path'], link['link']) for link in symlinks_to_install]
install_results = compas._os.create_symlinks(symlinks)

# set the exit code based on the installation results
if not all(install_results):
exit_code = -1

# make a list of installed packages
# based on the installation results
# and update the general results list
installed_packages = []
for install_data, success in zip(symlinks_to_install, install_results):
if success:
Expand All @@ -105,9 +154,7 @@ def install(version=None, packages=None):
result = 'ERROR: Cannot create symlink, try to run as administrator.'
results.append((install_data['name'], result))

if not all(install_results):
exit_code = -1

# finalize the general results list with info about the bootstrapper
if exit_code == -1:
results.append(('compas_bootstrapper', 'WARNING: One or more packages failed, will not install bootstrapper, try uninstalling first'))
else:
Expand All @@ -117,9 +164,13 @@ def install(version=None, packages=None):
except: # noqa: E722
results.append(('compas_bootstrapper', 'ERROR: Could not create compas_bootstrapper to auto-determine Python environment'))

# output the outcome of the installation process
# perhaps we should more info here
print('Installing COMPAS packages to Rhino {0} scripts folder:'.format(version))
print('{}\n'.format(scripts_path))

for package, status in results:
print(' {} {}'.format(package.ljust(20), status))

if status != 'OK':
exit_code = -1

Expand Down Expand Up @@ -271,9 +322,10 @@ def _filter_installable_packages(version, packages):

parser = argparse.ArgumentParser()

parser.add_argument('-v', '--version', choices=['5.0', '6.0', '7.0'], default='6.0', help="The version of Rhino to install the packages in.")
parser.add_argument('-v', '--version', choices=['5.0', '6.0', '7.0', '8.0'], default='6.0', help="The version of Rhino to install the packages in.")
parser.add_argument('-p', '--packages', nargs='+', help="The packages to install.")
parser.add_argument('--clean', dest='clean', default=False, action='store_true')

args = parser.parse_args()

install(version=args.version, packages=args.packages)
install(version=args.version, packages=args.packages, clean=args.clean)
31 changes: 22 additions & 9 deletions src/compas_rhino/install_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ def install_plugin(plugin, version=None):
----------
plugin : str
The path to the plugin folder.
version : {'5.0', '6.0', '7.0'}, optional
version : {'5.0', '6.0', '7.0', '8.0'}, optional
The version of Rhino for which the plugin should be installed.
Default is ``'6.0'``.

Expand Down Expand Up @@ -58,14 +58,29 @@ def install_plugin(plugin, version=None):
python -m compas_rhino.install_plugin -v 7.0 ui/Rhino/XXX

"""
if version not in ('5.0', '6.0', '7.0'):
if version not in ('5.0', '6.0', '7.0', '8.0'):
version = '6.0'

if not os.path.isdir(plugin):
raise Exception('Cannot find the plugin: {}'.format(plugin))

plugin_dir = os.path.abspath(plugin)

# clean up the plugin directory

# # Also remove all broken symlinks
# # because ... they're broken!
# broken = []
# for name in os.listdir(plugin_dir):
# path = os.path.join(plugin_dir, name)
# if os.path.islink(path):
# if not os.path.exists(path):
# broken.append(path)
# if broken:
# pass

# proceed with the installation

plugin_path, plugin_name = os.path.split(plugin_dir)
if not plugin_path:
plugin_path = os.getcwd()
Expand Down Expand Up @@ -98,15 +113,13 @@ def install_plugin(plugin, version=None):
source = plugin_dir
destination = os.path.join(python_plugins_path, plugin_fullname)

print('Installing PlugIn {} to Rhino PythonPlugIns.'.format(plugin_name))
print('\nInstalling PlugIn {} to Rhino PythonPlugIns.'.format(plugin_name))

remove_symlinks([destination])
create_symlinks([(source, destination)])

print()
print('PlugIn {} Installed.'.format(plugin_name))
print()
print('Restart Rhino and open the Python editor at least once to make it available.')
print('\nPlugIn {} Installed.'.format(plugin_name))
print('\nRestart Rhino and open the Python editor at least once to make it available.')


# ==============================================================================
Expand All @@ -118,10 +131,10 @@ def install_plugin(plugin, version=None):
import argparse

parser = argparse.ArgumentParser(
description='COMPAS Rhino PLugin Installation command-line utility.')
description='COMPAS Rhino Plugin Installation command-line utility.')

parser.add_argument('plugin', help="The path to the plugin directory.")
parser.add_argument('-v', '--version', choices=['5.0', '6.0', '7.0'], default='6.0', help="The version of Rhino.")
parser.add_argument('-v', '--version', choices=['5.0', '6.0', '7.0', '8.0'], default='6.0', help="The version of Rhino.")

args = parser.parse_args()

Expand Down
26 changes: 18 additions & 8 deletions src/compas_rhino/uninstall.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ def uninstall(version=None, packages=None):

Parameters
----------
version : {'5.0', '6.0', '7.0'}, optional
version : {'5.0', '6.0', '7.0', '8.0'}, optional
The version number of Rhino.
Default is ``'6.0'``.
packages : list of str, optional
Expand All @@ -42,16 +42,27 @@ def uninstall(version=None, packages=None):
python -m compas_rhino.uninstall -v 6.0

"""
if version not in ('5.0', '6.0', '7.0'):
if version not in ('5.0', '6.0', '7.0', '8.0'):
version = '6.0'

packages = _filter_installed_packages(version, packages)

ipylib_path = compas_rhino._get_ironpython_lib_path(version)
# We install COMPAS packages in the scripts folder
# instead of directly as IPy module.
scripts_path = compas_rhino._get_scripts_path(version)

# This is for old installs
ipylib_path = compas_rhino._get_ironpython_lib_path(version)

packages = _filter_installed_packages(version, packages)

# Also remove all broken symlinks
# because ... they're broken!
for name in os.listdir(scripts_path):
path = os.path.join(scripts_path, name)
if os.path.islink(path):
if not os.path.exists(path):
if name not in packages:
packages.append(name)

print('Uninstalling COMPAS packages from Rhino {0} scripts folder: \n{1}'.format(version, scripts_path))

results = []
Expand All @@ -60,8 +71,7 @@ def uninstall(version=None, packages=None):

for package in packages:
symlink_path = os.path.join(scripts_path, package)
if os.path.exists(symlink_path):
symlinks_to_uninstall.append(dict(name=package, link=symlink_path))
symlinks_to_uninstall.append(dict(name=package, link=symlink_path))

# Handle legacy install location
# This does not always work,
Expand Down Expand Up @@ -197,7 +207,7 @@ def after_rhino_uninstall(uninstalled_packages):

parser = argparse.ArgumentParser()

parser.add_argument('-v', '--version', choices=['5.0', '6.0', '7.0'], default='6.0', help="The version of Rhino to install the packages in.")
parser.add_argument('-v', '--version', choices=['5.0', '6.0', '7.0', '8.0'], default='6.0', help="The version of Rhino to install the packages in.")
parser.add_argument('-p', '--packages', nargs='+', help="The packages to uninstall.")

args = parser.parse_args()
Expand Down