add new command to idf

This commit is contained in:
martin.gano 2020-09-03 11:16:42 +02:00
parent eebc71b16d
commit b599f127b5
14 changed files with 272 additions and 2 deletions

View File

@ -117,6 +117,21 @@ Note that some older versions of CCache may exhibit bugs on some platforms, so i
- ``-v`` flag causes both ``idf.py`` and the build system to produce verbose build output. This can be useful for debugging build problems. - ``-v`` flag causes both ``idf.py`` and the build system to produce verbose build output. This can be useful for debugging build problems.
- ``--cmake-warn-uninitialized`` (or ``-w``) will cause CMake to print uninitialized variable warnings inside the project directory (not for directories not found inside the project directory). This only controls CMake variable warnings inside CMake itself, not other types of build warnings. This option can also be set permanently by setting the ``IDF_CMAKE_WARN_UNINITIALIZED`` environment variable to a non-zero value. - ``--cmake-warn-uninitialized`` (or ``-w``) will cause CMake to print uninitialized variable warnings inside the project directory (not for directories not found inside the project directory). This only controls CMake variable warnings inside CMake itself, not other types of build warnings. This option can also be set permanently by setting the ``IDF_CMAKE_WARN_UNINITIALIZED`` environment variable to a non-zero value.
Start a new project
-------------------
Use the command ``idf.py create-project`` for starting a new project. Execute ``idf.py create-project --help`` for more information.
Example:
.. code-block:: bash
idf.py create-project --path my_projects my_new_project
This example will create a new project called my_new_project directly into the directory my_projects.
Using CMake Directly Using CMake Directly
-------------------- --------------------
@ -344,6 +359,23 @@ are discussed :ref:`here<cmake-component-register>`.
See `example component requirements`_ and `example component CMakeLists`_ for more complete component ``CMakeLists.txt`` examples. See `example component requirements`_ and `example component CMakeLists`_ for more complete component ``CMakeLists.txt`` examples.
Create a new component
----------------------
Use the command ``idf.py create-component`` for creating a new component.
The new component will contain set of files necessary for building a component.
You may include the component's header file into your project and use its functionality.
For more information execute ``idf.py create-component --help``.
Example:
.. code-block:: bash
idf.py -C components create-component my_component
The example will create a new component in the subdirectory `components` under the current working directory.
For more information about components follow the documentation page :ref:`see above <component-directories>`.
.. _component variables: .. _component variables:
Preset Component Variables Preset Component Variables

View File

@ -18,7 +18,7 @@ Select the instructions depending on Espressif chip installed on your developmen
The project **hello_world** contains one source file in C language [hello_world_main.c](main/hello_world_main.c). The file is located in folder [main](main). The project **hello_world** contains one source file in C language [hello_world_main.c](main/hello_world_main.c). The file is located in folder [main](main).
ESP-IDF projects are build using CMake. The project build configuration is contained in `CMakeLists.txt` files that provide set of directives and instructions describing the project's source files and targets (executable, library, or both). ESP-IDF projects are built using CMake. The project build configuration is contained in `CMakeLists.txt` files that provide set of directives and instructions describing the project's source files and targets (executable, library, or both).
Below is short explanation of remaining files in the project folder. Below is short explanation of remaining files in the project folder.

View File

@ -0,0 +1,7 @@
# For more information about build system see https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html
# 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.5)
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
project(main)

View File

@ -0,0 +1,8 @@
#
# This is a project Makefile. It is assumed the directory this Makefile resides in is a
# project subdirectory.
#
PROJECT_NAME := sample_project
include $(IDF_PATH)/make/project.mk

View File

@ -0,0 +1,32 @@
# _Sample project_
(See the README.md file in the upper level 'examples' directory for more information about examples.)
This is the simplest buildable example. The example is used by command `idf.py create-project`
that copies the project to user specified path and set it's name. For more information follow the [docs page](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html#start-a-new-project)
## How to use example
We encourage the users to use the example as a template for the new projects.
A recommended way is to follow the instructions on a [docs page](https://docs.espressif.com/projects/esp-idf/en/latest/api-guides/build-system.html#start-a-new-project).
## Example folder contents
The project **sample_project** contains one source file in C language [main.c](main/main.c). The file is located in folder [main](main).
ESP-IDF projects are built using CMake. The project build configuration is contained in `CMakeLists.txt`
files that provide set of directives and instructions describing the project's source files and targets
(executable, library, or both).
Below is short explanation of remaining files in the project folder.
```
├── CMakeLists.txt
├── main
│   ├── CMakeLists.txt
│   └── main.c
└── README.md This is the file you are currently reading
```
Additionally, the sample project contains Makefile and component.mk files, used for the legacy Make based build system.
They are not used or needed when building with CMake and idf.py.

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS ".")

View File

