mirror of
https://github.com/espressif/esp-idf.git
synced 2026-05-28 16:46:31 +03:00
feat(tools): Added create-project argument to allow cpp project creation
Closes https://github.com/espressif/esp-idf/issues/16121
This commit is contained in:
@@ -25,7 +25,7 @@ Start a New Project: ``create-project``
|
||||
|
||||
idf.py create-project <project name>
|
||||
|
||||
This command creates a new ESP-IDF project. Additionally, the folder where the project will be created in can be specified by the ``--path`` option.
|
||||
This command creates a new ESP-IDF project. Additionally, the folder where the project will be created in can be specified by the ``--path`` option. Pass ``--cpp`` to create a C++ source file (``<project name>.cpp``) with C linkage for ``app_main`` instead of a ``.c`` file.
|
||||
|
||||
Create a New Component: ``create-component``
|
||||
--------------------------------------------
|
||||
|
||||
@@ -340,6 +340,7 @@ if __name__ == '__main__':
|
||||
_exclude_dirs = [
|
||||
os.path.join(IDF_PATH, 'tools', 'test_build_system'),
|
||||
os.path.join(IDF_PATH, 'tools', 'templates', 'sample_project'),
|
||||
os.path.join(IDF_PATH, 'tools', 'templates', 'sample_project_cpp'),
|
||||
os.path.join(IDF_PATH, 'tools', 'cmakev2', 'test'),
|
||||
]
|
||||
|
||||
|
||||
@@ -55,3 +55,6 @@ tools/templates/sample_component/main.c
|
||||
tools/templates/sample_project/CMakeLists.txt
|
||||
tools/templates/sample_project/main/CMakeLists.txt
|
||||
tools/templates/sample_project/main/main.c
|
||||
tools/templates/sample_project_cpp/CMakeLists.txt
|
||||
tools/templates/sample_project_cpp/main/CMakeLists.txt
|
||||
tools/templates/sample_project_cpp/main/main.cpp
|
||||
|
||||
@@ -1,12 +1,12 @@
|
||||
# SPDX-FileCopyrightText: 2022-2025 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-FileCopyrightText: 2022-2026 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Apache-2.0
|
||||
import os
|
||||
import re
|
||||
import stat
|
||||
import sys
|
||||
from collections.abc import Callable
|
||||
from shutil import copyfile
|
||||
from shutil import copytree
|
||||
from typing import Dict
|
||||
|
||||
import click
|
||||
|
||||
@@ -65,9 +65,11 @@ def make_directory_permissions_writable(root_path: str) -> None:
|
||||
continue
|
||||
|
||||
|
||||
def create_project(target_path: str, name: str) -> None:
|
||||
def create_project(target_path: str, name: str, *, use_cpp: bool = False) -> None:
|
||||
template = 'sample_project_cpp' if use_cpp else 'sample_project'
|
||||
main_ext = 'cpp' if use_cpp else 'c'
|
||||
copytree(
|
||||
os.path.join(os.environ['IDF_PATH'], 'tools', 'templates', 'sample_project'),
|
||||
os.path.join(os.environ['IDF_PATH'], 'tools', 'templates', template),
|
||||
target_path,
|
||||
# 'copyfile' ensures only data are copied, without any metadata (file permissions) - for files only
|
||||
copy_function=copyfile,
|
||||
@@ -76,7 +78,10 @@ def create_project(target_path: str, name: str) -> None:
|
||||
# since 'copyfile' does preserve directory metadata, we need to make sure the directories are writable
|
||||
make_directory_permissions_writable(target_path)
|
||||
main_folder = os.path.join(target_path, 'main')
|
||||
os.rename(os.path.join(main_folder, 'main.c'), os.path.join(main_folder, '.'.join((name, 'c'))))
|
||||
os.rename(
|
||||
os.path.join(main_folder, '.'.join(('main', main_ext))),
|
||||
os.path.join(main_folder, '.'.join((name, main_ext))),
|
||||
)
|
||||
replace_in_file(os.path.join(main_folder, 'CMakeLists.txt'), 'main', name)
|
||||
replace_in_file(os.path.join(target_path, 'CMakeLists.txt'), 'main', name)
|
||||
|
||||
@@ -100,14 +105,17 @@ def create_component(target_path: str, name: str) -> None:
|
||||
replace_in_file(os.path.join(target_path, 'CMakeLists.txt'), 'main', name)
|
||||
|
||||
|
||||
def action_extensions(base_actions: Dict, project_path: str) -> Dict:
|
||||
def create_new(action: str, ctx: click.core.Context, global_args: PropertyDict, **action_args: str) -> Dict:
|
||||
def action_extensions(base_actions: dict, project_path: str) -> dict:
|
||||
def create_new(action: str, ctx: click.core.Context, global_args: PropertyDict, **action_args: str) -> dict:
|
||||
target_path = action_args.get('path') or os.path.join(project_path, action_args['name'])
|
||||
|
||||
is_empty_and_create(target_path, action)
|
||||
|
||||
func_action_map = {'create-project': create_project, 'create-component': create_component}
|
||||
func_action_map[action](target_path, action_args['name'])
|
||||
func_action_map: dict[str, Callable[..., None]] = {
|
||||
'create-project': lambda tp, n, aa: create_project(tp, n, use_cpp=bool(aa.get('cpp'))),
|
||||
'create-component': lambda tp, n, aa: create_component(tp, n),
|
||||
}
|
||||
func_action_map[action](target_path, action_args['name'], action_args)
|
||||
|
||||
print('The', get_type(action), 'was created in', os.path.abspath(target_path))
|
||||
|
||||
@@ -125,6 +133,7 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
|
||||
'`idf.py create-project new_proj` '
|
||||
'will create a new project in subdirectory called `new_proj` '
|
||||
'of the current working directory. '
|
||||
'Use `--cpp` to generate a C++ source file (`NAME.cpp`) with `extern "C" void app_main(void)`. '
|
||||
"For specifying the new project's path, use either the option --path for specifying the "
|
||||
'destination directory, or the global option -C if the project should be created as a '
|
||||
'subdirectory of the specified directory. '
|
||||
@@ -143,6 +152,12 @@ def action_extensions(base_actions: Dict, project_path: str) -> Dict:
|
||||
'will be created directly in the given folder if it does not contain anything'
|
||||
),
|
||||
},
|
||||
{
|
||||
'names': ['--cpp'],
|
||||
'is_flag': True,
|
||||
'default': False,
|
||||
'help': 'Create a C++ main source file instead of C.',
|
||||
},
|
||||
],
|
||||
},
|
||||
'create-component': {
|
||||
|
||||
6
tools/templates/sample_project_cpp/CMakeLists.txt
Normal file
6
tools/templates/sample_project_cpp/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
||||
# The following five lines of boilerplate have to be in your project's
|
||||
# CMakeLists in this exact order for cmake to work correctly
|
||||
cmake_minimum_required(VERSION 3.22)
|
||||
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(main)
|
||||
2
tools/templates/sample_project_cpp/main/CMakeLists.txt
Normal file
2
tools/templates/sample_project_cpp/main/CMakeLists.txt
Normal file
@@ -0,0 +1,2 @@
|
||||
idf_component_register(SRCS "main.cpp"
|
||||
INCLUDE_DIRS ".")
|
||||
6
tools/templates/sample_project_cpp/main/main.cpp
Normal file
6
tools/templates/sample_project_cpp/main/main.cpp
Normal file
@@ -0,0 +1,6 @@
|
||||
#include <cstdio>
|
||||
|
||||
extern "C" void app_main(void)
|
||||
{
|
||||
|
||||
}
|
||||
@@ -220,6 +220,21 @@ def test_create_component_project(idf_copy: Path) -> None:
|
||||
run_idf_py('build', workdir=(idf_copy / 'projects' / 'temp_test_project'))
|
||||
|
||||
|
||||
def test_create_project_cpp(idf_copy: Path) -> None:
|
||||
logging.info('Create C++ project with idf.py create-project --cpp')
|
||||
proj_dir = idf_copy / 'projects' / 'cpp_test_project'
|
||||
run_idf_py('-C', 'projects', 'create-project', '--cpp', 'cpp_test_project', workdir=idf_copy)
|
||||
main_cpp = proj_dir / 'main' / 'cpp_test_project.cpp'
|
||||
assert main_cpp.is_file()
|
||||
text = main_cpp.read_text(encoding='utf-8')
|
||||
assert 'extern "C" void app_main(void)' in text
|
||||
cmake = (proj_dir / 'main' / 'CMakeLists.txt').read_text(encoding='utf-8')
|
||||
assert 'cpp_test_project.cpp' in cmake
|
||||
# Avoid `assert 'cpp_test_project.c' not in cmake`: that string is a substring of `cpp_test_project.cpp`.
|
||||
assert 'SRCS "cpp_test_project.c"' not in cmake
|
||||
run_idf_py('build', workdir=str(proj_dir))
|
||||
|
||||
|
||||
# In this test function, there are actually two logical tests in one test function.
|
||||
# It would be better to have every check in a separate
|
||||
# test case, but that would mean doing idf_copy each time, and copying takes most of the time
|
||||
|
||||
Reference in New Issue
Block a user