from __future__ import print_function
from collections import OrderedDict
from Version import build_time

import argparse
import io
import json
import os
import logging
import platform
import re
import shlex
import shutil
import subprocess
import sys
import tempfile
if sys.version_info >= (3, 0):
  from functools import reduce

from CCT_Lib import (
  currentDir, onWindows, cygpath, qacli, get_CCT_path, get_user_CCT_path
)
if onWindows():
  try:
    import _winreg
  except ModuleNotFoundError:
    import winreg as _winreg
  from CCT_Lib import getRegValue

QNX_GPP = "!qnx_gpp"

blacklist = { }

cct_logger = logging.getLogger(__name__)
out_logger = logging.getLogger(__name__+"_out")

def stringify(s):
  t = None
  if s.startswith("\"") and s.endswith("\""):
    t = s[1:-1]
  elif s.startswith("\\\"") and s.endswith("\\\""):
    t = s[2:-2]
  if t:
    t = "_PRL_STRINGIFY(" + t + ")"
  else:
    t = s
  result = t
  if "\"" in t:
    # escape any internal non-escaped quote
    result = ""
    escape = False
    for c in t:
      if escape:
        escape = False
        result += c
      elif c == '\\':
        escape = True
        result += c
      elif c == '"':
        result += "\\\""
      else:
        result += c
  return result

def process_define(var, setting, sep="="):
  eq = setting.find(sep)
  if eq < 0:
    v = setting
    val = "1" if sep == "=" else ""
  else:
    v = setting[:eq]
    while v.find("(") > 0 and v.find(")") < 0:
      eq = setting.find(sep, eq + 1)
      v = setting[:eq]
    val = stringify(setting[eq + 1:].strip())
  var[v] = val

def process_option(var, includePaths, opt):
  result = False
  if opt.startswith("-I"):
    includePaths.append(opt[2:].strip() )
    result = True
  elif opt.startswith("-YI"):
    includePaths.append(opt[3:].strip() )
    result = True
  elif (
    opt.startswith("--include_directory=") or 
    opt.startswith("--include_no_mmd=") or
    opt.startswith("--c_include_directory=") or
    opt.startswith("--cxx_include_directory=") or
    opt.startswith("--cxx_include_directory=") or
    opt.startswith("--sys_include=") or 
    opt.startswith("--sys_include_directory=") or 
    opt.startswith("--std_cxx_include_directory=") or 
    opt.startswith("--sys_cxx_include_directory=")
  ):
    includePaths.append(opt[opt.find("=") + 1:].strip() )
    result = True
  elif opt.startswith("-D"):
    process_define(var, opt[2:] )
    result = True
  return result

def ignore_opt(
  opt, ignore_options, ignore_arg_options, ignore_both_options, keep_options
):
  done = False
  ignore = False
  ignore_next = False
  if opt == "" or opt in ignore_options:
    ignore = True
  else:
    done = (opt in keep_options)
    if not done:
      for o in ignore_arg_options:
        if opt.startswith(o):
          i = opt.find("=")
          ignore = (i < 0) or (opt[0:i] == o)
          done = ignore
          if o == opt:
            ignore_next = True
    if not done:
      for o in ignore_both_options:
        if opt.startswith(o):
          ignore = True
          ignore_next = True
  return ignore, ignore_next

def is_derived_command(args):
  return args.build or args.qactrace or args.klocworktrace

# Compose compiler command from command line and extra options for obtaining
# built-in information.
# This should filter out:
# * file name
# * any option interfering with extra options
# * project options that will be picked up by process monitor
def compose_command(args, extra):
  seenFile = False
  ignore_options = [ ]
  ignore_arg_options = [ ]
  ignore_both_options = [ ]
  keep_options = [ ]
  if args.hierarchy == "gnu" or args.hierarchy == "ghs":
    ignore_options = [
      "-v", "-dM", "-E", "-Wp,", "-M", "-MM", "-MG", "-MP", "-"
    ]
    ignore_arg_options = [
      "-I", "-MD", "-MF", "-MMD", "-MQ", "-MT", "-dependency-file", "-iquote",
      "-o", "-include",
      "--outdir" # Microchip
    ]
    if args.hierarchy == "ghs":
      ignore_options += [ "-H", "-P", "-Q","-S" ]
      keep_options = [ "-os_dir" ]
    if (extra.count("-V") == 2) and ("clang" in args.command[0] ) :
      # When clang is invoked from xc8-cc we don't want to strip defines
      # and we want to use the standard GNU preprocessing options.
      extra = [ "-Wp,-dM", "-v", "-E" ]
    else:
      ignore_arg_options.append("-D")
  elif args.hierarchy == "arm":
    ignore_options = [ "-E", "--list_macros", "--asm", "--echo", "--list",
      "-M", "--md", "--mm", "--pch", "-S", "--show_cmdline" ]
    ignore_arg_options = [
      "-I", "-D", "-o", "--apcs", "-Aopt", "--create_pch", "--depend",
      "--list_dir", "--errors", "--info", "-Lopt", "--omf_browse",
      "--preinclude"
    ]
  elif args.hierarchy == "diab":
    ignore_options = [ "-E", "-P", "-Xcpp-dump-symbols" ]
    ignore_arg_options = [ "-I", "-D", "-o", "-Xmake-dependency", "-H" ]
  elif args.hierarchy == "iar":
    ignore_options = [ "-", "--mfc" ]
    ignore_arg_options = [
      "--predef_macros", "--dependencies", "-D",
      "--diagnostics_tables", "-I", "-o", "--output",
      "--preprocess=c", "--preprocess=n", "--preprocess=s"
    ]
    ignore_both_options = [ "-l" ]
  elif args.hierarchy == "renesas":
    # We only want to extract the version so ignore -nologo
    ignore_options = [ "-nologo", "-_msgoff" ]
    ignore_arg_options = [ "-o", "-output" ]
  elif args.hierarchy == "tasking":
    ignore_options = [ "--pch", "--pch-verbose" ]
    ignore_arg_options = [
      "--cpu", "-C", "--help", "--preprocess", "-E",
      "--create_pch", "--use-pch",
      "--include-directory", "-I", "--define", "-D", "--output", "-o",
      "--include-macros-file", "--include-file", "-H",
      "--error-file", "--dep-file", "--create"
    ]
  elif args.hierarchy == "ti":
    ignore_options = [ "--pch" ]
    ignore_arg_options = [
      "--preproc", "-pp", "--include_path", "-I", "--define", "-D",
      "--create_pch", "--obj_directory", "-fr", "--output_file", "-fe",
      "--pp_directory", "--c_extension", "-ec", "--cpp_extension", "-ep",
      "--cpp_file", "-fp", "--c_file", "-fc"
    ]
  ignore = False
  ignore_next = False
  result = None
  for opt in args.command:
    if ignore_next:
      ignore_next = False
    elif not result:
      result = [ opt ]
    else:
      o = opt
      if args.hierarchy == "gnu":
        if opt.startswith("-Wc,") or opt.startswith("-Wp,"):
          o = opt[4:]
      (ignore, ignore_next) = ignore_opt(
        o, ignore_options, ignore_arg_options, ignore_both_options, keep_options
      )
      if not ignore:
        if not(opt.startswith("-")):
          # file argument?
          ext = os.path.splitext(opt)[1]
          if ext.lower() in (".c", ".cc", ".cpp", ".cxx"):
            seenFile = True
            ignore = True
            if not args.language:
              args.language = "c" if ext == ".c" else "cpp"
      if not ignore:
        result.append(opt)
      ignore = False
  return (
    None if is_derived_command(args) and not seenFile else result + extra
  )

def dumpOutput(args, command, stdoutdata, stderrdata):
  if cct_logger.isEnabledFor(logging.DEBUG):
    if command:
      cct_logger.debug("Command: {cmd}".format(cmd=command))
    cct_logger.debug("Output:")
    for line in stdoutdata.splitlines():
      cct_logger.debug(line)
    cct_logger.debug("End of output:")
    cct_logger.debug("Error output:")
    for line in stderrdata.splitlines():
      cct_logger.debug(line)
    cct_logger.debug("End of error output:")

def extract_version(compilerInfo, line):
  words = line.split()
  for w in words:
    nr_digits = 0
    nr_dots = 0
    for c in w:
      if c.isdigit():
        nr_digits += 1
      if c == ".":
        nr_dots += 1
    if nr_digits > 1 and nr_dots > 0 and nr_digits >= nr_dots:
      compilerInfo["COMPILER_VERSION"] = w

