mirror of https://github.com/ARMmbed/mbed-os.git
Merge pull request #10021 from bridadan/uvision_postbuild_regions
Enable post build bootloader merging in uvisionpull/10385/head
commit
3bda0ef36f
|
@ -136,7 +136,7 @@ def get_exporter_toolchain(ide):
|
||||||
|
|
||||||
|
|
||||||
def generate_project_files(resources, export_path, target, name, toolchain, ide,
|
def generate_project_files(resources, export_path, target, name, toolchain, ide,
|
||||||
macros=None):
|
zip, macros=None):
|
||||||
"""Generate the project files for a project
|
"""Generate the project files for a project
|
||||||
|
|
||||||
Positional arguments:
|
Positional arguments:
|
||||||
|
@ -147,13 +147,14 @@ def generate_project_files(resources, export_path, target, name, toolchain, ide,
|
||||||
toolchain - a toolchain class that corresponds to the toolchain used by the
|
toolchain - a toolchain class that corresponds to the toolchain used by the
|
||||||
IDE or makefile
|
IDE or makefile
|
||||||
ide - IDE name to export to
|
ide - IDE name to export to
|
||||||
|
zip - True if the exported project will be zipped
|
||||||
|
|
||||||
Optional arguments:
|
Optional arguments:
|
||||||
macros - additional macros that should be defined within the exported
|
macros - additional macros that should be defined within the exported
|
||||||
project
|
project
|
||||||
"""
|
"""
|
||||||
exporter_cls, _ = get_exporter_toolchain(ide)
|
exporter_cls, _ = get_exporter_toolchain(ide)
|
||||||
exporter = exporter_cls(target, export_path, name, toolchain,
|
exporter = exporter_cls(target, export_path, name, toolchain, zip,
|
||||||
extra_symbols=macros, resources=resources)
|
extra_symbols=macros, resources=resources)
|
||||||
exporter.generate()
|
exporter.generate()
|
||||||
files = exporter.generated_files
|
files = exporter.generated_files
|
||||||
|
@ -278,9 +279,9 @@ def export_project(src_paths, export_path, target, ide, libraries_paths=None,
|
||||||
if toolchain.config.name:
|
if toolchain.config.name:
|
||||||
name = toolchain.config.name
|
name = toolchain.config.name
|
||||||
|
|
||||||
files, exporter = generate_project_files(resources, export_path,
|
files, exporter = generate_project_files(
|
||||||
target, name, toolchain, ide,
|
resources, export_path, target, name, toolchain, ide, zip_proj, macros=macros
|
||||||
macros=macros)
|
)
|
||||||
if zip_proj:
|
if zip_proj:
|
||||||
resources.add_features(ALLOWED_FEATURES)
|
resources.add_features(ALLOWED_FEATURES)
|
||||||
if isinstance(zip_proj, basestring):
|
if isinstance(zip_proj, basestring):
|
||||||
|
|
|
@ -73,7 +73,7 @@ class Exporter(object):
|
||||||
CLEAN_FILES = ("GettingStarted.html",)
|
CLEAN_FILES = ("GettingStarted.html",)
|
||||||
|
|
||||||
|
|
||||||
def __init__(self, target, export_dir, project_name, toolchain,
|
def __init__(self, target, export_dir, project_name, toolchain, zip,
|
||||||
extra_symbols=None, resources=None):
|
extra_symbols=None, resources=None):
|
||||||
"""Initialize an instance of class exporter
|
"""Initialize an instance of class exporter
|
||||||
Positional arguments:
|
Positional arguments:
|
||||||
|
@ -81,6 +81,7 @@ class Exporter(object):
|
||||||
export_dir - the directory of the exported project files
|
export_dir - the directory of the exported project files
|
||||||
project_name - the name of the project
|
project_name - the name of the project
|
||||||
toolchain - an instance of class toolchain
|
toolchain - an instance of class toolchain
|
||||||
|
zip - True if the exported project will be zipped
|
||||||
|
|
||||||
Keyword arguments:
|
Keyword arguments:
|
||||||
extra_symbols - a list of extra macros for the toolchain
|
extra_symbols - a list of extra macros for the toolchain
|
||||||
|
@ -94,6 +95,7 @@ class Exporter(object):
|
||||||
self.jinja_environment = Environment(loader=jinja_loader)
|
self.jinja_environment = Environment(loader=jinja_loader)
|
||||||
resources.win_to_unix()
|
resources.win_to_unix()
|
||||||
self.resources = resources
|
self.resources = resources
|
||||||
|
self.zip = zip
|
||||||
self.generated_files = []
|
self.generated_files = []
|
||||||
getting_started_name = "GettingStarted.html"
|
getting_started_name = "GettingStarted.html"
|
||||||
dot_mbed_name = ".mbed"
|
dot_mbed_name = ".mbed"
|
||||||
|
|
|
@ -2,13 +2,14 @@ from __future__ import print_function, absolute_import
|
||||||
from builtins import str
|
from builtins import str
|
||||||
|
|
||||||
import os
|
import os
|
||||||
from os.path import normpath, exists, dirname
|
from os.path import normpath, exists, dirname, join, abspath, relpath
|
||||||
import ntpath
|
import ntpath
|
||||||
import copy
|
import copy
|
||||||
from collections import namedtuple
|
from collections import namedtuple
|
||||||
import shutil
|
import shutil
|
||||||
from subprocess import Popen, PIPE
|
from subprocess import Popen, PIPE
|
||||||
import re
|
import re
|
||||||
|
import json
|
||||||
|
|
||||||
from tools.resources import FileType
|
from tools.resources import FileType
|
||||||
from tools.targets import TARGET_MAP, CORE_ARCH
|
from tools.targets import TARGET_MAP, CORE_ARCH
|
||||||
|
@ -243,7 +244,31 @@ class Uvision(Exporter):
|
||||||
'include_paths': ';'.join(self.filter_dot(d) for d in
|
'include_paths': ';'.join(self.filter_dot(d) for d in
|
||||||
self.resources.inc_dirs),
|
self.resources.inc_dirs),
|
||||||
'device': DeviceUvision(self.target),
|
'device': DeviceUvision(self.target),
|
||||||
|
'postbuild_step_active': 0,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if self.toolchain.config.has_regions and not self.zip:
|
||||||
|
# Serialize region information
|
||||||
|
export_info = {}
|
||||||
|
restrict_size = getattr(self.toolchain.config.target, "restrict_size")
|
||||||
|
if restrict_size:
|
||||||
|
export_info["target"] = {
|
||||||
|
"restrict_size": restrict_size
|
||||||
|
}
|
||||||
|
|
||||||
|
binary_path = "BUILD/{}.hex".format(self.project_name)
|
||||||
|
region_list = list(self.toolchain.config.regions)
|
||||||
|
export_info["region_list"] = [
|
||||||
|
r._replace(filename=binary_path) if r.active else r for r in region_list
|
||||||
|
]
|
||||||
|
# Enable the post build step
|
||||||
|
postbuild_script_path = join(relpath(dirname(__file__)), "postbuild.py")
|
||||||
|
ctx['postbuild_step'] = (
|
||||||
|
'python {} "$K\\" "#L"'.format(postbuild_script_path)
|
||||||
|
)
|
||||||
|
ctx['postbuild_step_active'] = 1
|
||||||
|
ctx['export_info'] = json.dumps(export_info, indent=4)
|
||||||
|
|
||||||
sct_file_ref = self.resources.get_file_refs(FileType.LD_SCRIPT)[0]
|
sct_file_ref = self.resources.get_file_refs(FileType.LD_SCRIPT)[0]
|
||||||
sct_file_ref = self.toolchain.correct_scatter_shebang(
|
sct_file_ref = self.toolchain.correct_scatter_shebang(
|
||||||
sct_file_ref, dirname(sct_file_ref.name)
|
sct_file_ref, dirname(sct_file_ref.name)
|
||||||
|
@ -271,10 +296,29 @@ class Uvision(Exporter):
|
||||||
'uvision/uvision_debug.tmpl', ctx, self.project_name + ".uvoptx"
|
'uvision/uvision_debug.tmpl', ctx, self.project_name + ".uvoptx"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
if ctx['postbuild_step_active']:
|
||||||
|
self.gen_file(
|
||||||
|
'uvision/debug_init.ini', ctx, 'debug_init.ini'
|
||||||
|
)
|
||||||
|
self.gen_file(
|
||||||
|
'uvision/flash_init.ini', ctx, 'flash_init.ini'
|
||||||
|
)
|
||||||
|
self.gen_file(
|
||||||
|
'uvision/export_info.tmpl', ctx, 'export_info.json'
|
||||||
|
)
|
||||||
|
|
||||||
@staticmethod
|
@staticmethod
|
||||||
def clean(project_name):
|
def clean(project_name):
|
||||||
os.remove(project_name + ".uvprojx")
|
os.remove(project_name + ".uvprojx")
|
||||||
os.remove(project_name + ".uvoptx")
|
os.remove(project_name + ".uvoptx")
|
||||||
|
|
||||||
|
if exists("export_info.json"):
|
||||||
|
os.remove("export_info.json")
|
||||||
|
if exists("debug_init.ini"):
|
||||||
|
os.remove("debug_init.ini")
|
||||||
|
if exists("flash_init.ini"):
|
||||||
|
os.remove("flash_init.ini")
|
||||||
|
|
||||||
# legacy .build directory cleaned if exists
|
# legacy .build directory cleaned if exists
|
||||||
if exists('.build'):
|
if exists('.build'):
|
||||||
shutil.rmtree('.build')
|
shutil.rmtree('.build')
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
// Add the debug symbols
|
||||||
|
Load "$L@L.axf" NOCODE
|
|
@ -0,0 +1 @@
|
||||||
|
{{export_info}}
|
|
@ -0,0 +1,2 @@
|
||||||
|
// Flash device
|
||||||
|
Load "$L@L_combined.hex" INCREMENTAL
|
|
@ -0,0 +1,109 @@
|
||||||
|
#! /usr/bin/env python2
|
||||||
|
"""
|
||||||
|
mbed SDK
|
||||||
|
Copyright (c) 2019 ARM Limited
|
||||||
|
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
|
||||||
|
LIBRARIES BUILD
|
||||||
|
"""
|
||||||
|
from __future__ import print_function, division, absolute_import
|
||||||
|
|
||||||
|
import sys
|
||||||
|
from os.path import join, abspath, dirname, normpath
|
||||||
|
import json
|
||||||
|
from shutil import copyfile
|
||||||
|
from argparse import ArgumentParser
|
||||||
|
from copy import copy
|
||||||
|
|
||||||
|
# Be sure that the tools directory is in the search path
|
||||||
|
ROOT = abspath(join(dirname(__file__), "../../../"))
|
||||||
|
sys.path.insert(0, ROOT)
|
||||||
|
|
||||||
|
from tools.regions import merge_region_list, UPDATE_WHITELIST
|
||||||
|
from tools.notifier.term import TerminalNotifier
|
||||||
|
from tools.config import Region
|
||||||
|
from tools.utils import split_path, run_cmd_ext, generate_update_filename
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
parser = ArgumentParser()
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"toolchain_path",
|
||||||
|
help="Path to the Keil folder"
|
||||||
|
)
|
||||||
|
|
||||||
|
parser.add_argument(
|
||||||
|
"linker_output",
|
||||||
|
help="Path to the built axf file"
|
||||||
|
)
|
||||||
|
|
||||||
|
options = parser.parse_args()
|
||||||
|
axf_file = normpath(options.linker_output)
|
||||||
|
output_directory, output_name, output_ext = split_path(axf_file)
|
||||||
|
hex_file = join(output_directory, output_name + ".hex")
|
||||||
|
combined_hex_file = join(output_directory, output_name + "_combined.hex")
|
||||||
|
|
||||||
|
command = [
|
||||||
|
join(normpath(options.toolchain_path), "ARM/ARMCC/bin/fromelf.exe"),
|
||||||
|
"--i32", "--output", hex_file, axf_file
|
||||||
|
]
|
||||||
|
stdout, stderr, retcode = run_cmd_ext(command)
|
||||||
|
|
||||||
|
if retcode:
|
||||||
|
err_msg = (
|
||||||
|
"Failed to convert axf to hex.\r\n"
|
||||||
|
"Command: {}\r\n"
|
||||||
|
"retcode: {}\r\n"
|
||||||
|
"stdout: {}\r\n"
|
||||||
|
"stderr: {}"
|
||||||
|
).format(command, retcode, stdout, stderr)
|
||||||
|
raise Exception(err_msg)
|
||||||
|
|
||||||
|
with open(join("export_info.json"), "r") as export_info_file:
|
||||||
|
export_info_data = json.load(export_info_file)
|
||||||
|
|
||||||
|
region_list = [Region(*r) for r in export_info_data.get("region_list", [])]
|
||||||
|
|
||||||
|
for index, region in enumerate(copy(region_list)):
|
||||||
|
if region.name == "application":
|
||||||
|
region_data = region._asdict()
|
||||||
|
region_data["filename"] = hex_file
|
||||||
|
region_list[index] = Region(**region_data)
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
raise Exception("No application region found")
|
||||||
|
|
||||||
|
notify = TerminalNotifier()
|
||||||
|
restrict_size = export_info_data.get("target", {}).get("restrict_size")
|
||||||
|
merge_region_list(
|
||||||
|
region_list, combined_hex_file, notify, restrict_size=restrict_size
|
||||||
|
)
|
||||||
|
|
||||||
|
update_regions = [
|
||||||
|
r for r in region_list if r.name in UPDATE_WHITELIST
|
||||||
|
]
|
||||||
|
|
||||||
|
if update_regions:
|
||||||
|
update_res = normpath(
|
||||||
|
join(
|
||||||
|
output_directory,
|
||||||
|
generate_update_filename(output_name, None)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
merge_region_list(
|
||||||
|
update_regions, update_res, notify, restrict_size=restrict_size
|
||||||
|
)
|
||||||
|
|
||||||
|
sys.exit(0)
|
|
@ -82,9 +82,9 @@
|
||||||
<nStopB2X>0</nStopB2X>
|
<nStopB2X>0</nStopB2X>
|
||||||
</BeforeMake>
|
</BeforeMake>
|
||||||
<AfterMake>
|
<AfterMake>
|
||||||
<RunUserProg1>0</RunUserProg1>
|
<RunUserProg1>{{postbuild_step_active}}</RunUserProg1>
|
||||||
<RunUserProg2>0</RunUserProg2>
|
<RunUserProg2>0</RunUserProg2>
|
||||||
<UserProg1Name></UserProg1Name>
|
<UserProg1Name>{{postbuild_step if postbuild_step_active else ''}}</UserProg1Name>
|
||||||
<UserProg2Name></UserProg2Name>
|
<UserProg2Name></UserProg2Name>
|
||||||
<UserProg1Dos16Mode>0</UserProg1Dos16Mode>
|
<UserProg1Dos16Mode>0</UserProg1Dos16Mode>
|
||||||
<UserProg2Dos16Mode>0</UserProg2Dos16Mode>
|
<UserProg2Dos16Mode>0</UserProg2Dos16Mode>
|
||||||
|
@ -182,7 +182,7 @@
|
||||||
<bUseTDR>1</bUseTDR>
|
<bUseTDR>1</bUseTDR>
|
||||||
<Flash2>BIN\UL2CM3.DLL</Flash2>
|
<Flash2>BIN\UL2CM3.DLL</Flash2>
|
||||||
<Flash3></Flash3>
|
<Flash3></Flash3>
|
||||||
<Flash4></Flash4>
|
<Flash4>{{'./flash_init.ini' if postbuild_step_active else ''}}</Flash4>
|
||||||
<pFcarmOut></pFcarmOut>
|
<pFcarmOut></pFcarmOut>
|
||||||
<pFcarmGrp></pFcarmGrp>
|
<pFcarmGrp></pFcarmGrp>
|
||||||
<pFcArmRoot></pFcArmRoot>
|
<pFcArmRoot></pFcArmRoot>
|
||||||
|
|
|
@ -6,11 +6,20 @@
|
||||||
<ToolsetNumber>0x4</ToolsetNumber>
|
<ToolsetNumber>0x4</ToolsetNumber>
|
||||||
<ToolsetName>ARM-ADS</ToolsetName>
|
<ToolsetName>ARM-ADS</ToolsetName>
|
||||||
<TargetOption>
|
<TargetOption>
|
||||||
|
{% if postbuild_step_active %}
|
||||||
|
<OPTTT>
|
||||||
|
<RunAbUc>1</RunAbUc>
|
||||||
|
</OPTTT>
|
||||||
|
{% endif %}
|
||||||
<DebugOpt>
|
<DebugOpt>
|
||||||
<uSim>0</uSim>
|
<uSim>0</uSim>
|
||||||
<uTrg>1</uTrg>
|
<uTrg>1</uTrg>
|
||||||
<nTsel>11</nTsel>
|
<nTsel>11</nTsel>
|
||||||
<pMon>{{device.debug_interface.bin_loc}}</pMon>
|
<pMon>{{device.debug_interface.bin_loc}}</pMon>
|
||||||
|
{% if postbuild_step_active %}
|
||||||
|
<tIfile>./debug_init.ini</tIfile>
|
||||||
|
<tLdApp>0</tLdApp>
|
||||||
|
{% endif %}
|
||||||
</DebugOpt>
|
</DebugOpt>
|
||||||
<TargetDriverDllRegistry>
|
<TargetDriverDllRegistry>
|
||||||
<SetRegEntry>
|
<SetRegEntry>
|
||||||
|
|
|
@ -106,8 +106,8 @@ def merge_region_list(
|
||||||
region_list,
|
region_list,
|
||||||
destination,
|
destination,
|
||||||
notify,
|
notify,
|
||||||
config,
|
padding=b'\xFF',
|
||||||
padding=b'\xFF'
|
restrict_size=None
|
||||||
):
|
):
|
||||||
"""Merge the region_list into a single image
|
"""Merge the region_list into a single image
|
||||||
|
|
||||||
|
@ -115,6 +115,7 @@ def merge_region_list(
|
||||||
region_list - list of regions, which should contain filenames
|
region_list - list of regions, which should contain filenames
|
||||||
destination - file name to write all regions to
|
destination - file name to write all regions to
|
||||||
padding - bytes to fill gaps with
|
padding - bytes to fill gaps with
|
||||||
|
restrict_size - check to ensure a region fits within the given size
|
||||||
"""
|
"""
|
||||||
merged = IntelHex()
|
merged = IntelHex()
|
||||||
_, format = splitext(destination)
|
_, format = splitext(destination)
|
||||||
|
@ -145,7 +146,7 @@ def merge_region_list(
|
||||||
# Normally, we assume that part.maxddr() can be beyond
|
# Normally, we assume that part.maxddr() can be beyond
|
||||||
# end of rom. If the size is restricted with config, don't
|
# end of rom. If the size is restricted with config, don't
|
||||||
# allow this.
|
# allow this.
|
||||||
if config.target.restrict_size is not None:
|
if restrict_size is not None:
|
||||||
part_size = (part.maxaddr() - part.minaddr()) + 1
|
part_size = (part.maxaddr() - part.minaddr()) + 1
|
||||||
if part_size > region.size:
|
if part_size > region.size:
|
||||||
raise ToolException(
|
raise ToolException(
|
||||||
|
|
|
@ -247,11 +247,9 @@ class BuildApiTests(unittest.TestCase):
|
||||||
"prepare_toolchain was called with an incorrect app_config")
|
"prepare_toolchain was called with an incorrect app_config")
|
||||||
|
|
||||||
@patch('tools.regions.intelhex_offset')
|
@patch('tools.regions.intelhex_offset')
|
||||||
@patch('tools.config')
|
def test_merge_region_no_fit(self, mock_intelhex_offset):
|
||||||
def test_merge_region_no_fit(self, mock_config, mock_intelhex_offset):
|
|
||||||
"""
|
"""
|
||||||
Test that merge_region_list call fails when part size overflows region size.
|
Test that merge_region_list call fails when part size overflows region size.
|
||||||
:param mock_config: config object that is mocked.
|
|
||||||
:param mock_intelhex_offset: mocked intel_hex_offset call.
|
:param mock_intelhex_offset: mocked intel_hex_offset call.
|
||||||
:return:
|
:return:
|
||||||
"""
|
"""
|
||||||
|
@ -267,15 +265,12 @@ class BuildApiTests(unittest.TestCase):
|
||||||
region_list = [region_application, region_post_application]
|
region_list = [region_application, region_post_application]
|
||||||
# path to store the result in, should not get used as we expect exception.
|
# path to store the result in, should not get used as we expect exception.
|
||||||
res = "./"
|
res = "./"
|
||||||
mock_config.target.restrict_size = 90000
|
|
||||||
toolexception = False
|
toolexception = False
|
||||||
|
|
||||||
try:
|
try:
|
||||||
merge_region_list(region_list, res, notify, mock_config)
|
merge_region_list(region_list, res, notify, restrict_size=90000)
|
||||||
except ToolException:
|
except ToolException:
|
||||||
toolexception = True
|
toolexception = True
|
||||||
except Exception as e:
|
|
||||||
print("%s %s" % (e.message, e.args))
|
|
||||||
|
|
||||||
self.assertTrue(toolexception, "Expected ToolException not raised")
|
self.assertTrue(toolexception, "Expected ToolException not raised")
|
||||||
|
|
||||||
|
|
|
@ -663,7 +663,10 @@ class mbedToolchain:
|
||||||
region_list = [r._replace(filename=binary) if r.active else r
|
region_list = [r._replace(filename=binary) if r.active else r
|
||||||
for r in region_list]
|
for r in region_list]
|
||||||
res = "{}.{}".format(join(self.build_dir, name), ext)
|
res = "{}.{}".format(join(self.build_dir, name), ext)
|
||||||
merge_region_list(region_list, res, self.notify, self.config)
|
merge_region_list(
|
||||||
|
region_list, res, self.notify,
|
||||||
|
restrict_size=self.config.target.restrict_size
|
||||||
|
)
|
||||||
update_regions = [
|
update_regions = [
|
||||||
r for r in region_list if r.name in UPDATE_WHITELIST
|
r for r in region_list if r.name in UPDATE_WHITELIST
|
||||||
]
|
]
|
||||||
|
@ -676,7 +679,7 @@ class mbedToolchain:
|
||||||
update_regions,
|
update_regions,
|
||||||
update_res,
|
update_res,
|
||||||
self.notify,
|
self.notify,
|
||||||
self.config
|
restrict_size=self.config.target.restrict_size
|
||||||
)
|
)
|
||||||
return res, update_res
|
return res, update_res
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Reference in New Issue