@@ -74,7 +74,6 @@ def iter_packages(self):
7474
7575 # versioned packages
7676 for version_str in self ._repository ._get_version_dirs (self .path ):
77-
7877 if _settings .check_package_definition_files :
7978 path = os .path .join (self .path , version_str )
8079 if not self ._repository ._get_file (path )[0 ]:
@@ -85,6 +84,7 @@ def iter_packages(self):
8584 location = self .location ,
8685 name = self .name ,
8786 version = version_str )
87+
8888 yield package
8989
9090
@@ -177,8 +177,6 @@ def _load_old_formats(self):
177177 pass
178178 return data
179179
180- # should be static or classmethod, since it's passed as an arg to
181- # load_from_file, which is memcached
182180 @staticmethod
183181 def _update_changelog (file_format , data ):
184182 # this is to deal with older package releases. They can contain long
@@ -404,6 +402,8 @@ class FileSystemPackageRepository(PackageRepository):
404402 "file_lock_dir" : Or (None , str ),
405403 "package_filenames" : [basestring ]}
406404
405+ building_prefix = ".building"
406+
407407 @classmethod
408408 def name (cls ):
409409 return "filesystem"
@@ -489,6 +489,24 @@ def file_lock_dir(self):
489489
490490 return dirname
491491
492+ def pre_variant_install (self , variant_resource ):
493+ if not variant_resource .version :
494+ return
495+
496+ # create 'building' tagfile, this makes sure that a resolve doesn't
497+ # pick up this package if it doesn't yet have a package.py created.
498+ path = self .location
499+
500+ family_path = os .path .join (path , variant_resource .name )
501+ if not os .path .isdir (family_path ):
502+ os .makedirs (family_path )
503+
504+ filename = self .building_prefix + str (variant_resource .version )
505+ filepath = os .path .join (family_path , filename )
506+
507+ with open (filepath , 'w' ): # create empty file
508+ pass
509+
492510 def install_variant (self , variant_resource , dry_run = False , overrides = None ):
493511 if variant_resource ._repository is self :
494512 return variant_resource
@@ -549,6 +567,7 @@ def _get_family_dirs(self):
549567 dirs = []
550568 if not os .path .isdir (self .location ):
551569 return dirs
570+
552571 for name in os .listdir (self .location ):
553572 path = os .path .join (self .location , name )
554573 if os .path .isdir (path ):
@@ -558,6 +577,7 @@ def _get_family_dirs(self):
558577 name_ , ext_ = os .path .splitext (name )
559578 if ext_ in (".py" , ".yaml" ) and is_valid_package_name (name_ ):
560579 dirs .append ((name_ , ext_ [1 :]))
580+
561581 return dirs
562582
563583 def _get_version_dirs__key (self , root ):
@@ -569,14 +589,59 @@ def _get_version_dirs__key(self, root):
569589 key = _get_version_dirs__key ,
570590 debug = config .debug_memcache )
571591 def _get_version_dirs (self , root ):
572- dirs = []
592+
593+ # simpler case if this test is on
594+ #
595+ if _settings .check_package_definition_files :
596+ dirs = []
597+
598+ for name in os .listdir (root ):
599+ if name .startswith ('.' ):
600+ continue
601+
602+ path = os .path .join (root , name )
603+ if os .path .isdir (path ):
604+ if not self ._is_valid_package_directory (path ):
605+ continue
606+
607+ dirs .append (name )
608+ return dirs
609+
610+ # with test off, we have to check for 'building' dirs, these have to be
611+ # tested regardless. Failed releases may cause 'building files' to be
612+ # left behind, so we need to clear these out also
613+ #
614+ dirs = set ()
615+ building_dirs = set ()
616+
617+ # find dirs and dirs marked as 'building'
573618 for name in os .listdir (root ):
574619 if name .startswith ('.' ):
575- continue
620+ if not name .startswith (self .building_prefix ):
621+ continue
622+
623+ ver_str = name [len (self .building_prefix ):]
624+ building_dirs .add (ver_str )
625+
576626 path = os .path .join (root , name )
577627 if os .path .isdir (path ):
578- dirs .append (name )
579- return dirs
628+ dirs .add (name )
629+
630+ # check 'building' dirs for validity
631+ for name in building_dirs :
632+ if name not in dirs :
633+ continue
634+
635+ path = os .path .join (root , name )
636+ if not self ._is_valid_package_directory (path ):
637+ # package probably still being built
638+ dirs .remove (name )
639+
640+ return list (dirs )
641+
642+ # True if `path` contains package.py or similar
643+ def _is_valid_package_directory (self , path ):
644+ return bool (self ._get_file (path , "package" )[0 ])
580645
581646 def _get_families (self ):
582647 families = []
@@ -593,6 +658,7 @@ def _get_families(self):
593658 name = name ,
594659 ext = ext )
595660 families .append (family )
661+
596662 return families
597663
598664 def _get_family (self , name ):
@@ -772,7 +838,7 @@ def _create_variant(self, variant, dry_run=False, overrides=None):
772838 package_data ["config" ] = parent_package ._data .get ("config" )
773839 package_data .pop ("base" , None )
774840
775- # create version dir and write out the new package definition file
841+ # create version dir if it doesn't already exist
776842 family_path = os .path .join (self .location , variant .name )
777843 if variant .version :
778844 path = os .path .join (family_path , str (variant .version ))
@@ -792,9 +858,27 @@ def _create_variant(self, variant, dry_run=False, overrides=None):
792858
793859 package_file = "." .join ([package_filename , package_extension ])
794860 filepath = os .path .join (path , package_file )
861+
795862 with open_file_for_write (filepath ) as f :
796863 dump_package_data (package_data , buf = f , format_ = package_format )
797864
865+ # delete the tmp 'building' file.
866+ if variant .version :
867+ filename = self .building_prefix + str (variant .version )
868+ filepath = os .path .join (family_path , filename )
869+ if os .path .exists (filepath ):
870+ try :
871+ os .remove (filepath )
872+ except :
873+ pass
874+
875+ # delete other stale building files; previous failed releases may have
876+ # left some around
877+ try :
878+ self ._delete_stale_build_tagfiles (family_path )
879+ except :
880+ pass
881+
798882 # touch the family dir, this keeps memcached resolves updated properly
799883 os .utime (family_path , None )
800884
@@ -816,6 +900,32 @@ def _create_variant(self, variant, dry_run=False, overrides=None):
816900 raise RezSystemError ("Internal failure - expected installed variant" )
817901 return new_variant
818902
903+ def _delete_stale_build_tagfiles (self , family_path ):
904+ now = time .time ()
905+
906+ for name in os .listdir (family_path ):
907+ if not name .startswith (self .building_prefix ):
908+ continue
909+
910+ tagfilepath = os .path .join (family_path , name )
911+ ver_str = name [len (self .building_prefix ):]
912+ pkg_path = os .path .join (family_path , ver_str )
913+
914+ if os .path .exists (pkg_path ):
915+ # build tagfile not needed if package is valid
916+ if self ._is_valid_package_directory (pkg_path ):
917+ os .remove (tagfilepath )
918+ continue
919+ else :
920+ # remove tagfile if pkg is gone. Delete only tagfiles over a certain
921+ # age, otherwise might delete a tagfile another process has created
922+ # just before it created the package directory.
923+ st = os .stat (tagfilepath )
924+ age = now - st .st_mtime
925+
926+ if age > 3600 :
927+ os .remove (tagfilepath )
928+
819929
820930def register_plugin ():
821931 return FileSystemPackageRepository
0 commit comments