def invoke_compiler(args, extra, var, includePaths, compilerInfo):
  try:
    command = compose_command(args, extra)
    # ignore command from build in case it contains no source files
    if not command:
      return
    stdoutdata = ""
    stderrdata = ""
    tmpp = None
    forceIncludeFile = None
    banner = True
    detectVersion = (args.hierarchy == "renesas")
    detectInclude = False
    source = "empty"
    env = os.environ.copy()
    env["LANG"] = "C"
    if (
      args.hierarchy == "iar" or
      args.hierarchy == "ti" or
      args.hierarchy == "renesas" and (not extra or "-P" in extra)
    ):
      (tmpf, tmpp) = tempfile.mkstemp(suffix=".predef")
      os.close(tmpf) # we don't want to inhibit the compiler in writing to this
      if args.hierarchy == "iar":
        command = command + [ "--predef_macros", tmpp ]
      elif args.hierarchy == "ti":
        command = command + [ "--preproc_macros=" + tmpp ]
      else:
        if extra:
          command = command + (
            [ "-o", tmpp ] if compilerInfo["COMPILER"] == "ccrl" else
            [ "-o" + tmpp ] # ccrh, cx
          )
        elif compilerInfo["COMPILER"] == "ccrx":
          command = command + [ "-output=prep=" + tmpp ]
        else:
          command = command + [ "-p" + tmpp, "-kdf" ] # cc78k0r
        source = "pp"
    elif args.hierarchy == "renesas" and "-E" in extra: # ca850
      source = "pp"
    elif args.hierarchy == "diab" and "-H5" in extra:
      detectInclude = True
      source = "include"
    elif args.hierarchy == "tasking":
      if "--help=intrinsics" in extra:
        forceIncludeFile = open(
          os.path.join(
            get_CCT_path(args), "DATA", compilerInfo["OUTPUT_PREFIX"], "Stub",
            "prlforceinclude", "030_intrinsics.h"
          ), "w")
        print("#ifndef QAC_FI_INTRINSICS", file=forceIncludeFile)
        print("#define QAC_FI_INTRINSICS", file=forceIncludeFile)
      elif "--help=typedefs" in extra:
        forceIncludeFile = open(
          os.path.join(
            get_CCT_path(args), "DATA", compilerInfo["OUTPUT_PREFIX"], "Stub",
            "prlforceinclude", "020_types.h"
          ), "w")
        print("#ifndef QAC_FI_TYPES", file=forceIncludeFile)
        print("#define QAC_FI_TYPES", file=forceIncludeFile)
      elif "--version" in extra:
        detectVersion = True
      elif "--preprocess=+includes" in extra:
        detectInclude = True
        source = "include"
    if not args.language:
      args.language = "c"
    p = subprocess.Popen(
      command + [ os.path.join(currentDir, source + "." + args.language) ],
      stdout=subprocess.PIPE, stderr=subprocess.PIPE,
      env=env, cwd=args.currentworkingdirectory
    )
    (stdoutdata, stderrdata) = p.communicate()
    exitCode = p.wait()
    if args.hierarchy == "ghs":
      terminator = None
      for opt in stdoutdata.split():
        if not isinstance(opt, str):
          opt = opt.decode()
        opt = opt.strip()
        if process_option(var, includePaths, opt):
          pass
        elif terminator:
          if opt.endswith(terminator):
            terminator = None
            process_option(var, includePaths, arg + opt[:-1] )
          else:
            arg += " " + opt
        elif opt.startswith("\"") or opt.startswith("'") :
          terminator = opt[0:1]
          if opt.endswith(terminator):
            terminator = None
            process_option(var, includePaths, opt[1:-1])
          else:
            arg = opt[1:]
      if terminator:
        cct_logger.error(
          "Unterminated compiler command line option: {argu}".format(argu=arg)
        )
    lines = [ ]
    if tmpp:
      f = open(tmpp)
      lines = f.readlines()
      f.close()
      os.remove(tmpp)
    else:
      lines = stdoutdata.splitlines()
    for line in lines:
      if not isinstance(line, str):
        line = line.decode()
      line = line.replace("\t", " ").strip()
      if forceIncludeFile:
        if banner and (line.startswith("void") or line.startswith("typedef")):
          banner = False
        if banner:
          print("/*", line, "*/", file=forceIncludeFile)
          if line.startswith("===="):
            banner = False
        else:
          print(line, file=forceIncludeFile)
          if "--help=typedefs" in extra:
            if (line.endswith("__wchar_t;") or line.endswith("__size_t;") or
                line.endswith("__ptrdiff_t;")):
              i = line.rfind("__")
              name = line[i + 2:line.find(";", i)]
              j = line.find("typedef")
              type = line[j + 7:i - 1].strip()
              t = None
              while not(type == t):
                if t:
                  type = t
                t = type.replace("  ", " ")
              if type.startswith("signed "):
                type = type[6:].strip()
              if type.endswith("short int") or type.endswith("long int"):
                type = type[:-4].strip()
              compilerInfo["INTRINSIC_TYPES"][name] = type
      elif line.startswith("#define"):
        setting = line[line.find(" "):].strip()
        if args.hierarchy == "ti":
          commentOpen = setting.find("/*")
          commentClose = setting.find("*/")
          if commentOpen > 0 and commentClose > commentOpen:
            setting = setting[:commentOpen].strip()
        process_define(var, setting, sep=" ")
      elif "\"CCTGEN:" in line:
        colon = line.find(":")
        secondq = line.find("\"", colon)
        v = line[colon + 1:secondq]
        comma = line.find(",", secondq)
        brace = line.find("}", comma)
        val = line[comma + 1:brace].strip()
        if not(v == val):
          var[v] = val
      elif not var and args.hierarchy == "gnu" and extra.count("-V") == 2:
        # search for clang invocation in Microchip xc8-cc output
        cppIdx = line.find("cpp")
        clangIdx = line.find("clang")
        clang = None
        if clangIdx > 0:
          clang = line[:line.find(" ", clangIdx)].strip()
        optStart = line.find("[", max(0, clangIdx))
        optEnd = line.rfind("]")
        if (optStart > 0 and optEnd > optStart) or cppIdx > 0:
          c = [ ]
          if clang:
            c.append(clang)
          lex = shlex.shlex(line[optStart + 1:optEnd].strip())
          lex.whitespace_split = True
          for tok in lex:
            c += splitOpt(tok)
          if clang:
            save = args.command
            args.command = c
            invoke_compiler(args, extra, var, includePaths, compilerInfo)
            args.command = save
          else:
            # occurs when cpp is used which does not support GNU options
            for opt in c:
              process_option(var, includePaths, opt)
            if var:
              var["__STDC__"] = "1"
    if forceIncludeFile:
      print("#endif", file=forceIncludeFile)
      forceIncludeFile.close()
    if detectVersion or detectInclude:
      isCPP = ("__cplusplus" in var)
      for line in stdoutdata.splitlines() + stderrdata.splitlines():
        if not isinstance(line, str):
          line = line.decode()
        line = line.strip()
        if detectInclude:
          if (
            args.hierarchy == "diab" and
            (line.endswith(":") or line.endswith(">"))
          ):
            line = line[:-1].strip()
          path = cygpath(line)
          if os.path.exists(path):
            if os.path.isdir(path):
              includePaths.append(path)
            else:
              dir = os.path.dirname(path)
              if not(dir in includePaths):
                file = os.path.basename(path)
                if file == "stdio.h" or isCPP and (
                  file == "intrinsics.h" or file == "cstddef"
                ):
                  includePaths.append(dir)
                # stl directory needs to be first
                if isCPP and file == "vector":
                  includePaths.insert(0, dir)
        if not("COMPILER_VERSION" in compilerInfo) and detectVersion:
          line = line.lower()
          i = line.find("compiler")
          if i < 0:
            i = line.find("front end")
          if i >= 0:
            v = line.find("v", i)
            if v < 0:
              v = line.find("w", i) # Renesas cx appears to have typo in version
            if v > i:
              compilerInfo["COMPILER_VERSION"] = line[v:line.find(" ", v)]
    if args.hierarchy == "gnu":
      inSearchPaths = False
      for line in stderrdata.splitlines():
        if not isinstance(line, str):
          line = line.decode()
        line = line.strip()
        words = line.split()
        if line.startswith("End of search list"):
          inSearchPaths = False
        elif line.startswith("#include") and line.find("search starts here:") > 0:
          inSearchPaths = True
        elif inSearchPaths:
          includePaths.append(cygpath(line))
        elif (
          not("TARGET" in compilerInfo) and len(words) == 2 and
          words[0] == "Target:"
        ):
          compilerInfo["TARGET"] = words[1]
        elif len(words) > 2 and words[1] == "version":
          compilerInfo["COMPILER_NAME"] = words[0].upper()
          compilerInfo["COMPILER_VERSION"] = words[2]
        elif len(words) > 2 and ("compiler" in words or "Compiler" in words):
          extract_version(compilerInfo, line)
        elif line.startswith("Configured with:"):
          for opt in words:
            if opt.startswith("--host="):
              compilerInfo["HOST"] = opt[opt.find("=") + 1:]
        elif (
          not("TARGET" in compilerInfo) and len(words) > 0 and
          "cc1" in words[0]
        ):
          i = words[0].rfind("/lib/gcc/")
          if i > 0:
            compilerInfo["TARGET"] = words[0][i + 9:words[0].find("/", i + 9)]
    if (not var or not includePaths) and not(exitCode == 0):
      cct_logger.warning(
        "Compiler returned exit code {no} for command:".format(no=exitCode)
      )
      cct_logger.warning(command)
      dumpOutput(args, None, stdoutdata, stderrdata)
  except IOError:
    cct_logger.error("IOError launching compiler!")
  except OSError as exc:
    cct_logger.error("Failed to launch compiler!")
    cct_logger.error("Error: {no}".format(no=exc.errno))
    cct_logger.error("Message: {msg}".format(msg=exc.strerror))
    dumpOutput(args, command, stdoutdata, stderrdata)

def fixIntrinsicType(type):
  type = re.sub("long\s*long\s*unsigned" ,"unsigned long long", type, 1)
  type = re.sub("long\s*unsigned", "unsigned long", type, 1)
  type = re.sub("short\s*unsigned", "unsigned short", type, 1)
  type = re.sub("short\s*int", "short", type, 1)
  type = re.sub("long\s*int", "long", type, 1)
  return type

def printParserOptions(f, opts):
  for opt, val in opts.items():
    if opt.startswith("!"):
      print("* " + opt, file=f)
    elif isinstance(val, bool):
      print("-" + opt + ("+" if val else "-"), file=f)
    else:
      for o in val:
        print("-" + opt, o, file=f)

def deriveParserOptions(args, compilerInfo):
  cppLangVers = compilerInfo["LANGUAGE_STANDARD_VERSION"]
  qacOpts = { }
  qacppOpts = { }
  qacOpts["d"] = [ ]
  qacppOpts["sd"] = [ ]
  qacOpts["bits"] = (args.hierarchy == "gnu")
  qacppOpts["sdep"] = (
    args.hierarchy in ("arm", "tasking") or
    compilerInfo["COMPILER_HIERARCHY"] == "LLVM"
  )
  qacppOpts["c++14tg"] = (args.hierarchy == "arm")
  qacppOpts["ifn"] = (
    args.hierarchy == "diab" or
    compilerInfo["COMPILER_HIERARCHY"] == "LLVM"
  )
  qacppOpts["sig"] = False
  qacppOpts["bits"] = (args.hierarchy == "gnu")
  qacppOpts["c++03nudl"] = (cppLangVers == "c++98")
  # See QCT-45807; we don't want to use this option with recent g++ versions.
  if cppLangVers == "c++14":
    use = True
    if args.hierarchy == "gnu":
      v = compilerInfo["COMPILER_VERSION"]
      v = v[:v.find(".") ]
      if v.isdigit() and int(v) > 8:
        use = False
    if use:
      qacppOpts["c++14es"] = True
  qacppOpts["c++03a"] = qacppOpts["c++03nudl"]
  qacppOpts["c++03np"] = qacppOpts["c++03a"]
  qacppOpts["c++03nuct"] = qacppOpts["c++03a"]
  if args.hierarchy in ("diab", "tasking"):
    qacOpts["ex"] = [ ]
  else:
    qacOpts["ex"] = [ "dollar" ]
  # We don't want to enable C++ comments for C90 since then qac doesn't
  # produce 1011 which is part of M3CM.
  if not(compilerInfo["LANGUAGE_STANDARD_VERSION"] == "c90"):
    qacOpts["ex"].append("c++")
  qacOpts["u"] = (args.hierarchy == "iar")
  if args.hierarchy == "renesas":
    qacOpts["bits"] = True
  for opt in args.command:
    if args.hierarchy == "arm":
      if opt == "--no_dep_name":
        qacppOpts["sdep"] = False
      elif opt == "--no_dollar":
        qacOpts["ex"].remove("dollar")
      elif opt == "--friend_injection":
        qacppOpts["ifn"] = True
      elif opt == "--no_trigraphs":
        qacppOpts["c++14tg"] = False
      elif opt == "--using_std":
        qacppOpts["sig"] = True
      elif opt == "--signed_bitfields":
        qacOpts["bits"] = True
        qacppOpts["bits"] = True
      elif opt == "--unsigned_bitfields":
        qacOpts["bits"] = False
        qacppOpts["bits"] = False
    elif args.hierarchy == "diab":
      if opt == "-Xbit-fields-signed":
        qacOpts["bits"] = True
      elif opt == "-Xdollar-in-ident":
        qacOpts["ex"].append("dollar")
    elif args.hierarchy == "gnu":
      if opt.startswith("-Wc,") or opt.startswith("-Wp,"):
        opt = opt[4:]
      if (
        opt in ("-ansi", "-trigraphs") or
        cppLangVers in ("c++98", "c++11", "c++14")
      ):
        qacppOpts["c++14tg"] = True
      elif opt in (
        "--unsigned-bitfields", "-funsigned-bitfields", "-fno-signed-bitfields"
      ):
        qacOpts["bits"] = False
        qacppOpts["bits"] = False
      elif opt in (
        "--signed-bitfields", "-fsigned-bitfields", "-fno-unsigned-bitfields"
      ):
        qacOpts["bits"] = True
        qacppOpts["bits"] = True
    elif args.hierarchy == "ghs":
      if opt == "--dep_name":
        qacppOpts["sdep"] = True
      elif opt == "--no_dep_name":
        qacppOpts["sdep"] = False
      elif opt == "--using_std":
        qacppOpts["sig"] = True
      elif opt == "--no_using_std":
        qacppOpts["sig"] = False
      elif opt == "--auto_type":
        qacppOpts["c++03a"] = False
      elif opt == "--no_auto_type":
        qacppOpts["c++03a"] = True
      elif opt == "--user_defined_literals":
        qacppOpts["c++03nudl"] = False
      elif opt == "--no_user_defined_literals":
        qacppOpts["c++03nudl"] = True
    elif args.hierarchy == "iar":
      if opt == "--char_is_signed":
        qacOpts["u"] = False
      elif opt == "--char_is_unsigned":
        qacOpts["u"] = True
    elif args.hierarchy == "tasking":
      if opt.startswith("--cpu") or opt.startswith("-C"):
        id = opt[2:] if opt.startswith("-C") else opt[6:]
        qacOpts["d"].append("__CPU__=" + id)
        qacOpts["d"].append("__CPU_" + id.upper() + "__")
        qacppOpts["sd"].append("__CPU__=" + id)
        qacppOpts["sd"].append("__CPU_" + id.upper() + "__")
      elif opt == "--uchar" or opt == "-u":
        qacOpts["u"] = True
      elif opt == "--dollar":
        qacOpts["ex"].append("dollar")
      elif opt == "--friend-injection":
        qacppOpts["ifn"] = True
      elif opt == "--ignore-std":
        qacppOpts["sig"] = True
      elif opt == "--no-arg-dep-lookup":
        qacppOpts["sdep"] = False
      elif opt == "--no-dep-name":
        qacppOpts["sdep"] = False
      elif opt == "--signed-bitfields":
        qacppOpts["bits"] = True
      elif opt == "--unsigned-bitfields":
        qacppOpts["bits"] = False
      elif opt == "--auto-type":
        qacppOpts["c++03a"] = False
      elif opt == "--no-auto-type":
        qacppOpts["c++03a"] = True
      elif opt == "--no-auto-storage":
        qacppOpts["c++03a"] = False
      elif opt == "--nullptr":
        qacppOpts["c++03np"] = False
      elif opt == "--no-nullptr":
        qacppOpts["c++03np"] = True
      elif opt == "--user-defined-literals":
        qacppOpts["c++03nudl"] = False
    elif args.hierarchy == "renesas":
      if opt in ("-Xbitfield=s", "-Xbitfield=signed"):
        qacOpts["bits"] = True
      elif opt in ("-Xbitfield=u", "-Xbitfield=unsigned"):
        qacOpts["bits"] = False
  return (qacOpts, qacppOpts)

def splitGNUOptions(args):
  if args.hierarchy == "gnu":
    split = [ ]
    for opt in args.command:
      if opt.startswith("-Wc,") or opt.startswith("-Wp,"):
        o = opt[4:]
        split += [ opt[0:4] + s for s in o.split(",") ]
      else:
        split.append(opt)
    args.command = split

