mirror of
https://github.com/espressif/esp-idf.git
synced 2024-10-05 20:47:46 -04:00
feat: add diff option support to idf.py size commands
This adds a possibility to specify --diff option to idf.py size, size-components and size-files commands. This can be map file directly, project directory or build directory. Usage example: idf.py size-components --diff ../hello_world2/build/hello_world.map idf.py size-components --diff ../hello_world2/build idf.py size-components --diff ../hello_world2 Signed-off-by: Frantisek Hrbata <frantisek.hrbata@espressif.com>
This commit is contained in:
parent
2edf936984
commit
6f41dee0cd
@ -32,6 +32,10 @@ if(DEFINED IDF_SIZE_MODE)
|
|||||||
list(APPEND IDF_SIZE_CMD ${IDF_SIZE_MODE})
|
list(APPEND IDF_SIZE_CMD ${IDF_SIZE_MODE})
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
|
if(DEFINED ENV{SIZE_DIFF_FILE})
|
||||||
|
list(APPEND IDF_SIZE_CMD "--diff=$ENV{SIZE_DIFF_FILE}")
|
||||||
|
endif()
|
||||||
|
|
||||||
list(APPEND IDF_SIZE_CMD ${MAP_FILE})
|
list(APPEND IDF_SIZE_CMD ${MAP_FILE})
|
||||||
|
|
||||||
execute_process(COMMAND ${IDF_SIZE_CMD}
|
execute_process(COMMAND ${IDF_SIZE_CMD}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
# SPDX-FileCopyrightText: 2022-2023 Espressif Systems (Shanghai) CO LTD
|
# SPDX-FileCopyrightText: 2022-2024 Espressif Systems (Shanghai) CO LTD
|
||||||
# SPDX-License-Identifier: Apache-2.0
|
# SPDX-License-Identifier: Apache-2.0
|
||||||
import fnmatch
|
import fnmatch
|
||||||
|
import glob
|
||||||
import json
|
import json
|
||||||
import locale
|
import locale
|
||||||
import os
|
import os
|
||||||
@ -8,18 +9,33 @@ import re
|
|||||||
import shutil
|
import shutil
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
from typing import Any, Dict, List, Optional
|
from typing import Any
|
||||||
|
from typing import Dict
|
||||||
|
from typing import List
|
||||||
|
from typing import Optional
|
||||||
from urllib.error import URLError
|
from urllib.error import URLError
|
||||||
from urllib.request import Request, urlopen
|
from urllib.request import Request
|
||||||
|
from urllib.request import urlopen
|
||||||
from webbrowser import open_new_tab
|
from webbrowser import open_new_tab
|
||||||
|
|
||||||
import click
|
import click
|
||||||
from click.core import Context
|
from click.core import Context
|
||||||
from idf_py_actions.constants import GENERATORS, PREVIEW_TARGETS, SUPPORTED_TARGETS, URL_TO_DOC
|
from idf_py_actions.constants import GENERATORS
|
||||||
|
from idf_py_actions.constants import PREVIEW_TARGETS
|
||||||
|
from idf_py_actions.constants import SUPPORTED_TARGETS
|
||||||
|
from idf_py_actions.constants import URL_TO_DOC
|
||||||
from idf_py_actions.errors import FatalError
|
from idf_py_actions.errors import FatalError
|
||||||
from idf_py_actions.global_options import global_options
|
from idf_py_actions.global_options import global_options
|
||||||
from idf_py_actions.tools import (PropertyDict, TargetChoice, ensure_build_directory, generate_hints, get_target,
|
from idf_py_actions.tools import ensure_build_directory
|
||||||
idf_version, merge_action_lists, print_warning, run_target, yellow_print)
|
from idf_py_actions.tools import generate_hints
|
||||||
|
from idf_py_actions.tools import get_target
|
||||||
|
from idf_py_actions.tools import idf_version
|
||||||
|
from idf_py_actions.tools import merge_action_lists
|
||||||
|
from idf_py_actions.tools import print_warning
|
||||||
|
from idf_py_actions.tools import PropertyDict
|
||||||
|
from idf_py_actions.tools import run_target
|
||||||
|
from idf_py_actions.tools import TargetChoice
|
||||||
|
from idf_py_actions.tools import yellow_print
|
||||||
|
|
||||||
|
|
||||||
def action_extensions(base_actions: Dict, project_path: str) -> Any:
|
def action_extensions(base_actions: Dict, project_path: str) -> Any:
|
||||||
@ -34,7 +50,7 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
|
|||||||
run_target(target_name, args, force_progression=GENERATORS[args.generator].get('force_progression', False))
|
run_target(target_name, args, force_progression=GENERATORS[args.generator].get('force_progression', False))
|
||||||
|
|
||||||
def size_target(target_name: str, ctx: Context, args: PropertyDict, output_format: str,
|
def size_target(target_name: str, ctx: Context, args: PropertyDict, output_format: str,
|
||||||
output_file: str, legacy: bool) -> None:
|
output_file: str, diff_map_file: str, legacy: bool) -> None:
|
||||||
"""
|
"""
|
||||||
Builds the app and then executes a size-related target passed in 'target_name'.
|
Builds the app and then executes a size-related target passed in 'target_name'.
|
||||||
`tool_error_handler` handler is used to suppress errors during the build,
|
`tool_error_handler` handler is used to suppress errors during the build,
|
||||||
@ -45,6 +61,8 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
|
|||||||
for hint in generate_hints(stdout, stderr):
|
for hint in generate_hints(stdout, stderr):
|
||||||
yellow_print(hint)
|
yellow_print(hint)
|
||||||
|
|
||||||
|
env: Dict[str, Any] = {}
|
||||||
|
|
||||||
if not legacy and output_format != 'json':
|
if not legacy and output_format != 'json':
|
||||||
try:
|
try:
|
||||||
import esp_idf_size.ng # noqa: F401
|
import esp_idf_size.ng # noqa: F401
|
||||||
@ -55,28 +73,43 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
|
|||||||
# Legacy mode is used only when explicitly requested with --legacy option
|
# Legacy mode is used only when explicitly requested with --legacy option
|
||||||
# or when "--format json" option is specified. Here we enable the
|
# or when "--format json" option is specified. Here we enable the
|
||||||
# esp-idf-size refactored version with ESP_IDF_SIZE_NG env. variable.
|
# esp-idf-size refactored version with ESP_IDF_SIZE_NG env. variable.
|
||||||
os.environ['ESP_IDF_SIZE_NG'] = '1'
|
env['ESP_IDF_SIZE_NG'] = '1'
|
||||||
# ESP_IDF_SIZE_FORCE_TERMINAL is set to force terminal control codes even
|
# ESP_IDF_SIZE_FORCE_TERMINAL is set to force terminal control codes even
|
||||||
# if stdout is not attached to terminal. This is set to pass color codes
|
# if stdout is not attached to terminal. This is set to pass color codes
|
||||||
# from esp-idf-size to idf.py.
|
# from esp-idf-size to idf.py.
|
||||||
os.environ['ESP_IDF_SIZE_FORCE_TERMINAL'] = '1'
|
env['ESP_IDF_SIZE_FORCE_TERMINAL'] = '1'
|
||||||
|
|
||||||
if legacy and output_format in ['json2', 'raw', 'tree']:
|
if legacy and output_format in ['json2', 'raw', 'tree']:
|
||||||
# These formats are supported in new version only.
|
# These formats are supported in new version only.
|
||||||
# We would get error from the esp-idf-size anyway, so print error early.
|
# We would get error from the esp-idf-size anyway, so print error early.
|
||||||
raise FatalError(f'Legacy esp-idf-size does not support {output_format} format')
|
raise FatalError(f'Legacy esp-idf-size does not support {output_format} format')
|
||||||
|
|
||||||
os.environ['SIZE_OUTPUT_FORMAT'] = output_format
|
env['SIZE_OUTPUT_FORMAT'] = output_format
|
||||||
if output_file:
|
if output_file:
|
||||||
os.environ['SIZE_OUTPUT_FILE'] = os.path.abspath(output_file)
|
env['SIZE_OUTPUT_FILE'] = os.path.abspath(output_file)
|
||||||
|
if diff_map_file:
|
||||||
|
diff_map_file = os.path.abspath(diff_map_file)
|
||||||
|
if os.path.isdir(diff_map_file):
|
||||||
|
# The diff_map_file argument is a directory. Try to look for the map
|
||||||
|
# file directly in it, in case it's a build directory or in one level below
|
||||||
|
# if it's a project directory.
|
||||||
|
files = glob.glob(os.path.join(diff_map_file, '*.map')) or glob.glob(os.path.join(diff_map_file, '*/*.map'))
|
||||||
|
if not files:
|
||||||
|
raise FatalError(f'No diff map file found in {diff_map_file} directory')
|
||||||
|
if len(files) > 1:
|
||||||
|
map_files = ', '.join(files)
|
||||||
|
raise FatalError(f'Two or more diff map files {map_files} found in {diff_map_file} directory')
|
||||||
|
diff_map_file = files[0]
|
||||||
|
|
||||||
|
env['SIZE_DIFF_FILE'] = diff_map_file
|
||||||
|
|
||||||
ensure_build_directory(args, ctx.info_name)
|
ensure_build_directory(args, ctx.info_name)
|
||||||
run_target('all', args, force_progression=GENERATORS[args.generator].get('force_progression', False),
|
run_target('all', args, force_progression=GENERATORS[args.generator].get('force_progression', False),
|
||||||
custom_error_handler=tool_error_handler)
|
custom_error_handler=tool_error_handler)
|
||||||
run_target(target_name, args)
|
run_target(target_name, args, env=env)
|
||||||
|
|
||||||
def list_build_system_targets(target_name: str, ctx: Context, args: PropertyDict) -> None:
|
def list_build_system_targets(target_name: str, ctx: Context, args: PropertyDict) -> None:
|
||||||
"""Shows list of targets known to build sytem (make/ninja)"""
|
"""Shows list of targets known to build system (make/ninja)"""
|
||||||
build_target('help', ctx, args)
|
build_target('help', ctx, args)
|
||||||
|
|
||||||
def menuconfig(target_name: str, ctx: Context, args: PropertyDict, style: str) -> None:
|
def menuconfig(target_name: str, ctx: Context, args: PropertyDict, style: str) -> None:
|
||||||
@ -383,6 +416,9 @@ def action_extensions(base_actions: Dict, project_path: str) -> Any:
|
|||||||
'is_flag': True,
|
'is_flag': True,
|
||||||
'default': os.environ.get('ESP_IDF_SIZE_LEGACY', '0') == '1',
|
'default': os.environ.get('ESP_IDF_SIZE_LEGACY', '0') == '1',
|
||||||
'help': 'Use legacy esp-idf-size version'},
|
'help': 'Use legacy esp-idf-size version'},
|
||||||
|
{'names': ['--diff', 'diff_map_file'],
|
||||||
|
'help': ('Show the differences in comparison with another project. '
|
||||||
|
'Argument can be map file or project directory.')},
|
||||||
{'names': ['--output-file', 'output_file'],
|
{'names': ['--output-file', 'output_file'],
|
||||||
'help': 'Print output to the specified file instead of to the standard output'}]
|
'help': 'Print output to the specified file instead of to the standard output'}]
|
||||||
|
|
||||||
|
@ -5,13 +5,56 @@ import logging
|
|||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from subprocess import DEVNULL
|
from shutil import copytree
|
||||||
|
from subprocess import PIPE
|
||||||
from subprocess import run
|
from subprocess import run
|
||||||
|
from subprocess import STDOUT
|
||||||
|
from tempfile import TemporaryDirectory
|
||||||
|
from typing import Any
|
||||||
|
from typing import Optional
|
||||||
|
from typing import Tuple
|
||||||
|
from typing import Union
|
||||||
|
|
||||||
|
|
||||||
|
IDF_PATH = Path(os.environ['IDF_PATH'])
|
||||||
|
IDF_PY_PATH = IDF_PATH / 'tools' / 'idf.py'
|
||||||
|
IDF_SIZE_PY_PATH = IDF_PATH / 'tools' / 'idf_size.py'
|
||||||
|
HELLO_WORLD_PATH = IDF_PATH / 'examples' / 'get-started' / 'hello_world'
|
||||||
|
|
||||||
|
PathLike = Union[str, Path]
|
||||||
|
|
||||||
|
|
||||||
|
def run_cmd(*cmd: PathLike, cwd: Optional[PathLike]=None, check: bool=True, text: bool=True) -> Tuple[int, str]:
|
||||||
|
logging.info('running: {}'.format(' '.join([str(arg) for arg in cmd])))
|
||||||
|
p = run(cmd, stdout=PIPE, stderr=STDOUT, cwd=cwd, check=check, text=text)
|
||||||
|
return p.returncode, p.stdout
|
||||||
|
|
||||||
|
|
||||||
|
def run_idf_py(*args: PathLike, **kwargs: Any) -> Tuple[int, str]:
|
||||||
|
return run_cmd(sys.executable, IDF_PY_PATH, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
def run_idf_size_py(*args: PathLike, **kwargs: Any) -> Tuple[int, str]:
|
||||||
|
return run_cmd(sys.executable, IDF_SIZE_PY_PATH, *args, **kwargs)
|
||||||
|
|
||||||
|
|
||||||
def test_idf_size() -> None:
|
def test_idf_size() -> None:
|
||||||
# Simple test to make sure that the idf_size.py wrapper is compatible
|
# Simple test to make sure that the idf_size.py wrapper is compatible
|
||||||
# with idf.py minimum required python version.
|
# with idf.py minimum required python version.
|
||||||
logging.info('idf_size.py python compatibility check')
|
logging.info('idf_size.py python compatibility check')
|
||||||
idf_size_path = Path(os.environ['IDF_PATH']) / 'tools' / 'idf_size.py'
|
run_idf_size_py('--help')
|
||||||
run([sys.executable, idf_size_path, '--help'], stdout=DEVNULL, stderr=DEVNULL, check=True)
|
|
||||||
|
|
||||||
|
def test_idf_py_size_diff() -> None:
|
||||||
|
# Test idf.py size with diff option, utilizing the same map file, as the focus
|
||||||
|
# of the test lies solely on the option, not on the resulting output.
|
||||||
|
logging.info('idf.py size --diff option test')
|
||||||
|
tmpdir = TemporaryDirectory()
|
||||||
|
app_path = Path(tmpdir.name) / 'app'
|
||||||
|
copytree(HELLO_WORLD_PATH, app_path, symlinks=True)
|
||||||
|
run_idf_py('fullclean', cwd=app_path)
|
||||||
|
run_idf_py('build', cwd=app_path)
|
||||||
|
run_idf_py('size', '--diff', '.', cwd=app_path)
|
||||||
|
# The diff map file should be found automatically in project or project's build directory
|
||||||
|
run_idf_py('size', '--diff', 'build', cwd=app_path)
|
||||||
|
run_idf_py('size', '--diff', Path('build') / 'hello_world.map', cwd=app_path)
|
||||||
|
Loading…
Reference in New Issue
Block a user