Add IAR project file parser
This commit is contained in:
parent
28cc72530f
commit
422697765b
221
ewp_parser.py
Normal file
221
ewp_parser.py
Normal 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)
|
||||
|
||||
Loading…
Reference in New Issue
Block a user