Esempio n. 1
0
 def clean_bootstrap_builds(self, args):
     '''Delete all the bootstrap builds.'''
     for bs in Bootstrap.list_bootstraps():
         bs = Bootstrap.get_bootstrap(bs, self.ctx)
         if bs.build_dir and exists(bs.build_dir):
             info('Cleaning build for {} bootstrap.'.format(bs.name))
             shutil.rmtree(bs.build_dir)
    def test__cmp_bootstraps_by_priority(self):
        # Test service_only has higher priority than sdl2:
        # (higher priority = smaller number/comes first)
        self.assertTrue(
            _cmp_bootstraps_by_priority(
                Bootstrap.get_bootstrap("service_only", self.ctx),
                Bootstrap.get_bootstrap("sdl2", self.ctx)) < 0)

        # Test a random bootstrap is always lower priority than sdl2:
        class _FakeBootstrap(object):
            def __init__(self, name):
                self.name = name

        bs1 = _FakeBootstrap("alpha")
        bs2 = _FakeBootstrap("zeta")
        self.assertTrue(
            _cmp_bootstraps_by_priority(
                bs1, Bootstrap.get_bootstrap("sdl2", self.ctx)) > 0)
        self.assertTrue(
            _cmp_bootstraps_by_priority(
                bs2, Bootstrap.get_bootstrap("sdl2", self.ctx)) > 0)

        # Test bootstraps that aren't otherwise recognized are ranked
        # alphabetically:
        self.assertTrue(_cmp_bootstraps_by_priority(
            bs2,
            bs1,
        ) > 0)
        self.assertTrue(_cmp_bootstraps_by_priority(
            bs1,
            bs2,
        ) < 0)
    def test_bootstrap_strip(
        self,
        mock_find_executable,
        mock_ensure_dir,
        mock_sh_command,
        mock_sh_print,
    ):
        mock_find_executable.return_value = "arm-linux-androideabi-gcc"
        # prepare arch, bootstrap, distribution and PythonRecipe
        arch = ArchARMv7_a(self.ctx)
        bs = Bootstrap().get_bootstrap(self.bootstrap_name, self.ctx)
        self.setUp_distribution_with_bootstrap(bs)
        self.ctx.python_recipe = Recipe.get_recipe("python3", self.ctx)

        # test that strip_libraries runs with a fake distribution
        bs.strip_libraries(arch)

        mock_find_executable.assert_called_once()
        self.assertEqual(
            mock_find_executable.call_args[0][0],
            mock_find_executable.return_value,
        )
        mock_sh_command.assert_called_once_with("arm-linux-androideabi-strip")
        # check that the other mocks we made are actually called
        mock_ensure_dir.assert_called()
        mock_sh_print.assert_called()
Esempio n. 4
0
 def clean_bootstrap_builds(self, args):
     '''Delete all the bootstrap builds.'''
     for bs in Bootstrap.list_bootstraps():
         bs = Bootstrap.get_bootstrap(bs, self.ctx)
         if bs.build_dir and exists(bs.build_dir):
             info('Cleaning build for {} bootstrap.'.format(bs.name))
             shutil.rmtree(bs.build_dir)
    def test_bootstrap_fry_eggs(self, mock_isdir, mock_sh_mv, mock_sh_rm,
                                mock_listdir):
        mock_listdir.return_value = [
            "jnius",
            "kivy",
            "Kivy-1.11.0.dev0-py3.7.egg-info",
            "pyjnius-1.2.1.dev0-py3.7.egg",
        ]

        # prepare bootstrap, context and distribution
        bs = Bootstrap().get_bootstrap(self.bootstrap_name, self.ctx)
        self.setUp_distribution_with_bootstrap(bs)

        # test that fry_eggs runs with a fake distribution
        site_packages = os.path.join(bs.dist_dir, "_python_bundle",
                                     "_python_bundle")
        bs.fry_eggs(site_packages)

        mock_listdir.assert_has_calls([
            mock.call(site_packages),
            mock.call(
                os.path.join(site_packages, "pyjnius-1.2.1.dev0-py3.7.egg")),
        ])
        self.assertEqual(mock_sh_rm.call_args[0][1],
                         "pyjnius-1.2.1.dev0-py3.7.egg")
        # check that the other mocks we made are actually called
        mock_isdir.assert_called()
        mock_sh_mv.assert_called()
Esempio n. 6
0
    def test_bootstrap_strip(
        self,
        mock_find_executable,
        mock_glob,
        mock_ensure_dir,
        mock_sh_command,
        mock_sh_print,
    ):
        mock_find_executable.return_value = os.path.join(
            self.ctx._ndk_dir,
            f"toolchains/llvm/prebuilt/{system().lower()}-x86_64/bin/clang",
        )
        mock_glob.return_value = [
            os.path.join(self.ctx._ndk_dir, "toolchains", "llvm")
        ]
        # prepare arch, bootstrap, distribution and PythonRecipe
        arch = ArchARMv7_a(self.ctx)
        bs = Bootstrap().get_bootstrap(self.bootstrap_name, self.ctx)
        self.setUp_distribution_with_bootstrap(bs)
        self.ctx.python_recipe = Recipe.get_recipe("python3", self.ctx)

        # test that strip_libraries runs with a fake distribution
        bs.strip_libraries(arch)

        mock_find_executable.assert_called_once()
        self.assertEqual(
            mock_find_executable.call_args[0][0],
            mock_find_executable.return_value,
        )
        mock_sh_command.assert_called_once_with("arm-linux-androideabi-strip")
        # check that the other mocks we made are actually called
        mock_ensure_dir.assert_called()
        mock_sh_print.assert_called()
Esempio n. 7
0
 def bootstraps(self, args):
     '''List all the bootstraps available to build with.'''
     for bs in Bootstrap.list_bootstraps():
         bs = Bootstrap.get_bootstrap(bs, self.ctx)
         print('{Fore.BLUE}{Style.BRIGHT}{bs.name}{Style.RESET_ALL}'
               .format(bs=bs, Fore=Out_Fore, Style=Out_Style))
         print('    {Fore.GREEN}depends: {bs.recipe_depends}{Fore.RESET}'
               .format(bs=bs, Fore=Out_Fore))
