4848from six import with_metaclass
4949
5050import llnl .util .tty as tty
51+ import llnl .util .filesystem as filesystem
5152import spack
5253import spack .store
5354import spack .compilers
6364import spack .binary_distribution as binary_distribution
6465
6566from llnl .util .filesystem import mkdirp , join_path , touch , ancestor
66- from llnl .util .filesystem import working_dir , install_tree , install
67+ from llnl .util .filesystem import working_dir , install_tree
6768from llnl .util .lang import memoized
6869from llnl .util .link_tree import LinkTree
6970from llnl .util .tty .log import log_output
@@ -552,6 +553,9 @@ class SomePackage(Package):
552553 #: index of patches by sha256 sum, built lazily
553554 _patches_by_hash = None
554555
556+ #: Used to stop early during installation, if set in instances
557+ last_phase = None
558+
555559 #: List of strings which contains GitHub usernames of package maintainers.
556560 #: Do not include @ here in order not to unnecessarily ping the users.
557561 maintainers = []
@@ -1287,18 +1291,18 @@ def do_install(self,
12871291 keep_prefix = False ,
12881292 keep_stage = False ,
12891293 install_source = False ,
1290- install_deps = True ,
12911294 skip_patch = False ,
12921295 verbose = False ,
12931296 make_jobs = None ,
12941297 fake = False ,
12951298 explicit = False ,
12961299 dirty = None ,
12971300 ** kwargs ):
1298- """Called by commands to install a package and its dependencies .
1301+ """Called by :py:func:`install` to install *this* package.
12991302
1300- Package implementations should override install() to describe
1301- their build process.
1303+ This method installs only ``self.spec`` and assumes all its
1304+ dependencies are already installed and the spec itself is
1305+ concrete.
13021306
13031307 Args:
13041308 keep_prefix (bool): Keep install prefix on failure. By default,
@@ -1308,8 +1312,6 @@ def do_install(self,
13081312 even with exceptions.
13091313 install_source (bool): By default, source is not installed, but
13101314 for debugging it might be useful to keep it around.
1311- install_deps (bool): Install dependencies before installing this
1312- package
13131315 skip_patch (bool): Skip patch stage of build if True.
13141316 verbose (bool): Display verbose build output (by default,
13151317 suppresses it)
@@ -1319,11 +1321,13 @@ def do_install(self,
13191321 explicit (bool): True if package was explicitly installed, False
13201322 if package was implicitly installed (as a dependency).
13211323 dirty (bool): Don't clean the build environment before installing.
1322- force (bool): Install again, even if already installed.
1324+
1325+ Raises:
1326+ ValueError: if the ``self.spec`` is not concrete
13231327 """
13241328 if not self .spec .concrete :
1325- raise ValueError ( "Can only install concrete packages: %s ."
1326- % self .spec .name )
1329+ msg = "Can only install concrete packages: {0} ."
1330+ raise ValueError ( msg . format ( self .spec .name ) )
13271331
13281332 # For external packages the workflow is simplified, and basically
13291333 # consists in module file generation and registration in the DB
@@ -1351,25 +1355,6 @@ def do_install(self,
13511355
13521356 return self ._update_explicit_entry_in_db (rec , explicit )
13531357
1354- self ._do_install_pop_kwargs (kwargs )
1355-
1356- # First, install dependencies recursively.
1357- if install_deps :
1358- tty .debug ('Installing {0} dependencies' .format (self .name ))
1359- for dep in self .spec .traverse (order = 'post' , root = False ):
1360- dep .package .do_install (
1361- install_deps = False ,
1362- explicit = False ,
1363- keep_prefix = keep_prefix ,
1364- keep_stage = keep_stage ,
1365- install_source = install_source ,
1366- fake = fake ,
1367- skip_patch = skip_patch ,
1368- verbose = verbose ,
1369- make_jobs = make_jobs ,
1370- dirty = dirty ,
1371- ** kwargs )
1372-
13731358 tty .msg (colorize ('@*{Installing} @*g{%s}' % self .name ))
13741359
13751360 if kwargs .get ('use_cache' , False ):
@@ -1542,19 +1527,6 @@ def check_for_unfinished_installation(
15421527
15431528 return partial
15441529
1545- def _do_install_pop_kwargs (self , kwargs ):
1546- """Pops kwargs from do_install before starting the installation
1547-
1548- Args:
1549- kwargs:
1550- 'stop_at': last installation phase to be executed (or None)
1551-
1552- """
1553- self .last_phase = kwargs .pop ('stop_at' , None )
1554- if self .last_phase is not None and self .last_phase not in self .phases :
1555- tty .die ('\' {0}\' is not an allowed phase for package {1}'
1556- .format (self .last_phase , self .name ))
1557-
15581530 def log (self ):
15591531 # Copy provenance into the install directory on success
15601532 log_install_path = spack .store .layout .build_log_path (self .spec )
@@ -1570,8 +1542,8 @@ def log(self):
15701542 # FIXME : this potentially catches too many things...
15711543 pass
15721544
1573- install (self .log_path , log_install_path )
1574- install (self .env_path , env_install_path )
1545+ filesystem . install (self .log_path , log_install_path )
1546+ filesystem . install (self .env_path , env_install_path )
15751547 dump_packages (self .spec , packages_dir )
15761548
15771549 def sanity_check_prefix (self ):
@@ -2083,6 +2055,51 @@ class Package(PackageBase):
20832055 run_after ('install' )(PackageBase .sanity_check_prefix )
20842056
20852057
2058+ def install (spec , what = ('root' , 'dependencies' ), stop_at = None , ** kwargs ):
2059+ """Install a concretized spec and / or its dependencies.
2060+
2061+ This function permits to install either the entire DAG, only the
2062+ dependencies or only the root package. In any case the root package
2063+ will be installed as 'explicit', while the children as 'non-explicit'.
2064+
2065+ The keyword arguments are forwarded directly to
2066+ :py:meth:`PackageBase.do_install` during the installation of the
2067+ various nodes.
2068+
2069+ Args:
2070+ spec (Spec): spec to be installed. Must be concrete.
2071+ what (tuple of str): specifies what needs to be installed. If 'root'
2072+ is in the argument, then the root package of the spec will be
2073+ installed. If 'dependencies' is in the argument the child nodes
2074+ of the DAG will be installed. By default it installs both.
2075+ stop_at (str or None): if specified, stops the installation at the
2076+ given phase. Valid only for the root package.
2077+ **kwargs: keyword arguments to be forwarded to
2078+ :py:meth:`PackageBase.do_install`.
2079+ """
2080+ # Check if we should stop early during the installation of the root spec
2081+ pkg = spec .package
2082+ pkg .last_phase = stop_at
2083+
2084+ if pkg .last_phase is not None and pkg .last_phase not in pkg .phases :
2085+ msg = '"{0}" is not an allowed phase for package {1}'
2086+ raise ValueError (msg .format (pkg .last_phase , pkg .name ))
2087+
2088+ # Remove the keyword explicit if present: the root spec will be marked
2089+ # explicit, all the child specs will be non-explicit
2090+ kwargs .pop ('explicit' , None )
2091+
2092+ # Install the dependencies if required to, traversing the DAG
2093+ # in post order and omitting root
2094+ if 'dependencies' in what :
2095+ tty .debug ('Installing {0} dependencies' .format (spec .name ))
2096+ for dep in spec .traverse (order = 'post' , root = False ):
2097+ dep .package .do_install (explicit = False , ** kwargs )
2098+
2099+ if 'root' in what :
2100+ spec .package .do_install (explicit = True , ** kwargs )
2101+
2102+
20862103def install_dependency_symlinks (pkg , spec , prefix ):
20872104 """Execute a dummy install and flatten dependencies"""
20882105 flatten_dependencies (spec , prefix )
0 commit comments