import argparse
import logging
import os
import shutil
import subprocess
import tempfile
from pathlib import Path
from typing import List, Optional
try:
# from import or `python -m esptest.tools.pip_check`
from ..logger import get_logger
logger = get_logger('copy_bin')
except ImportError:
logger = logging.getLogger('copy_bin')
IDF_PATH = os.getenv('IDF_PATH', '')
[docs]
class BuildFilesPatterns:
BIN_FILES = [
'*.bin',
'bootloader/*.bin',
'partition_table/*.bin',
'partition_table/*.csv',
'flasher_args.json',
'flash_args',
'flash_project_args',
'config/sdkconfig.json',
'sdkconfig',
]
# For extra debugging
MAP_AND_ELF_FILES = [
'project_description.json',
'bootloader/*.map',
'bootloader/*.elf',
'*.map',
'*.elf',
'dependencies.lock',
'build_log.txt',
'size.json',
]
[docs]
def copy_bin_to_new_path(
from_dir: str,
to_path: str,
*,
zip_output: bool = False,
force: bool = True,
copy_elf: bool = True,
extra_files: Optional[List[str]] = None,
) -> None:
"""Copy build files to new path
Args:
from_dir (str): the app build directory. eg: ./build/
to_path (str): Destination directory or zip file to save the bin files.
zip_output (bool, optional): Zip the destination directory. Defaults to False.
force (bool, optional): Delete the destination dir if it is already exists. Defaults to True.
copy_elf (bool, optional): Copy elf and map files as well. Defaults to True.
extra_files (Optional[List[str]], optional): . Defaults to None.
"""
from_path = Path(from_dir).resolve().absolute()
to_path_obj = Path(to_path).resolve().absolute()
assert from_path.is_dir()
if force and to_path_obj.exists():
logger.info(f'Removing existing destination directory or file {to_path_obj}')
if to_path_obj.is_dir():
shutil.rmtree(str(to_path_obj))
else:
to_path_obj.unlink()
if zip_output:
assert to_path_obj.suffix == '.zip'
to_path_obj.parent.mkdir(parents=True, exist_ok=True)
to_dir = Path(tempfile.mkdtemp())
else:
to_dir = to_path_obj
to_dir.mkdir(parents=True, exist_ok=True)
logger.debug(f'Copying bin files from {from_path} to {to_path_obj}')
all_patterns = BuildFilesPatterns.BIN_FILES[:]
if copy_elf:
all_patterns.extend(BuildFilesPatterns.MAP_AND_ELF_FILES)
if extra_files:
all_patterns.extend(extra_files)
for pattern in all_patterns:
for _file in from_path.glob(pattern):
assert _file.is_file()
relative_path = _file.relative_to(from_path)
to_file = to_dir / relative_path
if not to_file.parent.is_dir():
to_file.parent.mkdir(parents=True)
logging.debug(f'Copying file {relative_path}')
shutil.copy(_file, to_dir / relative_path)
# parse 'partition-table.bin'
if IDF_PATH:
part_csv = Path(to_dir) / 'partition_table' / 'partition-table.csv'
part_bin = Path(to_dir) / 'partition_table' / 'partition-table.bin'
parttool = Path(IDF_PATH) / 'components' / 'partition_table' / 'gen_esp32part.py'
if not part_csv.is_file() and part_bin.is_file():
assert parttool.is_file(), 'Can not find gen_esp32part.py'
try:
_cmd = ['python', str(parttool.absolute()), str(part_bin), str(part_csv)]
subprocess.check_call(_cmd, shell=False)
except subprocess.SubprocessError as e:
logger.error(f'Failed to gen partition-table.csv: {str(e)}')
# zip the destination directory
if zip_output:
shutil.make_archive(str(to_path_obj.with_suffix('')), 'zip', root_dir=str(to_dir))
shutil.rmtree(str(to_dir))
[docs]
def main() -> None:
logging.basicConfig(level=logging.DEBUG)
parser = argparse.ArgumentParser(description='Copy bin files')
parser.add_argument('from_dir', type=str, help='source directory of build bin files')
parser.add_argument('to_path', type=str, help='destination directory or zip file')
parser.add_argument(
'--zip',
action='store_true',
help='zip destination, and to_path should end with .zip',
)
parser.add_argument(
'--no-force',
dest='force',
action='store_false',
help='do not remove existing destination directory or file',
)
parser.set_defaults(force=True)
parser.add_argument(
'--no-copy-elf',
dest='copy_elf',
action='store_false',
help='do not copy elf/map files',
)
parser.set_defaults(copy_elf=True)
parser.add_argument(
'--extra-files',
metavar='PATTERN',
action='append',
nargs='+',
default=[],
help='extra glob patterns to copy, can be used multiple times',
)
args = parser.parse_args()
extra_files = [pattern for item in args.extra_files for pattern in item] or None
copy_bin_to_new_path(
args.from_dir,
args.to_path,
zip_output=args.zip,
force=args.force,
copy_elf=args.copy_elf,
extra_files=extra_files,
)
if __name__ == '__main__':
main()