mirror of
https://github.com/ArduPilot/ardupilot.git
synced 2026-10-02 10:23:25 +08:00
The bundled launcher and runner import process_utils, but the runtime file list omitted it. Include the dependency and exercise both entry points from the isolated bundle.
210 lines
7.2 KiB
Python
Executable File
210 lines
7.2 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# AP_FLAKE8_CLEAN
|
|
|
|
"""Build a source-checkout-independent ArduPilot Renode runtime tree."""
|
|
|
|
import argparse
|
|
import hashlib
|
|
import json
|
|
import os
|
|
import shutil
|
|
import stat
|
|
import sys
|
|
import tempfile
|
|
|
|
from pathlib import Path
|
|
|
|
RUNTIME_FILES = (
|
|
'README.md',
|
|
'THIRD_PARTY_NOTICES.md',
|
|
'build_bundle.py',
|
|
'chibios_gdb.py',
|
|
'device_emulator.py',
|
|
'dfu.py',
|
|
'driver_catalog.py',
|
|
'extract_logs.py',
|
|
'fat_image.py',
|
|
'gen_board.py',
|
|
'launch.py',
|
|
'physics_protocol.py',
|
|
'process_utils.py',
|
|
'renode_data.py',
|
|
'renode_firmware.py',
|
|
'run.py',
|
|
'usbip_attach.py',
|
|
)
|
|
RUNTIME_TREES = {
|
|
'peripherals': {'.cs'},
|
|
'platforms': {'.repl'},
|
|
'scripts': {'.resc'},
|
|
}
|
|
HWDEF_SUFFIXES = {'.dat', '.inc', '.parm'}
|
|
HWDEF_SCRIPT_SUFFIXES = {'.py', '.h'}
|
|
DMAMUX_HEADERS = (
|
|
'modules/ChibiOS/os/hal/ports/STM32/STM32G4xx/stm32_dmamux.h',
|
|
'modules/ChibiOS/os/hal/ports/STM32/STM32H7xx/stm32_dmamux.h',
|
|
'modules/ChibiOS/os/hal/ports/STM32/STM32L4xx+/stm32_dmamux.h',
|
|
)
|
|
|
|
|
|
def _copy_file(root, destination, relative):
|
|
source = root / relative
|
|
if not source.is_file():
|
|
raise FileNotFoundError(source)
|
|
target = destination / relative
|
|
target.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copy2(source, target)
|
|
|
|
|
|
def _copy_tree(source, target, suffixes=None):
|
|
if not source.is_dir():
|
|
raise FileNotFoundError(source)
|
|
for path in sorted(source.rglob('*')):
|
|
if (path.is_file() and
|
|
(suffixes is None or path.suffix.lower() in suffixes) and
|
|
'__pycache__' not in path.parts):
|
|
destination = target / path.relative_to(source)
|
|
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copy2(path, destination)
|
|
|
|
|
|
def _sha256(path):
|
|
digest = hashlib.sha256()
|
|
with path.open('rb') as stream:
|
|
for chunk in iter(lambda: stream.read(1024 * 1024), b''):
|
|
digest.update(chunk)
|
|
return digest.hexdigest()
|
|
|
|
|
|
def _manifest(destination, physics_name):
|
|
files = []
|
|
for path in sorted(destination.rglob('*')):
|
|
if path.is_file() and path.name != 'bundle.json':
|
|
files.append({
|
|
'path': path.relative_to(destination).as_posix(),
|
|
'size': path.stat().st_size,
|
|
'sha256': _sha256(path),
|
|
})
|
|
return {
|
|
'format': 1,
|
|
'launcher': 'Tools/renode/launch.py',
|
|
'physics': 'Tools/renode/bin/%s' % physics_name,
|
|
'files': files,
|
|
}
|
|
|
|
|
|
def _claim_output(destination):
|
|
destination.parent.mkdir(parents=True, exist_ok=True)
|
|
lock = destination.with_name('.%s.bundle-lock' % destination.name)
|
|
descriptor = os.open(lock, os.O_CREAT | os.O_RDWR, 0o600)
|
|
try:
|
|
if os.name == 'nt':
|
|
import msvcrt
|
|
if os.fstat(descriptor).st_size == 0:
|
|
os.write(descriptor, b'\0')
|
|
os.lseek(descriptor, 0, os.SEEK_SET)
|
|
msvcrt.locking(descriptor, msvcrt.LK_NBLCK, 1)
|
|
else:
|
|
import fcntl
|
|
fcntl.flock(descriptor, fcntl.LOCK_EX | fcntl.LOCK_NB)
|
|
except OSError as error:
|
|
os.close(descriptor)
|
|
raise FileExistsError(
|
|
'another bundle build owns %s' % lock) from error
|
|
try:
|
|
if destination.exists():
|
|
raise FileExistsError('%s already exists' % destination)
|
|
os.ftruncate(descriptor, 0)
|
|
os.lseek(descriptor, 0, os.SEEK_SET)
|
|
os.write(descriptor, ('pid=%u\n' % os.getpid()).encode())
|
|
except Exception:
|
|
os.close(descriptor)
|
|
raise
|
|
return descriptor
|
|
|
|
|
|
def create_bundle(root, destination, physics_binary):
|
|
"""Create an atomic runtime tree and return its generated manifest."""
|
|
root = Path(root).resolve()
|
|
destination = Path(destination).resolve()
|
|
physics_binary = Path(physics_binary).resolve()
|
|
if not physics_binary.is_file():
|
|
raise FileNotFoundError(physics_binary)
|
|
physics_name = ('renode-physics.exe'
|
|
if physics_binary.suffix.lower() == '.exe'
|
|
else 'renode-physics')
|
|
|
|
lock_descriptor = _claim_output(destination)
|
|
try:
|
|
temporary = Path(tempfile.mkdtemp(
|
|
prefix='.%s-' % destination.name, dir=destination.parent))
|
|
try:
|
|
renode_source = root / 'Tools' / 'renode'
|
|
for name in RUNTIME_FILES:
|
|
_copy_file(root, temporary, Path('Tools') / 'renode' / name)
|
|
for name, suffixes in RUNTIME_TREES.items():
|
|
_copy_tree(renode_source / name,
|
|
temporary / 'Tools' / 'renode' / name, suffixes)
|
|
|
|
hwdef = root / 'libraries' / 'AP_HAL_ChibiOS' / 'hwdef'
|
|
_copy_tree(hwdef, temporary / hwdef.relative_to(root),
|
|
HWDEF_SUFFIXES)
|
|
_copy_tree(hwdef / 'scripts',
|
|
temporary / hwdef.relative_to(root) / 'scripts',
|
|
HWDEF_SCRIPT_SUFFIXES)
|
|
_copy_tree(hwdef / 'common',
|
|
temporary / hwdef.relative_to(root) / 'common')
|
|
_copy_file(root, temporary,
|
|
Path('libraries/AP_HAL/hwdef/scripts/hwdef.py'))
|
|
_copy_file(root, temporary,
|
|
Path('Tools/AP_Bootloader/board_types.txt'))
|
|
for relative in DMAMUX_HEADERS:
|
|
_copy_file(root, temporary, Path(relative))
|
|
_copy_tree(root / 'Tools' / 'IO_Firmware',
|
|
temporary / 'Tools' / 'IO_Firmware', {'.bin'})
|
|
_copy_tree(root / 'Tools' / 'bootloaders',
|
|
temporary / 'Tools' / 'bootloaders', {'.bin'})
|
|
|
|
installed_physics = (temporary / 'Tools' / 'renode' / 'bin' /
|
|
physics_name)
|
|
installed_physics.parent.mkdir(parents=True, exist_ok=True)
|
|
shutil.copy2(physics_binary, installed_physics)
|
|
installed_physics.chmod(
|
|
installed_physics.stat().st_mode | stat.S_IXUSR)
|
|
|
|
manifest = _manifest(temporary, physics_name)
|
|
(temporary / 'bundle.json').write_text(
|
|
json.dumps(manifest, indent=2, sort_keys=True) + '\n')
|
|
if destination.exists():
|
|
raise FileExistsError('%s was created during the build' %
|
|
destination)
|
|
os.replace(temporary, destination)
|
|
return manifest
|
|
except Exception:
|
|
shutil.rmtree(temporary, ignore_errors=True)
|
|
raise
|
|
finally:
|
|
os.close(lock_descriptor)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument('output', help='new bundle directory to create')
|
|
parser.add_argument('--physics-binary', required=True,
|
|
help='native renode-physics executable to install')
|
|
args = parser.parse_args()
|
|
root = Path(__file__).resolve().parents[2]
|
|
try:
|
|
manifest = create_bundle(root, args.output, args.physics_binary)
|
|
except (FileExistsError, FileNotFoundError, OSError) as error:
|
|
parser.error(str(error))
|
|
total = sum(entry['size'] for entry in manifest['files'])
|
|
print('created %s with %u files (%u bytes)' %
|
|
(Path(args.output).resolve(), len(manifest['files']), total))
|
|
return 0
|
|
|
|
|
|
if __name__ == '__main__':
|
|
sys.exit(main())
|