From d4a5682e7dfa24bddaa5ba3cae7b8d74c4c60bd3 Mon Sep 17 00:00:00 2001 From: Angus Gratton Date: Thu, 29 Nov 2018 11:46:54 +1100 Subject: [PATCH] ldgen: Improve error output when linker input is invalid, don't create output file until end of process --- tools/ldgen/common.py | 23 +++++++++++++++++++++++ tools/ldgen/fragments.py | 10 ++++++++-- tools/ldgen/generation.py | 4 ++-- tools/ldgen/ldgen.py | 24 +++++++++++++----------- tools/ldgen/sdkconfig.py | 3 +-- 5 files changed, 47 insertions(+), 17 deletions(-) create mode 100644 tools/ldgen/common.py diff --git a/tools/ldgen/common.py b/tools/ldgen/common.py new file mode 100644 index 000000000..55a2a74bc --- /dev/null +++ b/tools/ldgen/common.py @@ -0,0 +1,23 @@ +# +# Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD +# +# 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. +# + + +class LdGenFailure(RuntimeError): + """ + Parent class for any ldgen runtime failure which is due to input data + """ + def __init__(self, message): + super(LdGenFailure, self).__init__(message) diff --git a/tools/ldgen/fragments.py b/tools/ldgen/fragments.py index d28ce01ba..f30770305 100644 --- a/tools/ldgen/fragments.py +++ b/tools/ldgen/fragments.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # # Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD # @@ -22,6 +21,7 @@ import os from sdkconfig import SDKConfig from pyparsing import * +from common import LdGenFailure """ Fragment file internal representation. Parses and stores instances of the fragment definitions @@ -43,7 +43,13 @@ class FragmentFileModel(): # Set any text beginnning with # as comment parser.ignore("#" + restOfLine) - self.fragments = parser.parseFile(fragment_file, parseAll=True) + try: + self.fragments = parser.parseFile(fragment_file, parseAll=True) + except ParseBaseException as e: + # the actual parse error is kind of useless for normal users, so just point to the location of + # the error + raise LdGenFailure("Parse error in linker fragment %s: error at line %d col %d (char %d)" % ( + fragment_file.name, e.lineno, e.column, e.loc)) for fragment in self.fragments: fragment.path = path diff --git a/tools/ldgen/generation.py b/tools/ldgen/generation.py index 3dc14de63..6c5407614 100644 --- a/tools/ldgen/generation.py +++ b/tools/ldgen/generation.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # # Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD # @@ -25,6 +24,7 @@ import fnmatch from sdkconfig import SDKConfig from fragments import FragmentFileModel, Sections, Scheme, Mapping, Fragment from pyparsing import * +from common import LdGenFailure """ Encapsulates a generated placement rule placed under a target @@ -561,7 +561,7 @@ class TemplateModel: Exception for linker script generation failures such as undefined references/ failure to evaluate conditions, duplicate mappings, etc. """ -class GenerationException(Exception): +class GenerationException(LdGenFailure): UNDEFINED_REFERENCE = "Undefined reference" diff --git a/tools/ldgen/ldgen.py b/tools/ldgen/ldgen.py index 25ce86e3c..714833404 100755 --- a/tools/ldgen/ldgen.py +++ b/tools/ldgen/ldgen.py @@ -19,10 +19,12 @@ import argparse import os import traceback import sys +import tempfile from fragments import FragmentFileModel from sdkconfig import SDKConfig from generation import GenerationModel, TemplateModel, SectionsInfo +from common import LdGenFailure def main(): @@ -48,7 +50,7 @@ def main(): argparser.add_argument( "--output", "-o", help = "Output linker script", - type = argparse.FileType("w")) + type = str) argparser.add_argument( "--config", "-c", @@ -70,10 +72,10 @@ def main(): input_file = args.input fragment_files = [] if not args.fragments else args.fragments config_file = args.config - output_file = args.output + output_path = args.output sections_info_files = [] if not args.sections else args.sections kconfig_file = args.kconfig - + try: sections_infos = SectionsInfo() @@ -92,14 +94,14 @@ def main(): script_model = TemplateModel(input_file) script_model.fill(mapping_rules, sdkconfig) - script_model.write(output_file) - except Exception as e: - print("linker script generation failed for %s\nERROR: %s" % (input_file.name, e.message)) - # Delete the file so the entire build will fail; and not use an outdated script. - os.remove(output_file.name) - # Print traceback and exit - traceback.print_exc() + with tempfile.TemporaryFile("w+") as output: + script_model.write(output) + output.seek(0) + with open(output_path, "w") as f: # only create output file after generation has suceeded + f.write(output.read()) + except LdGenFailure as e: + print("linker script generation failed for %s\nERROR: %s" % (input_file.name, e)) sys.exit(1) if __name__ == "__main__": - main() \ No newline at end of file + main() diff --git a/tools/ldgen/sdkconfig.py b/tools/ldgen/sdkconfig.py index 9ef23ec84..9afbf4784 100644 --- a/tools/ldgen/sdkconfig.py +++ b/tools/ldgen/sdkconfig.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # # Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD # @@ -84,4 +83,4 @@ class SDKConfig: ("&&", 2, opAssoc.LEFT), ("||", 2, opAssoc.LEFT)]) - return grammar \ No newline at end of file + return grammar