Esempio n. 8
0
 def bootstraps(self, args):
     '''List all the bootstraps available to build with.'''
     for bs in Bootstrap.list_bootstraps():
         bs = Bootstrap.get_bootstrap(bs, self.ctx)
         print('{Fore.BLUE}{Style.BRIGHT}{bs.name}{Style.RESET_ALL}'
               .format(bs=bs, Fore=Out_Fore, Style=Out_Style))
         print('    {Fore.GREEN}depends: {bs.recipe_depends}{Fore.RESET}'
               .format(bs=bs, Fore=Out_Fore))
    def test_prepare_dist_dir(self, mock_ensure_dir):
        """A test which will initialize a bootstrap and will check if the
        method :meth:`~pythonforandroid.bootstrap.Bootstrap.prepare_dist_dir`
        successfully calls once the method `endure_dir`
        """
        bs = Bootstrap().get_bootstrap("sdl2", self.ctx)

        bs.prepare_dist_dir("fake_name")
        mock_ensure_dir.assert_called_once_with(bs.dist_dir)
    def test_run_distribute(self, *args):
        # prepare bootstrap
        bs = Bootstrap().get_bootstrap(self.bootstrap_name, self.ctx)
        self.ctx.bootstrap = bs

        # test dist_dir error
        with self.assertRaises(SystemExit) as e:
            bs.run_distribute()
        self.assertEqual(e.exception.args[0], 1)
Esempio n. 11
0
    def test_bootstrap_prepare_build_dir_with_java_src(
        self,
        mock_sh_rm,
        mock_sh_mkdir,
        mock_listdir,
        mock_sh_ln,
        mock_chdir,
        mock_open,
        mock_os_unlink,
        mock_os_path_exists,
        mock_os_path_isfile,
    ):
        """A test which will initialize a bootstrap and will check perform
        another test for method
        :meth:`~pythonforandroid.bootstrap.Bootstrap.prepare_build_dir`. In
        here we will simulate that we have `with_java_src` set to some value.
        """
        self.ctx.symlink_java_src = ["some_java_src"]
        mock_listdir.return_value = [
            "jnius",
            "kivy",
            "Kivy-1.11.0.dev0-py3.7.egg-info",
            "pyjnius-1.2.1.dev0-py3.7.egg",
        ]

        # prepare bootstrap
        bs = Bootstrap().get_bootstrap("sdl2", self.ctx)
        self.ctx.bootstrap = bs

        # test that prepare_build_dir runs (notice that we mock
        # any file/dir creation so we can speed up the tests)
        bs.prepare_build_dir()
        # make sure that the open command has been called only once
        mock_open.assert_called_with("project.properties", "w")

        # check that the symlink was made 4 times and that
        self.assertEqual(
            len(mock_sh_ln.call_args_list), len(mock_listdir.return_value)
        )
        for i, directory in enumerate(mock_listdir.return_value):
            self.assertTrue(
                mock_sh_ln.call_args_list[i][0][1].endswith(directory)
            )

        # check that the other mocks we made are actually called
        mock_sh_rm.assert_called()
        mock_sh_mkdir.assert_called()
        mock_chdir.assert_called()
        mock_os_unlink.assert_called()
        mock_os_path_exists.assert_called()
        mock_os_path_isfile.assert_called()
Esempio n. 12
0
    def test_get_bootstraps_from_recipes(self):
        """A test which will initialize a bootstrap and will check if the
        method :meth:`~pythonforandroid.bootstrap.Bootstrap.
        get_bootstraps_from_recipes` returns the expected values
        """
        recipes_sdl2 = {"sdl2", "python3", "kivy"}
        bs = Bootstrap().get_bootstrap_from_recipes(recipes_sdl2, self.ctx)

        self.assertEqual(bs.name, "sdl2")

        # test wrong recipes
        wrong_recipes = {"python2", "python3", "pyjnius"}
        bs = Bootstrap().get_bootstrap_from_recipes(wrong_recipes, self.ctx)
        self.assertIsNone(bs)
Esempio n. 13
0
def build_dist_from_args(ctx, dist, args_list):
    '''Parses out any bootstrap related arguments, and uses them to build
    a dist.'''
    parser = argparse.ArgumentParser(description='Create a newAndroid project')
    parser.add_argument('--bootstrap',
                        help=('The name of the bootstrap type, \'pygame\' '
                              'or \'sdl2\', or leave empty to let a '
                              'bootstrap be chosen automatically from your '
                              'requirements.'),
                        default=None)
    args, unknown = parser.parse_known_args(args_list)

    bs = Bootstrap.get_bootstrap(args.bootstrap, ctx)
    build_order, python_modules, bs \
        = get_recipe_order_and_bootstrap(ctx, dist.recipes, bs)

    info('The selected bootstrap is {}'.format(bs.name))
    info_main('# Creating dist with {} bootstrap'.format(bs.name))
    bs.distribution = dist
    info_notify('Dist will have name {} and recipes ({})'.format(
        dist.name, ', '.join(dist.recipes)))

    ctx.dist_name = bs.distribution.name
    ctx.prepare_bootstrap(bs)
    ctx.prepare_dist(ctx.dist_name)

    build_recipes(build_order, python_modules, ctx)

    ctx.bootstrap.run_distribute()

    info_main('# Your distribution was created successfully, exiting.')
    info('Dist can be found at (for now) {}'.format(
        join(ctx.dist_dir, ctx.dist_name)))

    return unknown
