15f9996aaSopenharmony_ci#!/usr/bin/env python 25f9996aaSopenharmony_ci# -*- coding: utf-8 -*- 35f9996aaSopenharmony_ci# Copyright (c) 2021 Huawei Device Co., Ltd. 45f9996aaSopenharmony_ci# Licensed under the Apache License, Version 2.0 (the "License"); 55f9996aaSopenharmony_ci# you may not use this file except in compliance with the License. 65f9996aaSopenharmony_ci# You may obtain a copy of the License at 75f9996aaSopenharmony_ci# 85f9996aaSopenharmony_ci# http://www.apache.org/licenses/LICENSE-2.0 95f9996aaSopenharmony_ci# 105f9996aaSopenharmony_ci# Unless required by applicable law or agreed to in writing, software 115f9996aaSopenharmony_ci# distributed under the License is distributed on an "AS IS" BASIS, 125f9996aaSopenharmony_ci# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 135f9996aaSopenharmony_ci# See the License for the specific language governing permissions and 145f9996aaSopenharmony_ci# limitations under the License. 155f9996aaSopenharmony_ci 165f9996aaSopenharmony_ciimport shutil 175f9996aaSopenharmony_ciimport os 185f9996aaSopenharmony_ciimport hashlib 195f9996aaSopenharmony_ciimport json 205f9996aaSopenharmony_ciimport http.client as client 215f9996aaSopenharmony_cifrom . import build_utils 225f9996aaSopenharmony_ci 235f9996aaSopenharmony_ci 245f9996aaSopenharmony_ciclass Storage(): 255f9996aaSopenharmony_ci def __init__(self): 265f9996aaSopenharmony_ci pass 275f9996aaSopenharmony_ci 285f9996aaSopenharmony_ci @classmethod 295f9996aaSopenharmony_ci def retrieve_object(cls, cache_artifact, obj): 305f9996aaSopenharmony_ci possible_dir_cache_artifact = '{}.directory'.format(cache_artifact) 315f9996aaSopenharmony_ci 325f9996aaSopenharmony_ci if os.path.exists(cache_artifact): 335f9996aaSopenharmony_ci os.makedirs(os.path.dirname(obj), exist_ok=True) 345f9996aaSopenharmony_ci shutil.copyfile(cache_artifact, obj) 355f9996aaSopenharmony_ci os.utime(cache_artifact) 365f9996aaSopenharmony_ci if pycache_debug_enable: 375f9996aaSopenharmony_ci print('Retrieve {} from cache'.format(obj)) 385f9996aaSopenharmony_ci elif os.path.exists(possible_dir_cache_artifact): 395f9996aaSopenharmony_ci # Extract zip archive if it's cache artifact for directory. 405f9996aaSopenharmony_ci os.makedirs(obj, exist_ok=True) 415f9996aaSopenharmony_ci build_utils.extract_all(possible_dir_cache_artifact, 425f9996aaSopenharmony_ci obj, 435f9996aaSopenharmony_ci no_clobber=False) 445f9996aaSopenharmony_ci os.utime(possible_dir_cache_artifact) 455f9996aaSopenharmony_ci if pycache_debug_enable: 465f9996aaSopenharmony_ci print('Extract {} from cache'.format(obj)) 475f9996aaSopenharmony_ci else: 485f9996aaSopenharmony_ci if pycache_debug_enable: 495f9996aaSopenharmony_ci print('Failed to retrieve {} from cache'.format(obj)) 505f9996aaSopenharmony_ci return 0 515f9996aaSopenharmony_ci return 1 525f9996aaSopenharmony_ci 535f9996aaSopenharmony_ci @classmethod 545f9996aaSopenharmony_ci def add_object(cls, cache_artifact, obj): 555f9996aaSopenharmony_ci cache_dir = os.path.dirname(cache_artifact) 565f9996aaSopenharmony_ci os.makedirs(cache_dir, exist_ok=True) 575f9996aaSopenharmony_ci 585f9996aaSopenharmony_ci if not os.path.exists(obj): 595f9996aaSopenharmony_ci return 605f9996aaSopenharmony_ci # If path is directory, store an zip archive. 615f9996aaSopenharmony_ci if os.path.isdir(obj): 625f9996aaSopenharmony_ci dir_cache_artifact = '{}.directory'.format(cache_artifact) 635f9996aaSopenharmony_ci build_utils.zip_dir(dir_cache_artifact, obj) 645f9996aaSopenharmony_ci if pycache_debug_enable: 655f9996aaSopenharmony_ci print("archive {} to {}".format(obj, dir_cache_artifact)) 665f9996aaSopenharmony_ci else: 675f9996aaSopenharmony_ci shutil.copyfile(obj, cache_artifact) 685f9996aaSopenharmony_ci if pycache_debug_enable: 695f9996aaSopenharmony_ci print("copying {} to {}".format(obj, cache_artifact)) 705f9996aaSopenharmony_ci 715f9996aaSopenharmony_ci 725f9996aaSopenharmony_ciclass PyCache(): 735f9996aaSopenharmony_ci def __init__(self, cache_dir=None): 745f9996aaSopenharmony_ci cache_dir = os.environ.get('PYCACHE_DIR') 755f9996aaSopenharmony_ci if cache_dir: 765f9996aaSopenharmony_ci self.pycache_dir = cache_dir 775f9996aaSopenharmony_ci else: 785f9996aaSopenharmony_ci raise Exception('Error: failed to get PYCACHE_DIR') 795f9996aaSopenharmony_ci self.storage = Storage() 805f9996aaSopenharmony_ci 815f9996aaSopenharmony_ci @classmethod 825f9996aaSopenharmony_ci def cache_key(cls, path): 835f9996aaSopenharmony_ci sha256 = hashlib.sha256() 845f9996aaSopenharmony_ci sha256.update(path.encode()) 855f9996aaSopenharmony_ci return sha256.hexdigest() 865f9996aaSopenharmony_ci 875f9996aaSopenharmony_ci def retrieve(self, output_paths, prefix=''): 885f9996aaSopenharmony_ci for path in output_paths: 895f9996aaSopenharmony_ci _, cache_artifact = self.descend_directory('{}{}'.format( 905f9996aaSopenharmony_ci prefix, path)) 915f9996aaSopenharmony_ci result = self.storage.retrieve_object(cache_artifact, path) 925f9996aaSopenharmony_ci if not result: 935f9996aaSopenharmony_ci return result 945f9996aaSopenharmony_ci 955f9996aaSopenharmony_ci try: 965f9996aaSopenharmony_ci self.report_cache_stat('cache_hit') 975f9996aaSopenharmony_ci except: # noqa: E722 pylint: disable=bare-except 985f9996aaSopenharmony_ci pass 995f9996aaSopenharmony_ci return 1 1005f9996aaSopenharmony_ci 1015f9996aaSopenharmony_ci def save(self, output_paths, prefix=''): 1025f9996aaSopenharmony_ci for path in output_paths: 1035f9996aaSopenharmony_ci _, cache_artifact = self.descend_directory('{}{}'.format( 1045f9996aaSopenharmony_ci prefix, path)) 1055f9996aaSopenharmony_ci self.storage.add_object(cache_artifact, path) 1065f9996aaSopenharmony_ci 1075f9996aaSopenharmony_ci def report_cache_stat(self, hit_or_miss): 1085f9996aaSopenharmony_ci pyd_server, pyd_port = self.get_pyd() 1095f9996aaSopenharmony_ci conn = client.HTTPConnection(pyd_server, pyd_port) 1105f9996aaSopenharmony_ci conn.request(hit_or_miss, '/') 1115f9996aaSopenharmony_ci conn.close() 1125f9996aaSopenharmony_ci 1135f9996aaSopenharmony_ci def get_pyd(self): 1145f9996aaSopenharmony_ci daemon_config_file = '{}/.config'.format(self.pycache_dir) 1155f9996aaSopenharmony_ci if not os.path.exists(daemon_config_file): 1165f9996aaSopenharmony_ci raise Exception('Warning: no pycache daemon process exists.') 1175f9996aaSopenharmony_ci with open(daemon_config_file, 'r') as jsonfile: 1185f9996aaSopenharmony_ci data = json.load(jsonfile) 1195f9996aaSopenharmony_ci return data.get('host'), data.get('port') 1205f9996aaSopenharmony_ci 1215f9996aaSopenharmony_ci def descend_directory(self, path): 1225f9996aaSopenharmony_ci digest = self.cache_key(path) 1235f9996aaSopenharmony_ci cache_dir = os.path.join(self.pycache_dir, digest[:2]) 1245f9996aaSopenharmony_ci return cache_dir, os.path.join(cache_dir, digest[2:]) 1255f9996aaSopenharmony_ci 1265f9996aaSopenharmony_ci # Manifest file to record inputs/outputs/commands. 1275f9996aaSopenharmony_ci def get_manifest_path(self, path): 1285f9996aaSopenharmony_ci manifest_dir, manifest_file = self.descend_directory(path) 1295f9996aaSopenharmony_ci os.makedirs(manifest_dir, exist_ok=True) 1305f9996aaSopenharmony_ci return manifest_file 1315f9996aaSopenharmony_ci 1325f9996aaSopenharmony_ci 1335f9996aaSopenharmony_cipycache_enabled = (os.environ.get('PYCACHE_DIR') is not None) 1345f9996aaSopenharmony_cipycache_debug_enable = int(os.environ.get('PRINT_BUILD_EXPLANATIONS', 0)) 1355f9996aaSopenharmony_ciif pycache_enabled: 1365f9996aaSopenharmony_ci pycache = PyCache() 1375f9996aaSopenharmony_cielse: 1385f9996aaSopenharmony_ci pycache = None # pylint: disable=invalid-name 139