Introduced a new --sample-code option to the pio project init command

This commit is contained in:
Ivan Kravets
2023-05-03 11:21:49 +03:00
parent 7876626f04
commit d728e0e873
7 changed files with 134 additions and 133 deletions

View File

@ -18,10 +18,11 @@ PlatformIO Core 6
6.1.7 (2023-??-??) 6.1.7 (2023-??-??)
~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~
* Implemented a new feature to store device monitor logs in the project's ``logs`` folder, making it easier to access and review device monitor logs for your projects (`issue #4596 <https://github.com/platformio/platformio-core/issues/4596>`_) * Introduced a new ``--sample-code`` option to the `pio project init <https://docs.platformio.org/en/latest/core/userguide/project/cmd_init.html>`__ command, which allows users to include sample code in the newly created project
* Added validation for `project working environment names <https://docs.platformio.org/en/latest/projectconf/sections/env/index.html#working-env-name>`__ to ensure that they only contain lowercase letters ``a-z``, numbers ``0-9``, and special characters ``_`` (underscore) and ``-`` (hyphen)
* Added the ability to show a detailed library dependency tree only in `verbose mode <https://docs.platformio.org/en/latest/core/userguide/cmd_run.html#cmdoption-pio-run-v>`__, which can help you understand the relationship between libraries and troubleshoot issues more effectively (`issue #4517 <https://github.com/platformio/platformio-core/issues/4517>`_) * Added the ability to show a detailed library dependency tree only in `verbose mode <https://docs.platformio.org/en/latest/core/userguide/cmd_run.html#cmdoption-pio-run-v>`__, which can help you understand the relationship between libraries and troubleshoot issues more effectively (`issue #4517 <https://github.com/platformio/platformio-core/issues/4517>`_)
* Added the ability to run only the `device monitor <https://docs.platformio.org/en/latest/core/userguide/device/cmd_monitor.html>`__ when using the `pio run -t monitor <https://docs.platformio.org/en/latest/core/userguide/cmd_run.html>`__ command, saving you time and resources by skipping the build process * Added the ability to run only the `device monitor <https://docs.platformio.org/en/latest/core/userguide/device/cmd_monitor.html>`__ when using the `pio run -t monitor <https://docs.platformio.org/en/latest/core/userguide/cmd_run.html>`__ command, saving you time and resources by skipping the build process
* Added validation for `project working environment names <https://docs.platformio.org/en/latest/projectconf/sections/env/index.html#working-env-name>`__ to ensure that they only contain lowercase letters ``a-z``, numbers ``0-9``, and special characters ``_`` (underscore) and ``-`` (hyphen) * Implemented a new feature to store device monitor logs in the project's ``logs`` folder, making it easier to access and review device monitor logs for your projects (`issue #4596 <https://github.com/platformio/platformio-core/issues/4596>`_)
* Improved support for projects located on Windows network drives, including Network Shared Folder, Dropbox, OneDrive, Google Drive, and other similar services (`issue #3417 <https://github.com/platformio/platformio-core/issues/3417>`_) * Improved support for projects located on Windows network drives, including Network Shared Folder, Dropbox, OneDrive, Google Drive, and other similar services (`issue #3417 <https://github.com/platformio/platformio-core/issues/3417>`_)
* Improved source file filtering functionality for the `Static Code Analysis <https://docs.platformio.org/en/latest/advanced/static-code-analysis/index.html>`__ feature, making it easier to analyze only the code you need to * Improved source file filtering functionality for the `Static Code Analysis <https://docs.platformio.org/en/latest/advanced/static-code-analysis/index.html>`__ feature, making it easier to analyze only the code you need to
* Upgraded the build engine to the latest version of SCons (4.5.2) to improve build performance, reliability, and compatibility with other tools and systems (`release notes <https://github.com/SCons/scons/releases/tag/4.5.2>`__) * Upgraded the build engine to the latest version of SCons (4.5.2) to improve build performance, reliability, and compatibility with other tools and systems (`release notes <https://github.com/SCons/scons/releases/tag/4.5.2>`__)

2
docs

