7272valid_environment_name_re = r'^\w[\w-]*$'
7373
7474#: version of the lockfile format. Must increase monotonically.
75- lockfile_format_version = 1
75+ lockfile_format_version = 2
7676
7777#: legal first keys in the spack.yaml manifest file
7878env_schema_keys = ('spack' , 'env' )
@@ -533,11 +533,17 @@ def __init__(self, path, init_file=None, with_view=None):
533533
534534 if os .path .exists (self .lock_path ):
535535 with open (self .lock_path ) as f :
536- self ._read_lockfile (f )
536+ read_lock_version = self ._read_lockfile (f )
537537 if default_manifest :
538538 # No manifest, set user specs from lockfile
539539 self ._set_user_specs_from_lockfile ()
540540
541+ if read_lock_version == 1 :
542+ tty .debug (
543+ "Storing backup of old lockfile {0} at {1}" .format (
544+ self .lock_path , self ._lock_backup_v1_path ))
545+ shutil .copy (self .lock_path , self ._lock_backup_v1_path )
546+
541547 if with_view is False :
542548 self .views = {}
543549 elif with_view is True :
@@ -638,6 +644,11 @@ def lock_path(self):
638644 """Path to spack.lock file in this environment."""
639645 return os .path .join (self .path , lockfile_name )
640646
647+ @property
648+ def _lock_backup_v1_path (self ):
649+ """Path to backup of v1 lockfile before conversion to v2"""
650+ return self .lock_path + '.backup.v1'
651+
641652 @property
642653 def env_subdir_path (self ):
643654 """Path to directory where the env stores repos, logs, views."""
@@ -809,7 +820,7 @@ def remove(self, query_spec, list_name=user_speclist_name, force=False):
809820 del self .concretized_order [i ]
810821 del self .specs_by_hash [dag_hash ]
811822
812- def concretize (self , force = False ):
823+ def concretize (self , force = False , _display = True ):
813824 """Concretize user_specs in this environment.
814825
815826 Only concretizes specs that haven't been concretized yet unless
@@ -850,12 +861,13 @@ def concretize(self, force=False):
850861 concrete = _concretize_from_constraints (uspec_constraints )
851862 self ._add_concrete_spec (uspec , concrete )
852863
853- # Display concretized spec to the user
854- sys .stdout .write (concrete .tree (
855- recurse_dependencies = True ,
856- status_fn = spack .spec .Spec .install_status ,
857- hashlen = 7 , hashes = True )
858- )
864+ if _display :
865+ # Display concretized spec to the user
866+ sys .stdout .write (concrete .tree (
867+ recurse_dependencies = True ,
868+ status_fn = spack .spec .Spec .install_status ,
869+ hashlen = 7 , hashes = True )
870+ )
859871
860872 def install (self , user_spec , concrete_spec = None , ** install_args ):
861873 """Install a single spec into an environment.
@@ -872,7 +884,7 @@ def install(self, user_spec, concrete_spec=None, **install_args):
872884 # spec might be in the user_specs, but not installed.
873885 # TODO: Redo name-based comparison for old style envs
874886 spec = next (s for s in self .user_specs if s .satisfies (user_spec ))
875- concrete = self .specs_by_hash .get (spec .dag_hash ())
887+ concrete = self .specs_by_hash .get (spec .dag_hash (all_deps = True ))
876888 if not concrete :
877889 concrete = spec .concretized ()
878890 self ._add_concrete_spec (spec , concrete )
@@ -984,7 +996,7 @@ def _add_concrete_spec(self, spec, concrete, new=True):
984996 # update internal lists of specs
985997 self .concretized_user_specs .append (spec )
986998
987- h = concrete .dag_hash ()
999+ h = concrete .dag_hash (all_deps = True )
9881000 self .concretized_order .append (h )
9891001 self .specs_by_hash [h ] = concrete
9901002
@@ -1013,6 +1025,10 @@ def install_all(self, args=None):
10131025
10141026 def all_specs_by_hash (self ):
10151027 """Map of hashes to spec for all specs in this environment."""
1028+ # Note this uses dag-hashes calculated without build deps as keys,
1029+ # whereas the environment tracks specs based on dag-hashes calculated
1030+ # with all dependencies. This function should not be used by an
1031+ # Environment object for management of its own data structures
10161032 hashes = {}
10171033 for h in self .concretized_order :
10181034 specs = self .specs_by_hash [h ].traverse (deptype = ('link' , 'run' ))
@@ -1095,9 +1111,11 @@ def _to_lockfile_dict(self):
10951111 concrete_specs = {}
10961112 for spec in self .specs_by_hash .values ():
10971113 for s in spec .traverse ():
1098- dag_hash = s .dag_hash ()
1099- if dag_hash not in concrete_specs :
1100- concrete_specs [dag_hash ] = s .to_node_dict (all_deps = True )
1114+ dag_hash_all = s .dag_hash (all_deps = True )
1115+ if dag_hash_all not in concrete_specs :
1116+ spec_dict = s .to_node_dict (all_deps = True )
1117+ spec_dict [s .name ]['hash' ] = s .dag_hash ()
1118+ concrete_specs [dag_hash_all ] = spec_dict
11011119
11021120 hash_spec_list = zip (
11031121 self .concretized_order , self .concretized_user_specs )
@@ -1126,6 +1144,7 @@ def _read_lockfile(self, file_or_json):
11261144 """Read a lockfile from a file or from a raw string."""
11271145 lockfile_dict = sjson .load (file_or_json )
11281146 self ._read_lockfile_dict (lockfile_dict )
1147+ return lockfile_dict ['_meta' ]['lockfile-version' ]
11291148
11301149 def _read_lockfile_dict (self , d ):
11311150 """Read a lockfile dictionary into this environment."""
@@ -1146,8 +1165,25 @@ def _read_lockfile_dict(self, d):
11461165 specs_by_hash [dag_hash ]._add_dependency (
11471166 specs_by_hash [dep_hash ], deptypes )
11481167
1149- self .specs_by_hash = dict (
1150- (x , y ) for x , y in specs_by_hash .items () if x in root_hashes )
1168+ # If we are reading an older lockfile format (which uses dag hashes
1169+ # that exclude build deps), we use this to convert the old
1170+ # concretized_order to the full hashes (preserving the order)
1171+ old_hash_to_new = {}
1172+ self .specs_by_hash = {}
1173+ for _ , spec in specs_by_hash .items ():
1174+ dag_hash = spec .dag_hash ()
1175+ build_hash = spec .dag_hash (all_deps = True )
1176+ if dag_hash in root_hashes :
1177+ old_hash_to_new [dag_hash ] = build_hash
1178+
1179+ if (dag_hash in root_hashes or build_hash in root_hashes ):
1180+ self .specs_by_hash [build_hash ] = spec
1181+
1182+ if old_hash_to_new :
1183+ # Replace any older hashes in concretized_order with hashes
1184+ # that include build deps
1185+ self .concretized_order = [
1186+ old_hash_to_new .get (h , h ) for h in self .concretized_order ]
11511187
11521188 def write (self ):
11531189 """Writes an in-memory environment to its location on disk.
0 commit comments