-
Notifications
You must be signed in to change notification settings - Fork 12
uberenv updates to support dev with python in spack envs #124
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
Closed
Closed
Changes from 12 commits
Commits
Show all changes
21 commits
Select commit
Hold shift + click to select a range
d3efe08
better default view dir and start of support to exclude externals
cyrush 34c000f
typo
cyrush 0040a8d
fix editor corruption
cyrush f2dfd5f
keep moving
cyrush d911fc5
more options
cyrush 72a2162
fix for path loc in complex env case
cyrush 1aaf09e
gen patched host config with python view path
cyrush e3deb4f
fix
cyrush 5387b84
fixes and updates to handle more env view path patching
cyrush 3cea348
add comment
cyrush 0774d2b
smoke and extra mirrors
cyrush 0424996
move earlier option
cyrush 3fed325
use symlink to view instead of hacking view dir
cyrush 5430066
fix quoting issue with env inspect
cyrush 0c801e8
add fail fast to install command
cyrush 78ca0ac
Merge branch 'main' into task/2023_08_uberenv_py_and_view_tweaks
cyrush 823c812
complete merge
cyrush 95420cd
bootstrap defense
cyrush b0d06b8
fix arg check
cyrush 6932ef7
Merge branch 'main' into task/2023_08_uberenv_py_and_view_tweaks
cyrush b9bcfaa
remove uness option
cyrush File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -255,6 +255,13 @@ def parse_args(): | |
| default=False, | ||
| help="Only install (using pre-setup Spack).") | ||
|
|
||
| # Spack skip externals | ||
| parser.add_argument("--spack-skip-externals", | ||
| dest="spack_skip_externals", | ||
| default=None, | ||
| nargs="+", | ||
| help="Skip spack finding any externals") | ||
|
|
||
| # Spack externals list | ||
| parser.add_argument("--spack-externals", | ||
| dest="spack_externals", | ||
|
|
@@ -411,14 +418,14 @@ def set_from_args_or_json(self,setting, optional=True): | |
| setting_value = self.args[setting] | ||
| return setting_value | ||
|
|
||
| def set_from_json(self,setting, optional=True): | ||
| def set_from_json(self,setting, optional=True, default_value=None): | ||
| """ | ||
| When optional=False: | ||
| If the setting key is not in the json file, error and raise an exception. | ||
| When optional=True: | ||
| If the setting key is not in the json file or args, return None. | ||
| If the setting key is not in the json file or args, return {default_value}. | ||
| """ | ||
| setting_value = None | ||
| setting_value = default_value | ||
| try: | ||
| setting_value = self.project_args[setting] | ||
| except (KeyError): | ||
|
|
@@ -578,13 +585,15 @@ class SpackEnv(UberEnv): | |
|
|
||
| def __init__(self, args, extra_args): | ||
| UberEnv.__init__(self,args,extra_args) | ||
|
|
||
| self.pkg_version = self.set_from_json("package_version") | ||
| self.pkg_src_dir = self.set_from_args_or_json("package_source_dir", True) | ||
| self.pkg_final_phase = self.set_from_args_or_json("package_final_phase", True) | ||
| self.build_mode = self.set_from_args_or_json("spack_build_mode", True) | ||
| self.spack_skip_externals = self.set_from_args_or_json("spack_skip_externals", True) | ||
| self.spack_externals = self.set_from_args_or_json("spack_externals", True) | ||
| self.spack_compiler_paths = self.set_from_args_or_json("spack_compiler_paths", True) | ||
| default_dict = {} | ||
| self.spack_host_config_patches = self.set_from_json("spack_host_config_patches", True, default_dict) | ||
|
|
||
| # default spack build mode is dev-build | ||
| if self.build_mode is None: | ||
|
|
@@ -788,17 +797,22 @@ def find_spack_pkg_path_from_hash(self, pkg_name, pkg_hash): | |
| # TODO: at least print a warning when several choices exist. This will | ||
| # pick the first in the list. | ||
| if l.startswith(pkg_name) and len(l.split()) > 1: | ||
| return {"name": pkg_name, "path": l.split()[-1]} | ||
| pkg_path = l.split()[-1] | ||
| if os.path.exists(pkg_path): | ||
| return {"name": pkg_name, "path": pkg_path} | ||
| print("[ERROR: Failed to find package from hash named '{0}' with hash '{1}']".format(pkg_name, pkg_hash)) | ||
| sys.exit(-1) | ||
|
|
||
| def find_spack_pkg_path(self, pkg_name, spec = ""): | ||
| res, out = sexe("{0} find -p {1}".format(self.spack_exe(),self.pkg_name_with_spec), ret_output = True) | ||
| requested_pkg_name_with_spec = "'{0}{1}'".format(pkg_name,spec) | ||
| res, out = sexe("{0} find -p {1}".format(self.spack_exe(), requested_pkg_name_with_spec), ret_output = True) | ||
| for l in out.split("\n"): | ||
| # TODO: at least print a warning when several choices exist. This will | ||
| # pick the first in the list. | ||
| if l.startswith(pkg_name) and len(l.split()) > 1: | ||
| return {"name": pkg_name, "path": l.split()[-1]} | ||
| pkg_path = l.split()[-1] | ||
|
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. part of a fix for #123 |
||
| if os.path.exists(pkg_path): | ||
| return {"name": pkg_name, "path": pkg_path} | ||
| print("[ERROR: Failed to find package from spec named '{0}' with spec '{1}']".format(pkg_name, spec)) | ||
| sys.exit(-1) | ||
|
|
||
|
|
@@ -900,7 +914,7 @@ def create_spack_env(self): | |
| if res != 0: | ||
| print("[ERROR: Failed to create Spack Environment]") | ||
| sys.exit(-1) | ||
|
|
||
| # Find pre-installed compilers and packages and stop uberenv.py | ||
| if self.spack_setup_environment: | ||
| # Finding compilers | ||
|
|
@@ -914,27 +928,31 @@ def create_spack_env(self): | |
| print("[failed to setup environment]") | ||
| sys.exit(-1) | ||
|
|
||
| # Finding externals | ||
| spack_external_find_cmd = "{0} external find --not-buildable".format(self.spack_exe()) | ||
| if self.spack_externals is None: | ||
| print("[finding all packages Spack knows about]") | ||
| spack_external_find_cmd = "{0} --all".format(spack_external_find_cmd) | ||
| else: | ||
| print("[finding packages from list]") | ||
| spack_external_find_cmd = "{0} {1}".format(spack_external_find_cmd, self.spack_externals) | ||
| res_external = sexe(spack_external_find_cmd, echo=True) | ||
| if res_external != 0: | ||
| print("[failed to setup environment]") | ||
| sys.exit(-1) | ||
| if self.spack_skip_externals is None: | ||
| # Finding externals | ||
| spack_external_find_cmd = "{0} external find --not-buildable".format(self.spack_exe()) | ||
| if self.spack_externals is None: | ||
| print("[finding all packages Spack knows about]") | ||
| spack_external_find_cmd = "{0} --all".format(spack_external_find_cmd) | ||
| else: | ||
| print("[finding packages from list]") | ||
| spack_external_find_cmd = "{0} {1}".format(spack_external_find_cmd, self.spack_externals) | ||
| res_external = sexe(spack_external_find_cmd, echo=True) | ||
| if res_external != 0: | ||
| print("[failed to setup environment]") | ||
| sys.exit(-1) | ||
|
|
||
| # Copy spack.yaml to where you called package source dir | ||
| generated_spack_yaml = pjoin(self.spack_env_directory, "spack.yaml") | ||
| copied_spack_yaml = pjoin(pabs(self.pkg_src_dir), "spack.yaml") | ||
| print("[copying spack yaml file to {0}]".format(copied_spack_yaml)) | ||
| sexe("cp {0} {1}".format(generated_spack_yaml, copied_spack_yaml)) | ||
|
|
||
| print("[setup environment]") | ||
|
|
||
| # if the existing spack.yaml does not have a view entry, | ||
| # add a "view" folder at the root of the env | ||
| self.setup_spack_env_view() | ||
|
|
||
| # For each package path (if there is a repo.yaml), add Spack repository to environment | ||
| if len(self.packages_paths) > 0: | ||
| for _base_path in self.packages_paths: | ||
|
|
@@ -948,8 +966,8 @@ def create_spack_env(self): | |
| print("[ERROR: No Spack repo.yaml detected in {0}]".format(spack_pkg_repo)) | ||
| sys.exit(-1) | ||
|
|
||
| # Add spack package | ||
| print("[adding spack package]") | ||
| # Add main spack package | ||
| print("[adding spack package: {0}]".format(self.pkg_name_with_spec)) | ||
| spack_add_cmd = "{0} add {1}".format(self.spack_exe(), | ||
| self.pkg_name_with_spec) | ||
| sexe(spack_add_cmd, echo=True) | ||
|
|
@@ -961,6 +979,38 @@ def create_spack_env(self): | |
| self.spack_exe(), self.pkg_src_dir, self.pkg_name_with_spec) | ||
| sexe(spack_develop_cmd, echo=True) | ||
|
|
||
| def setup_spack_env_view(self): | ||
| """ | ||
| Update spack env yaml to provide view dir that isn't hidden. | ||
| """ | ||
| # Note: there is no way to do this via spack command line, we have to | ||
| # hack yaml | ||
| spack_yaml_file =pjoin(self.spack_env_directory, "spack.yaml") | ||
| spack_yaml_txt = open(spack_yaml_file).readlines() | ||
| found_view = False | ||
| view_indent = "" | ||
| res_lines = [] | ||
| for l in spack_yaml_txt: | ||
| # view: true is default case (complex view was not provided) | ||
| if l.strip().startswith("view:"): | ||
| if not l.strip().count("true"): | ||
| found_view = True | ||
| res_lines.append(l) | ||
| else: | ||
| view_indent = l[0:l.find("view")] | ||
| else: | ||
| res_lines.append(l) | ||
| if not found_view: | ||
| print("[adding spack view section to env yaml file {0}]".format(spack_yaml_file)) | ||
| ofile = open(spack_yaml_file,"w") | ||
| for l in res_lines: | ||
| ofile.write(l) | ||
| ofile.write("{0}view:\n".format(view_indent)) | ||
| ofile.write("{0}{0}default:\n".format(view_indent)) | ||
| ofile.write("{0}{0}{0}root: view\n".format(view_indent)) | ||
| ofile.write("{0}{0}{0}projections:\n".format(view_indent)) | ||
| ofile.write("{0}{0}{0}{0}all: ".format(view_indent) + "'{name}-{version}'\n") | ||
|
|
||
| def concretize_spack_env(self): | ||
| # Spack concretize | ||
| print("[concretizing spack env]") | ||
|
|
@@ -1046,18 +1096,18 @@ def install(self): | |
| if res != 0: | ||
| print("[ERROR: Failure of spack install]") | ||
| return res | ||
|
|
||
| # when using install or uberenv-pkg mode, create a symlink to the host config | ||
| if self.build_mode == "install" or \ | ||
| self.build_mode == "uberenv-pkg" \ | ||
| or self.use_install: | ||
| self.build_mode == "uberenv-pkg" or \ | ||
| self.use_install: | ||
| # only create a symlink if you're completing all phases | ||
| if self.pkg_final_phase == None or self.pkg_final_phase == "install": | ||
| # use spec_hash to locate b/c other helper won't work if complex | ||
| # deps are provided in the spec (e.g: @ver+variant ^package+variant) | ||
| pkg_path = self.find_spack_pkg_path_from_hash(self.pkg_name, self.spec_hash) | ||
| if self.pkg_name != pkg_path["name"]: | ||
| if self.pkg_name != pkg_path["name"] or not os.path.exists(pkg_path["path"]): | ||
| print("[ERROR: Could not find install of {0} with hash {1}]".format(self.pkg_name,self.spec_hash)) | ||
| print("[package info: {0}]".format(str(pkg_path))) | ||
| return -1 | ||
| else: | ||
| # Symlink host-config file | ||
|
|
@@ -1072,6 +1122,12 @@ def install(self): | |
| sexe("rm -f {0}".format(hc_symlink_path)) | ||
| print("[symlinking host config file {0} to {1}]".format(hc_path,hc_symlink_path)) | ||
| os.symlink(hc_path,hc_symlink_path) | ||
| # NOTE: you may want this for dev build as well | ||
| if len(self.spack_host_config_patches) > 0: | ||
| hc_patch_path = os.path.splitext(hc_fname)[0] + "-patch.cmake" | ||
| hc_patch_path = pjoin(self.dest_dir,hc_patch_path) | ||
| if self.patch_host_config_for_spack_view(hc_symlink_path,hc_patch_path) == -1: | ||
| return -1 | ||
| # if user opt'd for an install, we want to symlink the final | ||
| # install to an easy place: | ||
| # Symlink install directory | ||
|
|
@@ -1105,6 +1161,44 @@ def install(self): | |
| print("[ERROR: Unsupported build mode: {0}]".format(self.build_mode)) | ||
| return -1 | ||
|
|
||
| def patch_host_config_for_spack_view(self, host_config_file_src, host_config_file_patched): | ||
| """ | ||
| Patch host config entries to point final spack python view paths | ||
| """ | ||
| # | ||
| # NOTE: | ||
| # | ||
| # This isn't pretty, this logic exists to solve a chicken vs egg issue | ||
| # with how spack views are setup. | ||
| # | ||
| # We need to use the final python view layout created by spack if we | ||
| # want our dependent python modules to work outside of spack without | ||
| # env var gymnastics. | ||
| # | ||
| # This method replaces cmake cache entries with new entires that point | ||
| # to the result of a spack view path glob. | ||
| # | ||
| print("[patching spack view paths into host config file {0} to create {1}]".format(host_config_file_src,host_config_file_patched)) | ||
| hc_lines = open(host_config_file_src).readlines() | ||
| hc_out = open(host_config_file_patched,"w") | ||
| for l in hc_lines: | ||
| found = False | ||
| for k,v in self.spack_host_config_patches.items(): | ||
| if l.count(k) > 0: | ||
| found = True | ||
| # find view path | ||
| view_search_path = os.path.join(self.spack_env_directory,v) | ||
| view_path_glob = glob.glob(view_search_path) | ||
| if len(view_path_glob) == 0: | ||
| print("[ERROR: Could not find view entry {0} path: {1}]".format(k,view_search_path)) | ||
| return -1 | ||
| # replace existing entry with what we found | ||
| entry = " # NOTE: Pathed by uberenv to use spack view path instead of spack build path\n" | ||
| entry += 'set({0} "{1}" CACHE PATH "")\n'.format(k,view_path_glob[0]) | ||
| hc_out.write(entry) | ||
| if not found: | ||
| hc_out.write(l) | ||
|
|
||
| def get_mirror_path(self): | ||
| mirror_path = self.args["mirror"] | ||
| if not mirror_path: | ||
|
|
||
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
part of a fix for #123