Esempio n. 14
0
def build_dist_from_args(ctx, dist, args):
    """Parses out any bootstrap related arguments, and uses them to build
    a dist."""
    bs = Bootstrap.get_bootstrap(args.bootstrap, ctx)
    build_order, python_modules, bs \
        = get_recipe_order_and_bootstrap(ctx, dist.recipes, bs)
    ctx.recipe_build_order = build_order
    ctx.python_modules = python_modules

    info('The selected bootstrap is {}'.format(bs.name))
    info_main('# Creating dist with {} bootstrap'.format(bs.name))
    bs.distribution = dist
    info_notify('Dist will have name {} and recipes ({})'.format(
        dist.name, ', '.join(dist.recipes)))
    info('Dist will also contain modules ({}) installed from pip'.format(
        ', '.join(ctx.python_modules)))

    ctx.dist_name = bs.distribution.name
    ctx.prepare_bootstrap(bs)
    if dist.needs_build:
        ctx.prepare_dist(ctx.dist_name)

    build_recipes(build_order, python_modules, ctx)

    ctx.bootstrap.run_distribute()

    info_main('# Your distribution was created successfully, exiting.')
    info('Dist can be found at (for now) {}'.format(
        join(ctx.dist_dir, ctx.dist_name)))
Esempio n. 15
0
    def test_get_distributions_error_ndk_api_mismatch(self, mock_glob,
                                                      mock_exists,
                                                      mock_get_dists):
        """Test that method
        :meth:`~pythonforandroid.distribution.Distribution.get_distribution`
        raises an error in case that we have some distribution already build,
        with a given `name` and `ndk_api`, and we try to get another
        distribution with the same `name` but different `ndk_api`.
        """
        expected_dist = Distribution.get_distribution(
            self.ctx,
            name="test_prj",
            recipes=["python3", "kivy"],
            arch_name=self.TEST_ARCH,
        )
        mock_get_dists.return_value = [expected_dist]
        mock_glob.return_value = ["sdl2-python3"]

        with self.assertRaises(BuildInterruptingException) as e:
            self.setUp_distribution_with_bootstrap(
                Bootstrap().get_bootstrap("sdl2", self.ctx),
                allow_replace_dist=False,
                ndk_api=22,
            )
        self.assertEqual(
            e.exception.args[0],
            "Asked for dist with name test_prj with recipes (python3, kivy)"
            " and NDK API 22, but a dist with this name already exists and has"
            " either incompatible recipes (python3, kivy) or NDK API 21",
        )
Esempio n. 16
0
def build_dist_from_args(ctx, dist, args):
    '''Parses out any bootstrap related arguments, and uses them to build
    a dist.'''
    bs = Bootstrap.get_bootstrap(args.bootstrap, ctx)
    build_order, python_modules, bs \
        = get_recipe_order_and_bootstrap(ctx, dist.recipes, bs)
    ctx.recipe_build_order = build_order

    info('The selected bootstrap is {}'.format(bs.name))
    info_main('# Creating dist with {} bootstrap'.format(bs.name))
    bs.distribution = dist
    info_notify('Dist will have name {} and recipes ({})'.format(
        dist.name, ', '.join(dist.recipes)))

    ctx.dist_name = bs.distribution.name
    ctx.prepare_bootstrap(bs)
    ctx.prepare_dist(ctx.dist_name)

    build_recipes(build_order, python_modules, ctx)

    ctx.bootstrap.run_distribute()

    info_main('# Your distribution was created successfully, exiting.')
    info('Dist can be found at (for now) {}'
         .format(join(ctx.dist_dir, ctx.dist_name)))
Esempio n. 17
0
def build_dist_from_args(ctx, dist, args):
    '''Parses out any bootstrap related arguments, and uses them to build
    a dist.'''
    bs = Bootstrap.get_bootstrap(args.bootstrap, ctx)
    build_order, python_modules, bs \
        = get_recipe_order_and_bootstrap(ctx, dist.recipes, bs)
    ctx.recipe_build_order = build_order
    ctx.python_modules = python_modules

    if python_modules and hasattr(sys, 'real_prefix'):
        error('virtualenv is needed to install pure-Python modules, but')
        error('virtualenv does not support nesting, and you are running')
        error('python-for-android in one. Please run p4a outside of a')
        error('virtualenv instead.')
        exit(1)

    info('The selected bootstrap is {}'.format(bs.name))
    info_main('# Creating dist with {} bootstrap'.format(bs.name))
    bs.distribution = dist
    info_notify('Dist will have name {} and recipes ({})'.format(
        dist.name, ', '.join(dist.recipes)))
    info('Dist will also contain modules ({}) installed from pip'.format(
        ', '.join(ctx.python_modules)))

    ctx.dist_name = bs.distribution.name
    ctx.prepare_bootstrap(bs)
    ctx.prepare_dist(ctx.dist_name)

    build_recipes(build_order, python_modules, ctx)

    ctx.bootstrap.run_distribute()

    info_main('# Your distribution was created successfully, exiting.')
    info('Dist can be found at (for now) {}'
         .format(join(ctx.dist_dir, ctx.dist_name)))
Esempio n. 18
0
def build_dist_from_args(ctx, dist, args):
    '''Parses out any bootstrap related arguments, and uses them to build
    a dist.'''
    bs = Bootstrap.get_bootstrap(args.bootstrap, ctx)
    build_order, python_modules, bs \
        = get_recipe_order_and_bootstrap(ctx, dist.recipes, bs)
    ctx.recipe_build_order = build_order
    ctx.python_modules = python_modules

    if python_modules and hasattr(sys, 'real_prefix'):
        error('virtualenv is needed to install pure-Python modules, but')
        error('virtualenv does not support nesting, and you are running')
        error('python-for-android in one. Please run p4a outside of a')
        error('virtualenv instead.')
        exit(1)

    info('The selected bootstrap is {}'.format(bs.name))
    info_main('# Creating dist with {} bootstrap'.format(bs.name))
    bs.distribution = dist
    info_notify('Dist will have name {} and recipes ({})'.format(
        dist.name, ', '.join(dist.recipes)))

    ctx.dist_name = bs.distribution.name
    ctx.prepare_bootstrap(bs)
    ctx.prepare_dist(ctx.dist_name)

    build_recipes(build_order, python_modules, ctx)

    ctx.bootstrap.run_distribute()

    info_main('# Your distribution was created successfully, exiting.')
    info('Dist can be found at (for now) {}'
         .format(join(ctx.dist_dir, ctx.dist_name)))
