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-??-??)
~~~~~~~~~~~~~~~~~~
* 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 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 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>`__)

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(
project_init_cmd,
project_dir=build_dir,
board=board,
project_option=project_option,
boards=board,
project_options=project_option,
)
# process project

View File

@ -18,7 +18,7 @@ import time
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.base import BaseRPCHandler
from platformio.home.rpc.handlers.piocore import PIOCoreRPC
@ -185,83 +185,17 @@ class ProjectRPC(BaseRPCHandler):
async def init(self, board, framework, project_dir):
assert project_dir
state = AppRPC.load_state()
if not os.path.isdir(project_dir):
os.makedirs(project_dir)
args = ["init", "--board", board]
args = ["init", "--board", board, "--sample-code"]
if framework:
args.extend(["--project-option", "framework = %s" % framework])
if (
state["storage"]["coreCaller"]
and state["storage"]["coreCaller"] in ProjectGenerator.get_supported_ides()
):
args.extend(["--ide", state["storage"]["coreCaller"]])
ide = app.get_session_var("caller_id")
if ide in ProjectGenerator.get_supported_ides():
args.extend(["--ide", ide])
await PIOCoreRPC.call(
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
@staticmethod
@ -297,11 +231,9 @@ class ProjectRPC(BaseRPCHandler):
args.extend(
["--project-option", "lib_extra_dirs = ~/Documents/Arduino/libraries"]
)
if (
state["storage"]["coreCaller"]
and state["storage"]["coreCaller"] in ProjectGenerator.get_supported_ides()
):
args.extend(["--ide", state["storage"]["coreCaller"]])
ide = app.get_session_var("caller_id")
if ide in ProjectGenerator.get_supported_ides():
args.extend(["--ide", ide])
await PIOCoreRPC.call(
args, options={"cwd": project_dir, "force_subprocess": True}
)
@ -325,13 +257,10 @@ class ProjectRPC(BaseRPCHandler):
)
shutil.copytree(project_dir, new_project_dir, symlinks=True)
state = AppRPC.load_state()
args = ["init"]
if (
state["storage"]["coreCaller"]
and state["storage"]["coreCaller"] in ProjectGenerator.get_supported_ides()
):
args.extend(["--ide", state["storage"]["coreCaller"]])
ide = app.get_session_var("caller_id")
if ide in ProjectGenerator.get_supported_ides():
args.extend(["--ide", ide])
await PIOCoreRPC.call(
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):
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):
storages = {}
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)
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.manager.platform import PlatformPackageManager
from platformio.platform.exception import UnknownBoard
from platformio.platform.factory import PlatformFactory
from platformio.project.config import ProjectConfig
from platformio.project.helpers import is_platformio_project
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()
for id_ in value:
try:
@ -49,21 +51,31 @@ def validate_boards(ctx, param, value): # pylint: disable=W0613
default=os.getcwd,
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("-e", "--environment", help="Update existing environment")
@click.option("-O", "--project-option", multiple=True)
@click.option("--env-prefix", default="")
@click.option(
"-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("--env-prefix", default="")
@click.option("-s", "--silent", is_flag=True)
def project_init_cmd(
project_dir,
board,
boards,
ide,
environment,
project_option,
env_prefix,
project_options,
sample_code,
no_install_dependencies,
env_prefix,
silent,
):
is_new_project = not is_platformio_project(project_dir)
@ -72,23 +84,23 @@ def project_init_cmd(
print_header(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):
if environment:
update_project_env(environment, project_options)
elif boards:
update_board_envs(project_dir, boards, project_options, env_prefix)
generator = None
config = ProjectConfig.get_instance(os.path.join(project_dir, "platformio.ini"))
if ide:
config.validate()
# 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:
environment = generator.env_name
# resolve project dependencies
if not no_install_dependencies and (environment or board):
if not no_install_dependencies and (environment or boards):
install_project_dependencies(
options=dict(
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 not silent:
click.echo(
@ -104,8 +119,8 @@ def project_init_cmd(
)
generator.generate()
if is_new_project:
init_cvs_ignore(project_dir)
if is_new_project:
init_cvs_ignore()
if not silent:
print_footer(is_new_project)
@ -289,15 +304,15 @@ More information about PlatformIO Unit Testing:
)
def init_cvs_ignore(project_dir):
conf_path = os.path.join(project_dir, ".gitignore")
def init_cvs_ignore():
conf_path = ".gitignore"
if os.path.isfile(conf_path):
return
with open(conf_path, mode="w", encoding="utf8") as fp:
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(
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()
modified = False
for id_ in board_ids:
for id_ in boards:
board_config = pm.board_config(id_)
if id_ in used_boards:
continue
@ -322,7 +337,7 @@ def update_board_envs(project_dir, board_ids, project_option, env_prefix):
if frameworks:
envopts["framework"] = frameworks[0]
for item in project_option:
for item in extra_project_options:
if "=" not in item:
continue
_name, _value = item.split("=", 1)
@ -338,21 +353,74 @@ def update_board_envs(project_dir, board_ids, project_option, env_prefix):
config.save()
def update_project_env(project_dir, environment, project_option):
if not project_option:
def update_project_env(environment, extra_project_options=None):
if not extra_project_options:
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(
os.path.join(project_dir, "platformio.ini"), parse_extra=False
"platformio.ini", parse_extra=False, expand_interpolations=False
)
section = "env:%s" % environment
if not config.has_section(section):
config.add_section(section)
for item in project_option:
if "=" not in item:
continue
_name, _value = item.split("=", 1)
config.set(section, _name.strip(), _value.strip())
for section, options in option_to_sections.items():
if not config.has_section(section):
config.add_section(section)
for name, value in options:
config.set(section, name, value)
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:
def __init__(self, config, env_name, ide, board_ids=None):
def __init__(self, config, env_name, ide, boards=None):
self.config = config
self.project_dir = os.path.dirname(config.path)
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)
def get_best_envname(self, board_ids=None):
def get_best_envname(self, boards=None):
envname = None
default_envs = self.config.default_envs()
if default_envs:
envname = default_envs[0]
if not board_ids:
if not boards:
return envname
for env in self.config.envs():
if not board_ids:
if not boards:
return env
if not envname:
envname = env
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 envname