def splitOpt(tok):
  result = [ ]
  # remove trailing backslash
  last = tok[-1:]
  if last == "\\":
    tok = tok[:-1].strip()
  if tok == "":
    result = [ ]
  else:
    # remove mathcing quotes
    first = tok[:1]
    if first in "\"'" and last == first:
      tok = tok[1:-1].strip()
      if tok == "":
        result = [ ]
    # split if shlex splitter appears to have incorrectly merged options
    optStart = tok.rfind(" -")
    if optStart > 0:
      result = splitOpt(tok[:optStart].strip()).append(tok[optStart + 1:] )
    else:
      result = [ tok ]
  return result

def expandOptionFile(file):
  expandedOpts = [ ]
  if os.path.exists(file):
    lex = shlex.shlex(io.open(file, encoding="utf-8-sig").read())
    lex.whitespace_split = True
#    lex.wordchars += ":;@-.,/\\="
    for tok in lex:
      expandedOpts += splitOpt(tok)
  return expandedOpts

def check_cwd(args):
  if args.currentworkingdirectory and not(
    os.path.exists(args.currentworkingdirectory)
  ):
    cct_logger.warning(
      "Ignoring non-existing current working directory {cwd}.".format(
        cwd=args.currentworkingdirectory
      )
    )
    args.currentworkingdirectory = None

