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
 |