@ -0,0 +1,5 @@
#
# "main" pseudo-component makefile.
#
# (Uses default behaviour of compiling all source files in directory, adding 'include' to include path.)

View File

@ -0,0 +1,6 @@
#include <stdio.h>
void app_main(void)
{
}

View File

@ -758,7 +758,7 @@ endmenu\n" >> ${IDF_PATH}/Kconfig
clean_build_dir clean_build_dir
mkdir -p components/esp32 mkdir -p components/esp32
echo "idf_component_get_property(overriden_dir \${COMPONENT_NAME} COMPONENT_OVERRIDEN_DIR)" >> components/esp32/CMakeLists.txt echo "idf_component_get_property(overriden_dir \${COMPONENT_NAME} COMPONENT_OVERRIDEN_DIR)" >> components/esp32/CMakeLists.txt
echo "message(STATUS overriden_dir:\${overriden_dir})" >> components/esp32/CMakeLists.txt echo "message(STATUS overriden_dir:\${overriden_dir})" >> components/esp32/CMakeLists.txt
(idf.py reconfigure | grep "overriden_dir:$IDF_PATH/components/esp32") || failure "Failed to get overriden dir" # no registration, overrides registration as well (idf.py reconfigure | grep "overriden_dir:$IDF_PATH/components/esp32") || failure "Failed to get overriden dir" # no registration, overrides registration as well
print_status "Overriding Kconfig" print_status "Overriding Kconfig"
echo "idf_component_register(KCONFIG \${overriden_dir}/Kconfig)" >> components/esp32/CMakeLists.txt echo "idf_component_register(KCONFIG \${overriden_dir}/Kconfig)" >> components/esp32/CMakeLists.txt
@ -767,6 +767,44 @@ endmenu\n" >> ${IDF_PATH}/Kconfig
(idf.py reconfigure | grep "kconfig:$IDF_PATH/components/esp32/Kconfig") || failure "Failed to verify original `main` directory" (idf.py reconfigure | grep "kconfig:$IDF_PATH/components/esp32/Kconfig") || failure "Failed to verify original `main` directory"
rm -rf components rm -rf components
print_status "Create project using idf.py and build it"
echo "Trying to create project."
(idf.py -C projects create-project temp_test_project) || failure "Failed to create the project."
cd "$IDF_PATH/projects/temp_test_project"
echo "Building the project temp_test_project . . ."
idf.py build || failure "Failed to build the project."
cd "$IDF_PATH"
rm -rf "$IDF_PATH/projects/temp_test_project"
print_status "Create component using idf.py, create project using idf.py."
print_status "Add the component to the created project and build the project."
echo "Trying to create project . . ."
(idf.py -C projects create-project temp_test_project) || failure "Failed to create the project."
echo "Trying to create component . . ."
(idf.py -C components create-component temp_test_component) || failure "Failed to create the component."
${SED} -i '5i\\tfunc();' "$IDF_PATH/projects/temp_test_project/main/temp_test_project.c"
${SED} -i '5i#include "temp_test_component.h"' "$IDF_PATH/projects/temp_test_project/main/temp_test_project.c"
cd "$IDF_PATH/projects/temp_test_project"
idf.py build || failure "Failed to build the project."
cd "$IDF_PATH"
rm -rf "$IDF_PATH/projects/temp_test_project"
rm -rf "$IDF_PATH/components/temp_test_component"
print_status "Check that command for creating new project will fail if the target folder is not empty."
mkdir "$IDF_PATH/example_proj/"
touch "$IDF_PATH/example_proj/tmp_130698"
EXPECTED_EXIT_VALUE=3
expected_failure $EXPECTED_EXIT_VALUE idf.py create-project --path "$IDF_PATH/example_proj/" temp_test_project || failure "Command exit value is wrong."
rm -rf "$IDF_PATH/example_proj/"
print_status "Check that command for creating new project will fail if the target path is file."
touch "$IDF_PATH/example_proj"
EXPECTED_EXIT_VALUE=4
expected_failure $EXPECTED_EXIT_VALUE idf.py create-project --path "$IDF_PATH/example_proj" temp_test_project || failure "Command exit value is wrong."
rm -rf "$IDF_PATH/example_proj"
print_status "All tests completed" print_status "All tests completed"
if [ -n "${FAILURES}" ]; then if [ -n "${FAILURES}" ]; then
echo "Some failures were detected:" echo "Some failures were detected:"
@ -791,6 +829,15 @@ function failure()
FAILURES="${FAILURES}${STATUS} :: $1\n" FAILURES="${FAILURES}${STATUS} :: $1\n"
} }
function expected_failure() {
"${@:2}"
EXIT_VALUE=$?
if [ $EXIT_VALUE != "$1" ]; then
echo "[ERROR] Exit value of executed command is $EXIT_VALUE (expected $1)"; return 1
else return 0
fi
}
TESTDIR=${PWD}/build_system_tests_$$ TESTDIR=${PWD}/build_system_tests_$$
mkdir -p ${TESTDIR} mkdir -p ${TESTDIR}
# set NOCLEANUP=1 if you want to keep the test directory around # set NOCLEANUP=1 if you want to keep the test directory around

