]> git.proxmox.com Git - ceph.git/blob - ceph/src/rocksdb/build_tools/amalgamate.py
update source to Ceph Pacific 16.2.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 from os import path
32 import re
33 import sys
34
35 include_re = re.compile('^[ \t]*#include[ \t]+"(.*)"[ \t]*$')
36 included = set()
37 excluded = set()
38
39 def find_header(name, abs_path, include_paths):
40 samedir = path.join(path.dirname(abs_path), name)
41 if path.exists(samedir):
42 return samedir
43 for include_path in include_paths:
44 include_path = path.join(include_path, name)
45 if path.exists(include_path):
46 return include_path
47 return None
48
49 def expand_include(include_path, f, abs_path, source_out, header_out, include_paths, public_include_paths):
50 if include_path in included:
51 return False
52
53 included.add(include_path)
54 with open(include_path) as f:
55 print('#line 1 "{}"'.format(include_path), file=source_out)
56 process_file(f, include_path, source_out, header_out, include_paths, public_include_paths)
57 return True
58
59 def process_file(f, abs_path, source_out, header_out, include_paths, public_include_paths):
60 for (line, text) in enumerate(f):
61 m = include_re.match(text)
62 if m:
63 filename = m.groups()[0]
64 # first check private headers
65 include_path = find_header(filename, abs_path, include_paths)
66 if include_path:
67 if include_path in excluded:
68 source_out.write(text)
69 expanded = False
70 else:
71 expanded = expand_include(include_path, f, abs_path, source_out, header_out, include_paths, public_include_paths)
72 else:
73 # now try public headers
74 include_path = find_header(filename, abs_path, public_include_paths)
75 if include_path:
76 # found public header
77 expanded = False
78 if include_path in excluded:
79 source_out.write(text)
80 else:
81 expand_include(include_path, f, abs_path, header_out, None, public_include_paths, [])
82 else:
83 sys.exit("unable to find {}, included in {} on line {}".format(filename, abs_path, line))
84
85 if expanded:
86 print('#line {} "{}"'.format(line+1, abs_path), file=source_out)
87 elif text != "#pragma once\n":
88 source_out.write(text)
89
90 def main():
91 parser = argparse.ArgumentParser(description="Transform a unity build into an amalgamation")
92 parser.add_argument("source", help="source file")
93 parser.add_argument("-I", action="append", dest="include_paths", help="include paths for private headers")
94 parser.add_argument("-i", action="append", dest="public_include_paths", help="include paths for public headers")
95 parser.add_argument("-x", action="append", dest="excluded", help="excluded header files")
96 parser.add_argument("-o", dest="source_out", help="output C++ file", required=True)
97 parser.add_argument("-H", dest="header_out", help="output C++ header file", required=True)
98 args = parser.parse_args()
99
100 include_paths = list(map(path.abspath, args.include_paths or []))
101 public_include_paths = list(map(path.abspath, args.public_include_paths or []))
102 excluded.update(map(path.abspath, args.excluded or []))
103 filename = args.source
104 abs_path = path.abspath(filename)
105 with open(filename) as f, open(args.source_out, 'w') as source_out, open(args.header_out, 'w') as header_out:
106 print('#line 1 "{}"'.format(filename), file=source_out)
107 print('#include "{}"'.format(header_out.name), file=source_out)
108 process_file(f, abs_path, source_out, header_out, include_paths, public_include_paths)
109
110 if __name__ == "__main__":
111 main()