Esempio n. 19
0
    def test_get_distributions(self, mock_glob, mock_exists,
                               mock_open_dist_info):
        """Test that method
        :meth:`~pythonforandroid.distribution.Distribution.get_distributions`
        returns some expected values:

            - A list of instances of class
              `~pythonforandroid.distribution.Distribution
            - That one of the distributions returned in the result has the
              proper values (`name`, `ndk_api` and `recipes`)
        """
        self.setUp_distribution_with_bootstrap(Bootstrap().get_bootstrap(
            "sdl2", self.ctx))
        mock_glob.return_value = ["sdl2-python3"]
        mock_open_dist_info.side_effect = [
            mock.mock_open(read_data=json.dumps(dist_info_data)).return_value
        ]

        dists = self.ctx.bootstrap.distribution.get_distributions(self.ctx)
        self.assertIsInstance(dists, list)
        self.assertEqual(len(dists), 1)
        self.assertIsInstance(dists[0], Distribution)
        self.assertEqual(dists[0].name, "sdl2_dist")
        self.assertEqual(dists[0].dist_dir, "sdl2-python3")
        self.assertEqual(dists[0].ndk_api, 21)
        self.assertEqual(
            dists[0].recipes,
            ["hostpython3", "python3", "sdl2", "kivy", "requests"],
        )
        mock_open_dist_info.assert_called_with("sdl2-python3/dist_info.json")
        mock_open_dist_info.reset_mock()
Esempio n. 20
0
 def setUp(self):
     """
     Initialize a Context with a Bootstrap and a Distribution to properly
     test an library recipe, to do so we reuse `BaseClassSetupBootstrap`
     """
     super(TestLibraryRecipe, self).setUp()
     self.ctx.bootstrap = Bootstrap().get_bootstrap('sdl2', self.ctx)
     self.setUp_distribution_with_bootstrap(self.ctx.bootstrap)
Esempio n. 21
0
    def test_build_dist_dirs(self):
        """A test which will initialize a bootstrap and will check if the
        directories we set has the values that we expect. Here we test methods:

            - :meth:`~pythonforandroid.bootstrap.Bootstrap.get_build_dir`
            - :meth:`~pythonforandroid.bootstrap.Bootstrap.get_dist_dir`
            - :meth:`~pythonforandroid.bootstrap.Bootstrap.get_common_dir`
        """
        bs = Bootstrap().get_bootstrap("sdl2", self.ctx)

        self.assertTrue(
            bs.get_build_dir().endswith("build/bootstrap_builds/sdl2-python3")
        )
        self.assertTrue(bs.get_dist_dir("test_prj").endswith("dists/test_prj"))
        self.assertTrue(
            bs.get_common_dir().endswith("pythonforandroid/bootstraps/common")
        )
Esempio n. 22
0
 def test_get_distribution_no_name(self, mock_exists):
     """Test that method
     :meth:`~pythonforandroid.distribution.Distribution.get_distribution`
     returns the proper result which should `unnamed_dist_1`."""
     mock_exists.return_value = False
     self.ctx.bootstrap = Bootstrap().get_bootstrap("sdl2", self.ctx)
     dist = Distribution.get_distribution(self.ctx,
                                          arch_name=self.TEST_ARCH)
     self.assertEqual(dist.name, "unnamed_dist_1")
Esempio n. 23
0
 def test_delete(self, mock_rmtree):
     """Test that method
     :meth:`~pythonforandroid.distribution.Distribution.delete` is
     called once with the proper arguments."""
     self.setUp_distribution_with_bootstrap(Bootstrap().get_bootstrap(
         "sdl2", self.ctx))
     self.ctx.bootstrap.distribution.delete()
     mock_rmtree.assert_called_once_with(
         self.ctx.bootstrap.distribution.dist_dir)
    def test_attributes(self):
        """A test which will initialize a bootstrap and will check if the
        values are the expected.
        """
        bs = Bootstrap().get_bootstrap("sdl2", self.ctx)
        self.assertEqual(bs.name, "sdl2")
        self.assertEqual(bs.jni_dir, "sdl2/jni")
        self.assertEqual(bs.get_build_dir_name(), "sdl2-python3")

        # test dist_dir error
        bs.distribution = None
        with self.assertRaises(SystemExit) as e:
            bs.dist_dir
        self.assertEqual(e.exception.args[0], 1)

        # test dist_dir success
        self.setUp_distribution_with_bootstrap(bs)
        self.assertTrue(bs.dist_dir.endswith("dists/test_prj"))
 def setUp(self):
     """
     Initialize a Context with a Bootstrap and a Distribution to properly
     test a recipe which depends on android's STL library, to do so we reuse
     `BaseClassSetupBootstrap`
     """
     super().setUp()
     self.ctx.bootstrap = Bootstrap().get_bootstrap('sdl2', self.ctx)
     self.setUp_distribution_with_bootstrap(self.ctx.bootstrap)
     self.ctx.python_recipe = Recipe.get_recipe('python3', self.ctx)
Esempio n. 26
0
    def test_attributes(self):
        """A test which will initialize a bootstrap and will check if the
        values are the expected.
        """
        bs = Bootstrap().get_bootstrap("sdl2", self.ctx)
        self.assertEqual(bs.name, "sdl2")
        self.assertEqual(bs.jni_dir, "sdl2/jni")
        self.assertEqual(bs.get_build_dir_name(), "sdl2-python3")

        # bs.dist_dir should raise an error if there is no distribution to query
        bs.distribution = None
        with self.assertRaises(BuildInterruptingException):
            bs.dist_dir

        # test dist_dir success
        self.setUp_distribution_with_bootstrap(bs)
        expected_folder_name = generate_dist_folder_name(
            'test_prj', [self.TEST_ARCH])
        self.assertTrue(bs.dist_dir.endswith(f"dists/{expected_folder_name}"))
