Repository: easybuilders/easybuild-framework

Issue:
Improved `--try-toolchain` overly picky
The improved `--try-toolchain` now rejects cases that used to work perfectly, where the toolchain can be "downgraded" to a subtoolchain.

Example:
```
eb libxc-4.2.3-intel-2018a.eb --try-toolchain=iccifort,2018.3.222-GCC-7.3.0-2.30
== temporary log file in case of crash /localscratch/eb-oe4GFb/easybuild-77BHp6.log
ERROR: No possible mapping from source toolchain spec {'lapack_family': None, 'name': 'iimpi', 'blas_family': None, 'version': '2018a', 'cuda': None, 'comp_family': 'Intel', 'mpi_family': 'IntelMPI'} to target toolchain hierarchy specs [{'lapack_family': None, 'name': 'GCCcore', 'blas_family': None, 'version': '7.3.0', 'cuda': None, 'comp_family': 'GCC', 'mpi_family': None}, {'lapack_family': None, 'name': 'iccifort', 'blas_family': None, 'version': '2018.3.222-GCC-7.3.0-2.30', 'cuda': None, 'comp_family': 'Intel', 'mpi_family': None}]
```

@ocaisa 


Discussion:
Hmm, so I'm on the fence with this one. I think that if you really want to do that you should add a `--force` option and we could revert it back to regex method in that case.

The issue here for me would be that you should be only doing that if you know what you are doing, so protecting against this is reasonable default behaviour.
+1 on @ocaisa view on this, you should take care when mapping from a full toolchain to a lower subtoolchain that has less capabilities.

So, there should be a hint in the error message that mentions that you can use `--force` if desired, and then also a clear warning when `--force` is used.

The error message could also be improved a bit, e.g. `ERROR: Toolchain iccifort is not equivalent to intel toolchain in terms of capabilities: ...`?
+1 sure I don't mind force. Issue is here that there quite a few easyconfigs compiled with foss or intel that don't use MKL/OpenBLAS or MPI or both; as we try to compile things minimally we often lower the toolchain.
If you need a workaround for now you can use
```
--try-amend=''
```
since when `--try-toolchain` is coupled with another try option it will revert to using the regex method.
I think we will soon be at a place where we can be stricter on the toolchain used for easyconfigs and put them at the right level from the outset. We would just need to support mapping up to the top level, and that should be do-able fairly easily now.

Resolving patch:
diff --git a/easybuild/framework/easyconfig/tweak.py b/easybuild/framework/easyconfig/tweak.py
--- a/easybuild/framework/easyconfig/tweak.py
+++ b/easybuild/framework/easyconfig/tweak.py
@@ -119,8 +119,28 @@ def tweak(easyconfigs, build_specs, modtool, targetdirs=None):
             else:
                 target_toolchain['version'] = source_toolchain['version']
 
-            src_to_dst_tc_mapping = map_toolchain_hierarchies(source_toolchain, target_toolchain, modtool)
+            try:
+                src_to_dst_tc_mapping = map_toolchain_hierarchies(source_toolchain, target_toolchain, modtool)
+            except EasyBuildError as err:
+                # make sure exception was raised by match_minimum_tc_specs because toolchain mapping could not be done
+                if "No possible mapping from source toolchain" in err.msg:
+
+                    if build_option('force'):
+                        warning_msg = "Combining --try-toolchain with --force for toolchains with unequal capabilities:"
+                        warning_msg += " disabling recursion and not changing (sub)toolchains for dependencies."
+                        print_warning(warning_msg)
+                        revert_to_regex = True
+                        modifying_toolchains = False
+                    else:
+                        error_msg = err.msg + '\n'
+                        error_msg += "Toolchain %s is not equivalent to toolchain %s in terms of capabilities. "
+                        error_msg += "(If you know what you are doing, you can use --force to proceed anyway.)"
+                        raise EasyBuildError(error_msg, target_toolchain['name'], source_toolchain['name'])
+                else:
+                    # simply re-raise the exception if something else went wrong
+                    raise err
 
+        if not revert_to_regex:
             _log.debug("Applying build specifications recursively (no software name/version found): %s", build_specs)
             orig_ecs = resolve_dependencies(easyconfigs, modtool, retain_all_deps=True)
 


Test patch: