|
4 | 4 | # SPDX-License-Identifier: (Apache-2.0 OR MIT) |
5 | 5 |
|
6 | 6 | import argparse |
7 | | -import io |
8 | 7 | import os |
9 | 8 | import shutil |
10 | 9 | import sys |
|
24 | 23 | import spack.cmd.uninstall |
25 | 24 | import spack.config |
26 | 25 | import spack.environment as ev |
| 26 | +import spack.environment.depfile as depfile |
27 | 27 | import spack.environment.shell |
28 | 28 | import spack.schema.env |
| 29 | +import spack.spec |
29 | 30 | import spack.tengine |
30 | | -import spack.traverse as traverse |
31 | 31 | import spack.util.string as string |
32 | 32 | from spack.util.environment import EnvironmentModifications |
33 | 33 |
|
@@ -637,161 +637,22 @@ def env_depfile_setup_parser(subparser): |
637 | 637 | ) |
638 | 638 |
|
639 | 639 |
|
640 | | -def _deptypes(use_buildcache): |
641 | | - """What edges should we follow for a given node? If it's a cache-only |
642 | | - node, then we can drop build type deps.""" |
643 | | - return ("link", "run") if use_buildcache == "only" else ("build", "link", "run") |
644 | | - |
645 | | - |
646 | | -class MakeTargetVisitor(object): |
647 | | - """This visitor produces an adjacency list of a (reduced) DAG, which |
648 | | - is used to generate Makefile targets with their prerequisites.""" |
649 | | - |
650 | | - def __init__(self, target, pkg_buildcache, deps_buildcache): |
651 | | - """ |
652 | | - Args: |
653 | | - target: function that maps dag_hash -> make target string |
654 | | - pkg_buildcache (str): "only", "never", "auto": when "only", |
655 | | - redundant build deps of roots are dropped |
656 | | - deps_buildcache (str): same as pkg_buildcache, but for non-root specs. |
657 | | - """ |
658 | | - self.adjacency_list = [] |
659 | | - self.target = target |
660 | | - self.pkg_buildcache = pkg_buildcache |
661 | | - self.deps_buildcache = deps_buildcache |
662 | | - self.deptypes_root = _deptypes(pkg_buildcache) |
663 | | - self.deptypes_deps = _deptypes(deps_buildcache) |
664 | | - |
665 | | - def neighbors(self, node): |
666 | | - """Produce a list of spec to follow from node""" |
667 | | - deptypes = self.deptypes_root if node.depth == 0 else self.deptypes_deps |
668 | | - return traverse.sort_edges(node.edge.spec.edges_to_dependencies(deptype=deptypes)) |
669 | | - |
670 | | - def build_cache_flag(self, depth): |
671 | | - setting = self.pkg_buildcache if depth == 0 else self.deps_buildcache |
672 | | - if setting == "only": |
673 | | - return "--use-buildcache=only" |
674 | | - elif setting == "never": |
675 | | - return "--use-buildcache=never" |
676 | | - return "" |
677 | | - |
678 | | - def accept(self, node): |
679 | | - fmt = "{name}-{version}-{hash}" |
680 | | - tgt = node.edge.spec.format(fmt) |
681 | | - spec_str = node.edge.spec.format( |
682 | | - "{name}{@version}{%compiler}{variants}{arch=architecture}" |
683 | | - ) |
684 | | - buildcache_flag = self.build_cache_flag(node.depth) |
685 | | - prereqs = " ".join([self.target(dep.spec.format(fmt)) for dep in self.neighbors(node)]) |
686 | | - self.adjacency_list.append( |
687 | | - (tgt, prereqs, node.edge.spec.dag_hash(), spec_str, buildcache_flag) |
688 | | - ) |
689 | | - |
690 | | - # We already accepted this |
691 | | - return True |
692 | | - |
693 | | - |
694 | 640 | def env_depfile(args): |
695 | 641 | # Currently only make is supported. |
696 | 642 | spack.cmd.require_active_env(cmd_name="env depfile") |
697 | 643 | env = ev.active_environment() |
698 | 644 |
|
699 | | - # Special make targets are useful when including a makefile in another, and you |
700 | | - # need to "namespace" the targets to avoid conflicts. |
701 | | - if args.make_prefix is None: |
702 | | - prefix = os.path.join(env.env_subdir_path, "makedeps") |
703 | | - else: |
704 | | - prefix = args.make_prefix |
705 | | - |
706 | | - def get_target(name): |
707 | | - # The `all` and `clean` targets are phony. It doesn't make sense to |
708 | | - # have /abs/path/to/env/metadir/{all,clean} targets. But it *does* make |
709 | | - # sense to have a prefix like `env/all`, `env/clean` when they are |
710 | | - # supposed to be included |
711 | | - if name in ("all", "clean") and os.path.isabs(prefix): |
712 | | - return name |
713 | | - else: |
714 | | - return os.path.join(prefix, name) |
715 | | - |
716 | | - def get_install_target(name): |
717 | | - return os.path.join(prefix, "install", name) |
718 | | - |
719 | | - def get_install_deps_target(name): |
720 | | - return os.path.join(prefix, "install-deps", name) |
721 | | - |
722 | 645 | # What things do we build when running make? By default, we build the |
723 | 646 | # root specs. If specific specs are provided as input, we build those. |
724 | | - if args.specs: |
725 | | - abstract_specs = spack.cmd.parse_specs(args.specs) |
726 | | - roots = [env.matching_spec(s) for s in abstract_specs] |
727 | | - else: |
728 | | - roots = [s for _, s in env.concretized_specs()] |
| 647 | + filter_specs = spack.cmd.parse_specs(args.specs) if args.specs else None |
729 | 648 |
|
730 | | - # We produce a sub-DAG from the DAG induced by roots, where we drop build |
731 | | - # edges for those specs that are installed through a binary cache. |
732 | | - pkg_buildcache, dep_buildcache = args.use_buildcache |
733 | | - make_targets = MakeTargetVisitor(get_install_target, pkg_buildcache, dep_buildcache) |
734 | | - traverse.traverse_breadth_first_with_visitor( |
735 | | - roots, traverse.CoverNodesVisitor(make_targets, key=lambda s: s.dag_hash()) |
736 | | - ) |
737 | | - |
738 | | - # Root specs without deps are the prereqs for the environment target |
739 | | - root_install_targets = [get_install_target(h.format("{name}-{version}-{hash}")) for h in roots] |
740 | | - |
741 | | - all_pkg_identifiers = [] |
742 | | - |
743 | | - # The SPACK_PACKAGE_IDS variable is "exported", which can be used when including |
744 | | - # generated makefiles to add post-install hooks, like pushing to a buildcache, |
745 | | - # running tests, etc. |
746 | | - # NOTE: GNU Make allows directory separators in variable names, so for consistency |
747 | | - # we can namespace this variable with the same prefix as targets. |
748 | | - if args.make_prefix is None: |
749 | | - pkg_identifier_variable = "SPACK_PACKAGE_IDS" |
750 | | - else: |
751 | | - pkg_identifier_variable = os.path.join(prefix, "SPACK_PACKAGE_IDS") |
752 | | - |
753 | | - # All install and install-deps targets |
754 | | - all_install_related_targets = [] |
755 | | - |
756 | | - # Convenience shortcuts: ensure that `make install/pkg-version-hash` triggers |
757 | | - # <absolute path to env>/.spack-env/makedeps/install/pkg-version-hash in case |
758 | | - # we don't have a custom make target prefix. |
759 | | - phony_convenience_targets = [] |
760 | | - |
761 | | - for tgt, _, _, _, _ in make_targets.adjacency_list: |
762 | | - all_pkg_identifiers.append(tgt) |
763 | | - all_install_related_targets.append(get_install_target(tgt)) |
764 | | - all_install_related_targets.append(get_install_deps_target(tgt)) |
765 | | - if args.make_prefix is None: |
766 | | - phony_convenience_targets.append(os.path.join("install", tgt)) |
767 | | - phony_convenience_targets.append(os.path.join("install-deps", tgt)) |
768 | | - |
769 | | - buf = io.StringIO() |
| 649 | + pkg_use_bc, dep_use_bc = args.use_buildcache |
770 | 650 |
|
771 | 651 | template = spack.tengine.make_environment().get_template(os.path.join("depfile", "Makefile")) |
772 | | - |
773 | | - rendered = template.render( |
774 | | - { |
775 | | - "all_target": get_target("all"), |
776 | | - "env_target": get_target("env"), |
777 | | - "clean_target": get_target("clean"), |
778 | | - "all_install_related_targets": " ".join(all_install_related_targets), |
779 | | - "root_install_targets": " ".join(root_install_targets), |
780 | | - "dirs_target": get_target("dirs"), |
781 | | - "environment": env.path, |
782 | | - "install_target": get_target("install"), |
783 | | - "install_deps_target": get_target("install-deps"), |
784 | | - "any_hash_target": get_target("%"), |
785 | | - "jobserver_support": "+" if args.jobserver else "", |
786 | | - "adjacency_list": make_targets.adjacency_list, |
787 | | - "phony_convenience_targets": " ".join(phony_convenience_targets), |
788 | | - "pkg_ids_variable": pkg_identifier_variable, |
789 | | - "pkg_ids": " ".join(all_pkg_identifiers), |
790 | | - } |
791 | | - ) |
792 | | - |
793 | | - buf.write(rendered) |
794 | | - makefile = buf.getvalue() |
| 652 | + model = depfile.MakefileModel.from_env( |
| 653 | + env, filter_specs, pkg_use_bc, dep_use_bc, args.make_prefix, args.jobserver |
| 654 | + ) |
| 655 | + makefile = template.render(model.to_dict()) |
795 | 656 |
|
796 | 657 | # Finally write to stdout/file. |
797 | 658 | if args.output: |
|
0 commit comments