From 73ce3c94e97a534c3b04080261d4593aa5603c28 Mon Sep 17 00:00:00 2001 From: Ivan Kravets Date: Thu, 6 Feb 2020 23:32:43 +0200 Subject: [PATCH] Initial support for `Project Manager // Resolve #3335 --- HISTORY.rst | 7 +- docs | 2 +- platformio/commands/__init__.py | 13 ++++ platformio/commands/ci.py | 9 ++- platformio/commands/{init.py => project.py} | 76 ++++++++++++++++----- platformio/project/exception.py | 2 +- tests/commands/test_init.py | 2 +- 7 files changed, 88 insertions(+), 23 deletions(-) rename platformio/commands/{init.py => project.py} (85%) diff --git a/HISTORY.rst b/HISTORY.rst index b6f4354a..e0eb61f6 100644 --- a/HISTORY.rst +++ b/HISTORY.rst @@ -6,7 +6,7 @@ Release Notes PlatformIO Core 4.0 ------------------- -4.1.1 (2020-??-??) +4.2.0 (2020-02-??) ~~~~~~~~~~~~~~~~~~ * `PlatformIO Home 3.0 `__: @@ -18,6 +18,11 @@ PlatformIO Core 4.0 - Added support for `PVS-Studio `__ static code analyzer +* Initial support for `Project Manager `_ CLI: + + - Show computed project configuration with a new `platformio project config `_ command or dump to JSON with ``platformio project config --json-output`` (`issue #3335 `_) + - Moved ``platformio init`` command to `platformio project init `_ + * Generate `compilation database "compile_commands.json" `_ (`issue #2990 `_) * Control debug flags and optimization level with a new `debug_build_flags `__ option * Install a dev-platform with ALL declared packages using a new ``--with-all-packages`` option for `pio platform install `__ command (`issue #3345 `_) diff --git a/docs b/docs index ec5a17c3..506491d1 160000 --- a/docs +++ b/docs @@ -1 +1 @@ -Subproject commit ec5a17c390117b91c9f9b22d385ae535f3003964 +Subproject commit 506491d11c12c8d2c0a81729282e8c54f1877b68 diff --git a/platformio/commands/__init__.py b/platformio/commands/__init__.py index bc018f8c..f6bac830 100644 --- a/platformio/commands/__init__.py +++ b/platformio/commands/__init__.py @@ -63,5 +63,18 @@ class PlatformioCLI(click.MultiCommand): mod_path = "platformio.commands.%s.command" % cmd_name mod = __import__(mod_path, None, None, ["cli"]) except ImportError: + try: + return self._handle_obsolate_command(cmd_name) + except AttributeError: + pass raise click.UsageError('No such command "%s"' % cmd_name, ctx) return mod.cli + + @staticmethod + def _handle_obsolate_command(name): + # pylint: disable=import-outside-toplevel + if name == "init": + from platformio.commands.project import project_init + + return project_init + raise AttributeError() diff --git a/platformio/commands/ci.py b/platformio/commands/ci.py index 7c57f465..9a48f262 100644 --- a/platformio/commands/ci.py +++ b/platformio/commands/ci.py @@ -21,8 +21,8 @@ from tempfile import mkdtemp import click from platformio import app, fs -from platformio.commands.init import cli as cmd_init -from platformio.commands.init import validate_boards +from platformio.commands.project import project_init as cmd_project_init +from platformio.commands.project import validate_boards from platformio.commands.run.command import cli as cmd_run from platformio.compat import glob_escape from platformio.exception import CIBuildEnvsEmpty @@ -111,7 +111,10 @@ def cli( # pylint: disable=too-many-arguments, too-many-branches # initialise project ctx.invoke( - cmd_init, project_dir=build_dir, board=board, project_option=project_option + cmd_project_init, + project_dir=build_dir, + board=board, + project_option=project_option, ) # process project diff --git a/platformio/commands/init.py b/platformio/commands/project.py similarity index 85% rename from platformio/commands/init.py rename to platformio/commands/project.py index e37871c9..33d35006 100644 --- a/platformio/commands/init.py +++ b/platformio/commands/project.py @@ -14,19 +14,61 @@ # pylint: disable=too-many-arguments,too-many-locals, too-many-branches -from os import getcwd, makedirs -from os.path import isdir, isfile, join +import os import click +from tabulate import tabulate from platformio import exception, fs from platformio.commands.platform import platform_install as cli_platform_install from platformio.ide.projectgenerator import ProjectGenerator from platformio.managers.platform import PlatformManager from platformio.project.config import ProjectConfig +from platformio.project.exception import NotPlatformIOProjectError from platformio.project.helpers import is_platformio_project +@click.group(short_help="Project Manager") +def cli(): + pass + + +@cli.command("config", short_help="Show computed configuration") +@click.option( + "-d", + "--project-dir", + default=os.getcwd, + type=click.Path( + exists=True, file_okay=True, dir_okay=True, writable=True, resolve_path=True + ), +) +@click.option("--json-output", is_flag=True) +def project_config(project_dir, json_output): + if not is_platformio_project(project_dir): + raise NotPlatformIOProjectError(project_dir) + with fs.cd(project_dir): + config = ProjectConfig.get_instance() + if json_output: + return click.echo(config.to_json()) + click.echo( + "Computed project configuration for %s" % click.style(project_dir, fg="cyan") + ) + for section, options in config.as_tuple(): + click.echo() + click.secho(section, fg="cyan") + click.echo("-" * len(section)) + click.echo( + tabulate( + [ + (name, "=", "\n".join(value) if isinstance(value, list) else value) + for name, value in options + ], + tablefmt="plain", + ) + ) + return None + + def validate_boards(ctx, param, value): # pylint: disable=W0613 pm = PlatformManager() for id_ in value: @@ -40,11 +82,11 @@ def validate_boards(ctx, param, value): # pylint: disable=W0613 return value -@click.command("init", short_help="Initialize PlatformIO project or update existing") +@cli.command("init", short_help="Initialize a project or update existing") @click.option( "--project-dir", "-d", - default=getcwd, + default=os.getcwd, type=click.Path( exists=True, file_okay=False, dir_okay=True, writable=True, resolve_path=True ), @@ -55,7 +97,7 @@ def validate_boards(ctx, param, value): # pylint: disable=W0613 @click.option("--env-prefix", default="") @click.option("-s", "--silent", is_flag=True) @click.pass_context -def cli( +def project_init( ctx, # pylint: disable=R0913 project_dir, board, @@ -65,7 +107,7 @@ def cli( silent, ): if not silent: - if project_dir == getcwd(): + if project_dir == os.getcwd(): click.secho("\nThe current working directory", fg="yellow", nl=False) click.secho(" %s " % project_dir, fg="cyan", nl=False) click.secho("will be used for the project.", fg="yellow") @@ -137,16 +179,16 @@ def init_base_project(project_dir): (config.get_optional_dir("test"), init_test_readme), ] for (path, cb) in dir_to_readme: - if isdir(path): + if os.path.isdir(path): continue - makedirs(path) + os.makedirs(path) if cb: cb(path) def init_include_readme(include_dir): fs.write_file_contents( - join(include_dir, "README"), + os.path.join(include_dir, "README"), """ This directory is intended for project header files. @@ -193,7 +235,7 @@ https://gcc.gnu.org/onlinedocs/cpp/Header-Files.html def init_lib_readme(lib_dir): # pylint: disable=line-too-long fs.write_file_contents( - join(lib_dir, "README"), + os.path.join(lib_dir, "README"), """ This directory is intended for project specific (private) libraries. PlatformIO will compile them to static libraries and link into executable file. @@ -246,7 +288,7 @@ More information about PlatformIO Library Dependency Finder def init_test_readme(test_dir): fs.write_file_contents( - join(test_dir, "README"), + os.path.join(test_dir, "README"), """ This directory is intended for PIO Unit Testing and project tests. @@ -263,8 +305,8 @@ More information about PIO Unit Testing: def init_ci_conf(project_dir): - conf_path = join(project_dir, ".travis.yml") - if isfile(conf_path): + conf_path = os.path.join(project_dir, ".travis.yml") + if os.path.isfile(conf_path): return fs.write_file_contents( conf_path, @@ -340,8 +382,8 @@ def init_ci_conf(project_dir): def init_cvs_ignore(project_dir): - conf_path = join(project_dir, ".gitignore") - if isfile(conf_path): + conf_path = os.path.join(project_dir, ".gitignore") + if os.path.isfile(conf_path): return fs.write_file_contents(conf_path, ".pio\n") @@ -349,7 +391,9 @@ def init_cvs_ignore(project_dir): def fill_project_envs( ctx, project_dir, board_ids, project_option, env_prefix, force_download ): - config = ProjectConfig(join(project_dir, "platformio.ini"), parse_extra=False) + config = ProjectConfig( + os.path.join(project_dir, "platformio.ini"), parse_extra=False + ) used_boards = [] for section in config.sections(): cond = [section.startswith("env:"), config.has_option(section, "board")] diff --git a/platformio/project/exception.py b/platformio/project/exception.py index c2d7fd09..aa45eb07 100644 --- a/platformio/project/exception.py +++ b/platformio/project/exception.py @@ -24,7 +24,7 @@ class NotPlatformIOProjectError(ProjectError, UserSideException): MESSAGE = ( "Not a PlatformIO project. `platformio.ini` file has not been " "found in current working directory ({0}). To initialize new project " - "please use `platformio init` command" + "please use `platformio project init` command" ) diff --git a/tests/commands/test_init.py b/tests/commands/test_init.py index 5e0172a7..fccdee7d 100644 --- a/tests/commands/test_init.py +++ b/tests/commands/test_init.py @@ -17,7 +17,7 @@ from os import getcwd, makedirs from os.path import getsize, isdir, isfile, join from platformio.commands.boards import cli as cmd_boards -from platformio.commands.init import cli as cmd_init +from platformio.commands.project import project_init as cmd_init from platformio.project.config import ProjectConfig from platformio.project.exception import ProjectEnvsNotAvailableError