Esempio n. 27
0
 def test_properties(self):
     """Test that some attributes has the expected result (for now, we check
     that `__repr__` and `__str__` return the proper values"""
     self.setUp_distribution_with_bootstrap(Bootstrap().get_bootstrap(
         "sdl2", self.ctx))
     distribution = self.ctx.bootstrap.distribution
     self.assertEqual(self.ctx, distribution.ctx)
     expected_repr = (
         "<Distribution: name test_prj with recipes (python3, kivy)>")
     self.assertEqual(distribution.__str__(), expected_repr)
     self.assertEqual(distribution.__repr__(), expected_repr)
 def test_all_bootstraps(self):
     """A test which will initialize a bootstrap and will check if the
     method :meth:`~pythonforandroid.bootstrap.Bootstrap.list_bootstraps`
     returns the expected values, which should be: `empty", `service_only`,
     `webview` and `sdl2`
     """
     expected_bootstraps = {"empty", "service_only", "webview", "sdl2"}
     set_of_bootstraps = Bootstrap.all_bootstraps()
     self.assertEqual(expected_bootstraps,
                      expected_bootstraps & set_of_bootstraps)
     self.assertEqual(len(expected_bootstraps), len(set_of_bootstraps))
 def setUp(self):
     self.ctx = Context()
     self.ctx.ndk_api = 21
     self.ctx.android_api = 27
     self.ctx._sdk_dir = "/opt/android/android-sdk"
     self.ctx._ndk_dir = "/opt/android/android-ndk"
     self.ctx.setup_dirs(os.getcwd())
     self.ctx.bootstrap = Bootstrap().get_bootstrap("sdl2", self.ctx)
     self.ctx.bootstrap.distribution = Distribution.get_distribution(
         self.ctx, name="sdl2", recipes=["python3", "kivy"])
     self.ctx.python_recipe = Recipe.get_recipe("python3", self.ctx)
Esempio n. 30
0
    def test_folder_exist(self, mock_exists):
        """Test that method
        :meth:`~pythonforandroid.distribution.Distribution.folder_exist` is
        called once with the proper arguments."""

        mock_exists.return_value = False
        self.setUp_distribution_with_bootstrap(
            Bootstrap.get_bootstrap("sdl2", self.ctx))
        self.ctx.bootstrap.distribution.folder_exists()
        mock_exists.assert_called_with(
            self.ctx.bootstrap.distribution.dist_dir)
    def test_get_bootstraps_from_recipes(self):
        """A test which will initialize a bootstrap and will check if the
        method :meth:`~pythonforandroid.bootstrap.Bootstrap.
        get_bootstraps_from_recipes` returns the expected values
        """

        import pythonforandroid.recipe
        original_get_recipe = pythonforandroid.recipe.Recipe.get_recipe

        # Test that SDL2 works with kivy:
        recipes_sdl2 = {"sdl2", "python3", "kivy"}
        bs = Bootstrap.get_bootstrap_from_recipes(recipes_sdl2, self.ctx)
        self.assertEqual(bs.name, "sdl2")

        # Test that pysdl2 or kivy alone will also yield SDL2 (dependency):
        recipes_pysdl2_only = {"pysdl2"}
        bs = Bootstrap.get_bootstrap_from_recipes(recipes_pysdl2_only,
                                                  self.ctx)
        self.assertEqual(bs.name, "sdl2")
        recipes_kivy_only = {"kivy"}
        bs = Bootstrap.get_bootstrap_from_recipes(recipes_kivy_only, self.ctx)
        self.assertEqual(bs.name, "sdl2")

        with mock.patch("pythonforandroid.recipe.Recipe.get_recipe") as \
                mock_get_recipe:
            # Test that something conflicting with sdl2 won't give sdl2:
            def _add_sdl2_conflicting_recipe(name, ctx):
                if name == "conflictswithsdl2":
                    if name not in pythonforandroid.recipe.Recipe.recipes:
                        pythonforandroid.recipe.Recipe.recipes[name] = (
                            get_fake_recipe("sdl2", conflicts=["sdl2"]))
                return original_get_recipe(name, ctx)

            mock_get_recipe.side_effect = _add_sdl2_conflicting_recipe
            recipes_with_sdl2_conflict = {"python3", "conflictswithsdl2"}
            bs = Bootstrap.get_bootstrap_from_recipes(
                recipes_with_sdl2_conflict, self.ctx)
            self.assertNotEqual(bs.name, "sdl2")

        # Test using flask will default to webview:
        recipes_with_flask = {"python3", "flask"}
        bs = Bootstrap.get_bootstrap_from_recipes(recipes_with_flask, self.ctx)
        self.assertEqual(bs.name, "webview")

        # Test using random packages will default to service_only:
        recipes_with_no_sdl2_or_web = {"python3", "numpy"}
        bs = Bootstrap.get_bootstrap_from_recipes(recipes_with_no_sdl2_or_web,
                                                  self.ctx)
        self.assertEqual(bs.name, "service_only")

        # Test wrong recipes
        wrong_recipes = {"python2", "python3", "pyjnius"}
        bs = Bootstrap.get_bootstrap_from_recipes(wrong_recipes, self.ctx)
        self.assertIsNone(bs)
    def test_bootstrap_prepare_build_dir(self, mock_os_makedirs,
                                         mock_shutil_copy, mock_chdir,
                                         mock_open):
        """A test which will initialize a bootstrap and will check if the
        method :meth:`~pythonforandroid.bootstrap.Bootstrap.prepare_build_dir`
        successfully calls the methods that we need to prepare a build dir.
        """

        # prepare bootstrap
        bs = Bootstrap().get_bootstrap("service_only", self.ctx)
        self.ctx.bootstrap = bs

        # test that prepare_build_dir runs (notice that we mock
        # any file/dir creation so we can speed up the tests)
        bs.prepare_build_dir()

        # make sure that the open command has been called only once
        mock_open.assert_called_once_with("project.properties", "w")

        # check that the other mocks we made are actually called
        mock_os_makedirs.assert_called()
        mock_shutil_copy.assert_called()
        mock_chdir.assert_called()
