Przeglądaj źródła

ldgen: Improve error output when linker input is invalid, don't create output file until end of process

Angus Gratton 7 lat temu
rodzic
commit
d4a5682e7d

+ 23 - 0
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)

+ 8 - 2
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

+ 2 - 2
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"
 

+ 13 - 11
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()
+    main()

+ 1 - 2
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
+        return grammar