def generateCCT(args):
  global blacklist
  if len(args.command) == 0:
    return False
  args.command[0] = cygpath(args.command[0] )
  if (
    args.blacklist and args.command[0] in blacklist and
    blacklist[args.command[0] ] >= args.blacklist
  ):
    cct_logger.debug(
      "{binary} is blacklisted. Skipping CCT generation.".format(
        binary=args.command[0]
      )
    )
    return False
  check_cwd(args)
  compiler = os.path.splitext(os.path.basename(args.command[0] ))[0].lower()
  if onWindows() and compiler == "program":
    cct_logger.error(
      "Please use double quotes around compiler path if it contains spaces."
    )
  if not args.hierarchy:
    if compiler == "msbuild":
      cct_logger.error("The generator does not support Microsoft compilers.")
      return False
    elif compiler == "armcc":
      args.hierarchy = "arm"
    elif compiler in ("dcc", "dplus"):
      args.hierarchy = "diab"
    elif compiler.startswith("ccint") or compiler.startswith("cxint"):
      args.hierarchy = "ghs"
    elif (
      compiler.endswith("gcc") or compiler.endswith("g++") or
      compiler.endswith("qcc") or compiler.endswith("q++") or
      compiler.startswith("clang") or compiler.endswith("clang") or
      compiler.startswith("xc8")
    ):
      args.hierarchy = "gnu"
    elif compiler.startswith("icc"):
      args.hierarchy = "iar"
    elif compiler.startswith("cl") or compiler.endswith("cl"):
      args.hierarchy = "ti"
    elif compiler in (
      "ctc", "cptc", "cctc", "c166", "cp166", "cc166", "cmcs", "ccmcs"
    ):
      args.hierarchy = "tasking"
      if compiler.startswith("cc"):
        cct_logger.error(
          "{cmd} looks like a Tasking control program; ".format(cmd=compiler) +
          "use {test}{cmd2} for the compiler".format(
            test=("c" if args.language == "c" else "cp"), cmd2=compiler[2:]
          )
        )
        return False
    elif compiler in (
      "ccrx", "ccrl", "ccrh", "ca850", "cc78k0", "cc78k0r", "cx"
    ):
      args.hierarchy = "renesas"
    elif compiler == "cc" or compiler == "cc1" or compiler == "c++":
      args.hierarchy = "gnu"
    elif compiler.startswith("cc") or compiler.startswith("cx"):
      args.hierarchy = "ghs"
    else:
      cct_logger.info(
        "Cannot recognize compiler hierarchy for {cmd}.".format(
          cmd=args.command[0]
        )
      )
      cct_logger.info("Please use option --hierarchy to specify it.")
      return False
  if not args.language:
    if args.hierarchy == "gnu":
      if compiler.endswith("++"):
        args.language = "cpp"
      elif compiler.endswith("cc"):
        args.language = "c"
    elif args.hierarchy == "tasking":
      if compiler.startswith("cp"):
        args.language = "cpp"
      elif compiler.startswith("c"):
        args.language = "c"
    elif args.hierarchy == "ghs":
      if compiler.startswith("cx"):
        args.language = "cpp"
      elif compiler.startswith("cc"):
        args.language = "c"
    elif args.hierarchy == "diab":
      if compiler == "dplus":
        args.language = "cpp"
      elif compiler == "dcc":
        args.language = "cpp"
  splitGNUOptions(args)
  compilerInfo = { }
  if args.hierarchy == "arm":
    compilerInfo = {
      "COMPILER_HIERARCHY": "ARM",
      "COMPILER_NAME": "ARMCC",
      "FLAGS": [ "--via" ]
    }
  elif args.hierarchy == "diab":
    compilerInfo = {
      "COMPILER_HIERARCHY": "WindRiver",
      "COMPILER_NAME": "DIAB",
      "FLAGS": [ "@", "-@", "-@@" ]
    }
  elif args.hierarchy == "ghs":
    compilerInfo = {
      "COMPILER_HIERARCHY": "GHS_MULTI",
      "COMPILER_NAME": compiler.upper(),
      "FLAGS": [ "@" ]
    }
  elif args.hierarchy == "gnu":
    compilerInfo = {
      "COMPILER_HIERARCHY": "GNU",
      "COMPILER_NAME": "GCC",
      "FLAGS": [ "@" ]
    }
  elif args.hierarchy == "iar":
    compilerInfo = {
      "COMPILER_HIERARCHY": "IAR",
      "COMPILER_NAME": compiler.upper(),
      "FLAGS": [ "-f" ]
    }
  elif args.hierarchy == "renesas":
    compilerInfo = {
      "COMPILER_HIERARCHY": "RENESAS",
      "COMPILER_NAME": compiler.upper(),
      "FLAGS": [ "@", "-subcommand=", "-su=" ]
    }
  elif args.hierarchy == "tasking":
    compilerInfo = {
      "COMPILER_HIERARCHY": "TASKING",
      "COMPILER_NAME": compiler.upper(),
      "FLAGS": [ "--option-file=", "-f" ]
    }
  elif args.hierarchy == "ti":
    compilerInfo = {
      "COMPILER_HIERARCHY": "TI",
      "COMPILER_NAME": compiler.upper(),
      "FLAGS": [ "-@", "--cmd_file=" ]
    }
  (qacliPath, version) = qacli(args.qacversion)
  if not version:
    version = args.qacversion
  if not version:
    version = "9999 not specified"
  compilerInfo["QAC_VERSION"] = version
  compilerInfo["COMPILER"] = compiler
  compilerInfo["INTRINSIC_TYPES"] = { }
  # expand compiler option files
  opts = [ ]
  expandedOpts = args.command
  while not(len(opts) == len(expandedOpts)):
    opts = expandedOpts
    expandedOpts = [ ]
    next_is_path = False
    for opt in opts:
      if sys.version_info < (3, 0) and isinstance(opt, unicode):
        opt = opt.encode("utf-8")
      if next_is_path:
        next_is_path = False
        expandedOpts += expandOptionFile(opt)
      elif opt in compilerInfo["FLAGS"]:
        next_is_path = True
      else:
        done = False
        for o in compilerInfo["FLAGS"]:
          if opt.startswith(o):
            expandedOpts += expandOptionFile(opt[len(o):] )
            done = True
        if not done:
          expandedOpts.append(opt)
  args.command = expandedOpts
  command_line = reduce(
    lambda s,t: (s + " " + t),
    map(lambda s: s.replace("\xa0", " ").strip(), args.command)
  )
  compilerInfo["COMPILER_CMD"] = json.dumps(command_line)
  includePaths = [ ]
  commonOpts = { }
  commonOpts["fi"] = [ ]
  next_is_target = False
  next_is_path = False
  next_is_fi = False
  # search required target option for arm and ghs
  for opt in args.command:
    if args.hierarchy == "arm":
      if next_is_path:
        next_is_path = False
        includePaths = opt.split(",")
      elif next_is_target:
        next_is_target = False
        compilerInfo["TARGET"] = opt
      elif opt == "--cpu":
        next_is_target = True
      elif opt.startswith("--cpu"):
        target = opt[opt.find("=") + 1:].strip()
        target = target[opt.find(" ") + 1:].strip()
        compilerInfo["TARGET"] = target
      elif opt == "-J":
        next_is_path = True
      elif opt.startswith("-J"):
        includePaths = opt[2:].split(",")
      elif opt.startswith("--preinclude="):
        commonOpts["fi"].append("\"" +
          cygpath(opt[opt.find("=") + 1:].strip()) + "\"")
    elif args.hierarchy == "diab":
      if opt.startswith("-t"):
        compilerInfo["TARGET"] = opt[2:].replace(":", "_")
    elif args.hierarchy == "gnu":
      if opt.startswith("-Wc,") or opt.startswith("-Wp,"):
        opt = opt[4:]
      if next_is_path:
        next_is_path = False
        commonOpts["fi"].append("\"" + cygpath(opt) + "\"")
      elif opt == "-include":
        next_is_path = True
      elif (
        opt.startswith("-mcpu=") or # xc8-cc and xc16
        opt.startswith("-mprocessor=") or # xc32
        opt.lower().startswith("--chip=") # xc8
      ):
        compilerInfo["TARGET"] = opt[opt.find("=") + 1:]
      elif opt == "-Y_gpp":
        commonOpts[QNX_GPP] = True
    elif args.hierarchy == "ghs":
      if next_is_target:
        next_is_target = False
        compilerInfo["TARGET"] = opt
      elif next_is_path:
        next_is_path = False
        commonOpts["fi"] += [ "\"" + cygpath(p) + "\"" for p in opt.split(",") ]
      elif opt == "-include":
        next_is_path = True
      elif opt.startswith("-cpu=") or opt.startswith("-bsp="):
        target = opt[opt.find("=") + 1:].strip()
        target = target[opt.find(" ") + 1:].strip()
        compilerInfo["TARGET"] = target
      elif opt == "-cpu" or opt == "-bsp":
        next_is_target = True
    elif args.hierarchy == "iar":
      if next_is_path:
        next_is_path = False
        includePaths += opt.split(",")
      elif next_is_fi:
        next_is_fi = False
        commonOpts["fi"].append("\"" + cygpath(opt) + "\"")
      elif opt == "--preinclude":
        next_is_fi = True
      elif opt == "--system_include_dir":
        next_is_path = True
      if opt.startswith("--system_include_dir="):
        path = opt[opt.find("=") + 1:].strip()
        includePaths.append(opt.split(","))
    elif args.hierarchy == "tasking":
      if (
        opt.startswith("--include-file=") or
        opt.startswith("--include-macros-file=")
      ):
        commonOpts["fi"].append(
          "\"" + cygpath(opt[opt.find("=") + 1:].strip()) + "\""
        )
      elif opt.startswith("-H"):
        commonOpts["fi"].append("\"" + cygpath(opt[2:].strip()) + "\"")
    elif args.hierarchy == "ti":
      if opt.startswith("-I=") or opt.startswith("--include_path="):
        path = opt[opt.find("=") + 1:].strip()
        includePaths.append(path)
      elif opt.startswith("-I"):
        path = opt[2:].strip()
        includePaths.append(path)
      elif opt.startswith("--preinclude="):
        commonOpts["fi"].append(
          "\"" + cygpath(opt[opt.find("=") + 1:].strip()) + "\""
        )
  if not("TARGET" in compilerInfo):
    if args.hierarchy == "arm":
      cct_logger.error(
        "Compiler options must include --cpu to specify target."
      )
    elif args.hierarchy == "ghs":
      cct_logger.error(
        "Compiler options must include -cpu or -bsp to specify target."
      )
  var = { }
  sizeTypeMap = OrderedDict()
  sizeMaxMap = OrderedDict()
  alignTypeMap = OrderedDict()
  sizeMap = OrderedDict()
  alignMap = OrderedDict()
  variant = ""
  xc8_c90 = False
  if args.hierarchy == "arm":
    invoke_compiler(
      args, [ "--list_macros" ], var, includePaths, compilerInfo
    )
    if not("TARGET" in compilerInfo):
      for v in var:
        if v.startswith("__TARGET_CPU_"):
          t = v[13:].strip()
          while t.endswith("_"):
            t = t[:-1]
          compilerInfo["TARGET"] = t
    if "__ARMCC_VERSION" in var:
      compilerInfo["COMPILER_VERSION"] = var["__ARMCC_VERSION"]
    # derive system include paths from environment when not set by -J
    env = os.environ.copy()
    if not includePaths:
      if "__ARMCC_VERSION" in var:
        armv = "ARMCC" + var["__ARMCC_VERSION"][0:1] + "INC"
        if armv in env:
          includePaths = env[armv].strip().split(",")
    if not includePaths:
      if "ARMINC" in env:
        includePaths = env["ARMINC"].strip().split(",")
    # derive system include paths from registry when not found in environment
    if onWindows() and not includePaths:
      # Keil uVision4 installation path
      # Checking if registry entry is available
      ok = False
      KeilInstall = None
      for w64 in ("\\Wow6432Node", ""):
        if not ok:
          try:
            ok = True
            KeilInstall = getRegValue("SOFTWARE" + w64 +
                           "\\keil\\Products\\MDK", "Path")
          except WindowsError:
            ok = False
      if ok and KeilInstall:
        armccInclude = os.path.join(KeilInstall, "ARMCC", "include")
        includePaths = [ armccInclude ] + \
          [os.path.join(d, t) for d, ds, fs in os.walk(armccInclude) for t in ds]
    sizeTypeMap["int"] = "__sizeof_int"
    sizeTypeMap["long"] = "__sizeof_long"
    sizeTypeMap["codeptr"] = "__sizeof_ptr"
    sizeTypeMap["dataptr"] = "__sizeof_ptr"
  elif args.hierarchy == "diab":
    invoke_compiler(
      args, [ "-Xcpp-dump-symbols" ], var, includePaths, compilerInfo
    )
    var.pop("_Pragma", None) # this symbol is returned as empty but is C99
    var.pop("__COUNTER__", None) # this symbol is returned as empty but is in
                                 # fact a counter and handled in the cct
    var.pop("__TIMESTAMP__", None) # returned as empty but is a string
    var.pop("__FILE__", None) # returned as empty but is a string
    var.pop("__LINE__", None) # returned as empty but is a string
    if "__VERSION__" in var:
      v =  var["__VERSION__"]
      compilerInfo["COMPILER_VERSION"] = v[v.find("(") + 1:v.rfind(")")]
    invoke_compiler(args, [ "-H5" ], var, includePaths, compilerInfo)
    sizeTypeMap["codeptr"] = "__SIZEOF_POINTER__"
    sizeTypeMap["dataptr"] = "__SIZEOF_POINTER__"
    sizeMap["char"] = "8"
    sizeMap["short"] = "16"
    sizeMap["int"] = "32"
    sizeMap["long"] = "32"
    sizeMap["longlong"] = "128" if "__LP64__" in var else "64"
    sizeMap["float"] = "32"
    sizeMap["double"] = "64"
    sizeMap["ldouble"] = "128" if "__LDBL__" in var else "64"
    alignMap["char"] = "1"
    alignMap["short"] = "2"
    alignMap["int"] = "4"
    alignMap["long"] = "4"
    alignMap["longlong"] = "4"
    alignMap["float"] = "4"
    alignMap["double"] = "8"
    alignMap["ldouble"] = "8"
    alignMap["codeptr"] = "4"
    alignMap["dataptr"] = "4"
  elif args.hierarchy == "ghs":
    # try GCC method first
    invoke_compiler(
      args, [ "-v", "-dM", "-E" ], var, includePaths, compilerInfo
    )
    # if that failed, invoke compiler with -# -nested_diagnostics
    if not var:
      new_include = [ ]
      invoke_compiler(
        args, [ "-#", "-nested_diagnostics" ], var, includePaths, compilerInfo
      )
      if not includePaths:
        includePaths = new_include
    # if that failed, invoke compiler with -# -E
    if not var:
      new_include = [ ]
      invoke_compiler(args, [ "-#", "-E" ], var, includePaths, compilerInfo)
      if not includePaths:
        includePaths = new_include
    sizeTypeMap["char"] = "__CHAR_BIT"
    sizeTypeMap["short"] = "__SHRT_BIT"
    sizeTypeMap["int"] = "__INT_BIT"
    sizeTypeMap["long"] = "__LONG_BIT"
    sizeTypeMap["longlong"] = "__LLONG_BIT"
    sizeTypeMap["ldouble"] = "__LDBL_BIT"
    sizeTypeMap["codeptr"] = "__FUNCPTR_BIT"
    sizeTypeMap["dataptr"] = "__PTR_BIT"
    sizeMap["float"] = "32"
    sizeMap["double"] = "64"
    sizeMap["ldouble"] = "64"
    alignTypeMap["long"] = "__ghs_alignment"
    alignTypeMap["longlong"] = "__ghs_alignment"
    alignTypeMap["double"] = "__ghs_alignment"
    alignTypeMap["ldouble"] = "__ghs_alignment"
    # derive compiler version
    version = None
    compilerPath = cygpath(args.command[0] )
    gwhat = os.path.join(
      os.path.dirname(compilerPath), "gwhat" + (".exe" if onWindows() else "")
    )
    env = os.environ.copy()
    env["LANG"] = "C"
    if os.path.exists(gwhat):
      p = subprocess.Popen(
        [ gwhat, compilerPath ],
        stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env
      )
      (stdoutdata, stderrdata) = p.communicate()
      for line in stdoutdata.splitlines() + stderrdata.splitlines():
        if not isinstance(line, str):
          line = line.decode()
        if not version:
          i = line.find("Compiler")
          if i >= 0:
            version = line[line.find(" ", i):].replace(".", "").strip()
            if version.lower().startswith("v"):
              version = version[1:].strip()
      if not version:
        for line in stdoutdata.splitlines() + stderrdata.splitlines():
          if not isinstance(line, str):
            line = line.decode()
          if not version:
            i = line.find("MULTI")
            if i >= 0:
              version = line[line.find(" ", i):].replace(".", "").strip()
              if version.lower().startswith("v"):
                version = version[1:].strip()
    if not version and "__GHS_VERSION_NUMBER" in var:
      version = var["__GHS_VERSION_NUMBER"]
    for path in includePaths:
      if not version and path.find("comp_") >= 0:
        i = path.rfind("comp_") + 5
        version = path[i:path.find(os.sep, i)]
    if version:
      compilerInfo["COMPILER_VERSION"] = version
  elif args.hierarchy == "gnu":
    invoke_compiler(
      args, [ "-Wp,-dM", "-v", "-E" ], var, includePaths, compilerInfo
    )
    if not includePaths:
      new_var = { }
      invoke_compiler(
        args, [ "-v", "-E" ], new_var, includePaths, compilerInfo
      )
      if not var:
        var = new_var
    if not var or not includePaths:
      new_var = { }
      new_include = [ ]
      invoke_compiler(
        args, [ "-Wp,-v,-dM", "-E" ], new_var, new_include, compilerInfo
      )
      if not var:
        var = new_var
      if not includePaths:
        includePaths = new_include
    # some gcc derivatives don't know -Wp, e.g. HIGHTEC tricore-gcc
    if not var or not includePaths:
      new_var = { }
      new_include = [ ]
      invoke_compiler(
        args, [ "-dM", "-v", "-E" ], new_var, new_include, compilerInfo
      )
      if not var:
        var = new_var
      if not includePaths:
        includePaths = new_include
    # Microchip xc8-cc driver works differently
    if not var:
      new_include = [ ]
      invoke_compiler(
        args, [ "-V", "-V" ], var, new_include, compilerInfo
      )
      if not includePaths:
        includePaths = new_include
    if "__XC8_VERSION" in var:
      variant = "_xc8"
      compilerInfo["COMPILER_HIERARCHY"] = "Microchip"
      compilerInfo["COMPILER_NAME"] = "MPLAB_XC8"
      s = var["__XC8_VERSION"]
      compilerInfo["COMPILER_VERSION"] = s[:-3] + "." + s[-3:-1]
      var.pop("__interrupt(...)", None) # Case 702117
    if "_MSC_VER" in var:
      variant = "_msvc"
      compilerInfo["COMPILER_HIERARCHY"] = "LLVM"
      compilerInfo["COMPILER_NAME"] = "CLANG"
    if not("COMPILER_VERSION" in compilerInfo):
      if "__VERSION__" in var:
        version = var["__VERSION__"]
        extract_version(
          compilerInfo, version[version.find("(") + 1:version.rfind(")") ]
        )
      elif "__GNUC__" in var:
        compilerInfo["COMPILER_VERSION"] = var["__GNUC__"]
        if "__GNUC_MINOR__" in var:
          compilerInfo["COMPILER_VERSION"] += "." + var["__GNUC_MINOR__"]
          if "__GNUC_PATCHLEVEL__" in var:
            compilerInfo["COMPILER_VERSION"] += "." + var["__GNUC_PATCHLEVEL__"]
    # add PRL macros for enabling the correct glibcxx support in the stubs
    for v in (
      "__GNUC__",  "__GNUC_MINOR__", "__GNUC_PATCHLEVEL__", "__clang__"
    ):
      if v in var:
        var["__PRL" + v[1:]] = var[v]
    for incPath in includePaths:
      glibcxx_re = r'.*\\([0-9])\.([0-9])\.([0-9])\\include\\c\+\+' \
        if onWindows() else r'.*/include/c\+\+/([0-9])\.([0-9])\.([0-9])'
      prl_glibcxx = None
      match = re.match(glibcxx_re, incPath)
      if match:
        var["__PRL_GLIBCXX__"] = match.group(1)
        var["__PRL_GLIBCXX_MINOR__"] = match.group(2)
        var["__PRL_GLIBCXX_PATCHLEVEL__"] = match.group(3)
    if variant == "_xc8":
      sizeTypeMap["char"] = "__SIZEOF_CHAR__"
    else:
      sizeTypeMap["char"] = "__CHAR_BIT__"
    sizeTypeMap["short"] = "__SIZEOF_SHORT__"
    sizeTypeMap["int"] = "__SIZEOF_INT__"
    sizeTypeMap["long"] = "__SIZEOF_LONG__"
    sizeTypeMap["longlong"] = "__SIZEOF_LONG_LONG__"
    sizeTypeMap["float"] = "__SIZEOF_FLOAT__"
    sizeTypeMap["double"] = "__SIZEOF_DOUBLE__"
    sizeTypeMap["ldouble"] = "__SIZEOF_LONG_DOUBLE__"
    if not(variant == "_xc8"):
      sizeTypeMap["codeptr"] = "__SIZEOF_POINTER__"
      sizeTypeMap["dataptr"] = "__SIZEOF_POINTER__"
    # suport for older gnu based compilers without above sizeof symbols
    sizeMaxMap["short"] = "__SHRT_MAX__"
    sizeMaxMap["int"] = "__INT_MAX__"
    sizeMaxMap["long"] = "__LONG_MAX__"
    sizeMaxMap["longlong"] = "__LONG_LONG_MAX__"
    if "__cplusplus" in var:
      alignTypeMap["default_new"] = "__STDCPP_DEFAULT_NEW_ALIGNMENT__"
  elif args.hierarchy == "iar":
    invoke_compiler(args, [ ], var, includePaths, compilerInfo)
    if "__VERSION__" in var:
      v = var["__VERSION__"]
      i = v.lower().find("compiler v")
      if not(i < 0):
        v = v[i + 10:v.find(" ", i + 10)]
        while v.count(".") > 2:
          v = v[0:v.rfind(".") ]
      compilerInfo["COMPILER_VERSION"] = v
    if "__TID__" in var:
      compilerInfo["TARGET"] = var["__TID__"]
    # derive system include paths from environment when not set by -J
    env = os.environ.copy()
    if not includePaths:
      if "C_INCLUDE" in env:
        includePaths = env["C_INCLUDE"].strip().split(",")
    sizeTypeMap["char"] = "__CHAR_SIZE__"
    sizeTypeMap["short"] = "__SHORT_SIZE__"
    sizeTypeMap["int"] = "__INT_SIZE__"
    sizeTypeMap["long"] = "__LONG_SIZE__"
    sizeTypeMap["longlong"] = "__LONG_LONG_SIZE__"
    sizeTypeMap["float"] = "__FLOAT_SIZE__"
    sizeTypeMap["double"] = "__DOUBLE_SIZE__"
    sizeTypeMap["ldouble"] = "__LONG_DOUBLE_SIZE__"
    sizeTypeMap["codeptr"] = "__DEF_PTR_SIZE__"
    sizeTypeMap["dataptr"] = "__DEF_PTR_SIZE__"
    alignTypeMap["char"] = "__CHAR_ALIGN__"
    alignTypeMap["short"] = "__SHORT_ALIGN__"
    alignTypeMap["int"] = "__INT_ALIGN__"
    alignTypeMap["long"] = "__LONG_ALIGN__"
    alignTypeMap["longlong"] = "__LONG_LONG_ALIGN__"
    alignTypeMap["float"] = "__FLOAT_ALIGN__"
    alignTypeMap["double"] = "__DOUBLE_ALIGN__"
    alignTypeMap["ldouble"] = "__LONG_DOUBLE_ALIGN__"
  elif args.hierarchy == "renesas":
    invoke_compiler(args, [ "-V" ], var, includePaths, compilerInfo)
    invoke_compiler(
      args,
      [ ] if compiler in ("ccrx", "cc78k0", "cc78k0r") else
      [ "-E" if compiler == "ca850" else "-P" ],
      var, includePaths, compilerInfo
    )
    if not(compiler == "rcfrt") and not var:
      cct_logger.warning(
        "Command {cmd} did not produce settings.".format(cmd=command_line)
      )
      return False
    # set predefined macros based on compiler options
    # CC-RX 4.2.1 Macro Names
    # CC-RH 4.2.2 Macro
    env = os.environ.copy()
    next_is_define = False
    next_is_path = False
    next_is_fi = False
    next_is_cpu = False
    renesasDevice = None
    renesasDevPath = None
    cc78k0_mf = False
    for opt in args.command:
      # map ccrh -X options to ccrx equivalent
      if compiler == "ccrh":
        if opt.startswith("-X"):
          opt = "-" + opt[2:]
      sep = opt.find("=")
      val = None
      o = None
      if sep > 0:
        val = opt[sep + 1:].strip()
        o = opt[:sep]
      # define/include support is for rcfrt use with build option
      if next_is_define:
        next_is_define = False
        var[o if o else opt] = val if val else "1"
      elif next_is_path:
        next_is_path = False
        includePaths.append(opt)
      elif next_is_fi:
        next_is_fi = False
        commonOpts["fi"].append(opt)
      elif next_is_cpu: # ca850
        next_is_cpu = False
        compilerInfo["TARGET"] = opt
        var["__" + opt + "__"] = "1"
        renesasDevice = opt
      elif o =="-devpath" or o == "-dev_path": # ca850, xc
        renesasDevPath = val
      elif opt == "-D":
        next_is_define = True
      elif opt == "-I":
        next_is_path = True
      elif o == "--cpu":
        compilerInfo["TARGET"] = val
      elif o == "--preinclude":
        next_is_fi = True
      # end of rcfrt support
      elif compiler == "ccrx" and opt == "-pid":
        var["_PID_TOP"] = ""
      elif o in ("-cpu", "-isa"):
        # "g3m", "g3k", "g3mh", "g3kh", "g4mh" # ccrh
        compilerInfo["TARGET"] = val
      elif o == "-base":
        for q in ("ram", "rom"):
          if q == val:
            var["_" + q.upper() + "_BASE"] = ""
          else:
            var.pop("_" + q.upper() + "_BASE", None)
      elif o == "-preinclude":
        ps = val.strip().split(",")
        for p in ps:
          commonOpts["fi"].append("\"" + p.strip() + "\"")
      # ccrh
      elif opt == "-use_fp16":
        var["__fp16"] = "float"
      # ccrl
      elif o == "-dev":
        renesasDevice = val
      # ca850
      elif opt == "-cpu":
        next_is_cpu = True
      # cc78k0(r)
      elif compiler in ("cc78k0", "cc78k0r"):
        if (
          opt.startswith("-c") and
          not(compiler == "cc78k0r" and opt == "-common")
        ):
          t = opt[2:]
          renesasDevice = t
          compilerInfo["TARGET"] = t
          var["__" + t.upper() + "_"] = "1"
        elif opt.startswith("-y"):
          renesasDevPath = opt[2:]
        elif compiler == "cc78k0" and opt.startswith("-mf"):
          cc78k0_mf = True
      elif compiler == "cx":
        if opt.startswith("-C"):
          t = opt[2:]
          if "TARGET" in compilerInfo:
            renesasDevice = compilerInfo["TARGET"]
          else:
            renesasDevice = t
          compilerInfo["TARGET"] = t
          var["__" + t.upper() + "_"] = "1"
          var["__" + t.upper() + "__"] = "1"
    if (
      "__RENESAS_VERSION__" in var and not("COMPILER_VERSION" in compilerInfo)
    ):
      v = var["__RENESAS_VERSION__"]
      compilerInfo["COMPILER_VERSION"] = (
        "v" + v[2:4].lstrip("0") + "." + v[4:6] + "." + v[6:8]
      )
    elif not("COMPILER_VERSION" in compilerInfo):
      cct_logger.error(
        "Command {cmd} did not yield Renesas compiler version.".format(
          cmd=command_line
        )
      )
      return False
    if "INC_RX" in env:
      includePaths = env["INC_RX"].strip().split(",")
    else:
      # Usually include is next to bin where compiler is located
      for i in ("include", "inc", "inc850", "Inc78k0", "Inc78k0r"):
        d = os.path.join(os.path.dirname(args.command[0] ), os.pardir, i)
        if os.path.exists(d):
          includePaths = [ d ]
    for d in includePaths:
      fi = os.path.join(d, "builtin.h")
      if os.path.exists(fi):
        commonOpts["fi"].insert(0, "\"" + fi + "\"")
    is16bit = "__CCRL__" in var or "__CA78K0__" in var or "__CA78K0R__" in var
    sizeMap["char"] = "8"
    sizeMap["short"] = "16"
    sizeMap["int"] = "16" if is16bit or "__INT_SHORT" in var else "32"
    sizeMap["long"] = "32"
    sizeMap["float"] = "32"
    sizeMap["double"] = (
      "64" if "__DBL8" in var or "__DOUBLE_IS_64BITS__" in var else "32"
    )
    sizeMap["ldouble"] = sizeMap["double"]
    sizeMap["codeptr"] = "16" if is16bit and not cc78k0_mf else "32"
    sizeMap["dataptr"] = "16" if is16bit else "32"
  elif args.hierarchy == "tasking":
    invoke_compiler(
      args, [ "--preprocess=+list" ], var, includePaths, compilerInfo
    )
    invoke_compiler(
      args, [ "--preprocess=+includes" ], var, includePaths, compilerInfo
    )
    invoke_compiler(args, [ "--version" ], var, includePaths, compilerInfo)
    for v in var:
      if v.startswith("__CORE_") and v.endswith("__"):
        t = v[7:-2].strip()
        if len(t) > 0:
          compilerInfo["TARGET"] = t
    sizeMap["char"] = "8"
    sizeMap["short"] = "16"
    sizeMap["int"] = "32"
    sizeMap["long"] = "32"
    sizeMap["longlong"] = "64"
    sizeMap["float"] = "32"
    sizeMap["double"] = "64"
    sizeMap["ldouble"] = "64"
    sizeMap["codeptr"] = "32"
    sizeMap["dataptr"] = "32"
    alignMap["char"] = "1"
    alignMap["short"] = "2"
    alignMap["int"] = "2"
    alignMap["long"] = "2"
    alignMap["longlong"] = "4"
    alignMap["float"] = "2"
    alignMap["double"] = "4"
    alignMap["ldouble"] = "4"
    alignMap["codeptr"] = "4"
    alignMap["dataptr"] = "4"
    sizeTypeMap["char"] = "__CHAR_BIT__"
    sizeTypeMap["short"] = "__SHORT_BIT__"
    sizeTypeMap["int"] = "__INT_BIT__"
    sizeTypeMap["long"] = "__LONG_BIT__"
    sizeTypeMap["longlong"] = "__LLONG_BIT__"
  elif args.hierarchy == "ti":
    invoke_compiler(args, [ ], var, includePaths, compilerInfo)
    if "__TI_COMPILER_VERSION__" in var:
      compilerInfo["COMPILER_VERSION"] = var["__TI_COMPILER_VERSION__"]
    elif "__COMPILER_VERSION__" in var:
      compilerInfo["COMPILER_VERSION"] = var["__COMPILER_VERSION__"]