Esempio n. 33
0
 def setUp(self):
     self.ctx = Context()
     self.ctx.ndk_api = 21
     self.ctx.android_api = 27
     self.ctx._sdk_dir = "/opt/android/android-sdk"
     self.ctx._ndk_dir = "/opt/android/android-ndk"
     self.ctx.setup_dirs(os.getcwd())
     self.ctx.bootstrap = Bootstrap().get_bootstrap("sdl2", self.ctx)
     self.ctx.bootstrap.distribution = Distribution.get_distribution(
         self.ctx, name="sdl2", recipes=["python3", "kivy"])
     self.ctx.python_recipe = Recipe.get_recipe("python3", self.ctx)
     # Here we define the expected compiler, which, as per ndk >= r19,
     # should be the same for all the tests (no more gcc compiler)
     self.expected_compiler = ("/opt/android/android-ndk/toolchains/"
                               "llvm/prebuilt/linux-x86_64/bin/clang")
Esempio n. 34
0
 def test_save_info(self, mock_open_dist_info, mock_chdir):
     """Test that method
     :meth:`~pythonforandroid.distribution.Distribution.save_info`
     is called once with the proper arguments."""
     self.setUp_distribution_with_bootstrap(Bootstrap().get_bootstrap(
         "sdl2", self.ctx))
     self.ctx.hostpython = "/some/fake/hostpython3"
     self.ctx.python_recipe = Recipe.get_recipe("python3", self.ctx)
     self.ctx.python_modules = ["requests"]
     mock_open_dist_info.side_effect = [
         mock.mock_open(read_data=json.dumps(dist_info_data)).return_value
     ]
     self.ctx.bootstrap.distribution.save_info("/fake_dir")
     mock_open_dist_info.assert_called_once_with("dist_info.json", "w")
     mock_open_dist_info.reset_mock()
Esempio n. 35
0
def test_blacklist():
    # First, get order without blacklist:
    build_order, python_modules, bs = get_recipe_order_and_bootstrap(
        ctx, ["python3", "kivy"], None
    )
    # Now, obtain again with blacklist:
    build_order_2, python_modules_2, bs_2 = get_recipe_order_and_bootstrap(
        ctx, ["python3", "kivy"], None, blacklist=["libffi"]
    )
    assert "libffi" not in build_order_2
    assert set(build_order_2).union({"libffi"}) == set(build_order)

    # Check that we get a conflict when using webview and kivy combined:
    wbootstrap = Bootstrap.get_bootstrap('webview', ctx)
    with pytest.raises(BuildInterruptingException) as e_info:
        get_recipe_order_and_bootstrap(ctx, ["flask", "kivy"], wbootstrap)
    assert "conflict" in e_info.value.message.lower()

    # We should no longer get a conflict blacklisting sdl2 and pygame:
    get_recipe_order_and_bootstrap(
        ctx, ["flask", "kivy"], wbootstrap, blacklist=["sdl2", "pygame"]
    )
Esempio n. 36
0
def build_dist_from_args(ctx, dist, args_list):
    '''Parses out any bootstrap related arguments, and uses them to build
    a dist.'''
    parser = argparse.ArgumentParser(
        description='Create a newAndroid project')
    parser.add_argument(
        '--bootstrap',
        help=('The name of the bootstrap type, \'pygame\' '
              'or \'sdl2\', or leave empty to let a '
              'bootstrap be chosen automatically from your '
              'requirements.'),
        default=None)
    args, unknown = parser.parse_known_args(args_list)

    bs = Bootstrap.get_bootstrap(args.bootstrap, ctx)
    build_order, python_modules, bs \
        = get_recipe_order_and_bootstrap(ctx, dist.recipes, bs)

    info('The selected bootstrap is {}'.format(bs.name))
    info_main('# Creating dist with {} bootstrap'.format(bs.name))
    bs.distribution = dist
    info_notify('Dist will have name {} and recipes ({})'.format(
        dist.name, ', '.join(dist.recipes)))

    ctx.dist_name = bs.distribution.name
    ctx.prepare_bootstrap(bs)
    ctx.prepare_dist(ctx.dist_name)

    build_recipes(build_order, python_modules, ctx)

    ctx.bootstrap.run_distribute()

    info_main('# Your distribution was created successfully, exiting.')
    info('Dist can be found at (for now) {}'
         .format(join(ctx.dist_dir, ctx.dist_name)))

    return unknown
Esempio n. 37
0
def get_recipe_order_and_bootstrap(ctx, names, bs=None, blacklist=None):
    # Get set of recipe/dependency names, clean up and add bootstrap deps:
    names = set(names)
    if bs is not None and bs.recipe_depends:
        names = names.union(set(bs.recipe_depends))
    names = fix_deplist([
        ([name] if not isinstance(name, (list, tuple)) else name)
        for name in names
    ])
    if blacklist is None:
        blacklist = set()
    blacklist = {bitem.lower() for bitem in blacklist}

    # Remove all values that are in the blacklist:
    names_before_blacklist = list(names)
    names = []
    for name in names_before_blacklist:
        cleaned_up_tuple = tuple([
            item for item in name if item not in blacklist
        ])
        if cleaned_up_tuple:
            names.append(cleaned_up_tuple)

    # Do check for obvious conflicts (that would trigger in any order, and
    # without comitting to any specific choice in a multi-choice tuple of
    # dependencies):
    obvious_conflict_checker(ctx, names, blacklist=blacklist)
    # If we get here, no obvious conflicts!

    # get all possible order graphs, as names may include tuples/lists
    # of alternative dependencies
    possible_orders = []
    for name_set in product(*names):
        new_possible_orders = [RecipeOrder(ctx)]
        for name in name_set:
            new_possible_orders = recursively_collect_orders(
                name, ctx, name_set, orders=new_possible_orders,
                blacklist=blacklist
            )
        possible_orders.extend(new_possible_orders)

    # turn each order graph into a linear list if possible
    orders = []
    for possible_order in possible_orders:
        try:
            order = find_order(possible_order)
        except ValueError:  # a circular dependency was found
            info('Circular dependency found in graph {}, skipping it.'.format(
                possible_order))
            continue
        orders.append(list(order))

    # prefer python3 and SDL2 if available
    orders = sorted(orders,
                    key=lambda order: -('python3' in order) - ('sdl2' in order))

    if not orders:
        raise BuildInterruptingException(
            'Didn\'t find any valid dependency graphs. '
            'This means that some of your '
            'requirements pull in conflicting dependencies.')

    # It would be better to check against possible orders other
    # than the first one, but in practice clashes will be rare,
    # and can be resolved by specifying more parameters
    chosen_order = orders[0]
    if len(orders) > 1:
        info('Found multiple valid dependency orders:')
        for order in orders:
            info('    {}'.format(order))
        info('Using the first of these: {}'.format(chosen_order))
    else:
        info('Found a single valid recipe set: {}'.format(chosen_order))

    if bs is None:
        bs = Bootstrap.get_bootstrap_from_recipes(chosen_order, ctx)
        if bs is None:
            # Note: don't remove this without thought, causes infinite loop
            raise BuildInterruptingException(
                "Could not find any compatible bootstrap!"
            )
        recipes, python_modules, bs = get_recipe_order_and_bootstrap(
            ctx, chosen_order, bs=bs, blacklist=blacklist
        )
    else:
        # check if each requirement has a recipe
        recipes = []
        python_modules = []
        for name in chosen_order:
            try:
                recipe = Recipe.get_recipe(name, ctx)
                python_modules += recipe.python_depends
            except ValueError:
                python_modules.append(name)
            else:
                recipes.append(name)

    python_modules = list(set(python_modules))
    return recipes, python_modules, bs
