mirror of
https://github.com/platformio/platformio-core.git
synced 2025-07-29 17:47:14 +02:00
Resolve project dependencies with pio project init
command
This commit is contained in:
@ -87,6 +87,7 @@ Please check the `Migration guide from 5.x to 6.0 <https://docs.platformio.org/e
|
||||
|
||||
- Added a new build variable (``COMPILATIONDB_INCLUDE_TOOLCHAIN``) to include toolchain paths in the compilation database (`issue #3735 <https://github.com/platformio/platformio-core/issues/3735>`_)
|
||||
- Changed a default path for compilation database `compile_commands.json <https://docs.platformio.org/en/latest/integration/compile_commands.html>`__ to the project root
|
||||
- Enhanced integration for Qt Creator (`issue #3046 <https://github.com/platformio/platformio-core/issues/3046>`_)
|
||||
|
||||
* **Project Configuration**
|
||||
|
||||
|
2
docs
2
docs
Submodule docs updated: a9aad82cb9...def7ca7a23
@ -21,7 +21,7 @@ import os
|
||||
import click
|
||||
|
||||
from platformio import fs
|
||||
from platformio.commands.platform import platform_install as cli_platform_install
|
||||
from platformio.package.commands.install import install_project_dependencies
|
||||
from platformio.package.manager.platform import PlatformPackageManager
|
||||
from platformio.platform.exception import UnknownBoard
|
||||
from platformio.project.config import ProjectConfig
|
||||
@ -53,61 +53,47 @@ def validate_boards(ctx, param, value): # pylint: disable=W0613
|
||||
)
|
||||
@click.option("-b", "--board", multiple=True, metavar="ID", callback=validate_boards)
|
||||
@click.option("--ide", type=click.Choice(ProjectGenerator.get_supported_ides()))
|
||||
@click.option("-e", "--environment", help="Update using existing environment")
|
||||
@click.option("-e", "--environment", help="Update existing environment")
|
||||
@click.option("-O", "--project-option", multiple=True)
|
||||
@click.option("--env-prefix", default="")
|
||||
@click.option("--no-install-dependencies", is_flag=True)
|
||||
@click.option("-s", "--silent", is_flag=True)
|
||||
@click.pass_context
|
||||
def project_init_cmd(
|
||||
ctx,
|
||||
project_dir,
|
||||
board,
|
||||
ide,
|
||||
environment,
|
||||
project_option,
|
||||
env_prefix,
|
||||
no_install_dependencies,
|
||||
silent,
|
||||
):
|
||||
is_new_project = not is_platformio_project(project_dir)
|
||||
if not silent and is_new_project:
|
||||
if project_dir == os.getcwd():
|
||||
click.secho("\nThe current working directory ", fg="yellow", nl=False)
|
||||
try:
|
||||
click.secho(project_dir, fg="cyan", nl=False)
|
||||
except UnicodeEncodeError:
|
||||
click.secho(json.dumps(project_dir), fg="cyan", nl=False)
|
||||
click.secho(" will be used for the project.", fg="yellow")
|
||||
click.echo("")
|
||||
|
||||
click.echo("The next files/directories have been created in ", nl=False)
|
||||
try:
|
||||
click.secho(project_dir, fg="cyan")
|
||||
except UnicodeEncodeError:
|
||||
click.secho(json.dumps(project_dir), fg="cyan")
|
||||
click.echo(
|
||||
"%s - Put project header files here" % click.style("include", fg="cyan")
|
||||
)
|
||||
click.echo(
|
||||
"%s - Put here project specific (private) libraries"
|
||||
% click.style("lib", fg="cyan")
|
||||
)
|
||||
click.echo("%s - Put project source files here" % click.style("src", fg="cyan"))
|
||||
click.echo(
|
||||
"%s - Project Configuration File" % click.style("platformio.ini", fg="cyan")
|
||||
)
|
||||
|
||||
if is_new_project:
|
||||
if not silent:
|
||||
print_header(project_dir)
|
||||
init_base_project(project_dir)
|
||||
|
||||
if environment:
|
||||
update_project_env(project_dir, environment, project_option)
|
||||
elif board:
|
||||
update_board_envs(
|
||||
ctx, project_dir, board, project_option, env_prefix, ide is not None
|
||||
update_board_envs(project_dir, board, project_option, env_prefix)
|
||||
|
||||
# resolve project dependencies
|
||||
if not no_install_dependencies and (environment or board):
|
||||
install_project_dependencies(
|
||||
options=dict(
|
||||
project_dir=project_dir,
|
||||
environments=[environment] if environment else [],
|
||||
silent=silent,
|
||||
)
|
||||
)
|
||||
|
||||
if ide:
|
||||
click.echo("Updating metadata for the %s IDE..." % click.style(ide, fg="cyan"))
|
||||
if not silent:
|
||||
click.echo(
|
||||
"Updating metadata for the %s IDE..." % click.style(ide, fg="cyan")
|
||||
)
|
||||
with fs.cd(project_dir):
|
||||
config = ProjectConfig.get_instance(
|
||||
os.path.join(project_dir, "platformio.ini")
|
||||
@ -118,11 +104,39 @@ def project_init_cmd(
|
||||
if is_new_project:
|
||||
init_cvs_ignore(project_dir)
|
||||
|
||||
if silent:
|
||||
return
|
||||
if not silent:
|
||||
print_footer(is_new_project)
|
||||
|
||||
|
||||
def print_header(project_dir):
|
||||
if project_dir == os.getcwd():
|
||||
click.secho("\nThe current working directory ", fg="yellow", nl=False)
|
||||
try:
|
||||
click.secho(project_dir, fg="cyan", nl=False)
|
||||
except UnicodeEncodeError:
|
||||
click.secho(json.dumps(project_dir), fg="cyan", nl=False)
|
||||
click.secho(" will be used for the project.", fg="yellow")
|
||||
click.echo("")
|
||||
|
||||
click.echo("The next files/directories have been created in ", nl=False)
|
||||
try:
|
||||
click.secho(project_dir, fg="cyan")
|
||||
except UnicodeEncodeError:
|
||||
click.secho(json.dumps(project_dir), fg="cyan")
|
||||
click.echo("%s - Put project header files here" % click.style("include", fg="cyan"))
|
||||
click.echo(
|
||||
"%s - Put here project specific (private) libraries"
|
||||
% click.style("lib", fg="cyan")
|
||||
)
|
||||
click.echo("%s - Put project source files here" % click.style("src", fg="cyan"))
|
||||
click.echo(
|
||||
"%s - Project Configuration File" % click.style("platformio.ini", fg="cyan")
|
||||
)
|
||||
|
||||
|
||||
def print_footer(is_new_project):
|
||||
if is_new_project:
|
||||
click.secho(
|
||||
return click.secho(
|
||||
"\nProject has been successfully initialized! Useful commands:\n"
|
||||
"`pio run` - process/build project from the current directory\n"
|
||||
"`pio run --target upload` or `pio run -t upload` "
|
||||
@ -131,11 +145,10 @@ def project_init_cmd(
|
||||
"\n`pio run --help` - additional information",
|
||||
fg="green",
|
||||
)
|
||||
else:
|
||||
click.secho(
|
||||
"Project has been successfully updated!",
|
||||
fg="green",
|
||||
)
|
||||
return click.secho(
|
||||
"Project has been successfully updated!",
|
||||
fg="green",
|
||||
)
|
||||
|
||||
|
||||
def init_base_project(project_dir):
|
||||
@ -281,9 +294,7 @@ def init_cvs_ignore(project_dir):
|
||||
fp.write(".pio\n")
|
||||
|
||||
|
||||
def update_board_envs(
|
||||
ctx, project_dir, board_ids, project_option, env_prefix, force_download
|
||||
):
|
||||
def update_board_envs(project_dir, board_ids, project_option, env_prefix):
|
||||
config = ProjectConfig(
|
||||
os.path.join(project_dir, "platformio.ini"), parse_extra=False
|
||||
)
|
||||
@ -294,11 +305,9 @@ def update_board_envs(
|
||||
used_boards.append(config.get(section, "board"))
|
||||
|
||||
pm = PlatformPackageManager()
|
||||
used_platforms = []
|
||||
modified = False
|
||||
for id_ in board_ids:
|
||||
board_config = pm.board_config(id_)
|
||||
used_platforms.append(board_config["platform"])
|
||||
if id_ in used_boards:
|
||||
continue
|
||||
used_boards.append(id_)
|
||||
@ -322,24 +331,10 @@ def update_board_envs(
|
||||
for option, value in envopts.items():
|
||||
config.set(section, option, value)
|
||||
|
||||
if force_download and used_platforms:
|
||||
_install_dependent_platforms(ctx, used_platforms)
|
||||
|
||||
if modified:
|
||||
config.save()
|
||||
|
||||
|
||||
def _install_dependent_platforms(ctx, platforms):
|
||||
installed_platforms = [
|
||||
pkg.metadata.name for pkg in PlatformPackageManager().get_installed()
|
||||
]
|
||||
if set(platforms) <= set(installed_platforms):
|
||||
return
|
||||
ctx.invoke(
|
||||
cli_platform_install, platforms=list(set(platforms) - set(installed_platforms))
|
||||
)
|
||||
|
||||
|
||||
def update_project_env(project_dir, environment, project_option):
|
||||
if not project_option:
|
||||
return
|
||||
|
@ -13,48 +13,49 @@
|
||||
# limitations under the License.
|
||||
|
||||
import json
|
||||
from os import getcwd, makedirs
|
||||
from os.path import getsize, isdir, isfile, join
|
||||
import os
|
||||
|
||||
import pytest
|
||||
|
||||
from platformio import proc
|
||||
from platformio.commands import platform as cli_platform
|
||||
from platformio.commands.boards import cli as cmd_boards
|
||||
from platformio.package.commands.exec import package_exec_cmd
|
||||
from platformio.project.commands.init import project_init_cmd
|
||||
from platformio.project.config import ProjectConfig
|
||||
from platformio.project.exception import ProjectEnvsNotAvailableError
|
||||
|
||||
|
||||
def validate_pioproject(pioproject_dir):
|
||||
pioconf_path = join(pioproject_dir, "platformio.ini")
|
||||
assert isfile(pioconf_path) and getsize(pioconf_path) > 0
|
||||
assert isdir(join(pioproject_dir, "src")) and isdir(join(pioproject_dir, "lib"))
|
||||
pioconf_path = os.path.join(pioproject_dir, "platformio.ini")
|
||||
assert os.path.isfile(pioconf_path) and os.path.getsize(pioconf_path) > 0
|
||||
assert os.path.isdir(os.path.join(pioproject_dir, "src")) and os.path.isdir(
|
||||
os.path.join(pioproject_dir, "lib")
|
||||
)
|
||||
|
||||
|
||||
def test_init_default(clirunner, validate_cliresult):
|
||||
with clirunner.isolated_filesystem():
|
||||
result = clirunner.invoke(project_init_cmd)
|
||||
validate_cliresult(result)
|
||||
validate_pioproject(getcwd())
|
||||
validate_pioproject(os.getcwd())
|
||||
|
||||
|
||||
def test_init_ext_folder(clirunner, validate_cliresult):
|
||||
with clirunner.isolated_filesystem():
|
||||
ext_folder_name = "ext_folder"
|
||||
makedirs(ext_folder_name)
|
||||
os.makedirs(ext_folder_name)
|
||||
result = clirunner.invoke(project_init_cmd, ["-d", ext_folder_name])
|
||||
validate_cliresult(result)
|
||||
validate_pioproject(join(getcwd(), ext_folder_name))
|
||||
validate_pioproject(os.path.join(os.getcwd(), ext_folder_name))
|
||||
|
||||
|
||||
def test_init_duplicated_boards(clirunner, validate_cliresult, tmpdir):
|
||||
with tmpdir.as_cwd():
|
||||
for _ in range(2):
|
||||
result = clirunner.invoke(project_init_cmd, ["-b", "uno", "-b", "uno"])
|
||||
result = clirunner.invoke(
|
||||
project_init_cmd,
|
||||
["-b", "uno", "-b", "uno", "--no-install-dependencies"],
|
||||
)
|
||||
validate_cliresult(result)
|
||||
validate_pioproject(str(tmpdir))
|
||||
config = ProjectConfig(join(getcwd(), "platformio.ini"))
|
||||
config = ProjectConfig(os.path.join(os.getcwd(), "platformio.ini"))
|
||||
config.validate()
|
||||
assert set(config.sections()) == set(["env:uno"])
|
||||
|
||||
@ -69,7 +70,16 @@ def test_init_ide_without_board(clirunner, tmpdir):
|
||||
def test_init_ide_vscode(clirunner, validate_cliresult, tmpdir):
|
||||
with tmpdir.as_cwd():
|
||||
result = clirunner.invoke(
|
||||
project_init_cmd, ["--ide", "vscode", "-b", "uno", "-b", "teensy31"]
|
||||
project_init_cmd,
|
||||
[
|
||||
"--ide",
|
||||
"vscode",
|
||||
"-b",
|
||||
"uno",
|
||||
"-b",
|
||||
"teensy31",
|
||||
"--no-install-dependencies",
|
||||
],
|
||||
)
|
||||
validate_cliresult(result)
|
||||
validate_pioproject(str(tmpdir))
|
||||
@ -84,7 +94,8 @@ def test_init_ide_vscode(clirunner, validate_cliresult, tmpdir):
|
||||
|
||||
# switch to NodeMCU
|
||||
result = clirunner.invoke(
|
||||
project_init_cmd, ["--ide", "vscode", "-b", "nodemcuv2"]
|
||||
project_init_cmd,
|
||||
["--ide", "vscode", "-b", "nodemcuv2", "--no-install-dependencies"],
|
||||
)
|
||||
validate_cliresult(result)
|
||||
validate_pioproject(str(tmpdir))
|
||||
@ -95,7 +106,8 @@ def test_init_ide_vscode(clirunner, validate_cliresult, tmpdir):
|
||||
|
||||
# switch to teensy31 via env name
|
||||
result = clirunner.invoke(
|
||||
project_init_cmd, ["--ide", "vscode", "-e", "teensy31"]
|
||||
project_init_cmd,
|
||||
["--ide", "vscode", "-e", "teensy31", "--no-install-dependencies"],
|
||||
)
|
||||
validate_cliresult(result)
|
||||
validate_pioproject(str(tmpdir))
|
||||
@ -105,7 +117,9 @@ def test_init_ide_vscode(clirunner, validate_cliresult, tmpdir):
|
||||
)
|
||||
|
||||
# switch to the first board
|
||||
result = clirunner.invoke(project_init_cmd, ["--ide", "vscode"])
|
||||
result = clirunner.invoke(
|
||||
project_init_cmd, ["--ide", "vscode", "--no-install-dependencies"]
|
||||
)
|
||||
validate_cliresult(result)
|
||||
validate_pioproject(str(tmpdir))
|
||||
assert (
|
||||
@ -116,23 +130,26 @@ def test_init_ide_vscode(clirunner, validate_cliresult, tmpdir):
|
||||
|
||||
def test_init_ide_eclipse(clirunner, validate_cliresult):
|
||||
with clirunner.isolated_filesystem():
|
||||
result = clirunner.invoke(project_init_cmd, ["-b", "uno", "--ide", "eclipse"])
|
||||
result = clirunner.invoke(
|
||||
project_init_cmd,
|
||||
["-b", "uno", "--ide", "eclipse", "--no-install-dependencies"],
|
||||
)
|
||||
validate_cliresult(result)
|
||||
validate_pioproject(getcwd())
|
||||
assert all(isfile(f) for f in (".cproject", ".project"))
|
||||
validate_pioproject(os.getcwd())
|
||||
assert all(os.path.isfile(f) for f in (".cproject", ".project"))
|
||||
|
||||
|
||||
def test_init_special_board(clirunner, validate_cliresult):
|
||||
with clirunner.isolated_filesystem():
|
||||
result = clirunner.invoke(project_init_cmd, ["-b", "uno"])
|
||||
validate_cliresult(result)
|
||||
validate_pioproject(getcwd())
|
||||
validate_pioproject(os.getcwd())
|
||||
|
||||
result = clirunner.invoke(cmd_boards, ["Arduino Uno", "--json-output"])
|
||||
validate_cliresult(result)
|
||||
boards = json.loads(result.output)
|
||||
|
||||
config = ProjectConfig(join(getcwd(), "platformio.ini"))
|
||||
config = ProjectConfig(os.path.join(os.getcwd(), "platformio.ini"))
|
||||
config.validate()
|
||||
|
||||
expected_result = dict(
|
||||
@ -149,11 +166,18 @@ def test_init_special_board(clirunner, validate_cliresult):
|
||||
def test_init_enable_auto_uploading(clirunner, validate_cliresult):
|
||||
with clirunner.isolated_filesystem():
|
||||
result = clirunner.invoke(
|
||||
project_init_cmd, ["-b", "uno", "--project-option", "targets=upload"]
|
||||
project_init_cmd,
|
||||
[
|
||||
"-b",
|
||||
"uno",
|
||||
"--project-option",
|
||||
"targets=upload",
|
||||
"--no-install-dependencies",
|
||||
],
|
||||
)
|
||||
validate_cliresult(result)
|
||||
validate_pioproject(getcwd())
|
||||
config = ProjectConfig(join(getcwd(), "platformio.ini"))
|
||||
validate_pioproject(os.getcwd())
|
||||
config = ProjectConfig(os.path.join(os.getcwd(), "platformio.ini"))
|
||||
config.validate()
|
||||
expected_result = dict(
|
||||
targets=["upload"], platform="atmelavr", board="uno", framework=["arduino"]
|
||||
@ -167,11 +191,18 @@ def test_init_enable_auto_uploading(clirunner, validate_cliresult):
|
||||
def test_init_custom_framework(clirunner, validate_cliresult):
|
||||
with clirunner.isolated_filesystem():
|
||||
result = clirunner.invoke(
|
||||
project_init_cmd, ["-b", "teensy31", "--project-option", "framework=mbed"]
|
||||
project_init_cmd,
|
||||
[
|
||||
"-b",
|
||||
"teensy31",
|
||||
"--project-option",
|
||||
"framework=mbed",
|
||||
"--no-install-dependencies",
|
||||
],
|
||||
)
|
||||
validate_cliresult(result)
|
||||
validate_pioproject(getcwd())
|
||||
config = ProjectConfig(join(getcwd(), "platformio.ini"))
|
||||
validate_pioproject(os.getcwd())
|
||||
config = ProjectConfig(os.path.join(os.getcwd(), "platformio.ini"))
|
||||
config.validate()
|
||||
expected_result = dict(platform="teensy", board="teensy31", framework=["mbed"])
|
||||
assert config.has_section("env:teensy31")
|
||||
@ -187,81 +218,80 @@ def test_init_incorrect_board(clirunner):
|
||||
assert isinstance(result.exception, SystemExit)
|
||||
|
||||
|
||||
@pytest.mark.skipif(not proc.is_ci(), reason="runs on CI")
|
||||
def test_init_ide_clion(clirunner, isolated_pio_core, validate_cliresult, tmpdir):
|
||||
result = clirunner.invoke(
|
||||
cli_platform.platform_install,
|
||||
[
|
||||
"ststm32",
|
||||
"--skip-default-package",
|
||||
"--with-package",
|
||||
"tool-cmake",
|
||||
"--with-package",
|
||||
"tool-ninja",
|
||||
],
|
||||
)
|
||||
|
||||
def test_init_ide_clion(clirunner, validate_cliresult, tmpdir):
|
||||
project_dir = tmpdir.join("project").mkdir()
|
||||
# Add extra libraries to cover cases with possible unwanted backslashes
|
||||
lib_extra_dirs = isolated_pio_core.join("extra_libs").mkdir()
|
||||
lib_extra_dirs = tmpdir.join("extra_libs").mkdir()
|
||||
extra_lib = lib_extra_dirs.join("extra_lib").mkdir()
|
||||
extra_lib.join("extra_lib.h").write(" ")
|
||||
extra_lib.join("extra_lib.cpp").write(" ")
|
||||
|
||||
with tmpdir.as_cwd():
|
||||
with project_dir.as_cwd():
|
||||
result = clirunner.invoke(
|
||||
project_init_cmd,
|
||||
[
|
||||
"-b",
|
||||
"nucleo_f401re",
|
||||
"uno",
|
||||
"--ide",
|
||||
"clion",
|
||||
"--project-option",
|
||||
"framework=arduino",
|
||||
"--project-option",
|
||||
"platform_packages=platformio/tool-ninja",
|
||||
"--project-option",
|
||||
"lib_extra_dirs=%s" % str(lib_extra_dirs),
|
||||
],
|
||||
)
|
||||
|
||||
validate_cliresult(result)
|
||||
assert all(isfile(f) for f in ("CMakeLists.txt", "CMakeListsPrivate.txt"))
|
||||
assert all(
|
||||
os.path.isfile(f) for f in ("CMakeLists.txt", "CMakeListsPrivate.txt")
|
||||
)
|
||||
|
||||
tmpdir.join("src").join("main.cpp").write(
|
||||
project_dir.join("src").join("main.cpp").write(
|
||||
"""#include <Arduino.h>
|
||||
#include "extra_lib.h"
|
||||
void setup(){}
|
||||
void loop(){}
|
||||
"""
|
||||
)
|
||||
cmake_path = str(
|
||||
isolated_pio_core.join("packages")
|
||||
.join("tool-cmake")
|
||||
.join("bin")
|
||||
.join("cmake")
|
||||
)
|
||||
tmpdir.join("build_dir").mkdir()
|
||||
result = proc.exec_command(
|
||||
project_dir.join("build_dir").mkdir()
|
||||
result = clirunner.invoke(
|
||||
package_exec_cmd,
|
||||
[
|
||||
cmake_path,
|
||||
"-DCMAKE_BUILD_TYPE=nucleo_f401re",
|
||||
"-p",
|
||||
"tool-cmake",
|
||||
"--",
|
||||
"cmake",
|
||||
"-DCMAKE_BUILD_TYPE=uno",
|
||||
"-DCMAKE_MAKE_PROGRAM=%s"
|
||||
% str(
|
||||
isolated_pio_core.join("packages").join("tool-ninja").join("ninja")
|
||||
% os.path.join(
|
||||
ProjectConfig().get("platformio", "packages_dir"),
|
||||
"tool-ninja",
|
||||
"ninja",
|
||||
),
|
||||
"-G",
|
||||
"Ninja",
|
||||
"-S",
|
||||
str(tmpdir),
|
||||
str(project_dir),
|
||||
"-B",
|
||||
"build_dir",
|
||||
]
|
||||
],
|
||||
)
|
||||
validate_cliresult(result)
|
||||
|
||||
# Check if CMake was able to generate a native project for Ninja
|
||||
assert result["returncode"] == 0, result["out"]
|
||||
|
||||
result = proc.exec_command(
|
||||
[cmake_path, "--build", "build_dir", "--target", "Debug"]
|
||||
# build
|
||||
result = clirunner.invoke(
|
||||
package_exec_cmd,
|
||||
[
|
||||
"-p",
|
||||
"tool-cmake",
|
||||
"--",
|
||||
"cmake",
|
||||
"--build",
|
||||
"build_dir",
|
||||
"--target",
|
||||
"Debug",
|
||||
],
|
||||
)
|
||||
|
||||
assert result["returncode"] == 0
|
||||
assert "[SUCCESS]" in str(result["out"])
|
||||
validate_cliresult(result)
|
||||
|
Reference in New Issue
Block a user