Skip to content

Commit 75c71d6

Browse files
committed
Merge pull request #537 from kived/custom-recipes
add custom recipe support [needs py3 testing]
2 parents d38901f + 0e62ee6 commit 75c71d6

File tree

3 files changed

+69
-22
lines changed

3 files changed

+69
-22
lines changed

pythonforandroid/build.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,6 +411,8 @@ def __init__(self):
411411
self.toolchain_prefix = None
412412
self.toolchain_version = None
413413

414+
self.local_recipes = None
415+
414416
# root of the toolchain
415417
self.setup_dirs()
416418

pythonforandroid/recipe.py

Lines changed: 51 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
import importlib
33
import zipfile
44
import glob
5+
from six import PY2
6+
57
import sh
68
import shutil
79
from os import listdir, unlink, environ, mkdir
@@ -13,6 +15,27 @@
1315
from pythonforandroid.logger import (logger, info, warning, shprint, info_main)
1416
from pythonforandroid.util import (urlretrieve, current_directory, ensure_dir)
1517

18+
# this import is necessary to keep imp.load_source from complaining :)
19+
import pythonforandroid.recipes
20+
21+
22+
if PY2:
23+
import imp
24+
import_recipe = imp.load_source
25+
else:
26+
import importlib.util
27+
if hasattr(importlib.util, 'module_from_spec'):
28+
def import_recipe(module, filename):
29+
spec = importlib.util.spec_from_file_location(module, filename)
30+
mod = importlib.util.module_from_spec(spec)
31+
spec.loader.exec_module(mod)
32+
return mod
33+
else:
34+
from importlib.machinery import SourceFileLoader
35+
36+
def import_recipe(module, filename):
37+
return SourceFileLoader(module, filename).load_module()
38+
1639

1740
class Recipe(object):
1841
url = None
@@ -514,15 +537,22 @@ def clean_build(self, arch=None):
514537
'did not exist').format(self.name))
515538

516539
@classmethod
517-
def list_recipes(cls):
540+
def recipe_dirs(cls, ctx):
541+
return [ctx.local_recipes,
542+
join(ctx.storage_dir, 'recipes'),
543+
join(ctx.root_dir, "recipes")]
544+
545+
@classmethod
546+
def list_recipes(cls, ctx):
518547
forbidden_dirs = ('__pycache__', )
519-
recipes_dir = join(dirname(__file__), "recipes")
520-
for name in listdir(recipes_dir):
521-
if name in forbidden_dirs:
522-
continue
523-
fn = join(recipes_dir, name)
524-
if isdir(fn):
525-
yield name
548+
for recipes_dir in cls.recipe_dirs(ctx):
549+
if recipes_dir and exists(recipes_dir):
550+
for name in listdir(recipes_dir):
551+
if name in forbidden_dirs:
552+
continue
553+
fn = join(recipes_dir, name)
554+
if isdir(fn):
555+
yield name
526556

527557
@classmethod
528558
def get_recipe(cls, name, ctx):
@@ -531,17 +561,24 @@ def get_recipe(cls, name, ctx):
531561
cls.recipes = {}
532562
if name in cls.recipes:
533563
return cls.recipes[name]
534-
recipe_dir = join(ctx.root_dir, 'recipes', name)
535-
if not exists(recipe_dir): # AND: This will need modifying
536-
# for user-supplied recipes
564+
565+
recipe_file = None
566+
for recipes_dir in cls.recipe_dirs(ctx):
567+
recipe_file = join(recipes_dir, name, '__init__.py')
568+
if exists(recipe_file):
569+
break
570+
recipe_file = None
571+
572+
if not recipe_file:
537573
raise IOError('Recipe folder does not exist')
538-
mod = importlib.import_module(
539-
"pythonforandroid.recipes.{}".format(name))
574+
575+
mod = import_recipe('pythonforandroid.recipes.{}'.format(name), recipe_file)
540576
if len(logger.handlers) > 1:
541577
logger.removeHandler(logger.handlers[1])
542578
recipe = mod.recipe
543-
recipe.recipe_dir = recipe_dir
579+
recipe.recipe_dir = dirname(recipe_file)
544580
recipe.ctx = ctx
581+
cls.recipes[name] = recipe
545582
return recipe
546583

547584

pythonforandroid/toolchain.py

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -261,6 +261,11 @@ def __init__(self):
261261
description=('Whether the dist recipes must perfectly match '
262262
'those requested'))
263263

264+
parser.add_argument(
265+
'--local-recipes', '--local_recipes',
266+
dest='local_recipes', default='./p4a-recipes',
267+
help='Directory to look for local recipes')
268+
264269
self._read_configuration()
265270

266271
args, unknown = parser.parse_known_args(sys.argv[1:])
@@ -293,6 +298,9 @@ def __init__(self):
293298
print('Unrecognized command')
294299
parser.print_help()
295300
exit(1)
301+
302+
self.ctx.local_recipes = args.local_recipes
303+
296304
getattr(self, args.command)(unknown)
297305

298306
def _read_configuration(self):
@@ -322,9 +330,9 @@ def recipes(self, args):
322330
help="Produce a compact list suitable for scripting")
323331

324332
add_boolean_option(
325-
parser, ["color"],
326-
default=True,
327-
description='Whether the output should be colored:')
333+
parser, ["color"],
334+
default=True,
335+
description='Whether the output should be colored:')
328336

329337
args = parser.parse_args(args)
330338

@@ -334,18 +342,18 @@ def recipes(self, args):
334342
Fore = Null_Fore
335343
Style = Null_Style
336344

345+
ctx = self.ctx
337346
if args.compact:
338-
print(" ".join(list(Recipe.list_recipes())))
347+
print(" ".join(set(Recipe.list_recipes(ctx))))
339348
else:
340-
ctx = self.ctx
341-
for name in sorted(Recipe.list_recipes()):
349+
for name in sorted(Recipe.list_recipes(ctx)):
342350
recipe = Recipe.get_recipe(name, ctx)
343351
version = str(recipe.version)
344352
print('{Fore.BLUE}{Style.BRIGHT}{recipe.name:<12} '
345353
'{Style.RESET_ALL}{Fore.LIGHTBLUE_EX}'
346354
'{version:<8}{Style.RESET_ALL}'.format(
347-
recipe=recipe, Fore=Fore, Style=Style,
348-
version=version))
355+
recipe=recipe, Fore=Fore, Style=Style,
356+
version=version))
349357
print(' {Fore.GREEN}depends: {recipe.depends}'
350358
'{Fore.RESET}'.format(recipe=recipe, Fore=Fore))
351359
if recipe.conflicts:

0 commit comments

Comments
 (0)