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