Tools: --disable-* argument for removing features

Optional argument --disable-* for removing features in install scripts.
This commit is contained in:
Marek Fiala 2022-05-20 15:15:52 +02:00
parent be4d13d888
commit 90a69c4490
4 changed files with 45 additions and 15 deletions

View File

@ -109,7 +109,7 @@ Any mirror server can be used provided the URL matches the ``github.com`` downlo
* ``check``: For each tool, checks whether the tool is available in the system path and in ``IDF_TOOLS_PATH``.
* ``install-python-env``: Create a Python virtual environment in the ``${IDF_TOOLS_PATH}/python_env`` directory and install there the required Python packages. An optional ``--features`` argument allows one to specify a comma-separated list of features. For each feature a requirements file must exist. For example, feature ``XY`` is a valid feature if ``${IDF_PATH}/tools/requirements/requirements.XY.txt`` is an existing file with a list of Python packages to be installed. There is one mandatory ``core`` feature ensuring core functionality of ESP-IDF (build, flash, monitor, debug in console). There can be an arbitrary number of optional features. The selected list of features is stored in ``idf-env.json``. The requirement files contain a list of the desired Python packages to be installed and ``espidf.constraints.*.txt`` downloaded from https://dl.espressif.com and stored in ``${IDF_TOOLS_PATH}`` the package version requirements for a given ESP-IDF version.
* ``install-python-env``: Create a Python virtual environment in the ``${IDF_TOOLS_PATH}/python_env`` directory and install there the required Python packages. An optional ``--features`` argument allows one to specify a comma-separated list of features to be added or removed. Feature that begins with ``-`` will be removed and features with ``+`` or without any sign will be added. Example syntax for removing feature ``XY`` is ``--features=-XY`` and for adding ``--features=+XY`` or ``--features=XY``. If both removing and adding options are provided with the same feature, no operation is performed. For each feature a requirements file must exist. For example, feature ``XY`` is a valid feature if ``${IDF_PATH}/tools/requirements/requirements.XY.txt`` is an existing file with a list of Python packages to be installed. There is one mandatory ``core`` feature ensuring core functionality of ESP-IDF (build, flash, monitor, debug in console). There can be an arbitrary number of optional features. The selected list of features is stored in ``idf-env.json``. The requirement files contain a list of the desired Python packages to be installed and ``espidf.constraints.*.txt`` downloaded from https://dl.espressif.com and stored in ``${IDF_TOOLS_PATH}`` the package version requirements for a given ESP-IDF version.
* ``check-python-dependencies``: Checks if all required Python packages are installed. Packages from ``${IDF_PATH}/tools/requirements/requirements.*.txt`` files selected by the feature list of ``idf-env.json`` are checked with the package versions specified in the ``espidf.constraints.*.txt`` file. The constraint file will be downloaded from https://dl.espressif.com if this step hasn't been done already in the last day.

View File

@ -1022,9 +1022,20 @@ class IDFRecord:
def features(self) -> List[str]:
return self._features
def extend_features(self, features: List[str]) -> None:
# Features can be only updated, but always maintain existing features.
self._features = list(set(features + self._features))
def update_features(self, add: Tuple[str, ...] = (), remove: Tuple[str, ...] = ()) -> None:
# Update features, but maintain required feature 'core'
# If the same feature is present in both argument's tuples, do not update this feature
add_set = set(add)
remove_set = set(remove)
# Remove duplicates
features_to_add = add_set.difference(remove_set)
features_to_remove = remove_set.difference(add_set)
features = set(self._features)
features.update(features_to_add)
features.difference_update(features_to_remove)
features.add('core')
self._features = list(features)
@property
def targets(self) -> List[str]:
@ -1051,7 +1062,7 @@ class IDFRecord:
# When some of these key attributes, which are irreplaceable with default values, are not found, raise VallueError
raise ValueError('Inconsistent record')
idf_record_obj.extend_features(record_dict.get('features', []))
idf_record_obj.update_features(record_dict.get('features', []))
idf_record_obj.extend_targets(record_dict.get('targets', []))
unset = record_dict.get('unset')
@ -1328,13 +1339,18 @@ def feature_to_requirements_path(feature): # type: (str) -> str
return os.path.join(global_idf_path or '', 'tools', 'requirements', 'requirements.{}.txt'.format(feature))
def add_and_check_features(idf_env_obj, features_str): # type: (IDFEnv, str) -> list[str]
def process_and_check_features(idf_env_obj, features_str): # type: (IDFEnv, str) -> list[str]
new_features = []
remove_features = []
for new_feature_candidate in features_str.split(','):
if os.path.isfile(feature_to_requirements_path(new_feature_candidate)):
new_features += [new_feature_candidate]
idf_env_obj.get_active_idf_record().extend_features(new_features)
if new_feature_candidate.startswith('-'):
remove_features += [new_feature_candidate.lstrip('-')]
else:
new_feature_candidate = new_feature_candidate.lstrip('+')
# Feature to be added needs to be checked if is valid
if os.path.isfile(feature_to_requirements_path(new_feature_candidate)):
new_features += [new_feature_candidate]
idf_env_obj.get_active_idf_record().update_features(tuple(new_features), tuple(remove_features))
return idf_env_obj.get_active_idf_record().features
@ -1847,7 +1863,7 @@ def get_wheels_dir(): # type: () -> Optional[str]
def get_requirements(new_features): # type: (str) -> list[str]
idf_env_obj = IDFEnv.get_idf_env()
features = add_and_check_features(idf_env_obj, new_features)
features = process_and_check_features(idf_env_obj, new_features)
idf_env_obj.save()
return [feature_to_requirements_path(feature) for feature in features]

View File

@ -13,13 +13,20 @@ from itertools import chain
def action_extract_features(args: str) -> None:
"""
Command line arguments starting with "--enable-" are features. This function selects those and prints them.
Command line arguments starting with "--enable-" or "--disable" are features. This function selects those, add them signs '+' or '-' and prints them.
"""
features = ['core'] # "core" features should be always installed
features = ['+core'] # "core" features should be always installed
if args:
arg_prefix = '--enable-'
features += [arg[len(arg_prefix):] for arg in args.split() if arg.startswith(arg_prefix)]
arg_enable_prefix = '--enable-'
arg_disable_prefix = '--disable-'
# features to be enabled has prefix '+', disabled has prefix '-'
for arg in args.split():
if arg.startswith(arg_enable_prefix):
features.append('+' + arg[len(arg_enable_prefix):])
elif arg.startswith(arg_disable_prefix):
features.append('-' + arg[len(arg_disable_prefix):])
features = list(set(features))
print(','.join(features))

View File

@ -61,6 +61,13 @@ class TestPythonInstall(unittest.TestCase):
self.assertIn(REQ_CORE, output)
self.assertIn(REQ_GDBGUI, output)
# Argument that begins with '-' can't stand alone to be parsed as value
output = self.run_idf_tools(['install-python-env', '--features=-gdbgui'])
# After removing the gdbgui should not be present
self.assertIn(CONSTR, output)
self.assertIn(REQ_CORE, output)
self.assertNotIn(REQ_GDBGUI, output)
def test_no_constraints(self): # type: () -> None
output = self.run_idf_tools(['install-python-env', '--no-constraints'])
self.assertNotIn(CONSTR, output)