esp-idf/tools/ldgen/ldgen.py
Tian Yunhao b246ec86f3 ldgen: override LC_ALL to C before running objdump
When using a Linux system configured with `zh_CN.UTF-8` as `$LANG`,
and running raw cmake command to build the project (rather than using
`idf.py build`), output of objdump will be Chinese
(like `在归档文件 libesp_pm.a 中`), resulting in parsing error
`pyparsing.ParseException: Expected "In archive" (at char 0), (line:1, col:1)`
at entity.py line 129.

This commit forces objdump to use raw locale setting (`C`), to ensure
it always make English output that's able to be parsed.

Closes https://github.com/espressif/esp-idf/pull/7903
2021-11-15 17:55:24 +01:00

181 lines
5.8 KiB
Python
Executable File

#!/usr/bin/env python
#
# Copyright 2021 Espressif Systems (Shanghai) CO 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.
#
import argparse
import errno
import json
import os
import subprocess
import sys
import tempfile
from io import StringIO
from entity import EntityDB
from fragments import FragmentFile
from generation import Generation
from ldgen_common import LdGenFailure
from linker_script import LinkerScript
from pyparsing import ParseException, ParseFatalException
from sdkconfig import SDKConfig
try:
import confgen
except Exception:
parent_dir_name = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
kconfig_new_dir = os.path.abspath(parent_dir_name + '/kconfig_new')
sys.path.insert(0, kconfig_new_dir)
import confgen
def _update_environment(args):
env = [(name, value) for (name,value) in (e.split('=',1) for e in args.env)]
for name, value in env:
value = ' '.join(value.split())
os.environ[name] = value
if args.env_file is not None:
env = json.load(args.env_file)
os.environ.update(confgen.dict_enc_for_env(env))
def main():
argparser = argparse.ArgumentParser(description='ESP-IDF linker script generator')
argparser.add_argument(
'--input', '-i',
help='Linker template file',
type=argparse.FileType('r'))
argparser.add_argument(
'--fragments', '-f',
type=argparse.FileType('r'),
help='Input fragment files',
nargs='+')
argparser.add_argument(
'--libraries-file',
type=argparse.FileType('r'),
help='File that contains the list of libraries in the build')
argparser.add_argument(
'--output', '-o',
help='Output linker script',
type=str)
argparser.add_argument(
'--config', '-c',
help='Project configuration')
argparser.add_argument(
'--kconfig', '-k',
help='IDF Kconfig file')
argparser.add_argument(
'--check-mapping',
help='Perform a check if a mapping (archive, obj, symbol) exists',
action='store_true'
)
argparser.add_argument(
'--check-mapping-exceptions',
help='Mappings exempted from check',
type=argparse.FileType('r')
)
argparser.add_argument(
'--env', '-e',
action='append', default=[],
help='Environment to set when evaluating the config file', metavar='NAME=VAL')
argparser.add_argument('--env-file', type=argparse.FileType('r'),
help='Optional file to load environment variables from. Contents '
'should be a JSON object where each key/value pair is a variable.')
argparser.add_argument(
'--objdump',
help='Path to toolchain objdump')
args = argparser.parse_args()
input_file = args.input
fragment_files = [] if not args.fragments else args.fragments
libraries_file = args.libraries_file
config_file = args.config
output_path = args.output
kconfig_file = args.kconfig
objdump = args.objdump
check_mapping = args.check_mapping
if args.check_mapping_exceptions:
check_mapping_exceptions = [line.strip() for line in args.check_mapping_exceptions]
else:
check_mapping_exceptions = None
try:
sections_infos = EntityDB()
for library in libraries_file:
library = library.strip()
if library:
new_env = os.environ.copy()
new_env['LC_ALL']='C'
dump = StringIO(subprocess.check_output([objdump, '-h', library], env=new_env).decode())
dump.name = library
sections_infos.add_sections_info(dump)
generation_model = Generation(check_mapping, check_mapping_exceptions)
_update_environment(args) # assign args.env and args.env_file to os.environ
sdkconfig = SDKConfig(kconfig_file, config_file)
for fragment_file in fragment_files:
try:
fragment_file = FragmentFile(fragment_file, sdkconfig)
except (ParseException, ParseFatalException) as e:
# ParseException is raised on incorrect grammar
# ParseFatalException is raised on correct grammar, but inconsistent contents (ex. duplicate
# keys, key unsupported by fragment, unexpected number of values, etc.)
raise LdGenFailure('failed to parse %s\n%s' % (fragment_file.name, str(e)))
generation_model.add_fragments_from_file(fragment_file)
mapping_rules = generation_model.generate(sections_infos)
script_model = LinkerScript(input_file)
script_model.fill(mapping_rules)
with tempfile.TemporaryFile('w+') as output:
script_model.write(output)
output.seek(0)
if not os.path.exists(os.path.dirname(output_path)):
try:
os.makedirs(os.path.dirname(output_path))
except OSError as exc:
if exc.errno != errno.EEXIST:
raise
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()