ensure_dir and rmdir (#2871)
import jsonimport globfrom os.path import exists, joinfrom pythonforandroid.logger import (debug, info, info_notify, warning, Err_Style, Err_Fore)from pythonforandroid.util import (current_directory, BuildInterruptingException, rmdir)class Distribution:'''State container for information about a distribution (i.e. anAndroid project).This is separate from a Bootstrap because the Bootstrap isconcerned with building and populating the dist directory, whereasthe dist itself could also come from e.g. a binary download.'''ctx = Nonename = None # A name identifying the dist. May not be None.needs_build = False # Whether the dist needs compilingurl = Nonedist_dir = None # Where the dist dir ultimately is. Should not be None.ndk_api = Nonearchs = []'''The names of the arch targets that the dist is built for.'''recipes = []description = '' # A long descriptiondef __init__(self, ctx):self.ctx = ctxdef __str__(self):return '<Distribution: name {} with recipes ({})>'.format(# self.name, ', '.join([recipe.name for recipe in self.recipes]))self.name, ', '.join(self.recipes))def __repr__(self):return str(self)@classmethoddef get_distribution(cls,ctx,*,archs, # required keyword argument: there is no sensible defaultname=None,recipes=[],ndk_api=None,force_build=False,extra_dist_dirs=[],require_perfect_match=False,allow_replace_dist=True):'''Takes information about the distribution, and decides what kind ofdistribution it will be.If parameters conflict (e.g. a dist with that name alreadyexists, but doesn't have the right set of recipes),an error is thrown.Parameters----------name : strThe name of the distribution. If a dist with this name already 'exists, it will be used.ndk_api : intThe NDK API to compile against, included in the dist because it cannotbe changed later during APK packaging.archs : listThe target architectures list to compile against, included in the dist becauseit cannot be changed later during APK packaging.recipes : listThe recipes that the distribution must contain.force_download: boolIf True, only downloaded dists are considered.force_build : boolIf True, the dist is forced to be built locally.extra_dist_dirs : listAny extra directories in which to search for dists.require_perfect_match : boolIf True, will only match distributions with precisely thecorrect set of recipes.allow_replace_dist : boolIf True, will allow an existing dist with the specifiedname but incompatible requirements to be overwritten bya new one with the current requirements.'''possible_dists = Distribution.get_distributions(ctx)debug(f"All possible dists: {possible_dists}")# Will hold dists that would be built in the same folder as an existing distfolder_match_dist = None# 0) Check if a dist with that name and architecture already existsif name is not None and name:possible_dists = [d for d in possible_dists if(d.name == name) and all(arch_name in d.archs for arch_name in archs)]debug(f"Dist matching name and arch: {possible_dists}")if possible_dists:# There should only be one folder with a given dist name *and* arch.# We could check that here, but for compatibility let's let it slide# and just record the details of one of them. We only use this data to# possibly fail the build later, so it doesn't really matter if there# was more than one clash.folder_match_dist = possible_dists[0]# 1) Check if any existing dists meet the requirements_possible_dists = []for dist in possible_dists:if (ndk_api is not None and dist.ndk_api != ndk_api) or dist.ndk_api is None:debug(f"dist {dist} failed to match ndk_api, target api {ndk_api}, dist api {dist.ndk_api}")continuefor recipe in recipes:if recipe not in dist.recipes:debug(f"dist {dist} missing recipe {recipe}")breakelse:_possible_dists.append(dist)possible_dists = _possible_distsdebug(f"Dist matching ndk_api and recipe: {possible_dists}")if possible_dists:info('Of the existing distributions, the following meet ''the given requirements:')pretty_log_dists(possible_dists)else:info('No existing dists meet the given requirements!')# If any dist has perfect recipes, arch and NDK API, return itfor dist in possible_dists:if force_build:debug("Skipping dist due to forced build")continueif ndk_api is not None and dist.ndk_api != ndk_api:debug("Skipping dist due to ndk_api mismatch")continueif not all(arch_name in dist.archs for arch_name in archs):debug("Skipping dist due to arch mismatch")continueif (set(dist.recipes) == set(recipes) or(set(recipes).issubset(set(dist.recipes)) andnot require_perfect_match)):info_notify('{} has compatible recipes, using this one'.format(dist.name))return distelse:debug(f"Skipping dist due to recipes mismatch, expected {set(recipes)}, actual {set(dist.recipes)}")# If there was a name match but we didn't already choose it,# then the existing dist is incompatible with the requested# configuration and the build cannot continueif folder_match_dist is not None and not allow_replace_dist:raise BuildInterruptingException('Asked for dist with name {name} with recipes ({req_recipes}) and ''NDK API {req_ndk_api}, but a dist ''with this name already exists and has either incompatible recipes ''({dist_recipes}) or NDK API {dist_ndk_api}'.format(name=name,req_ndk_api=ndk_api,dist_ndk_api=folder_match_dist.ndk_api,req_recipes=', '.join(recipes),dist_recipes=', '.join(folder_match_dist.recipes)))assert len(possible_dists) < 2# If we got this far, we need to build a new distdist = Distribution(ctx)dist.needs_build = Trueif not name:filen = 'unnamed_dist_{}'i = 1while exists(join(ctx.dist_dir, filen.format(i))):i += 1name = filen.format(i)dist.name = namedist.dist_dir = join(ctx.dist_dir,name)dist.recipes = recipesdist.ndk_api = ctx.ndk_apidist.archs = archsreturn distdef folder_exists(self):return exists(self.dist_dir)def delete(self):rmdir(self.dist_dir)@classmethoddef get_distributions(cls, ctx, extra_dist_dirs=[]):'''Returns all the distributions found locally.'''if extra_dist_dirs:raise BuildInterruptingException('extra_dist_dirs argument to get_distributions ''is not yet implemented')dist_dir = ctx.dist_dirfolders = glob.glob(join(dist_dir, '*'))for dir in extra_dist_dirs:folders.extend(glob.glob(join(dir, '*')))dists = []for folder in folders:if exists(join(folder, 'dist_info.json')):with open(join(folder, 'dist_info.json')) as fileh:dist_info = json.load(fileh)dist = cls(ctx)dist.name = dist_info['dist_name']dist.dist_dir = folderdist.needs_build = Falsedist.recipes = dist_info['recipes']if 'archs' in dist_info:dist.archs = dist_info['archs']if 'ndk_api' in dist_info:dist.ndk_api = dist_info['ndk_api']else:dist.ndk_api = Nonewarning("Distribution {distname}: ({distdir}) has been ""built with an unknown api target, ignoring it, ""you might want to delete it".format(distname=dist.name,distdir=dist.dist_dir))dists.append(dist)return distsdef save_info(self, dirn):'''Save information about the distribution in its dist_dir.'''with current_directory(dirn):info('Saving distribution info')with open('dist_info.json', 'w') as fileh:json.dump({'dist_name': self.name,'bootstrap': self.ctx.bootstrap.name,'archs': [arch.arch for arch in self.ctx.archs],'ndk_api': self.ctx.ndk_api,'use_setup_py': self.ctx.use_setup_py,'recipes': self.ctx.recipe_build_order + self.ctx.python_modules,'hostpython': self.ctx.hostpython,'python_version': self.ctx.python_recipe.major_minor_version_string},fileh)def pretty_log_dists(dists, log_func=info):infos = []for dist in dists:ndk_api = 'unknown' if dist.ndk_api is None else dist.ndk_apiinfos.append('{Fore.GREEN}{Style.BRIGHT}{name}{Style.RESET_ALL}: min API {ndk_api}, ''includes recipes ({Fore.GREEN}{recipes}''{Style.RESET_ALL}), built for archs ({Fore.BLUE}''{archs}{Style.RESET_ALL})'.format(ndk_api=ndk_api,name=dist.name, recipes=', '.join(dist.recipes),archs=', '.join(dist.archs) if dist.archs else 'UNKNOWN',Fore=Err_Fore, Style=Err_Style))for line in infos:log_func('\t' + line)
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。