#      if onWindows() and not includePaths:
#        regkey =
    sizeTypeMap["char"] = "__CHAR_BIT__"
    sizeTypeMap["short"] = "__SIZEOF_SHORT__"
    sizeTypeMap["int"] = "__SIZEOF_INT__"
    sizeTypeMap["long"] = "__SIZEOF_LONG__"
    sizeTypeMap["longlong"] = "__SIZEOF_LONG_LONG__"
    sizeTypeMap["float"] = "__SIZEOF_FLOAT__"
    sizeTypeMap["double"] = "__SIZEOF_DOUBLE__"
    sizeTypeMap["ldouble"] = "__SIZEOF_LONG_DOUBLE__"
    if not("TARGET" in compilerInfo):
      if compiler == "cl430":
        compilerInfo["TARGET"] = "MSP430"
      elif compiler == "cl2000":
        compilerInfo["TARGET"] = "C2000"
      elif compiler == "cl6x":
        compilerInfo["TARGET"] = "C6000"
      elif compiler.startswith("cl"):
        compilerInfo["TARGET"] = compiler[2:].upper()
      elif compiler.endswith("cl"):
        compilerInfo["TARGET"] = compiler[:-2].upper()
  # did we find anyting?
  if not var:
    cct_logger.warning(
      "Command {cmd} did not produce settings.".format(cmd=command_line)
    )
    if args.blacklist:
      if not(args.command[0] in blacklist):
        blacklist[args.command[0] ] = 1
      else:
        blacklist[args.command[0] ] += 1
      cct_logger.debug(
        "Blacklist level of {binary} now is {count}".format(
           binary=args.command[0], count=blacklist[args.command[0]]
        )
      )
    return False
  # figure out platform
  if args.hierarchy == "gnu":
    if "__sparc" in var:
      if "__arch64__" in var:
        compilerInfo["PLATFORM"] = "PLATFORM_SOL64"
      else:
        compilerInfo["PLATFORM"] = "PLATFORM_SOL32"
    elif "__linux" in var:
      if "__x86_64" in var:
        compilerInfo["PLATFORM"] = "PLATFORM_LIN64"
      else:
        compilerInfo["PLATFORM"] = "PLATFORM_LIN32"
    else:
      if "__x86_64" in var:
        compilerInfo["PLATFORM"] = "PLATFORM_WIN64"
      else:
        compilerInfo["PLATFORM"] = "PLATFORM_WIN32"
  else:
    # figure out platform from environment
    platf = platform.system()
    bits = "64" if sys.maxsize > 2**32 else "32"
    if platf == "Windows":
      compilerInfo["PLATFORM"] = "PLATFORM_WIN" + bits
    elif platf == "Linux":
      compilerInfo["PLATFORM"] = "PLATFORM_LIN" + bits
    else:
      compilerInfo["PLATFORM"] = "unsupported"
  if not("HOST" in compilerInfo):
    compilerInfo["HOST"] = compilerInfo["PLATFORM"]
  # C(++)?
  isCPP = ("__cplusplus" in var)
  if args.hierarchy == "iar":
    def add_IAR_includes(var, path):
      if path:
        inc = os.path.join(os.path.normpath(path), "inc")
        if os.path.exists(inc):
          includePaths.append(inc)
          libs = [ ]
          if isCPP:
            libs += [
              "cpp", "cpplib", "ecpp", "ecpplib", os.path.join("dlib", "ecpp")
            ]
          libs +=[
            os.path.join("c", "aarch64" if "__aarch64__" in var else "aarch32"),
            "c",
            os.path.join("dlib", "c"),
            "clib"
          ]
          for d in libs:
            lib = os.path.join(inc, d)
            if os.path.exists(lib):
              includePaths.append(lib)
        lib = os.path.join(path, "lib")
        if os.path.exists(lib):
          includePaths.append(lib)

    # try first based on compiler path
    if not includePaths and compiler.startswith("icc"):
      add_IAR_includes(
        var, os.path.join(os.path.dirname(args.command[0] ), os.pardir)
      )
    # derive system include paths from registry when not found in environment
    if onWindows() and not includePaths:
      for v in var:
        if v.startswith("__ICC") and v.endswith("__"):
          tgt = v[5:-2].upper()
          reg = _winreg.ConnectRegistry(None, _winreg.HKEY_LOCAL_MACHINE)
          key = None
          ok = False
          for w64 in ("\\Wow6432Node", ""):
            if not ok:
              try:
                ok = True
                key = _winreg.OpenKey(reg, "SOFTWARE" + w64 +
                        "\\IAR Systems\\Embedded Workbench")
              except WindowsError:
                ok = False
          if ok and key:
            i = 0
            try:
              while True:
                verskey = _winreg.EnumKey(key, i)
                pkey = None
                path = None
                try:
                  pkey = _winreg.OpenKey(
                    key,
                    verskey + "\\EW" + tgt + "\\" +
                    compilerInfo["COMPILER_VERSION"]
                  )
                except WindowsError as exc:
                  pass
                if not pkey:
                  try:
                    pkey = _winreg.OpenKey(key, verskey + "\\EW" + tgt)
                  except WindowsError as exc:
                    pass
                if pkey:
                  path = _winreg.QueryValueEx(pkey, "InstallPath")[0]
                  add_IAR_includes(var, os.path.join(path, tgt.lower()))
                  _winreg.CloseKey(pkey)
                i += 1
            except WindowsError as exc:
              pass
            _winreg.CloseKey(key)
  defineOption = None
  if isCPP:
    defineOption = "-sd"
    compilerInfo["SOURCE_LANGUAGE"] = "C++"
    vcpp = var["__cplusplus"]
    if vcpp > "197000L" and vcpp <= "199711L":
      edg_cpp11 = "__EDG_CPP11_IL_EXTENSIONS_SUPPORTED"
      compilerInfo["LANGUAGE_STANDARD_VERSION"] = (
        "c++11" if edg_cpp11 in var and var[edg_cpp11] == "1" else "c++98"
      )
    elif vcpp > "199711L" and vcpp <= "201103L":
      compilerInfo["LANGUAGE_STANDARD_VERSION"] = "c++11"
    elif vcpp > "201103L" and vcpp <= "201402L":
      compilerInfo["LANGUAGE_STANDARD_VERSION"] = "c++14"
    elif vcpp > "201402L" and vcpp <= "201703L":
      compilerInfo["LANGUAGE_STANDARD_VERSION"] = "c++17"
    elif "__GXX_EXPERIMENTAL_CXX0X__" in var:
    # old g++ versions (prior to 4.7.0) have a bug that it sets __cplusplus to 1
      compilerInfo["LANGUAGE_STANDARD_VERSION"] = "c++11"
    else:
      # by default, fall back to oldest
      compilerInfo["LANGUAGE_STANDARD_VERSION"] = "c++98"
  else:
    defineOption = "-d" if compilerInfo["QAC_VERSION"] < "2021.2" else "-sd"
    compilerInfo["SOURCE_LANGUAGE"] = "C"
    compilerInfo["LANGUAGE_STANDARD_VERSION"] = "c90"
    if "__STDC_VERSION__" in var:
      vc = var["__STDC_VERSION__"]
      if vc == "199901L":
        compilerInfo["LANGUAGE_STANDARD_VERSION"] = "c99"
      elif vc == "201112L":
        compilerInfo["LANGUAGE_STANDARD_VERSION"] = "c11"
  if not("TARGET" in compilerInfo):
    compilerInfo["TARGET"] = "unknown"
  if not("COMPILER_VERSION" in compilerInfo):
    compilerInfo["COMPILER_VERSION"] = "unknown"
  cctName = args.name
  if not cctName:
    cctName = (
      compilerInfo["COMPILER_HIERARCHY"] + "_" +
      compilerInfo["COMPILER_NAME"] + "-" +
      compilerInfo["COMPILER"] + "_" +
      compilerInfo["COMPILER_VERSION"] + "-" +
      compilerInfo["TARGET"] + "-" +
      compilerInfo["SOURCE_LANGUAGE"] + "-" +
      compilerInfo["LANGUAGE_STANDARD_VERSION"]
    )
  compilerInfo["OUTPUT_PREFIX"] = cctName
  opDir = get_CCT_path(args)
  if not opDir:
    cct_logger.error(
      "No framework installation found. Please use -op to specify CCT location."
    )
    return False
  elif not os.path.exists(opDir):
    os.mkdir(opDir)
  cctPath = os.path.join(opDir, cctName + ".cct")
  cctDataPath = os.path.join(opDir, "DATA", compilerInfo["OUTPUT_PREFIX"] )
  if args.overwrite:
    if os.path.exists(cctDataPath):
      shutil.rmtree(cctDataPath)
  elif os.path.exists(cctPath):
    cct_logger.info(
      "CCT {cct} already exists! ".format(cct=cctPath) +
      "Use --overwrite to replace it."
    )
    return False
  elif os.path.exists(cctDataPath):
    cct_logger.info(
      "CCT data path {data} already exists! ".format(data=cctDataPath) +
      "Use --overwrite to replace it."
    )
    return False
  out_logger.info("Writing {filename}".format(filename=cctPath))
  cctFile = open(cctPath, "w")
  shutil.copytree(
    os.path.join(
      currentDir, args.hierarchy + variant,
      "DATA", compilerInfo["SOURCE_LANGUAGE"]
    ),
    cctDataPath
  )
  if isCPP and args.hierarchy == "ghs":
    # only provide type_traits stub for C++ compilers from 2018 on
    for v in ("2018", "2019", "2020"):
      if not compilerInfo["COMPILER_VERSION"].startswith(v):
        shutil.rmtree(os.path.join(cctDataPath, "Stub", v))
  elif isCPP and args.hierarchy == "gnu":
    # for clang, QNX q++ LLVM and Microchip xc32-g++ don't provide gcc stubs
    # see https://community.rti.com/kb/how-choose-qnx-target-libraries
    if "__XC32__" in var or "__clang__" in var:
      gccStubs = os.path.join(cctDataPath, "Stub", "gcc")
      if os.path.exists(gccStubs):
        shutil.rmtree(gccStubs)
    elif "__QNX__" in var and not(QNX_GPP in commonOpts):
      shutil.rmtree(os.path.join(cctDataPath, "Stub", "gcc"))
      # for QNX q++ LLVM we need to force include __refstring
      for path in includePaths:
        refstring = os.path.join(path, "__refstring")
        if os.path.exists(refstring):
          commonOpts["fi"].append("\"" + cygpath(refstring) + "\"")
  if args.hierarchy == "tasking":
    if isCPP:
      first = True
      isSTL = False
      for path in includePaths:
        # force include file with intrinsic function definitions
        intrinsics = os.path.join(path, "intrinsics.h")
        if os.path.exists(intrinsics):
          commonOpts["fi"].append("\"" + cygpath(intrinsics) + "\"")
        # create stubs without file extension to deal with stdh include suffix
        # for compiler versions using include.stl
        if first:
          first = False
          isSTL = os.path.basename(path) == "include.stl"
        if isSTL:
          stubDir = os.path.join(cctDataPath, "Stub", os.path.basename(path))
          if not os.path.exists(stubDir):
            os.mkdir(stubDir)
            for p in os.listdir(path):
              f = os.path.join(path, p)
              (n, ext) = os.path.splitext(p)
              if os.path.isfile(f) and ext == ".stdh":
                shutil.copyfile(f, os.path.join(stubDir, n))
    else:
      # use compiler options to produce force include files for
      # built-in types and intrinsic functions
      invoke_compiler(
        args, [ "--help=typedefs" ], var, includePaths, compilerInfo
      )
      invoke_compiler(
        args, [ "--help=intrinsics" ], var, includePaths, compilerInfo
      )
  elif args.hierarchy == "renesas":
    if renesasDevice:
      if not os.path.exists(renesasDevice):
        df = os.path.join(
          os.pardir, os.pardir, os.pardir, "Device",
          "78K0R" if compiler == "cc78k0r" else
          "78K0" if compiler == "cc78k0" else
          "V850" if compiler == "ca850" else
          "V850E2" if compiler == "cx" else
          "RL78", # if compiler == "ccrl"
          "Devicefile"
        )
        d = os.path.join(os.path.dirname(args.command[0] ), df)
        if os.path.exists(d):
          if not renesasDevPath:
            renesasDevPath = d
          rd = os.path.join(d, os.path.basename(renesasDevice))
          if os.path.exists(rd):
            renesasDevice = rd
      if renesasDevPath and not os.path.exists(renesasDevice):
        for (pre, ext) in (
          ("D", ".800"), ("D", ".78K"),
          ("D", ".DVF"), ("DR5", ".DVF"), ("DR7", ".DVF")
        ):
          rd = os.path.join(renesasDevPath, pre + renesasDevice.upper() + ext)
          if os.path.exists(rd):
            renesasDevice = rd
      if os.path.exists(renesasDevice):
        dfi = os.path.join(
          cctDataPath, "Stub", "prlforceinclude",
          os.path.basename(renesasDevice)
        )
        shutil.copyfile(renesasDevice, dfi)
        dir = None
        if getattr(sys, "frozen", False):
          dir = sys._MEIPASS
        else:
          dir = os.path.join(
            os.path.dirname(os.path.realpath(__file__)), "renesas", "bin"
          )
        exe = os.path.join(dir, "dev_file_conv.exe")
        if os.path.exists(exe):
          p = subprocess.Popen(
            [ exe, dfi ], stdout=subprocess.PIPE, stderr=subprocess.PIPE
          )
          (stdoutdata, stderrdata) = p.communicate()
        else:
          cct_logger.error(
            "Renesas device file support is only available " +
            "in the compiled version of the generator."
          )
        os.remove(dfi)
  # produce system header path lists for use by the stub script
  dirFile = open(os.path.join(cctDataPath, "Stub", "syshdr.lst"), "w")
  for path in includePaths:
    print(cygpath(path), file=dirFile)
  dirFile.close()
  stubTemplate = open(os.path.join(currentDir, "stub.py"))
  stubFile = open(os.path.join(cctDataPath, "Script", cctName + ".py"), "w")
  # fill in compiler information in stub template
  for line in stubTemplate:
    print(line % compilerInfo, end="", file=stubFile)
  stubFile.close()
  cctTemplate = open(
    os.path.join(
      currentDir,
      args.hierarchy + variant,
      compilerInfo["SOURCE_LANGUAGE"] + ".cct"
    )
  )
  # fill in compiler information in header template
  for line in cctTemplate:
    if "%(FLAG)s" in line:
      print(line.strip() % {
        "FLAG": reduce(lambda s,t: (s + "," + t), compilerInfo["FLAGS"] )
      }, file=cctFile)
    elif "%(EXTENSION)s" in line:
      print(line.strip() % {
        "EXTENSION": ("," * (len(compilerInfo["FLAGS"] ) - 1))
      }, file=cctFile)
    elif "%(LITERAL_QUOTES)s" in line:
      print(line.strip() % {
        "LITERAL_QUOTES": ("," * (len(compilerInfo["FLAGS"] ) - 1))
      }, file=cctFile)
    else:
      print(line.strip() % compilerInfo, file=cctFile)
  print("* Generated by CCT_Generator {d}".format(d=build_time), file=cctFile)
  if (
    # clang
    args.hierarchy == "gnu" and "__clang__" in var or
    # for TI libcxx
    args.hierarchy == "ti" and "__cpp_generic_lambdas" in var
  ):
    clangVia = open(os.path.join(currentDir, args.hierarchy,
      "clang_" + compilerInfo["SOURCE_LANGUAGE"] + ".via"))
    for line in clangVia:
      print(line.strip() % { "defineOption": defineOption }, file=cctFile)
  if isCPP:
    print("* C++ language version specific options", file=cctFile)
    # for TI libcxx
    if args.hierarchy == "ti" and "__cpp_generic_lambdas" in var:
      for f in (
        "150_builtins_all.h",
        "500_qacpp_reference_traits.h",
        "550_qacpp_typeof.h"
      ):
        shutil.copyfile(
          os.path.join(
            currentDir, "gnu", "DATA", compilerInfo["SOURCE_LANGUAGE"],
            "Stub", "prlforceinclude", f
          ),
          os.path.join(cctDataPath, "Stub", "prlforceinclude", f)
        )
    if (
      args.hierarchy == "gnu" and ("__ARM_NEON" in var or "__ARM_NEON__" in var)
    ):
      neonVia = open(os.path.join(currentDir, args.hierarchy,
        "arm_neon_" + compilerInfo["SOURCE_LANGUAGE"] + ".via"))
      for line in neonVia:
        print(line.strip(), file=cctFile)
  print("* preprocessor symbol definitions", file=cctFile)
  print(defineOption, "\"_PRL_STRINGIFY(x)=#x\"", file=cctFile)

  for v, val in var.items():
    if (
      isCPP and args.hierarchy == "iar" and
      v in (
        "__HEAP_MEMORY_LIST1__()", "__HEAPND_MEMORY_LIST1__()",
        "__DATA_MEMORY_LIST1__()", "__DATA_PTR_MEMORY_LIST1__()",
        "__CODE_PTR_MEMORY_LIST1__()"
      )
    ):
      print("*erasing problematic", v + "=" + val, file=cctFile)
      print(defineOption, "\"" + v + "=\"", file=cctFile)
    # Case 783567, substitute __ESCAPE__, see
