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