Submodule docs updated: 54a797fb54...89233bd239

View File

@ -107,8 +107,8 @@ def cli( # pylint: disable=too-many-arguments, too-many-branches
ctx.invoke( ctx.invoke(
project_init_cmd, project_init_cmd,
project_dir=build_dir, project_dir=build_dir,
board=board, boards=board,
project_option=project_option, project_options=project_option,
) )
# process project # process project

View File

@ -18,7 +18,7 @@ import time
from ajsonrpc.core import JSONRPC20DispatchException from ajsonrpc.core import JSONRPC20DispatchException
from platformio import exception, fs from platformio import app, exception, fs
from platformio.home.rpc.handlers.app import AppRPC from platformio.home.rpc.handlers.app import AppRPC
from platformio.home.rpc.handlers.base import BaseRPCHandler from platformio.home.rpc.handlers.base import BaseRPCHandler
from platformio.home.rpc.handlers.piocore import PIOCoreRPC from platformio.home.rpc.handlers.piocore import PIOCoreRPC
@ -185,83 +185,17 @@ class ProjectRPC(BaseRPCHandler):
async def init(self, board, framework, project_dir): async def init(self, board, framework, project_dir):
assert project_dir assert project_dir
state = AppRPC.load_state()
if not os.path.isdir(project_dir): if not os.path.isdir(project_dir):
os.makedirs(project_dir) os.makedirs(project_dir)
args = ["init", "--board", board] args = ["init", "--board", board, "--sample-code"]
if framework: if framework:
args.extend(["--project-option", "framework = %s" % framework]) args.extend(["--project-option", "framework = %s" % framework])
if ( ide = app.get_session_var("caller_id")
state["storage"]["coreCaller"] if ide in ProjectGenerator.get_supported_ides():
and state["storage"]["coreCaller"] in ProjectGenerator.get_supported_ides() args.extend(["--ide", ide])
):
args.extend(["--ide", state["storage"]["coreCaller"]])
await PIOCoreRPC.call( await PIOCoreRPC.call(
args, options={"cwd": project_dir, "force_subprocess": True} args, options={"cwd": project_dir, "force_subprocess": True}
) )
return self._generate_project_main(project_dir, board, framework)
@staticmethod
def _generate_project_main(project_dir, board, framework):
main_content = None
if framework == "arduino":
main_content = "\n".join(
[
"#include <Arduino.h>",
"",
"void setup() {",
" // put your setup code here, to run once:",
"}",
"",
"void loop() {",
" // put your main code here, to run repeatedly:",
"}",
"",
]
)
elif framework == "mbed":
main_content = "\n".join(
[
"#include <mbed.h>",
"",
"int main() {",
"",
" // put your setup code here, to run once:",
"",
" while(1) {",
" // put your main code here, to run repeatedly:",
" }",
"}",
"",
]
)
if not main_content:
return project_dir
is_cpp_project = True
pm = PlatformPackageManager()
try:
board = pm.board_config(board)
platforms = board.get("platforms", board.get("platform"))
if not isinstance(platforms, list):
platforms = [platforms]
c_based_platforms = ["intel_mcs51", "ststm8"]
is_cpp_project = not set(platforms) & set(c_based_platforms)
except exception.PlatformioException:
pass
with fs.cd(project_dir):
config = ProjectConfig()
src_dir = config.get("platformio", "src_dir")
main_path = os.path.join(
src_dir, "main.%s" % ("cpp" if is_cpp_project else "c")
)
if os.path.isfile(main_path):
return project_dir
if not os.path.isdir(src_dir):
os.makedirs(src_dir)
with open(main_path, mode="w", encoding="utf8") as fp:
fp.write(main_content.strip())
return project_dir return project_dir
@staticmethod @staticmethod
@ -297,11 +231,9 @@ class ProjectRPC(BaseRPCHandler):
args.extend( args.extend(
["--project-option", "lib_extra_dirs = ~/Documents/Arduino/libraries"] ["--project-option", "lib_extra_dirs = ~/Documents/Arduino/libraries"]
) )
if ( ide = app.get_session_var("caller_id")
state["storage"]["coreCaller"] if ide in ProjectGenerator.get_supported_ides():
and state["storage"]["coreCaller"] in ProjectGenerator.get_supported_ides() args.extend(["--ide", ide])
):
args.extend(["--ide", state["storage"]["coreCaller"]])
await PIOCoreRPC.call( await PIOCoreRPC.call(
args, options={"cwd": project_dir, "force_subprocess": True} args, options={"cwd": project_dir, "force_subprocess": True}
) )
@ -325,13 +257,10 @@ class ProjectRPC(BaseRPCHandler):
) )
shutil.copytree(project_dir, new_project_dir, symlinks=True) shutil.copytree(project_dir, new_project_dir, symlinks=True)
state = AppRPC.load_state()
args = ["init"] args = ["init"]
if ( ide = app.get_session_var("caller_id")
state["storage"]["coreCaller"] if ide in ProjectGenerator.get_supported_ides():
and state["storage"]["coreCaller"] in ProjectGenerator.get_supported_ides() args.extend(["--ide", ide])
):
args.extend(["--ide", state["storage"]["coreCaller"]])
await PIOCoreRPC.call( await PIOCoreRPC.call(
args, options={"cwd": new_project_dir, "force_subprocess": True} args, options={"cwd": new_project_dir, "force_subprocess": True}
) )