# https://community.arm.com/support-forums/f/keil-forum/37221/__escape__-macro
    elif args.hierarchy == "gnu" and "__clang__" in var and v == "__ESCAPE__":
      print("*replacing problematic", v + "=" + val, file=cctFile)
      print(defineOption, "\"" + v + "(x)=x\"", file=cctFile)
    else:
      print(defineOption, "\"" + v + "=" + val + "\"", file=cctFile)
  if (
    args.hierarchy == "ghs" and isCPP and
    compilerInfo["COMPILER_VERSION"].startswith("2017")
  ):
    print("* type_traits fix for 2017", file=cctFile)
    print("-sd _ALIGN_OF=alignof", file=cctFile)
    if compilerInfo["LANGUAGE_STANDARD_VERSION"] == "c++11":
      print("* C++11 support", file=cctFile)
      print("-sd __EDG_VERSION__=413", file=cctFile)
      print("-sd __CHAR16_T_AND_CHAR32_T", file=cctFile)
  print("* intrinsic types", file=cctFile)
  intrinsicTypes = compilerInfo["INTRINSIC_TYPES"]
  if args.hierarchy == "arm":
# C:/Keil_v5/ARM/ARMCC/include/stddef.h
# #if defined(__WCHAR32) || (defined(__ARM_SIZEOF_WCHAR_T) && __ARM_SIZEOF_WCHAR_T == 4)
#   typedef unsigned int wchar_t; /* also in <stdlib.h> and <inttypes.h> */
# #else
#   typedef unsigned short wchar_t; /* also in <stdlib.h> and <inttypes.h> */
# #endif
    # for qacpp wchar_t should be treated as keyword, so no intrinsic type
    if not isCPP:
      if "__WCHAR32" in var or ("__ARM_SIZEOF_WCHAR_T" in var and
           int(var["__ARM_SIZEOF_WCHAR_T"] ) == 8):
        intrinsicTypes["wchar_t"] = "unsigned int"
      else:
        intrinsicTypes["wchar_t"] = "unsigned short"
