@@ -1147,6 +1147,12 @@ class Bootstrap(object):
11471147 distribution = None
11481148
11491149 recipe_depends = []
1150+
1151+ can_be_chosen_automatically = True
1152+ '''Determines whether the bootstrap can be chosen as one that
1153+ satisfies user requirements. If False, it will not be returned
1154+ from Bootstrap.get_bootstrap_from_recipes.
1155+ '''
11501156
11511157 # Other things a Bootstrap might need to track (maybe separately):
11521158 # ndk_main.c
@@ -1221,6 +1227,39 @@ def list_bootstraps(cls):
12211227 if isdir (filen ):
12221228 yield name
12231229
1230+ @classmethod
1231+ def get_bootstrap_from_recipes (cls , recipes , ctx ):
1232+ '''Returns a bootstrap whose recipe requirements do not conflict with
1233+ the given recipes.'''
1234+ info ('Trying to find a bootstrap that matches the given recipes.' )
1235+ bootstraps = [cls .get_bootstrap (name , ctx ) for name in cls .list_bootstraps ()]
1236+ acceptable_bootstraps = []
1237+ for bs in bootstraps :
1238+ ok = True
1239+ if not bs .can_be_chosen_automatically :
1240+ ok = False
1241+ for recipe in bs .recipe_depends :
1242+ recipe = Recipe .get_recipe (recipe , ctx )
1243+ if any ([conflict in recipes for conflict in recipe .conflicts ]):
1244+ ok = False
1245+ break
1246+ for recipe in recipes :
1247+ recipe = Recipe .get_recipe (recipe , ctx )
1248+ if any ([conflict in bs .recipe_depends for conflict in recipe .conflicts ]):
1249+ ok = False
1250+ break
1251+ if ok :
1252+ acceptable_bootstraps .append (bs )
1253+ info ('Found {} acceptable bootstraps: {}' .format (
1254+ len (acceptable_bootstraps ), [bs .name for bs in acceptable_bootstraps ]))
1255+ if acceptable_bootstraps :
1256+ info ('Using the first of these: {}' .format (acceptable_bootstraps [0 ].name ))
1257+ return acceptable_bootstraps [0 ]
1258+ return None
1259+
1260+
1261+
1262+
12241263 @classmethod
12251264 def get_bootstrap (cls , name , ctx ):
12261265 '''Returns an instance of a bootstrap with the given name.
@@ -1230,6 +1269,8 @@ def get_bootstrap(cls, name, ctx):
12301269 '''
12311270 # AND: This method will need to check user dirs, and access
12321271 # bootstraps in a slightly different way
1272+ if name is None :
1273+ return None
12331274 if not hasattr (cls , 'bootstraps' ):
12341275 cls .bootstraps = {}
12351276 if name in cls .bootstraps :
@@ -1946,54 +1987,9 @@ def get_recipe_env(self, arch):
19461987 return env
19471988
19481989
1949- def build_recipes (names , ctx ):
1990+ def build_recipes (build_order , python_modules , ctx ):
19501991 # Put recipes in correct build order
1951- graph = Graph ()
1952- recipes_to_load = set (names )
19531992 bs = ctx .bootstrap
1954- if bs is not None and bs .recipe_depends :
1955- info_notify ('Bootstrap requires recipes {}' .format (bs .recipe_depends ))
1956- recipes_to_load = recipes_to_load .union (set (bs .recipe_depends ))
1957- recipes_to_load = list (recipes_to_load )
1958- recipe_loaded = []
1959- python_modules = []
1960- while recipes_to_load :
1961- name = recipes_to_load .pop (0 )
1962- if name in recipe_loaded or isinstance (name , (list , tuple )):
1963- continue
1964- try :
1965- recipe = Recipe .get_recipe (name , ctx )
1966- except ImportError :
1967- info ('No recipe named {}; will attempt to install with pip' .format (name ))
1968- python_modules .append (name )
1969- continue
1970- graph .add (name , name )
1971- info ('Loaded recipe {} (depends on {}{})' .format (
1972- name , recipe .depends ,
1973- ', conflicts {}' .format (recipe .conflicts ) if recipe .conflicts else '' ))
1974- for depend in recipe .depends :
1975- graph .add (name , depend )
1976- recipes_to_load += recipe .depends
1977- for conflict in recipe .conflicts :
1978- if graph .conflicts (conflict ):
1979- warning (
1980- ('{} conflicts with {}, but both have been '
1981- 'included or pulled into the requirements.' .format (recipe .name , conflict )))
1982- warning ('Due to this conflict the build cannot continue, exiting.' )
1983- exit (1 )
1984- recipe_loaded .append (name )
1985- graph .remove_remaining_conflicts (ctx )
1986- if len (graph .graphs ) > 1 :
1987- info ('Found multiple valid recipe sets:' )
1988- for g in graph .graphs :
1989- info (' {}' .format (g .keys ()))
1990- info_notify ('Using the first of these: {}' .format (graph .graphs [0 ].keys ()))
1991- elif len (graph .graphs ) == 0 :
1992- warning ('Didn\' t find any valid dependency graphs, exiting.' )
1993- exit (1 )
1994- else :
1995- info ('Found a single valid recipe set (this is good)' )
1996- build_order = list (graph .find_order (0 ))
19971993 info_notify ("Recipe build order is {}" .format (build_order ))
19981994 if python_modules :
19991995 info_notify (('The requirements ({}) were not found as recipes, they will be '
@@ -2192,11 +2188,15 @@ def build_dist_from_args(ctx, dist, args_list):
21922188 parser = argparse .ArgumentParser (
21932189 description = 'Create a newAndroid project' )
21942190 parser .add_argument ('--bootstrap' , help = ('The name of the bootstrap type, \' pygame\' '
2195- 'or \' sdl2\' ' ),
2196- default = 'sdl2' )
2191+ 'or \' sdl2\' , or leave empty to let a '
2192+ 'bootstrap be chosen automatically from your '
2193+ 'requirements.' ),
2194+ default = None )
21972195 args , unknown = parser .parse_known_args (args_list )
21982196
21992197 bs = Bootstrap .get_bootstrap (args .bootstrap , ctx )
2198+ build_order , python_modules , bs = get_recipe_order_and_bootstrap (ctx , dist .recipes , bs )
2199+
22002200 info ('The selected bootstrap is {}' .format (bs .name ))
22012201 info_main ('# Creating dist with {} bootstrap' .format (bs .name ))
22022202 bs .distribution = dist
@@ -2207,8 +2207,7 @@ def build_dist_from_args(ctx, dist, args_list):
22072207 ctx .prepare_bootstrap (bs )
22082208 ctx .prepare_dist (ctx .dist_name )
22092209
2210- recipes = dist .recipes
2211- build_recipes (recipes , ctx )
2210+ build_recipes (build_order , python_modules , ctx )
22122211
22132212 ctx .bootstrap .run_distribute ()
22142213
@@ -2217,6 +2216,105 @@ def build_dist_from_args(ctx, dist, args_list):
22172216
22182217 return unknown
22192218
2219+ def get_recipe_order_and_bootstrap (ctx , names , bs = None ):
2220+ '''Takes a list of recipe names and (optionally) a bootstrap. Then
2221+ works out the dependency graph (including bootstrap recipes if
2222+ necessary). Finally, if no bootstrap was initially selected,
2223+ chooses one that supports all the recipes.
2224+ '''
2225+ graph = Graph ()
2226+ recipes_to_load = set (names )
2227+ if bs is not None and bs .recipe_depends :
2228+ info_notify ('Bootstrap requires recipes {}' .format (bs .recipe_depends ))
2229+ recipes_to_load = recipes_to_load .union (set (bs .recipe_depends ))
2230+ recipes_to_load = list (recipes_to_load )
2231+ recipe_loaded = []
2232+ python_modules = []
2233+ while recipes_to_load :
2234+ name = recipes_to_load .pop (0 )
2235+ if name in recipe_loaded or isinstance (name , (list , tuple )):
2236+ continue
2237+ try :
2238+ recipe = Recipe .get_recipe (name , ctx )
2239+ except ImportError :
2240+ info ('No recipe named {}; will attempt to install with pip' .format (name ))
2241+ python_modules .append (name )
2242+ continue
2243+ graph .add (name , name )
2244+ info ('Loaded recipe {} (depends on {}{})' .format (
2245+ name , recipe .depends ,
2246+ ', conflicts {}' .format (recipe .conflicts ) if recipe .conflicts else '' ))
2247+ for depend in recipe .depends :
2248+ graph .add (name , depend )
2249+ recipes_to_load += recipe .depends
2250+ for conflict in recipe .conflicts :
2251+ if graph .conflicts (conflict ):
2252+ warning (
2253+ ('{} conflicts with {}, but both have been '
2254+ 'included or pulled into the requirements.' .format (recipe .name , conflict )))
2255+ warning ('Due to this conflict the build cannot continue, exiting.' )
2256+ exit (1 )
2257+ recipe_loaded .append (name )
2258+ graph .remove_remaining_conflicts (ctx )
2259+ if len (graph .graphs ) > 1 :
2260+ info ('Found multiple valid recipe sets:' )
2261+ for g in graph .graphs :
2262+ info (' {}' .format (g .keys ()))
2263+ info_notify ('Using the first of these: {}' .format (graph .graphs [0 ].keys ()))
2264+ elif len (graph .graphs ) == 0 :
2265+ warning ('Didn\' t find any valid dependency graphs, exiting.' )
2266+ exit (1 )
2267+ else :
2268+ info ('Found a single valid recipe set (this is good)' )
2269+
2270+ build_order = list (graph .find_order (0 ))
2271+ if bs is None : # It would be better to check against possible
2272+ # orders other than the first one, but in practice
2273+ # there will rarely be clashes, and the user can
2274+ # specify more parameters if necessary to resolve
2275+ # them.
2276+ bs = Bootstrap .get_bootstrap_from_recipes (build_order , ctx )
2277+ if bs is None :
2278+ info ('Could not find a bootstrap compatible with the required recipes.' )
2279+ info ('If you think such a combination should exist, try '
2280+ 'specifying the bootstrap manually with --bootstrap.' )
2281+ exit (1 )
2282+ info ('{} bootstrap appears compatible with the required recipes.' .format (bs .name ))
2283+ info ('Checking this...' )
2284+ recipes_to_load = bs .recipe_depends
2285+ # This code repeats the code from earlier! Should move to a function:
2286+ while recipes_to_load :
2287+ name = recipes_to_load .pop (0 )
2288+ if name in recipe_loaded or isinstance (name , (list , tuple )):
2289+ continue
2290+ try :
2291+ recipe = Recipe .get_recipe (name , ctx )
2292+ except ImportError :
2293+ info ('No recipe named {}; will attempt to install with pip' .format (name ))
2294+ python_modules .append (name )
2295+ continue
2296+ graph .add (name , name )
2297+ info ('Loaded recipe {} (depends on {}{})' .format (
2298+ name , recipe .depends ,
2299+ ', conflicts {}' .format (recipe .conflicts ) if recipe .conflicts else '' ))
2300+ for depend in recipe .depends :
2301+ graph .add (name , depend )
2302+ recipes_to_load += recipe .depends
2303+ for conflict in recipe .conflicts :
2304+ if graph .conflicts (conflict ):
2305+ warning (
2306+ ('{} conflicts with {}, but both have been '
2307+ 'included or pulled into the requirements.' .format (recipe .name , conflict )))
2308+ warning ('Due to this conflict the build cannot continue, exiting.' )
2309+ exit (1 )
2310+ recipe_loaded .append (name )
2311+ graph .remove_remaining_conflicts (ctx )
2312+ build_order = list (graph .find_order (0 ))
2313+ return build_order , python_modules , bs
2314+
2315+ # Do a final check that the new bs doesn't pull in any conflicts
2316+
2317+
22202318
22212319def split_argument_list (l ):
22222320 if not len (l ):
0 commit comments