1#!/bin/env python3
2
3# Copyright (C) 2024 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9#      http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
16
17import argparse
18
19
20def fetch_arguments():
21  parser = argparse.ArgumentParser(
22      prog="ditto2cpp",
23      description="Translate .ditto file to C++ source files to embed in the benchmark."
24  )
25  parser.add_argument("-o", "--output", required=True, help="Output file",
26                      type=str)
27  parser.add_argument("-s", "--sources", required=True, nargs='+',
28                      help="Source .ditto files", type=str)
29  parser.add_argument("-v", "--verbose", help="Verbose output",
30                      action='store_true')
31  args = parser.parse_args()
32
33  if args.verbose:
34    print('From: "{}"'.format(args.sources))
35    print('Output: "{}"'.format(args.output))
36
37  return args
38
39
40def compute_common_prefix(arr):
41  result = arr[0]
42  length = len(result)
43
44  # Iterate for the rest of the elements in the array
45  for i in range(1, len(arr)):
46    # Find the index of result in the current string
47    while arr[i].find(result) != 0:
48      # Update the matched substring prefix
49      result = result[:length - 1]
50      length -= 1
51
52      # Check for an empty case and return if true
53      if not result:
54        raise Exception("No results")
55  return result
56
57
58def generate_benchmark_source(output, sources):
59  common_prefix = compute_common_prefix(sources)
60  last_slash_in_prefix = 0
61  try:
62    last_slash_in_prefix = len(common_prefix) - common_prefix[::-1].index('/')
63  except ValueError:
64    # This happens when '/' cannot be found in `common_prefix`
65    pass
66  prefix_length = min(len(common_prefix), last_slash_in_prefix)
67  suffix_length = len(".ditto")
68
69  file_header = '''
70   #include <ditto/embedded_benchmarks.h>
71   const std::map<std::string, std::string> ditto_static_config = {
72   '''
73  file_footer = '''
74   };
75   '''
76  with open(output, 'w') as fo:
77    fo.write(file_header)
78    for fi_path in sources:
79      fi_name = fi_path[prefix_length:-suffix_length]
80      with open(fi_path, 'r') as fi:
81        fo.write('\t')
82        fo.write('{{"{}", "{}"}},'.format(
83            fi_name,
84            fi.read().replace('\n', '').replace('"', '\\"')
85        ))
86        fo.write('\n')
87    fo.write(file_footer)
88
89
90if __name__ == '__main__':
91  args = fetch_arguments()
92
93  generate_benchmark_source(args.output, args.sources)
94
95  if args.verbose:
96    print("Output file content:")
97    print('-' * 32)
98    with open(args.output, 'r') as fo:
99      print(fo.read())
100    print('-' * 32)
101