View File

@ -210,6 +210,7 @@ def action_extensions(base_actions, project_path):
}, },
{ {
"names": ["-C", "--project-dir"], "names": ["-C", "--project-dir"],
"scope": "shared",
"help": "Project directory.", "help": "Project directory.",
"type": click.Path(), "type": click.Path(),
"default": os.getcwd(), "default": os.getcwd(),

View File

@ -0,0 +1,120 @@
from __future__ import print_function
from distutils.dir_util import copy_tree
import os
import re
import sys
def get_type(action):
return action.split("-")[1]
def replace_in_file(filename, pattern, replacement):
with open(filename, 'r+') as f:
content = f.read()
overwritten_content = re.sub(pattern, replacement, content, flags=re.M)
f.seek(0)
f.write(overwritten_content)
f.truncate()
def is_empty_and_create(path, action):
abspath = os.path.abspath(path)
if not os.path.exists(abspath):
os.makedirs(abspath)
elif not os.path.isdir(abspath):
print("Your target path is not a directory. Please remove the", os.path.abspath(abspath),
"or use different target path.")
sys.exit(4)
elif len(os.listdir(path)) > 0:
print("The directory", abspath, "is not empty. To create a", get_type(action),
"you must empty the directory or choose a different path.")
sys.exit(3)
def create_project(target_path, name):
copy_tree(os.path.join(os.environ['IDF_PATH'], "examples", "get-started", "sample_project"), 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"))))
replace_in_file(os.path.join(main_folder, "CMakeLists.txt"), "main", name)
replace_in_file(os.path.join(target_path, "CMakeLists.txt"), "main", name)
os.remove(os.path.join(target_path, "README.md"))
# after manual removing "Makefile" and "component.mk" from `examples/get-started/sample_project`
# remove following two lines as well
os.remove(os.path.join(target_path, "Makefile"))
os.remove(os.path.join(target_path, "main", "component.mk"))
def create_component(target_path, name):
copy_tree(os.path.join(os.environ['IDF_PATH'], "tools", "templates", "sample_component"), target_path)
os.rename(os.path.join(target_path, "main.c"), os.path.join(target_path, ".".join((name, "c"))))
os.rename(os.path.join(target_path, "include", "main.h"),
os.path.join(target_path, "include", ".".join((name, "h"))))
replace_in_file(os.path.join(target_path, ".".join((name, "c"))), "main", name)
replace_in_file(os.path.join(target_path, "CMakeLists.txt"), "main", name)
def action_extensions(base_actions, project_path):
def create_new(action, ctx, global_args, **action_args):
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'])
print("The", get_type(action), "was created in", os.path.abspath(target_path))
# after the command execution, no other commands are accepted and idf.py terminates
sys.exit(0)
return {
"actions": {
"create-project": {
"callback": create_new,
"short_help": "Create a new project.",
"help": ("Create a new project with the name NAME specified as argument. "
"For example: "
"`idf.py create-project new_proj` "
"will create a new project in subdirectory called `new_proj` "
"of the current working directory. "
"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. "
"If the target path does not exist it will be created. If the target folder is not empty "
"then the operation will fail with return code 3. "
"If the target path is not a folder, the script will fail with return code 4. "
"After the execution idf.py terminates "
"so this operation should be used alone."),
"arguments": [{"names": ["name"]}],
"options": [
{
"names": ["-p", "--path"],
"help": ("Set the path for the new project. The project "
"will be created directly in the given folder if it does not contain anything"),
},
],
},
"create-component": {
"callback": create_new,
"short_help": "Create a new component.",
"help": ("Create a new component with the name NAME specified as argument. "
"For example: "
"`idf.py create-component new_comp` "
"will create a new component in subdirectory called `new_comp` "
"of the current working directory. "
"For specifying the new component's path use the option -C. "
"If the target path does not exist then it will be created. "
"If the target folder is not empty "
"then the operation will fail with return code 3. "
"If the target path is not a folder, the script will fail with return code 4. "
"After the execution idf.py terminates "
"so this operation should be used alone."),
"arguments": [{"names": ["name"]}],
}
}
}

View File

@ -0,0 +1,2 @@
idf_component_register(SRCS "main.c"
INCLUDE_DIRS "include")

View File

@ -0,0 +1 @@
void func(void);

View File

@ -0,0 +1,7 @@
#include <stdio.h>
#include "main.h"
void func(void)
{
}