forked from platformio/platformio-core
		
	
		
			
	
	
		
			118 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
		
		
			
		
	
	
			118 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			Python
		
	
	
	
	
	
|   | # Copyright (c) 2014-present PlatformIO <contact@platformio.org> | ||
|  | # | ||
|  | # Licensed under the Apache License, Version 2.0 (the "License"); | ||
|  | # you may not use this file except in compliance with the License. | ||
|  | # You may obtain a copy of the License at | ||
|  | # | ||
|  | #    http://www.apache.org/licenses/LICENSE-2.0 | ||
|  | # | ||
|  | # Unless required by applicable law or agreed to in writing, software | ||
|  | # distributed under the License is distributed on an "AS IS" BASIS, | ||
|  | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
|  | # See the License for the specific language governing permissions and | ||
|  | # limitations under the License. | ||
|  | 
 | ||
|  | import os | ||
|  | import tarfile | ||
|  | from binascii import crc32 | ||
|  | from os.path import getmtime, getsize, isdir, isfile, join | ||
|  | 
 | ||
|  | from twisted.python import constants  # pylint: disable=import-error | ||
|  | 
 | ||
|  | from platformio.compat import hashlib_encode_data | ||
|  | 
 | ||
|  | 
 | ||
|  | class PROJECT_SYNC_STAGE(constants.Flags): | ||
|  |     INIT = constants.FlagConstant() | ||
|  |     DBINDEX = constants.FlagConstant() | ||
|  |     DELETE = constants.FlagConstant() | ||
|  |     UPLOAD = constants.FlagConstant() | ||
|  |     EXTRACTED = constants.FlagConstant() | ||
|  |     COMPLETED = constants.FlagConstant() | ||
|  | 
 | ||
|  | 
 | ||
|  | class ProjectSync(object): | ||
|  |     def __init__(self, path): | ||
|  |         self.path = path | ||
|  |         if not isdir(self.path): | ||
|  |             os.makedirs(self.path) | ||
|  |         self.items = [] | ||
|  |         self._db = {} | ||
|  | 
 | ||
|  |     def add_item(self, path, relpath, cb_filter=None): | ||
|  |         self.items.append((path, relpath, cb_filter)) | ||
|  | 
 | ||
|  |     def get_items(self): | ||
|  |         return self.items | ||
|  | 
 | ||
|  |     def rebuild_dbindex(self): | ||
|  |         self._db = {} | ||
|  |         for (path, relpath, cb_filter) in self.items: | ||
|  |             if cb_filter and not cb_filter(path): | ||
|  |                 continue | ||
|  |             self._insert_to_db(path, relpath) | ||
|  |             if not isdir(path): | ||
|  |                 continue | ||
|  |             for (root, _, files) in os.walk(path, followlinks=True): | ||
|  |                 for name in files: | ||
|  |                     self._insert_to_db( | ||
|  |                         join(root, name), join(relpath, root[len(path) + 1 :], name) | ||
|  |                     ) | ||
|  | 
 | ||
|  |     def _insert_to_db(self, path, relpath): | ||
|  |         if not isfile(path): | ||
|  |             return | ||
|  |         index_hash = "%s-%s-%s" % (relpath, getmtime(path), getsize(path)) | ||
|  |         index = crc32(hashlib_encode_data(index_hash)) | ||
|  |         self._db[index] = (path, relpath) | ||
|  | 
 | ||
|  |     def get_dbindex(self): | ||
|  |         return list(self._db.keys()) | ||
|  | 
 | ||
|  |     def delete_dbindex(self, dbindex): | ||
|  |         for index in dbindex: | ||
|  |             if index not in self._db: | ||
|  |                 continue | ||
|  |             path = self._db[index][0] | ||
|  |             if isfile(path): | ||
|  |                 os.remove(path) | ||
|  |             del self._db[index] | ||
|  |         self.delete_empty_folders() | ||
|  |         return True | ||
|  | 
 | ||
|  |     def delete_empty_folders(self): | ||
|  |         deleted = False | ||
|  |         for item in self.items: | ||
|  |             if not isdir(item[0]): | ||
|  |                 continue | ||
|  |             for root, dirs, files in os.walk(item[0]): | ||
|  |                 if not dirs and not files and root != item[0]: | ||
|  |                     deleted = True | ||
|  |                     os.rmdir(root) | ||
|  |         if deleted: | ||
|  |             return self.delete_empty_folders() | ||
|  | 
 | ||
|  |         return True | ||
|  | 
 | ||
|  |     def compress_items(self, fileobj, dbindex, max_size): | ||
|  |         compressed = [] | ||
|  |         total_size = 0 | ||
|  |         tar_opts = dict(fileobj=fileobj, mode="w:gz", bufsize=0, dereference=True) | ||
|  |         with tarfile.open(**tar_opts) as tgz: | ||
|  |             for index in dbindex: | ||
|  |                 compressed.append(index) | ||
|  |                 if index not in self._db: | ||
|  |                     continue | ||
|  |                 path, relpath = self._db[index] | ||
|  |                 tgz.add(path, relpath) | ||
|  |                 total_size += getsize(path) | ||
|  |                 if total_size > max_size: | ||
|  |                     break | ||
|  |         return compressed | ||
|  | 
 | ||
|  |     def decompress_items(self, fileobj): | ||
|  |         fileobj.seek(0) | ||
|  |         with tarfile.open(fileobj=fileobj, mode="r:gz") as tgz: | ||
|  |             tgz.extractall(self.path) | ||
|  |         return True |