diff --git a/pythonforandroid/archs.py b/pythonforandroid/archs.py index a9eccdd80a..d0be5c44d7 100644 --- a/pythonforandroid/archs.py +++ b/pythonforandroid/archs.py @@ -61,6 +61,7 @@ def get_env(self, with_flags_in_cc=True): ccache = self.ctx.ccache + ' ' env['USE_CCACHE'] = '1' env['NDK_CCACHE'] = self.ctx.ccache + env.update({k: v for k, v in environ.items() if k.startswith('CCACHE_')}) print('path is', environ['PATH']) cc = find_executable('{command_prefix}-gcc'.format( diff --git a/pythonforandroid/build.py b/pythonforandroid/build.py index 3556e472ef..b4b1103516 100644 --- a/pythonforandroid/build.py +++ b/pythonforandroid/build.py @@ -8,7 +8,6 @@ import sys import re import sh -from appdirs import user_data_dir from pythonforandroid.util import (ensure_dir, current_directory) from pythonforandroid.logger import (info, warning, error, info_notify, @@ -88,19 +87,22 @@ def get_python_install_dir(self): dir = join(self.python_installs_dir, self.bootstrap.distribution.name) return dir - def setup_dirs(self): + def setup_dirs(self, storage_dir): '''Calculates all the storage and build dirs, and makes sure the directories exist where necessary.''' - self.root_dir = realpath(dirname(__file__)) - - # AND: TODO: Allow the user to set the build_dir - self.storage_dir = user_data_dir('python-for-android') + self.storage_dir = expanduser(storage_dir) + if ' ' in self.storage_dir: + raise ValueError('storage dir path cannot contain spaces, please ' + 'specify a path with --storage-dir') self.build_dir = join(self.storage_dir, 'build') self.dist_dir = join(self.storage_dir, 'dists') + def ensure_dirs(self): ensure_dir(self.storage_dir) ensure_dir(self.build_dir) ensure_dir(self.dist_dir) + ensure_dir(join(self.build_dir, 'bootstrap_builds')) + ensure_dir(join(self.build_dir, 'other_builds')) @property def android_api(self): @@ -163,6 +165,8 @@ def prepare_build_environment(self, user_sdk_dir, user_ndk_dir, ''' + self.ensure_dirs() + if self._build_env_prepared: return @@ -434,9 +438,6 @@ def __init__(self): self.local_recipes = None self.copy_libs = False - # root of the toolchain - self.setup_dirs() - # this list should contain all Archs, it is pruned later self.archs = ( ArchARM(self), @@ -445,9 +446,7 @@ def __init__(self): ArchAarch_64(self), ) - ensure_dir(join(self.build_dir, 'bootstrap_builds')) - ensure_dir(join(self.build_dir, 'other_builds')) - # other_builds: where everything else is built + self.root_dir = realpath(dirname(__file__)) # remove the most obvious flags that can break the compilation self.env.pop("LDFLAGS", None) diff --git a/pythonforandroid/recipe.py b/pythonforandroid/recipe.py index b2f500ca10..5f2105dbe8 100644 --- a/pythonforandroid/recipe.py +++ b/pythonforandroid/recipe.py @@ -565,9 +565,13 @@ def clean_build(self, arch=None): info('Deleting {}'.format(directory)) shutil.rmtree(directory) - def install_libs(self, arch, lib, *libs): + def install_libs(self, arch, *libs): libs_dir = self.ctx.get_libs_dir(arch.arch) - shprint(sh.cp, '-t', libs_dir, lib, *libs) + if not libs: + warning('install_libs called with no libraries to install!') + return + args = libs + (libs_dir,) + shprint(sh.cp, *args) def has_libs(self, arch, *libs): return all(map(lambda l: self.ctx.has_lib(arch.arch, l), libs)) @@ -577,8 +581,9 @@ def recipe_dirs(cls, ctx): recipe_dirs = [] if ctx.local_recipes is not None: recipe_dirs.append(ctx.local_recipes) - recipe_dirs.extend([join(ctx.storage_dir, 'recipes'), - join(ctx.root_dir, "recipes")]) + if ctx.storage_dir: + recipe_dirs.append(join(ctx.storage_dir, 'recipes')) + recipe_dirs.append(join(ctx.root_dir, "recipes")) return recipe_dirs @classmethod diff --git a/pythonforandroid/toolchain.py b/pythonforandroid/toolchain.py index c6ebe28eed..7c31c47f8b 100755 --- a/pythonforandroid/toolchain.py +++ b/pythonforandroid/toolchain.py @@ -22,7 +22,7 @@ import argparse import sh - +from appdirs import user_data_dir from pythonforandroid.recipe import (Recipe, PythonRecipe, CythonRecipe, CompiledComponentsPythonRecipe, @@ -171,8 +171,6 @@ def split_argument_list(l): class ToolchainCL(object): def __init__(self): - self._ctx = None - parser = argparse.ArgumentParser( description="Tool for managing the Android / Python toolchain", usage="""toolchain [] @@ -206,18 +204,23 @@ def __init__(self): '--debug', dest='debug', action='store_true', help='Display debug output and all build info') parser.add_argument( - '--sdk_dir', dest='sdk_dir', default='', + '--sdk-dir', '--sdk_dir', dest='sdk_dir', default='', help='The filepath where the Android SDK is installed') parser.add_argument( - '--ndk_dir', dest='ndk_dir', default='', + '--ndk-dir', '--ndk_dir', dest='ndk_dir', default='', help='The filepath where the Android NDK is installed') parser.add_argument( - '--android_api', dest='android_api', default=0, type=int, + '--android-api', '--android_api', dest='android_api', default=0, type=int, help='The Android API level to build against.') parser.add_argument( - '--ndk_version', dest='ndk_version', default='', + '--ndk-version', '--ndk_version', dest='ndk_version', default='', help=('The version of the Android NDK. This is optional, ' 'we try to work it out automatically from the ndk_dir.')) + parser.add_argument( + '--storage-dir', dest='storage_dir', + default=self.default_storage_dir, + help=('Primary storage directory for downloads and builds ' + '(default: {})'.format(self.default_storage_dir))) # AND: This option doesn't really fit in the other categories, the # arg structure needs a rethink @@ -228,7 +231,7 @@ def __init__(self): # Options for specifying the Distribution parser.add_argument( - '--dist_name', + '--dist-name', '--dist_name', help='The name of the distribution to use or create', default='') parser.add_argument( @@ -291,6 +294,10 @@ def __init__(self): if args.debug: logger.setLevel(logging.DEBUG) + + self.ctx = Context() + self.storage_dir = args.storage_dir + self.ctx.setup_dirs(self.storage_dir) self.sdk_dir = args.sdk_dir self.ndk_dir = args.ndk_dir self.android_api = args.android_api @@ -322,6 +329,13 @@ def __init__(self): getattr(self, args.command)(unknown) + @property + def default_storage_dir(self): + udd = user_data_dir('python-for-android') + if ' ' in udd: + udd = '~/.python-for-android' + return udd + def _read_configuration(self): # search for a .p4a configuration file in the current directory if not exists(".p4a"): @@ -335,12 +349,6 @@ def _read_configuration(self): for arg in line: sys.argv.append(arg) - @property - def ctx(self): - if self._ctx is None: - self._ctx = Context() - return self._ctx - def recipes(self, args): parser = argparse.ArgumentParser( description="List all the available recipes") @@ -409,7 +417,7 @@ def clean_dists(self, args): parser = argparse.ArgumentParser( description="Delete any distributions that have been built.") args = parser.parse_args(args) - ctx = Context() + ctx = self.ctx if exists(ctx.dist_dir): shutil.rmtree(ctx.dist_dir) @@ -424,7 +432,7 @@ def clean_builds(self, args): parser = argparse.ArgumentParser( description="Delete all build files (but not download caches)") args = parser.parse_args(args) - ctx = Context() + ctx = self.ctx # if exists(ctx.dist_dir): # shutil.rmtree(ctx.dist_dir) if exists(ctx.build_dir): @@ -462,7 +470,7 @@ def clean_download_cache(self, args): parser = argparse.ArgumentParser( description="Delete all download caches") args = parser.parse_args(args) - ctx = Context() + ctx = self.ctx if exists(ctx.packages_path): shutil.rmtree(ctx.packages_path) @@ -627,7 +635,7 @@ def print_context_info(self, args): python-for-android will internally use for package building, along with information about where the Android SDK and NDK will be called from.''' - ctx = Context() + ctx = self.ctx for attribute in ('root_dir', 'build_dir', 'dist_dir', 'libs_dir', 'ccache', 'cython', 'sdk_dir', 'ndk_dir', 'ndk_platform', 'ndk_ver', 'android_api'): @@ -647,7 +655,7 @@ def dists(self, args): def distributions(self, args): '''Lists all distributions currently available (i.e. that have already been built).''' - ctx = Context() + ctx = self.ctx dists = Distribution.get_distributions(ctx) if dists: