Add IAR project file parser

This commit is contained in:
Julien LE PAGE 2023-08-04 10:04:56 +02:00
parent 28cc72530f
commit 422697765b

221
ewp_parser.py Normal file
View File

@ -0,0 +1,221 @@
#---Imports---------------------------------------------------------------------
import os
import xml.etree.ElementTree as ETree
import argparse
import json
#---definitions-----------------------------------------------------------------
#---ewp parsing-----------------------------------------------------------------
def parse_ewp(ewp_file, arg_file=None):
"""
Parse an ewp file, returning a list of defines, includes and files to compile.
The first valid configuration in the file is used. An additionnal argvars file
can be used to directly expand the variables present in the various fields
:param ewp_file: the ewp file to parse
:param arg_file: additionnal argvars file
:returns: tuple of defines, includes and files to compile
"""
#parse ewp file for defines and include paths
ewp_base_path = os.path.dirname(os.path.abspath(ewp_file))
ewp_tree = ETree.parse(ewp_file)
for config in ewp_tree.findall(".//configuration"):
raw_defines = list_option_values(config, "CCDefines")
raw_includes = list_option_values(config, "CCIncludePath2")
if 0 != len(raw_defines) and 0 != len(raw_includes):
break
raw_files = list_project_files(ewp_tree)
#parse argvars, if any, for global values
if None != arg_file:
arg_tree = ETree.parse(arg_file)
variables = list_variables(arg_tree)
else:
variables = {}
variables["PROJ_DIR"] = ewp_base_path
#expand variables in the defines and include paths
defines = expand_variables(raw_defines, variables)
includes = expand_variables(raw_includes, variables)
files = expand_variables(raw_files, variables)
norm_files = [os.path.normpath(file) for file in files]
return (defines, includes, norm_files)
def list_option_values(config, option_name):
"""
Tool function to list the compilation options with the given name defined in
an ewp file's configuration
:param config: xml tree representing an ewp configuration
:param option_name: the name of the options to list
:returns: a list of the compilations options
"""
found = False
values = []
for option in config.findall('settings/data/option'):
for child in option:
if (child.text == option_name):
found = True
if found and (child.tag == "state") and (None != child.text):
values.append(normalize_path(child.text))
if found:
break
return values
def list_variables(arg_tree):
"""
Tool function to list the variables defined in an argvars file
:param argvars: xml tree representing an argvars file
:returns: a list of the defined variables
"""
variables = {}
for variable in arg_tree.findall(".//variable"):
name = variable.find("name")
value = variable.find("value")
if None != name:
variables[name.text] = value.text
return variables
def list_project_files(ewp_tree):
"""
Tool function to generate a list of the file to compile from a ewp file
:param ewp_tree: xml tree representing a ewp file
:returns: a list of the file to compile
"""
raw_files = [file.find("name").text for file in ewp_tree.findall(".//file")]
return [os.path.abspath(normalize_path(file)) for file in raw_files]
def expand_variables(raw_values, variables):
"""
Tool function to expand the given values (paths, defines, ...) using the
global project variables. These variables are usually stored in the
custom_argvars file. Unknown variables will be left as-is
:param raw_values: list of non-expanded values
:param variables: dictionnay of the variables and their associated values
:returns: the list of values with the variables expanded
"""
values = []
for raw_value in raw_values:
value = raw_value
for (key, var) in variables.items():
value = value.replace("$" + key + "$", var)
values.append(value)
return values
#---compile_commands generation-------------------------------------------------
def generate_compile_commands(output_file, root_path, options, files):
"""
Generates a json compilation database according to this standard:
https://clang.llvm.org/docs/JSONCompilationDatabase.html, using the given
options and list of files. The options must be full, valid, compiler options
:param output_file: the name of the file to generate
:param options: a list of the compilation options
:param files: a list of files
"""
with open(output_file, "w") as output:
commands = []
for file in files:
commands.append(
{"directory": root_path, "arguments": options, "file": file})
json.dump(commands, output, indent=True)
def normalize_path(path):
"""
Normalizes the given path. Handles mixed-convention paths (dos/unix), which
the standard library functions don't
:param path: the path to normalize
:returns: the normalized path
"""
return os.path.normpath(path.replace("\\", "/"))
#---main function---------------------------------------------------------------
if "__main__" == __name__:
#setup command line
parser = argparse.ArgumentParser(
prog="Ewp2CompileCommands",
description="Parse the .ewp file of an IAR project and generates the "
"corresponding compile_commands.json")
parser.add_argument("ewp_file", type=str,
help="path to the project's ewp file")
parser.add_argument("-a", "--argvars", type=str,
help="path to the project's argvars file")
parser.add_argument("-f", "--files", type=str,
help="list of files to use instead of the ones in the "
"ewp file")
parser.add_argument("-o", "--options", type=str,
help="list of options to add to project options")
parser.add_argument("-r", "--root", type=str,
help="path to project root. current path is used if not "
"specified")
#get command line parameters
args = parser.parse_args()
ewp_file = args.ewp_file
arg_file = args.argvars
file_list = args.files
opt_list = args.options
root_path = args.root
#parse ewp and argvars files
(defs, incs, files) = parse_ewp(ewp_file, arg_file)
#replace files if necessary
if None != file_list:
with open(file_list, "r") as f:
raw_files = [line.strip() for line in f.readlines()]
files = [os.path.abspath(normalize_path(file)) for file in raw_files]
#parse additionnal options
opts = []
if None != opt_list:
with open(opt_list, "r") as f:
opts = [line.strip() for line in f.readlines()]
#set default root path if necessary
if None == root_path:
root_path = os.getcwd()
#generate compile_commands
defs = ["-D" + define for define in defs]
incs = ["-I" + inc for inc in incs]
generate_compile_commands("compile_commands.json", root_path,
defs + incs + opts, files)