View File

@ -207,6 +207,15 @@ class PlatformBase( # pylint: disable=too-many-instance-attributes,too-many-pub
def configure_debug_session(self, debug_config): def configure_debug_session(self, debug_config):
raise NotImplementedError raise NotImplementedError
def generate_sample_code(self, project_config, environment):
raise NotImplementedError
def on_installed(self):
pass
def on_uninstalled(self):
pass
def get_lib_storages(self): def get_lib_storages(self):
storages = {} storages = {}
for opts in (self.frameworks or {}).values(): for opts in (self.frameworks or {}).values():
@ -227,9 +236,3 @@ class PlatformBase( # pylint: disable=too-many-instance-attributes,too-many-pub
storages[libcore_dir] = "%s-core-%s" % (opts["package"], item) storages[libcore_dir] = "%s-core-%s" % (opts["package"], item)
return [dict(name=name, path=path) for path, name in storages.items()] return [dict(name=name, path=path) for path, name in storages.items()]
def on_installed(self):
pass
def on_uninstalled(self):
pass

View File

@ -24,12 +24,14 @@ from platformio import fs
from platformio.package.commands.install import install_project_dependencies from platformio.package.commands.install import install_project_dependencies
from platformio.package.manager.platform import PlatformPackageManager from platformio.package.manager.platform import PlatformPackageManager
from platformio.platform.exception import UnknownBoard from platformio.platform.exception import UnknownBoard
from platformio.platform.factory import PlatformFactory
from platformio.project.config import ProjectConfig from platformio.project.config import ProjectConfig
from platformio.project.helpers import is_platformio_project from platformio.project.helpers import is_platformio_project
from platformio.project.integration.generator import ProjectGenerator from platformio.project.integration.generator import ProjectGenerator
from platformio.project.options import ProjectOptions
def validate_boards(ctx, param, value): # pylint: disable=W0613 def validate_boards(ctx, param, value): # pylint: disable=unused-argument
pm = PlatformPackageManager() pm = PlatformPackageManager()
for id_ in value: for id_ in value:
try: try:
@ -49,21 +51,31 @@ def validate_boards(ctx, param, value): # pylint: disable=W0613
default=os.getcwd, default=os.getcwd,
type=click.Path(exists=True, file_okay=False, dir_okay=True, writable=True), type=click.Path(exists=True, file_okay=False, dir_okay=True, writable=True),
) )
@click.option("-b", "--board", multiple=True, metavar="ID", callback=validate_boards) @click.option(
"-b", "--board", "boards", multiple=True, metavar="ID", callback=validate_boards
)
@click.option("--ide", type=click.Choice(ProjectGenerator.get_supported_ides())) @click.option("--ide", type=click.Choice(ProjectGenerator.get_supported_ides()))
@click.option("-e", "--environment", help="Update existing environment") @click.option("-e", "--environment", help="Update existing environment")
@click.option("-O", "--project-option", multiple=True) @click.option(
@click.option("--env-prefix", default="") "-O",
"--project-option",
"project_options",
multiple=True,
help="A `name=value` pair",
)
@click.option("--sample-code", is_flag=True)
@click.option("--no-install-dependencies", is_flag=True) @click.option("--no-install-dependencies", is_flag=True)
@click.option("--env-prefix", default="")
@click.option("-s", "--silent", is_flag=True) @click.option("-s", "--silent", is_flag=True)
def project_init_cmd( def project_init_cmd(
project_dir, project_dir,
board, boards,
ide, ide,
environment, environment,
project_option, project_options,
env_prefix, sample_code,
no_install_dependencies, no_install_dependencies,
env_prefix,
silent, silent,
): ):
is_new_project = not is_platformio_project(project_dir) is_new_project = not is_platformio_project(project_dir)
@ -72,23 +84,23 @@ def project_init_cmd(
print_header(project_dir) print_header(project_dir)
init_base_project(project_dir) init_base_project(project_dir)
if environment:
update_project_env(project_dir, environment, project_option)
elif board:
update_board_envs(project_dir, board, project_option, env_prefix)
with fs.cd(project_dir): with fs.cd(project_dir):
if environment:
update_project_env(environment, project_options)
elif boards:
update_board_envs(project_dir, boards, project_options, env_prefix)
generator = None generator = None
config = ProjectConfig.get_instance(os.path.join(project_dir, "platformio.ini")) config = ProjectConfig.get_instance(os.path.join(project_dir, "platformio.ini"))
if ide: if ide:
config.validate() config.validate()
# init generator and pick the best env if user didn't specify # init generator and pick the best env if user didn't specify
generator = ProjectGenerator(config, environment, ide, board) generator = ProjectGenerator(config, environment, ide, boards)
if not environment: if not environment:
environment = generator.env_name environment = generator.env_name
# resolve project dependencies # resolve project dependencies
if not no_install_dependencies and (environment or board): if not no_install_dependencies and (environment or boards):
install_project_dependencies( install_project_dependencies(
options=dict( options=dict(
project_dir=project_dir, project_dir=project_dir,
@ -97,6 +109,9 @@ def project_init_cmd(
) )
) )
if environment and sample_code:
init_sample_code(config, environment)
if generator: if generator:
if not silent: if not silent:
click.echo( click.echo(
@ -104,8 +119,8 @@ def project_init_cmd(
) )
generator.generate() generator.generate()
if is_new_project: if is_new_project:
init_cvs_ignore(project_dir) init_cvs_ignore()
if not silent: if not silent:
print_footer(is_new_project) print_footer(is_new_project)
@ -289,15 +304,15 @@ More information about PlatformIO Unit Testing:
) )
def init_cvs_ignore(project_dir): def init_cvs_ignore():
conf_path = os.path.join(project_dir, ".gitignore") conf_path = ".gitignore"
if os.path.isfile(conf_path): if os.path.isfile(conf_path):
return return
with open(conf_path, mode="w", encoding="utf8") as fp: with open(conf_path, mode="w", encoding="utf8") as fp:
fp.write(".pio\n") fp.write(".pio\n")
def update_board_envs(project_dir, board_ids, project_option, env_prefix): def update_board_envs(project_dir, boards, extra_project_options, env_prefix):
config = ProjectConfig( config = ProjectConfig(
os.path.join(project_dir, "platformio.ini"), parse_extra=False os.path.join(project_dir, "platformio.ini"), parse_extra=False
) )
@ -309,7 +324,7 @@ def update_board_envs(project_dir, board_ids, project_option, env_prefix):
pm = PlatformPackageManager() pm = PlatformPackageManager()
modified = False modified = False
for id_ in board_ids: for id_ in boards:
board_config = pm.board_config(id_) board_config = pm.board_config(id_)
if id_ in used_boards: if id_ in used_boards:
continue continue
@ -322,7 +337,7 @@ def update_board_envs(project_dir, board_ids, project_option, env_prefix):
if frameworks: if frameworks:
envopts["framework"] = frameworks[0] envopts["framework"] = frameworks[0]
for item in project_option: for item in extra_project_options:
if "=" not in item: if "=" not in item:
continue continue
_name, _value = item.split("=", 1) _name, _value = item.split("=", 1)
@ -338,21 +353,74 @@ def update_board_envs(project_dir, board_ids, project_option, env_prefix):
config.save() config.save()
def update_project_env(project_dir, environment, project_option): def update_project_env(environment, extra_project_options=None):
if not project_option: if not extra_project_options:
return return
env_section = "env:%s" % environment
option_to_sections = {"platformio": [], env_section: []}
for item in extra_project_options:
assert "=" in item
name, value = item.split("=", 1)
name = name.strip()
destination = env_section
for option in ProjectOptions.values():
if option.scope in option_to_sections and option.name == name:
destination = option.scope
break
option_to_sections[destination].append((name, value.strip()))
config = ProjectConfig( config = ProjectConfig(
os.path.join(project_dir, "platformio.ini"), parse_extra=False "platformio.ini", parse_extra=False, expand_interpolations=False
) )
for section, options in option_to_sections.items():
section = "env:%s" % environment if not config.has_section(section):
if not config.has_section(section): config.add_section(section)
config.add_section(section) for name, value in options:
config.set(section, name, value)
for item in project_option:
if "=" not in item:
continue
_name, _value = item.split("=", 1)
config.set(section, _name.strip(), _value.strip())
config.save() config.save()
def init_sample_code(config, environment):
platform_spec = config.get(f"env:{environment}", "platform", None)
if not platform_spec:
return None
p = PlatformFactory.new(platform_spec)
try:
return p.generate_sample_code(config, environment)
except NotImplementedError:
pass
framework = config.get(f"env:{environment}", "framework", None)
if framework != ["arduino"]:
return None
main_content = """
#include <Arduino.h>
// put function declarations here:
int myFunction(int, int);
void setup() {
// put your setup code here, to run once:
int result = myFunction(2, 3);
}
void loop() {
// put your main code here, to run repeatedly:
}
// put function definitions here:
int myFunction(int x, int y) {
return x + y;
}
"""
is_cpp_project = p.name not in ["intel_mcs51", "ststm8"]
src_dir = config.get("platformio", "src_dir")
main_path = os.path.join(src_dir, "main.%s" % ("cpp" if is_cpp_project else "c"))
if os.path.isfile(main_path):
return None
if not os.path.isdir(src_dir):
os.makedirs(src_dir)
with open(main_path, mode="w", encoding="utf8") as fp:
fp.write(main_content.strip())
return True

View File

@ -25,28 +25,28 @@ from platformio.project.helpers import load_build_metadata
class ProjectGenerator: class ProjectGenerator:
def __init__(self, config, env_name, ide, board_ids=None): def __init__(self, config, env_name, ide, boards=None):
self.config = config self.config = config
self.project_dir = os.path.dirname(config.path) self.project_dir = os.path.dirname(config.path)
self.forced_env_name = env_name self.forced_env_name = env_name
self.env_name = str(env_name or self.get_best_envname(board_ids)) self.env_name = str(env_name or self.get_best_envname(boards))
self.ide = str(ide) self.ide = str(ide)
def get_best_envname(self, board_ids=None): def get_best_envname(self, boards=None):
envname = None envname = None
default_envs = self.config.default_envs() default_envs = self.config.default_envs()
if default_envs: if default_envs:
envname = default_envs[0] envname = default_envs[0]
if not board_ids: if not boards:
return envname return envname
for env in self.config.envs(): for env in self.config.envs():
if not board_ids: if not boards:
return env return env
if not envname: if not envname:
envname = env envname = env
items = self.config.items(env=env, as_dict=True) items = self.config.items(env=env, as_dict=True)
if "board" in items and items.get("board") in board_ids: if "board" in items and items.get("board") in boards:
return env return env
return envname return envname