ldgen.py 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158
  1. #!/usr/bin/env python
  2. #
  3. # Copyright 2018-2019 Espressif Systems (Shanghai) PTE LTD
  4. #
  5. # Licensed under the Apache License, Version 2.0 (the "License");
  6. # you may not use this file except in compliance with the License.
  7. # You may obtain a copy of the License at
  8. #
  9. # http://www.apache.org/licenses/LICENSE-2.0
  10. #
  11. # Unless required by applicable law or agreed to in writing, software
  12. # distributed under the License is distributed on an "AS IS" BASIS,
  13. # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. # See the License for the specific language governing permissions and
  15. # limitations under the License.
  16. #
  17. import argparse
  18. import json
  19. import sys
  20. import tempfile
  21. import subprocess
  22. import os
  23. import errno
  24. from fragments import FragmentFile
  25. from sdkconfig import SDKConfig
  26. from generation import GenerationModel, TemplateModel, SectionsInfo
  27. from ldgen_common import LdGenFailure
  28. from pyparsing import ParseException, ParseFatalException
  29. from io import StringIO
  30. try:
  31. import confgen
  32. except Exception:
  33. parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
  34. kconfig_new_dir = os.path.abspath(parent_dir_name + "/kconfig_new")
  35. sys.path.insert(0, kconfig_new_dir)
  36. import confgen
  37. def _update_environment(args):
  38. env = [(name, value) for (name,value) in (e.split("=",1) for e in args.env)]
  39. for name, value in env:
  40. value = " ".join(value.split())
  41. os.environ[name] = value
  42. if args.env_file is not None:
  43. env = json.load(args.env_file)
  44. os.environ.update(confgen.dict_enc_for_env(env))
  45. def main():
  46. argparser = argparse.ArgumentParser(description="ESP-IDF linker script generator")
  47. argparser.add_argument(
  48. "--input", "-i",
  49. help="Linker template file",
  50. type=argparse.FileType("r"))
  51. argparser.add_argument(
  52. "--fragments", "-f",
  53. type=argparse.FileType("r"),
  54. help="Input fragment files",
  55. nargs="+")
  56. argparser.add_argument(
  57. "--libraries-file",
  58. type=argparse.FileType("r"),
  59. help="File that contains the list of libraries in the build")
  60. argparser.add_argument(
  61. "--output", "-o",
  62. help="Output linker script",
  63. type=str)
  64. argparser.add_argument(
  65. "--config", "-c",
  66. help="Project configuration")
  67. argparser.add_argument(
  68. "--kconfig", "-k",
  69. help="IDF Kconfig file")
  70. argparser.add_argument(
  71. "--env", "-e",
  72. action='append', default=[],
  73. help='Environment to set when evaluating the config file', metavar='NAME=VAL')
  74. argparser.add_argument('--env-file', type=argparse.FileType('r'),
  75. help='Optional file to load environment variables from. Contents '
  76. 'should be a JSON object where each key/value pair is a variable.')
  77. argparser.add_argument(
  78. "--objdump",
  79. help="Path to toolchain objdump")
  80. args = argparser.parse_args()
  81. input_file = args.input
  82. fragment_files = [] if not args.fragments else args.fragments
  83. libraries_file = args.libraries_file
  84. config_file = args.config
  85. output_path = args.output
  86. kconfig_file = args.kconfig
  87. objdump = args.objdump
  88. try:
  89. sections_infos = SectionsInfo()
  90. for library in libraries_file:
  91. library = library.strip()
  92. if library:
  93. dump = StringIO(subprocess.check_output([objdump, "-h", library]).decode())
  94. dump.name = library
  95. sections_infos.add_sections_info(dump)
  96. generation_model = GenerationModel()
  97. _update_environment(args) # assign args.env and args.env_file to os.environ
  98. sdkconfig = SDKConfig(kconfig_file, config_file)
  99. for fragment_file in fragment_files:
  100. try:
  101. fragment_file = FragmentFile(fragment_file, sdkconfig)
  102. except (ParseException, ParseFatalException) as e:
  103. # ParseException is raised on incorrect grammar
  104. # ParseFatalException is raised on correct grammar, but inconsistent contents (ex. duplicate
  105. # keys, key unsupported by fragment, unexpected number of values, etc.)
  106. raise LdGenFailure("failed to parse %s\n%s" % (fragment_file.name, str(e)))
  107. generation_model.add_fragments_from_file(fragment_file)
  108. mapping_rules = generation_model.generate_rules(sections_infos)
  109. script_model = TemplateModel(input_file)
  110. script_model.fill(mapping_rules)
  111. with tempfile.TemporaryFile("w+") as output:
  112. script_model.write(output)
  113. output.seek(0)
  114. if not os.path.exists(os.path.dirname(output_path)):
  115. try:
  116. os.makedirs(os.path.dirname(output_path))
  117. except OSError as exc:
  118. if exc.errno != errno.EEXIST:
  119. raise
  120. with open(output_path, "w") as f: # only create output file after generation has suceeded
  121. f.write(output.read())
  122. except LdGenFailure as e:
  123. print("linker script generation failed for %s\nERROR: %s" % (input_file.name, e))
  124. sys.exit(1)
  125. if __name__ == "__main__":
  126. main()