# C:/Keil_v5/ARM/ARMCC/include/stddef.h
# #if __sizeof_ptr == 8
#   typedef signed long ptrdiff_t;
# #else
#   typedef signed int ptrdiff_t;
# #endif
#
# #if __sizeof_ptr == 8
#   typedef unsigned long size_t;   /* see <stddef.h> */
# #else
#   typedef unsigned int size_t;   /* see <stddef.h> */
# #endif
    if "__sizeof_ptr" in var and int(var["__sizeof_ptr" ] ) == 8:
      intrinsicTypes["ptrdiff_t"] = "long"
      intrinsicTypes["size_t"] = "unsigned long"
    else:
      intrinsicTypes["ptrdiff_t"] = "int"
      intrinsicTypes["size_t"] = "unsigned int"
  elif args.hierarchy == "diab":
    if not isCPP:
      intrinsicTypes["wchar_t"] = "unsigned short"
    if "__LP64__" in var:
      intrinsicTypes["ptrdiff_t"] = "long"
      intrinsicTypes["size_t"] = "unsigned long"
    else:
      intrinsicTypes["ptrdiff_t"] = "int"
      intrinsicTypes["size_t"] = "unsigned int"
  elif args.hierarchy == "ghs":
# C:/ghs/comp_201714/ansi/ghs_wchar.h:
# if defined(__WCHAR_TYPE__)
# elif defined(__WChar_Is_Int__) && defined(__WChar_Is_Unsigned__)
#   define __WCHAR_TYPE__ unsigned int
# elif defined(__WChar_Is_Int__)
#   define __WCHAR_TYPE__          int
# elif (__WCHAR_BIT == __LONG_BIT) && defined(__WChar_Is_Unsigned__)
#   define __WCHAR_TYPE__ unsigned long
# elif (__WCHAR_BIT == __LONG_BIT)
#   define __WCHAR_TYPE__          long
# elif (__WCHAR_BIT == __INT_BIT) && defined(__WChar_Is_Unsigned__)
#   define __WCHAR_TYPE__ unsigned int
# elif (__WCHAR_BIT == __INT_BIT)
#   define __WCHAR_TYPE__          int
# elif (__WCHAR_BIT == __SHRT_BIT) && defined(__WChar_Is_Unsigned__)
#   define __WCHAR_TYPE__ unsigned short
# elif (__WCHAR_BIT == __SHRT_BIT)
#   define __WCHAR_TYPE__          short
# else
#   error Unknown size of wchar_t
# endif
    if not isCPP:
      if "__WChar_Is_Int__" in var:
        intrinsicTypes["wchar_t"] = "int"
      elif var["__WCHAR_BIT"] == var["__LONG_BIT"]:
        intrinsicTypes["wchar_t"] = "long"
      elif var["__WCHAR_BIT"] == var["__INT_BIT"]:
        intrinsicTypes["wchar_t"] = "int"
      elif var["__WCHAR_BIT"] == var["__SHRT_BIT"]:
        intrinsicTypes["wchar_t"] = "short"
      if "wchar_t" in intrinsicTypes and "__WChar_Is_Unsigned__" in var:
        intrinsicTypes["wchar_t"] = "unsigned " + intrinsicTypes["wchar_t"]
# C:/ghs/comp_201714/ansi/stddef.h:
# if (__PTR_BIT <= __INT_BIT) || (__INT_BIT == __LONG_BIT)
#typedef int ptrdiff_t;
# elif (__PTR_BIT <= __LONG_BIT) || (__LONG_BIT == __LLONG_BIT)
#typedef long ptrdiff_t;
# else
#typedef long long ptrdiff_t;
# endif
    if int(var["__PTR_BIT"]) <= int(var["__INT_BIT"]) or \
       var["__INT_BIT"] == var["__LONG_BIT"]:
      intrinsicTypes["ptrdiff_t"] = "int"
    elif int(var["__PTR_BIT"]) <= int(var["__LONG_BIT"]) or \
       var["__LONG_BIT"] == var["__LLONG_BIT"]:
      intrinsicTypes["ptrdiff_t"] = "long"
    else:
      intrinsicTypes["ptrdiff_t"] = "long long"
