ldgen.py 5.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. #!/usr/bin/env python
  2. #
  3. # SPDX-FileCopyrightText: 2021-2022 Espressif Systems (Shanghai) CO LTD
  4. # SPDX-License-Identifier: Apache-2.0
  5. #
  6. import argparse
  7. import errno
  8. import json
  9. import os
  10. import subprocess
  11. import sys
  12. import tempfile
  13. from io import StringIO
  14. from ldgen.entity import EntityDB
  15. from ldgen.fragments import parse_fragment_file
  16. from ldgen.generation import Generation
  17. from ldgen.ldgen_common import LdGenFailure
  18. from ldgen.linker_script import LinkerScript
  19. from ldgen.sdkconfig import SDKConfig
  20. from pyparsing import ParseException, ParseFatalException
  21. try:
  22. import confgen
  23. except Exception:
  24. parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
  25. kconfig_new_dir = os.path.abspath(parent_dir_name + '/kconfig_new')
  26. sys.path.insert(0, kconfig_new_dir)
  27. import confgen
  28. def _update_environment(args):
  29. env = [(name, value) for (name,value) in (e.split('=',1) for e in args.env)]
  30. for name, value in env:
  31. value = ' '.join(value.split())
  32. os.environ[name] = value
  33. if args.env_file is not None:
  34. env = json.load(args.env_file)
  35. os.environ.update(confgen.dict_enc_for_env(env))
  36. def main():
  37. argparser = argparse.ArgumentParser(description='ESP-IDF linker script generator')
  38. argparser.add_argument(
  39. '--input', '-i',
  40. help='Linker template file',
  41. type=argparse.FileType('r'))
  42. fragments_group = argparser.add_mutually_exclusive_group()
  43. fragments_group.add_argument(
  44. '--fragments', '-f',
  45. type=argparse.FileType('r'),
  46. help='Input fragment files',
  47. nargs='+'
  48. )
  49. fragments_group.add_argument(
  50. '--fragments-list',
  51. help='Input fragment files as a semicolon-separated list',
  52. type=str
  53. )
  54. argparser.add_argument(
  55. '--libraries-file',
  56. type=argparse.FileType('r'),
  57. help='File that contains the list of libraries in the build')
  58. argparser.add_argument(
  59. '--output', '-o',
  60. help='Output linker script',
  61. type=str)
  62. argparser.add_argument(
  63. '--config', '-c',
  64. help='Project configuration')
  65. argparser.add_argument(
  66. '--kconfig', '-k',
  67. help='IDF Kconfig file')
  68. argparser.add_argument(
  69. '--check-mapping',
  70. help='Perform a check if a mapping (archive, obj, symbol) exists',
  71. action='store_true'
  72. )
  73. argparser.add_argument(
  74. '--check-mapping-exceptions',
  75. help='Mappings exempted from check',
  76. type=argparse.FileType('r')
  77. )
  78. argparser.add_argument(
  79. '--env', '-e',
  80. action='append', default=[],
  81. help='Environment to set when evaluating the config file', metavar='NAME=VAL')
  82. argparser.add_argument('--env-file', type=argparse.FileType('r'),
  83. help='Optional file to load environment variables from. Contents '
  84. 'should be a JSON object where each key/value pair is a variable.')
  85. argparser.add_argument(
  86. '--objdump',
  87. help='Path to toolchain objdump')
  88. args = argparser.parse_args()
  89. input_file = args.input
  90. libraries_file = args.libraries_file
  91. config_file = args.config
  92. output_path = args.output
  93. kconfig_file = args.kconfig
  94. objdump = args.objdump
  95. fragment_files = []
  96. if args.fragments_list:
  97. fragment_files = args.fragments_list.split(';')
  98. elif args.fragments:
  99. fragment_files = args.fragments
  100. check_mapping = args.check_mapping
  101. if args.check_mapping_exceptions:
  102. check_mapping_exceptions = [line.strip() for line in args.check_mapping_exceptions]
  103. else:
  104. check_mapping_exceptions = None
  105. try:
  106. sections_infos = EntityDB()
  107. for library in libraries_file:
  108. library = library.strip()
  109. if library:
  110. new_env = os.environ.copy()
  111. new_env['LC_ALL'] = 'C'
  112. dump = StringIO(subprocess.check_output([objdump, '-h', library], env=new_env).decode())
  113. dump.name = library
  114. sections_infos.add_sections_info(dump)
  115. generation_model = Generation(check_mapping, check_mapping_exceptions)
  116. _update_environment(args) # assign args.env and args.env_file to os.environ
  117. sdkconfig = SDKConfig(kconfig_file, config_file)
  118. for fragment_file in fragment_files:
  119. try:
  120. fragment_file = parse_fragment_file(fragment_file, sdkconfig)
  121. except (ParseException, ParseFatalException) as e:
  122. # ParseException is raised on incorrect grammar
  123. # ParseFatalException is raised on correct grammar, but inconsistent contents (ex. duplicate
  124. # keys, key unsupported by fragment, unexpected number of values, etc.)
  125. raise LdGenFailure('failed to parse %s\n%s' % (fragment_file, str(e)))
  126. generation_model.add_fragments_from_file(fragment_file)
  127. mapping_rules = generation_model.generate(sections_infos)
  128. script_model = LinkerScript(input_file)
  129. script_model.fill(mapping_rules)
  130. with tempfile.TemporaryFile('w+') as output:
  131. script_model.write(output)
  132. output.seek(0)
  133. if not os.path.exists(os.path.dirname(output_path)):
  134. try:
  135. os.makedirs(os.path.dirname(output_path))
  136. except OSError as exc:
  137. if exc.errno != errno.EEXIST:
  138. raise
  139. with open(output_path, 'w') as f: # only create output file after generation has suceeded
  140. f.write(output.read())
  141. except LdGenFailure as e:
  142. print('linker script generation failed for %s\nERROR: %s' % (input_file.name, e))
  143. sys.exit(1)
  144. if __name__ == '__main__':
  145. main()