Esempio n. 38
0
    get_recipe_order_and_bootstrap, obvious_conflict_checker,
)
from pythonforandroid.bootstrap import Bootstrap
from pythonforandroid.recipe import Recipe
from pythonforandroid.util import BuildInterruptingException
from itertools import product

import mock
import pytest

ctx = Context()

name_sets = [['python2'],
             ['kivy']]
bootstraps = [None,
              Bootstrap.get_bootstrap('sdl2', ctx)]
valid_combinations = list(product(name_sets, bootstraps))
valid_combinations.extend(
    [(['python3crystax'], Bootstrap.get_bootstrap('sdl2', ctx)),
     (['kivy', 'python3crystax'], Bootstrap.get_bootstrap('sdl2', ctx)),
     (['flask'], Bootstrap.get_bootstrap('webview', ctx)),
     (['pysdl2'], None),  # auto-detect bootstrap! important corner case
    ]
)
invalid_combinations = [
    [['python2', 'python3crystax'], None],
    [['pysdl2', 'genericndkbuild'], None],
]
invalid_combinations_simple = list(invalid_combinations)
# NOTE !! keep in mind when setting invalid_combinations_simple:
#
Esempio n. 39
0
from pythonforandroid.build import Context
from pythonforandroid.graph import get_recipe_order_and_bootstrap
from pythonforandroid.bootstrap import Bootstrap
from itertools import product

import pytest


ctx = Context()

name_sets = [['python2'],
             ['kivy']]
bootstraps = [None,
              Bootstrap.get_bootstrap('pygame', ctx),
              Bootstrap.get_bootstrap('sdl2', ctx)]
valid_combinations = list(product(name_sets, bootstraps))
valid_combinations.extend(
    [(['python3crystax'], Bootstrap.get_bootstrap('sdl2', ctx)),
     (['kivy', 'python3crystax'], Bootstrap.get_bootstrap('sdl2', ctx))])

@pytest.mark.parametrize('names,bootstrap', valid_combinations)
def test_valid_recipe_order_and_bootstrap(names, bootstrap):
    get_recipe_order_and_bootstrap(ctx, names, bootstrap)

invalid_combinations = [[['python2', 'python3crystax'], None],
                        [['python3'], Bootstrap.get_bootstrap('pygame', ctx)]]

@pytest.mark.parametrize('names,bootstrap', invalid_combinations)
def test_invalid_recipe_order_and_bootstrap(names, bootstrap):
    with pytest.raises(SystemExit):