# C:/ghs/comp_201714/ansi/ghs_null.h:
#  if defined(__LLONG_BIT) && __PTR_BIT > __LONG_BIT
#     typedef unsigned long long size_t;
#  elif __PTR_BIT > __INT_BIT
#     typedef unsigned long size_t;
#  else
#     typedef unsigned int size_t;
#  endif
    if "__LLONG_BIT" in var and int(var["__PTR_BIT"]) > int(var["__LONG_BIT"]):
      intrinsicTypes["size_t"] = "unsigned long long"
    elif int(var["__PTR_BIT"]) > int(var["__INT_BIT"]):
      intrinsicTypes["size_t"] = "unsigned long"
    else:
      intrinsicTypes["size_t"] = "unsigned int"
  elif args.hierarchy == "gnu" or args.hierarchy == "tasking":
    if not isCPP:
      if variant == "_xc8":
        xc8_c90 = (
          var["_CLIB_VERSION"] == "1990" if "_CLIB_VERSION" in var else
          compilerInfo["LANGUAGE_STANDARD_VERSION"] == "c90"
        )
      if xc8_c90:
        intrinsicTypes["wchar_t"] = "unsigned short"
      elif "__WCHAR_TYPE__" in var:
        intrinsicTypes["wchar_t"] = fixIntrinsicType(var["__WCHAR_TYPE__"] )
    if args.hierarchy == "tasking" and "__WCHAR_MAX" in var:
      t = ""
      if var["__WCHAR_MAX"].startswith("U"):
        t = "unsigned "
      if "INT32" in var["__WCHAR_MAX"]:
        t += "int"
      else:
        t += "short"
      if not isCPP or not "_WCHAR_T" in var:
        intrinsicTypes["wchar_t"] = t
    if variant == "_xc8":
      intrinsicTypes["ptrdiff_t"] = "int" if xc8_c90 else "long"
    elif "__PTRDIFF_TYPE__" in var:
      intrinsicTypes["ptrdiff_t"] = fixIntrinsicType(var[ "__PTRDIFF_TYPE__"] )
    if variant == "_xc8":
      intrinsicTypes["size_t"] = "unsigned int"
    elif "__SIZE_TYPE__" in var:
      intrinsicTypes["size_t"] = fixIntrinsicType(var["__SIZE_TYPE__"] )
  elif args.hierarchy == "iar" or args.hierarchy == "ti":
    if not isCPP and "__WCHAR_T_TYPE__" in var:
      intrinsicTypes["wchar_t"] = var["__WCHAR_T_TYPE__"]
    if "__PTRDIFF_T_TYPE__" in var:
      intrinsicTypes["ptrdiff_t"] = var[ "__PTRDIFF_T_TYPE__"]
    if "__SIZE_T_TYPE__" in var:
      intrinsicTypes["size_t"] = var["__SIZE_T_TYPE__"]
  elif args.hierarchy == "renesas":
    intrinsicTypes["ptrdiff_t"] = "int"
    intrinsicTypes["size_t"] = "unsigned int"
    if "__CCRX__" in var:
      intrinsicTypes["ptrdiff_t"] = "long"
    if "__CCRX__" in var or "__CCRH__" in var:
      intrinsicTypes["size_t"] = "unsigned long"
    if not isCPP and (
      "__CCRX__" in var or "__CCRH__" in var or "__CCRL__" in var
    ):
      intrinsicTypes["wchar_t"] = "unsigned short"
  for intrinsic, type in intrinsicTypes.items():
    print("-it \"" + intrinsic + "=" + type + "\"", file=cctFile)
    if isCPP and args.hierarchy == "tasking" and intrinsic == "size_t":
      print(
        defineOption,
        "\"__ssize_t=" + type.replace("unsigned", "").strip() + "\"",
        file=cctFile
      )
  print("* basic type size and alignment", file=cctFile)
  prefix = True
  sizeof = None
  if args.hierarchy == "arm":
    sizeof = "__sizeof_"
  elif args.hierarchy in ("diab", "gnu", "ti"):
    sizeof = "__SIZEOF_"
  elif args.hierarchy == "iar":
    prefix = False
    sizeof = "_SIZE__"
  def removeIntSuffix(s):
    while s[-1:] in "uUlL":
      s = s[:-1]
    return s
  for t, v in sizeTypeMap.items():
    if v in var:
      bits = removeIntSuffix(var[v] )
      if sizeof and (v.startswith(sizeof) if prefix else v.endswith(sizeof)):
        bits = str(
          (
            8 if t == "char" or not "char" in sizeMap else int(sizeMap["char"] )
          ) * int(bits)
        )
      sizeMap[t] = bits
  for t, v in sizeMaxMap.items():
    if not(t in sizeMap) and v in var:
      s = bin(int(var[v].rstrip("L"))).lstrip("-0b")
      sizeMap[t] = str(len(s) + 1)
  # hard code type sizes for xc8 driver
  if variant == "_xc8":
    if not sizeMap:
      sizeMap["char"] = "8"
      sizeMap["short"] = "16"
      sizeMap["int"] = "16"
      sizeMap["long"] = "32"
      sizeMap["longlong"] = "32"
      sizeMap["float"] = "32"
      sizeMap["double"] = "32"
      sizeMap["ldouble"] = "32"
    sizeMap["codeptr"] = "16"
    sizeMap["dataptr"] = "16"
  for t, v in sizeTypeMap.items():
    if not(t in sizeMap):
      print(v, "not defined by compiler! Using default size for", t + ".")
  for t, v in alignTypeMap.items():
    if v in var:
      alignMap[t] = removeIntSuffix(var[v] )
    else:
      print(
        v, "not defined by compiler! Using default alignment for", t + "."
      )
  for type, size in sizeMap.items():
    print("-s \"" + type +"=" + size + "\"", file=cctFile)
    char_size = sizeMap.get("char", "8")
    if type in alignMap:
      s = min(int(alignMap[type] ), int(size)//int(char_size))
      print("-a \"" + type +"=" + str(s)  + "\"", file=cctFile)
    else:
      # I was unable to find comprehensive documentation for type alignment,
      # so let us do the typical alignment on boundary.
      # DSPs usually have word size alignment for all types
      # and char size larger than 8.
      # So what we do is if char is not 8, align everything to word and else
      # align to size of the type at hand:
      int_size = sizeMap.get("int", "32")
      print(
        "-a \"" + type +"=" + (
          str(int(size) // 8) if char_size == "8" else
          str(int(int_size) // int(char_size))
        ) + "\"",
        file=cctFile
      )
  for type, size in alignMap.items():
    if not(type in sizeMap):
      print("-a \"" + type +"=" + alignMap[type]  + "\"", file=cctFile)
  if args.hierarchy == "gnu":
    print("* GNU type mode helpers", file=cctFile)
    qi = 8
    if "__CHAR_BIT__" in var:
      qi = int(var["__CHAR_BIT__"] )
    hi = 2 * qi
    si = 2 * hi
    di = 2 * si
    sf = si
    df = di

    def stdType(type):
      return (
        "long long" if type == "longlong" else
        "long double" if type == "ldouble" else
        "signed char" if type == "char" else
        type
      )
    def mode(type):
      return (
        'I' if type in ("char", "short", "int", "long", "longlong") else
        'F' if type in ("float",  "double", "ldouble") else
        None
      )

    for bits, id in [
      (qi, "QI"), (hi, "HI"), (si, "SI"), (di, "DI"), (sf, "SF"), (df, "DF")
    ]:
      for type, size in sizeMap.items():
        if int(size) == bits and mode(type) == id[1]:
          print(
            defineOption,
            "\"__QAC_MODE_" + id + "_INT_TYPE=" + stdType(type) + "\"",
            file=cctFile
          )
          bits = -1
  print("* common parser options", file=cctFile)
  printParserOptions(cctFile, commonOpts)
  (qacOpts, qacppOpts) = deriveParserOptions(args, compilerInfo)
  if isCPP:
    if args.hierarchy == "diab":
      qacppOpts["sig"] = "__ETOA_IMPLICIT_USING_STD" in var
    elif args.hierarchy == "ghs":
      if "__Field_Is_Signed__" in var:
        qacppOpts["bits"] = True
      elif "__Field_Is_Unsigned__" in var:
        qacppOpts["bits"] = False
    elif args.hierarchy == "gnu" and "__clang__" in var:
      qacppOpts["c++03np"] = False
    print("* qacpp parser options", file=cctFile)
    printParserOptions(cctFile, qacppOpts)
  else:
    if args.hierarchy == "arm":
      qacOpts["u"] = not("__FEATURE_SIGNED_CHAR" in var)
    elif args.hierarchy == "diab":
      qacOpts["u"] = ("__CHAR_UNSIGNED__" in var)
    elif args.hierarchy == "ghs":
      if "__Char_Is_Signed__" in var:
        qacOpts["u"] = False
      elif "__Char_Is_Unsigned__" in var:
        qacOpts["u"] = True
      if "__Field_Is_Signed__" in var:
        qacOpts["bits"] = True
      elif "__Field_Is_Unsigned__" in var:
        qacOpts["bits"] = False
    elif args.hierarchy == "gnu":
      if "__CHAR_SIGNED__" in var:
        qacOpts["u"] = False
      elif "__CHAR_UNSIGNED__" in var:
        qacOpts["u"] = True
    elif args.hierarchy == "renesas":
      qacOpts["spragma"] = [ "asm,endasm" ]
      if "__CHAR_SIGNED__" in var or "__SCHAR" in var:
        qacOpts["u"] = False
      elif "__CHAR_UNSIGNED__" in var or "__UCHAR" in var:
        qacOpts["u"] = True
      if "__UBIT" in var:
        qacOpts["bits"] = False
      elif "__SBIT" in var:
        qacOpts["bits"] = True
    elif args.hierarchy == "ti":
      qacOpts["u"] = ("__unsigned_chars__" in var)
    print("* qac parser options", file=cctFile)
    printParserOptions(cctFile, qacOpts)
  cctFile.close()
  done = (isCPP == (args.language == "cpp"))
  if done:
    out_logger.warning("CCT generated for command " + command_line)
  else:
    cct_logger.warning(
      "CCT generated for command " + command_line + " is for language " +
      ("cpp" if isCPP else "c") + " which is not the requested language " +
      args.language
    )
  return done

def check_args():
  parser = argparse.ArgumentParser(
    description="Generate CCT for compiler command."
  )
  parser.add_argument(
    "-v", "--version", action="version",
    version="%(prog)s {d}".format(d=build_time)
  )
  parser.add_argument(
    "-lang", "--language", choices=[ "c", "cpp" ], default=None,
    help="specify source language"
  )
  parser.add_argument(
    "-qv", "--qacversion", default=None, help="specify target framework version"
  )
  parser.add_argument(
    "-o", "--overwrite", action="store_true", help="overwrite existing CCT"
  )
  parser.add_argument("-n", "--name", help="CCT name")
  parser.add_argument("-op", "--outputpath", help="CCT destination path")
  parser.add_argument(
    "-vb", "--verbose", action="store_true",
    help="show compiler output on error and keep sync projects"
  )
  parser.add_argument(
    "-ch", "--hierarchy",
    choices=[
      "arm", "diab", "ghs", "gnu", "iar", "renesas", "tasking",  "ti"
    ],
    default=None, help="specify compiler hierarchy"
  )
  parser.add_argument(
    "-qtrace", "--qactrace", nargs='?', default=None, const="////",
    help="qacli sync trace log file"
  )
  parser.add_argument(
    "-kwtrace", "--klocworktrace", help="Klocwork kwinject trace output file"
  )
  parser.add_argument(
    "-bl", "--blacklist", nargs='?', const=1, type=int,
    help="blacklist binaries for which CCT generation failed BLACKLIST times"
  )
  parser.add_argument(
    "-cwd", "--currentworkingdirectory", 
    help="current working directory for build or compiler command"
  )
  parser.add_argument(
    "-st", "--synctype", choices=[ "MONITOR", "AUTO_CCT" ], default="MONITOR",
    help="sync type used for build command"
  )
  parser.add_argument(
    "-c", "--command", nargs=argparse.REMAINDER, default=None,
    help="compiler binary and target or language options (must be specified as final option)"
  )
  parser.add_argument(
    "-b", "--build", nargs=argparse.REMAINDER, default=None,
    help="project build command (must be specified as final option)"
  )
  return parser.parse_args()

def loginit(args):
  global cct_logger
  global out_logger
  formatter = logging.Formatter("%(message)s")
  ch = logging.StreamHandler()  # by default sys.stderr
  oh = logging.StreamHandler(sys.stdout)
  ch.setFormatter(formatter)
  ch.setLevel(logging.DEBUG)
  oh.setFormatter(formatter)
  oh.setLevel(logging.DEBUG)
  cct_logger.addHandler(ch)
  out_logger.addHandler(oh)
  if args.verbose:
    cct_logger.setLevel(logging.DEBUG)
    out_logger.setLevel(logging.DEBUG)
  else:
    out_logger.setLevel(logging.INFO)
    if is_derived_command(args):
      cct_logger.setLevel(logging.WARNING)
    else:
      cct_logger.setLevel(logging.INFO)

def externlogs(filehandler, loglevel):
  global cct_logger
  global out_logger
  numeric_level = getattr(logging, loglevel.upper(), None)
  if not isinstance(numeric_level, int):
    raise ValueError("Invalid log level: %s" % loglevel)

  ch = logging.StreamHandler()
  formatter = logging.Formatter(
    "%(asctime)s - %(name)s - %(levelname)s - %(message)s"
  )
  ch.setFormatter(formatter)
  ch.setLevel(logging.WARNING)
  cct_logger.setLevel(numeric_level)
  cct_logger.addHandler(filehandler)
  cct_logger.addHandler(ch)
  out_logger = cct_logger

def init_unset_args(args):
  if not hasattr(args, "language"):
    args.language = None
  if not hasattr(args, "qacversion"):
    args.qacversion = None
  if not hasattr(args, "overwrite"):
    args.overwrite = None
  if not hasattr(args, "name"):
    args.name = None
  if not hasattr(args, "outputpath"):
    args.outputpath = None
  if not hasattr(args, "verbose"):
    args.verbose = None
  if not hasattr(args, "hierarchy"):
    args.hierarchy = None
  if not hasattr(args, "qactrace"):
    args.qactrace = None
  if not hasattr(args, "klocworktrace"):
    args.klocworktrace = None
  if not hasattr(args, "blacklist"):
    args.blacklist = None
  if not hasattr(args, "currentworkingdirectory"):
    args.currentworkingdirectory = None
  if not hasattr(args, "command"):
    args.command = None
  if not hasattr(args, "build"):
    args.build = None

def main(args):
  if args is None:
    args = check_args()
    loginit(args)
  else:
    externlogs(args.handler, args.log)

  init_unset_args(args)
  if args.command:
    generateCCT(args)
  elif args.build:
    check_cwd(args)
    tmpd = tempfile.mkdtemp(suffix=".d")
    # copy CCT to tmp to avoid qaf UNC path limitations
    syncCCT = os.path.join(tmpd, "SyncCCT")
    shutil.copytree(os.path.join(currentDir, "SyncCCT"), syncCCT)
    syncProject = os.path.join(tmpd, "SyncProject")
    buildCommands = os.path.join(syncProject, "build_commands.txt")
    (qacliPath, version) = qacli(args.qacversion)
    if not version:
      cct_logger.error("No framework installation found.")
    elif version < "2.4.0":
      cct_logger.error(
        "Tool version found does not include CCT process monitor support."
      )
    else:
      if os.path.exists(buildCommands):
        os.remove(buildCommands)
      env = os.environ.copy()
      env["LANG"] = "C"
      # create project that uses the sync CCT
      command = [
        qacliPath, "admin", "-P", syncProject, "--qaf-project-config",
        "-A", "default.acf", "-R",
        "default" + ("-1_0" if version < "2.5" else "") + "-en_US.rcf",
        "-C", os.path.join(syncCCT, "Sync_C++.cct")
      ]
      p = subprocess.Popen(
        command, stdout=subprocess.PIPE, stderr=subprocess.PIPE, env=env
      )
      (stdoutdata, stderrdata) = p.communicate()
      dumpOutput(args, command, stdoutdata, stderrdata)
      # sync the build
      command = [
        qacliPath, "sync", "-P", syncProject, "-t", args.synctype, "--",
        reduce(lambda s,t: (s + " " + t), args.build)
      ]
      p = subprocess.Popen(
        command, stdout=subprocess.PIPE, stderr=subprocess.PIPE,
        env=env, cwd=args.currentworkingdirectory
      )
      (stdoutdata, stderrdata) = p.communicate()
      dumpOutput(args, command, stdoutdata, stderrdata)
    done = False
    if os.path.exists(buildCommands):
      f = open(buildCommands)
      lines = f.readlines()
      f.close()
      for s in lines:
        if not done:
          args.command = json.loads(s)
          done = generateCCT(args)
    if not args.verbose:
      shutil.rmtree(tmpd)
    return done
  elif args.qactrace:
  # produce CCT for commands found in qac sync trace log
    def process_log(f):
      tf = open(f)
      tl = tf.readlines()
      tf.close()
      done = False
      next_is_command = False
      command = ""
      synclinux = "::QAProcessMonitor::LinuxProcessMonitor::HandleProcess: Command Line:"
      synclog = "::QAProjectManager::compilerWrapper: Stringified arguments:"
      syncjson = "::QASynchronization::ParseCommandLine: CWD:"
      synccwd = "CWD: ["
      synccmd = "], Command Line; ["
      def process_command(command):
        lex = shlex.shlex(command, posix=True)
        lex.whitespace_split = True
        lex.escape = ""
        try:
          args.command = list(lex)
        except Exception:
          args.command = [ ]
        return generateCCT(args)
      for line in tl:
        if not done:
          line = line.strip()
          synclog_idx = line.find(synclog)
          syncjson_idx = line.find(syncjson)
          linux_idx = line.find(synclinux)
          if linux_idx > 0:
            line = line[linux_idx + len(synclinux):line.rfind(";") ]
            line = re.sub(";", " ", line)
            done = process_command(line)
          elif line.endswith("::MonitorArgv: Command line run:"):
            next_is_command = True
          elif next_is_command:
            pid = line.rfind(" PID(")
            ppid = line.rfind(" PPID (")
            next_is_command = (pid < 0) or (ppid <= pid)
            if next_is_command:
              if line.endswith("\\"):
                line = line[:-1]
              command += line + " "
            else:
              command += line[:pid]
              done = process_command(command)
              command = ""
          elif synclog_idx > 0:
            line = line[synclog_idx + len(synclog):].strip()
# TODO       if line.endswith("\\"):
#              line = line[:-1]
#              command += line + " "
#            else:
            command += line
            done = process_command(command)
            if not done and command.find(";") > 0:
              for cmd in command.split(";"):
                if not done:
                  done = process_command(cmd)
            command = ""
          elif syncjson_idx > 0:
            cwd_idx = line.find(synccwd)
            cmd_idx = line.find(synccmd)
            if cwd_idx > 0 and cmd_idx > cwd_idx:
              args.currentworkingdirectory = line[
                cwd_idx + len(synccwd):cmd_idx
              ].strip()
              process_command(
                line[cmd_idx + len(synccmd):line.rfind("]") ].strip()
              )
      return done
    if args.qactrace == "////":
      logd = os.path.join(
        get_user_CCT_path(args.qacversion), os.pardir, os.pardir, "app", "logs"
      )
      logs = [
        p for f in os.listdir(
          logd
        ) if f.startswith("Helix-QAC" ) or f.startswith(
          "qaframework"
        ) for p in [ os.path.join(logd, f) ] if os.path.isfile(p)
      ]
      logs.sort(reverse=True)
      done = False
      for f in logs:
        if not done:
          done = process_log(f)
          if done:
            out_logger.warning("Compiler command found in " + f)
      return done
    else:
      return process_log(args.qactrace)
  elif args.klocworktrace:
  # produce CCT's for commands found in Klocwork's kwinject trace output
    tf = open(args.klocworktrace)
    tl = tf.readlines()
    tf.close()
    done = False
    for c in tl:
      js = json.loads(c)
      if "work_dir" in js:
        args.currentworkingdirectory = js["work_dir"]
      if "args" in js:
        args.command = js["args"]
        done = generateCCT(args) or done
    return done

if __name__=="__main__":
  main(None)

