1# Copyright 2018 The Chromium Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5import argparse
6import os
7import subprocess
8import sys
9
10def main():
11  args = parseInput()
12
13  assert validateHeaderInput(args.header), \
14         "Error: '%s' is not a valid .h file" % args.header
15  assert validateCodeInput(args.cc), \
16         "Error: '%s' is not a valid .cc file" % args.cc
17  assert validatePathInput(args.gen_dir), \
18         "Error: '%s' is not a valid output directory" % args.gen_dir
19  assert validateCddlInput(args.file), \
20         "Error: '%s' is not a valid CDDL file" % args.file
21
22  if args.log:
23    logPath = os.path.join(args.gen_dir, args.log)
24    log = open(logPath, "w")
25    log.write("OUTPUT FOR CDDL CODE GENERATION TOOL:\n\n")
26    log = open(logPath, "a")
27
28    if (args.verbose):
29      print("Logging to %s" % logPath)
30  else:
31    log = None
32
33  if (args.verbose):
34    print('Creating C++ files from provided CDDL file...')
35  echoAndRunCommand([args.cddl, "--header", args.header, "--cc", args.cc,
36                     "--gen-dir", args.gen_dir, args.file],
37                     False, log, args.verbose)
38
39  clangFormatLocation = findClangFormat()
40  if not clangFormatLocation:
41    if args.verbose:
42      print("WARNING: clang-format could not be found")
43    return
44
45  for filename in [args.header, args.cc]:
46    echoAndRunCommand([clangFormatLocation + 'clang-format', "-i",
47                       os.path.join(args.gen_dir, filename)],
48                       True, verbose=args.verbose)
49
50def parseInput():
51  parser = argparse.ArgumentParser()
52  parser.add_argument("--cddl", help="path to the cddl executable to use")
53  parser.add_argument("--header", help="Specify the filename of the output \
54     header file. This is also the name that will be used for the include \
55     guard and as the include path in the source file.")
56  parser.add_argument("--cc", help="Specify the filename of the output \
57     source file")
58  parser.add_argument("--gen-dir", help="Specify the directory prefix that \
59     should be added to the output header and source file.")
60  parser.add_argument("--log", help="Specify the file to which stdout should \
61     be redirected.")
62  parser.add_argument("--verbose", help="Specify that we should log info \
63     messages to stdout")
64  parser.add_argument("file", help="the input file which contains the spec")
65  return parser.parse_args()
66
67def validateHeaderInput(headerFile):
68  return headerFile and headerFile.endswith('.h')
69
70def validateCodeInput(ccFile):
71  return ccFile and ccFile.endswith('.cc')
72
73def validatePathInput(dirPath):
74  return dirPath and os.path.isdir(dirPath)
75
76def validateCddlInput(cddlFile):
77  return cddlFile and os.path.isfile(cddlFile)
78
79def echoAndRunCommand(commandArray, allowFailure,
80                      logfile = None, verbose = False):
81  if verbose:
82    print("\tExecuting Command: '%s'" % " ".join(commandArray))
83
84  if logfile != None:
85    process = subprocess.Popen(commandArray, stdout=logfile, stderr=logfile)
86    process.wait()
87    logfile.flush()
88  else:
89    process = subprocess.Popen(commandArray)
90    process.wait()
91
92  returncode = process.returncode
93  if returncode != None and returncode != 0:
94    if not allowFailure:
95      sys.exit("\t\tERROR: Command failed with error code: '%i'!" % returncode)
96    elif verbose:
97      print("\t\tWARNING: Command failed with error code: '%i'!" % returncode)
98
99def findClangFormat():
100  executable = "clang-format"
101
102  # Try and run from the environment variable
103  for directory in os.environ["PATH"].split(os.pathsep):
104    fullPath = os.path.join(directory, executable)
105    if os.path.isfile(fullPath):
106      return ""
107
108  # Check 2 levels up since this should be correct on the build machine
109  path = "../../"
110  fullPath = os.path.join(path, executable)
111  if os.path.isfile(fullPath):
112    return path
113
114  return None
115
116if __name__ == "__main__":
117  main()
118