Esempio n. 40
0
def get_recipe_order_and_bootstrap(ctx, names, bs=None):
    '''Takes a list of recipe names and (optionally) a bootstrap. Then
    works out the dependency graph (including bootstrap recipes if
    necessary). Finally, if no bootstrap was initially selected,
    chooses one that supports all the recipes.
    '''
    graph = Graph()
    recipes_to_load = set(names)
    if bs is not None and bs.recipe_depends:
        info_notify('Bootstrap requires recipes {}'.format(bs.recipe_depends))
        recipes_to_load = recipes_to_load.union(set(bs.recipe_depends))
    recipes_to_load = list(recipes_to_load)
    recipe_loaded = []
    python_modules = []
    while recipes_to_load:
        name = recipes_to_load.pop(0)
        if name in recipe_loaded or isinstance(name, (list, tuple)):
            continue
        try:
            recipe = Recipe.get_recipe(name, ctx)
        except IOError:
            info('No recipe named {}; will attempt to install with pip'
                 .format(name))
            python_modules.append(name)
            continue
        except (KeyboardInterrupt, SystemExit):
            raise
        except:
            warning('Failed to import recipe named {}; the recipe exists '
                    'but appears broken.'.format(name))
            warning('Exception was:')
            raise
        graph.add(name, name)
        info('Loaded recipe {} (depends on {}{})'.format(
            name, recipe.depends,
            ', conflicts {}'.format(recipe.conflicts) if recipe.conflicts
            else ''))
        for depend in recipe.depends:
            graph.add(name, depend)
            recipes_to_load += recipe.depends
        for conflict in recipe.conflicts:
            if graph.conflicts(conflict):
                warning(
                    ('{} conflicts with {}, but both have been '
                     'included or pulled into the requirements.'
                     .format(recipe.name, conflict)))
                warning(
                    'Due to this conflict the build cannot continue, exiting.')
                exit(1)
        python_modules += recipe.python_depends
        recipe_loaded.append(name)
    graph.remove_remaining_conflicts(ctx)
    if len(graph.graphs) > 1:
        info('Found multiple valid recipe sets:')
        for g in graph.graphs:
            info('    {}'.format(g.keys()))
        info_notify('Using the first of these: {}'
                    .format(graph.graphs[0].keys()))
    elif len(graph.graphs) == 0:
        warning('Didn\'t find any valid dependency graphs, exiting.')
        exit(1)
    else:
        info('Found a single valid recipe set (this is good)')

    build_order = list(graph.find_order(0))
    if bs is None:  # It would be better to check against possible
                    # orders other than the first one, but in practice
                    # there will rarely be clashes, and the user can
                    # specify more parameters if necessary to resolve
                    # them.
        bs = Bootstrap.get_bootstrap_from_recipes(build_order, ctx)
        if bs is None:
            info('Could not find a bootstrap compatible with the '
                 'required recipes.')
            info('If you think such a combination should exist, try '
                 'specifying the bootstrap manually with --bootstrap.')
            exit(1)
        info('{} bootstrap appears compatible with the required recipes.'
             .format(bs.name))
        info('Checking this...')
        recipes_to_load = bs.recipe_depends
        # This code repeats the code from earlier! Should move to a function:
        while recipes_to_load:
            name = recipes_to_load.pop(0)
            if name in recipe_loaded or isinstance(name, (list, tuple)):
                continue
            try:
                recipe = Recipe.get_recipe(name, ctx)
            except ImportError:
                info('No recipe named {}; will attempt to install with pip'
                     .format(name))
                python_modules.append(name)
                continue
            graph.add(name, name)
            info('Loaded recipe {} (depends on {}{})'.format(
                name, recipe.depends,
                ', conflicts {}'.format(recipe.conflicts) if recipe.conflicts
                else ''))
            for depend in recipe.depends:
                graph.add(name, depend)
                recipes_to_load += recipe.depends
            for conflict in recipe.conflicts:
                if graph.conflicts(conflict):
                    warning(
                        ('{} conflicts with {}, but both have been '
                         'included or pulled into the requirements.'
                         .format(recipe.name, conflict)))
                    warning('Due to this conflict the build cannot continue, '
                            'exiting.')
                    exit(1)
            recipe_loaded.append(name)
        graph.remove_remaining_conflicts(ctx)
        build_order = list(graph.find_order(0))
    return build_order, python_modules, bs
Esempio n. 41
0
from pythonforandroid.build import Context
from pythonforandroid.graph import get_recipe_order_and_bootstrap
from pythonforandroid.bootstrap import Bootstrap
from itertools import product

import pytest


ctx = Context()

name_sets = [['python2'],
             ['kivy']]
bootstraps = [None,
              Bootstrap.get_bootstrap('pygame', ctx),
              Bootstrap.get_bootstrap('sdl2', ctx)]
valid_combinations = list(product(name_sets, bootstraps))
valid_combinations.extend(
    [(['python3crystax'], Bootstrap.get_bootstrap('sdl2', ctx)),
     (['kivy', 'python3crystax'], Bootstrap.get_bootstrap('sdl2', ctx))])
invalid_combinations = [[['python2', 'python3crystax'], None]]


@pytest.mark.parametrize('names,bootstrap', valid_combinations)
def test_valid_recipe_order_and_bootstrap(names, bootstrap):
    get_recipe_order_and_bootstrap(ctx, names, bootstrap)


@pytest.mark.parametrize('names,bootstrap', invalid_combinations)
def test_invalid_recipe_order_and_bootstrap(names, bootstrap):
    with pytest.raises(SystemExit):
        get_recipe_order_and_bootstrap(ctx, names, bootstrap)
Esempio n. 42
0
def get_recipe_order_and_bootstrap(ctx, names, bs=None):
    recipes_to_load = set(names)
    if bs is not None and bs.recipe_depends:
        recipes_to_load = recipes_to_load.union(set(bs.recipe_depends))

    possible_orders = []

    # get all possible order graphs, as names may include tuples/lists
    # of alternative dependencies
    names = [([name] if not isinstance(name, (list, tuple)) else name)
             for name in names]
    for name_set in product(*names):
        new_possible_orders = [RecipeOrder(ctx)]
        for name in name_set:
            new_possible_orders = recursively_collect_orders(
                name, ctx, orders=new_possible_orders)
        possible_orders.extend(new_possible_orders)

    # turn each order graph into a linear list if possible
    orders = []
    for possible_order in possible_orders:
        try:
            order = find_order(possible_order)
        except ValueError:  # a circular dependency was found
            info('Circular dependency found in graph {}, skipping it.'.format(
                possible_order))
            continue
        except:
            warning('Failed to import recipe named {}; the recipe exists '
                    'but appears broken.'.format(name))
            warning('Exception was:')
            raise
        orders.append(list(order))

    # prefer python2 and SDL2 if available
    orders = sorted(orders,
                    key=lambda order: -('python2' in order) - ('sdl2' in order))

    if not orders:
        error('Didn\'t find any valid dependency graphs.')
        error('This means that some of your requirements pull in '
              'conflicting dependencies.')
        error('Exiting.')
        exit(1)
    # It would be better to check against possible orders other
    # than the first one, but in practice clashes will be rare,
    # and can be resolved by specifying more parameters
    chosen_order = orders[0]
    if len(orders) > 1:
        info('Found multiple valid dependency orders:')
        for order in orders:
            info('    {}'.format(order))
        info('Using the first of these: {}'.format(chosen_order))
    else:
        info('Found a single valid recipe set: {}'.format(chosen_order))

    if bs is None:
        bs = Bootstrap.get_bootstrap_from_recipes(chosen_order, ctx)
        recipes, python_modules, bs = get_recipe_order_and_bootstrap(
            ctx, chosen_order, bs=bs)
    else:
        # check if each requirement has a recipe
        recipes = []
        python_modules = []
        for name in chosen_order:
            try:
                recipe = Recipe.get_recipe(name, ctx)
                python_modules += recipe.python_depends
            except IOError:
                python_modules.append(name)
            else:
                recipes.append(name)

    python_modules = list(set(python_modules))
    return recipes, python_modules, bs