]> git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/build_tools/amalgamate.py
update ceph source to reef 18.1.2
[ceph.git] / ceph / src / rocksdb / build_tools / amalgamate.py
1 #!/usr/bin/python
2 # Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
3
4 # amalgamate.py creates an amalgamation from a unity build.
5 # It can be run with either Python 2 or 3.
6 # An amalgamation consists of a header that includes the contents of all public
7 # headers and a source file that includes the contents of all source files and
8 # private headers.
9 #
10 # This script works by starting with the unity build file and recursively expanding
11 # #include directives. If the #include is found in a public include directory,
12 # that header is expanded into the amalgamation header.
13 #
14 # A particular header is only expanded once, so this script will
15 # break if there are multiple inclusions of the same header that are expected to
16 # expand differently. Similarly, this type of code causes issues:
17 #
18 # #ifdef FOO
19 # #include "bar.h"
20 # // code here
21 # #else
22 # #include "bar.h" // oops, doesn't get expanded
23 # // different code here
24 # #endif
25 #
26 # The solution is to move the include out of the #ifdef.
27
28 from __future__ import print_function
29
30 import argparse
31 import re
32 import sys
33 from os import path
34
35 include_re = re.compile('^[ \t]*#include[ \t]+"(.*)"[ \t]*$')
36 included = set()
37 excluded = set()
38
39
40 def find_header(name, abs_path, include_paths):
41 samedir = path.join(path.dirname(abs_path), name)
42 if path.exists(samedir):
43 return samedir
44 for include_path in include_paths:
45 include_path = path.join(include_path, name)
46 if path.exists(include_path):
47 return include_path
48 return None
49
50
51 def expand_include(
52 include_path,
53 f,
54 abs_path,
55 source_out,
56 header_out,
57 include_paths,
58 public_include_paths,
59 ):
60 if include_path in included:
61 return False
62
63 included.add(include_path)
64 with open(include_path) as f:
65 print('#line 1 "{}"'.format(include_path), file=source_out)
66 process_file(
67 f, include_path, source_out, header_out, include_paths, public_include_paths
68 )
69 return True
70
71
72 def process_file(
73 f, abs_path, source_out, header_out, include_paths, public_include_paths
74 ):
75 for (line, text) in enumerate(f):
76 m = include_re.match(text)
77 if m:
78 filename = m.groups()[0]
79 # first check private headers
80 include_path = find_header(filename, abs_path, include_paths)
81 if include_path:
82 if include_path in excluded:
83 source_out.write(text)
84 expanded = False
85 else:
86 expanded = expand_include(
87 include_path,
88 f,
89 abs_path,
90 source_out,
91 header_out,
92 include_paths,
93 public_include_paths,
94 )
95 else:
96 # now try public headers
97 include_path = find_header(filename, abs_path, public_include_paths)
98 if include_path:
99 # found public header
100 expanded = False
101 if include_path in excluded:
102 source_out.write(text)
103 else:
104 expand_include(
105 include_path,
106 f,
107 abs_path,
108 header_out,
109 None,
110 public_include_paths,
111 [],
112 )
113 else:
114 sys.exit(
115 "unable to find {}, included in {} on line {}".format(
116 filename, abs_path, line
117 )
118 )
119
120 if expanded:
121 print('#line {} "{}"'.format(line + 1, abs_path), file=source_out)
122 elif text != "#pragma once\n":
123 source_out.write(text)
124
125
126 def main():
127 parser = argparse.ArgumentParser(
128 description="Transform a unity build into an amalgamation"
129 )
130 parser.add_argument("source", help="source file")
131 parser.add_argument(
132 "-I",
133 action="append",
134 dest="include_paths",
135 help="include paths for private headers",
136 )
137 parser.add_argument(
138 "-i",
139 action="append",
140 dest="public_include_paths",
141 help="include paths for public headers",
142 )
143 parser.add_argument(
144 "-x", action="append", dest="excluded", help="excluded header files"
145 )
146 parser.add_argument("-o", dest="source_out", help="output C++ file", required=True)
147 parser.add_argument(
148 "-H", dest="header_out", help="output C++ header file", required=True
149 )
150 args = parser.parse_args()
151
152 include_paths = list(map(path.abspath, args.include_paths or []))
153 public_include_paths = list(map(path.abspath, args.public_include_paths or []))
154 excluded.update(map(path.abspath, args.excluded or []))
155 filename = args.source
156 abs_path = path.abspath(filename)
157 with open(filename) as f, open(args.source_out, "w") as source_out, open(
158 args.header_out, "w"
159 ) as header_out:
160 print('#line 1 "{}"'.format(filename), file=source_out)
161 print('#include "{}"'.format(header_out.name), file=source_out)
162 process_file(
163 f, abs_path, source_out, header_out, include_paths, public_include_paths
164 )
165
166
167 if __name__ == "__main__":
168 main()