2015-09-24 22:29:05 +00:00
|
|
|
#!/usr/bin/python
|
2019-04-18 17:51:19 +00:00
|
|
|
# Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
|
2015-09-24 22:29:05 +00:00
|
|
|
|
|
|
|
# amalgamate.py creates an amalgamation from a unity build.
|
|
|
|
# It can be run with either Python 2 or 3.
|
|
|
|
# An amalgamation consists of a header that includes the contents of all public
|
|
|
|
# headers and a source file that includes the contents of all source files and
|
|
|
|
# private headers.
|
|
|
|
#
|
|
|
|
# This script works by starting with the unity build file and recursively expanding
|
|
|
|
# #include directives. If the #include is found in a public include directory,
|
|
|
|
# that header is expanded into the amalgamation header.
|
|
|
|
#
|
|
|
|
# A particular header is only expanded once, so this script will
|
|
|
|
# break if there are multiple inclusions of the same header that are expected to
|
|
|
|
# expand differently. Similarly, this type of code causes issues:
|
|
|
|
#
|
|
|
|
# #ifdef FOO
|
|
|
|
# #include "bar.h"
|
|
|
|
# // code here
|
|
|
|
# #else
|
|
|
|
# #include "bar.h" // oops, doesn't get expanded
|
|
|
|
# // different code here
|
|
|
|
# #endif
|
|
|
|
#
|
|
|
|
# The solution is to move the include out of the #ifdef.
|
|
|
|
|
|
|
|
|
|
|
|
import argparse
|
|
|
|
import re
|
|
|
|
import sys
|
2022-09-21 00:47:52 +00:00
|
|
|
from os import path
|
2015-09-24 22:29:05 +00:00
|
|
|
|
|
|
|
include_re = re.compile('^[ \t]*#include[ \t]+"(.*)"[ \t]*$')
|
|
|
|
included = set()
|
|
|
|
excluded = set()
|
|
|
|
|
2022-09-21 00:47:52 +00:00
|
|
|
|
2015-09-24 22:29:05 +00:00
|
|
|
def find_header(name, abs_path, include_paths):
|
|
|
|
samedir = path.join(path.dirname(abs_path), name)
|
|
|
|
if path.exists(samedir):
|
|
|
|
return samedir
|
|
|
|
for include_path in include_paths:
|
|
|
|
include_path = path.join(include_path, name)
|
|
|
|
if path.exists(include_path):
|
|
|
|
return include_path
|
|
|
|
return None
|
|
|
|
|
2022-09-21 00:47:52 +00:00
|
|
|
|
|
|
|
def expand_include(
|
|
|
|
include_path,
|
|
|
|
f,
|
|
|
|
abs_path,
|
|
|
|
source_out,
|
|
|
|
header_out,
|
|
|
|
include_paths,
|
|
|
|
public_include_paths,
|
|
|
|
):
|
2015-09-24 22:29:05 +00:00
|
|
|
if include_path in included:
|
|
|
|
return False
|
|
|
|
|
|
|
|
included.add(include_path)
|
|
|
|
with open(include_path) as f:
|
2024-10-14 10:01:20 +00:00
|
|
|
print(f'#line 1 "{include_path}"', file=source_out)
|
2022-09-21 00:47:52 +00:00
|
|
|
process_file(
|
|
|
|
f, include_path, source_out, header_out, include_paths, public_include_paths
|
|
|
|
)
|
2015-09-24 22:29:05 +00:00
|
|
|
return True
|
|
|
|
|
2022-09-21 00:47:52 +00:00
|
|
|
|
|
|
|
def process_file(
|
|
|
|
f, abs_path, source_out, header_out, include_paths, public_include_paths
|
|
|
|
):
|
2015-09-24 22:29:05 +00:00
|
|
|
for (line, text) in enumerate(f):
|
|
|
|
m = include_re.match(text)
|
|
|
|
if m:
|
|
|
|
filename = m.groups()[0]
|
|
|
|
# first check private headers
|
|
|
|
include_path = find_header(filename, abs_path, include_paths)
|
|
|
|
if include_path:
|
|
|
|
if include_path in excluded:
|
|
|
|
source_out.write(text)
|
|
|
|
expanded = False
|
|
|
|
else:
|
2022-09-21 00:47:52 +00:00
|
|
|
expanded = expand_include(
|
|
|
|
include_path,
|
|
|
|
f,
|
|
|
|
abs_path,
|
|
|
|
source_out,
|
|
|
|
header_out,
|
|
|
|
include_paths,
|
|
|
|
public_include_paths,
|
|
|
|
)
|
2015-09-24 22:29:05 +00:00
|
|
|
else:
|
|
|
|
# now try public headers
|
|
|
|
include_path = find_header(filename, abs_path, public_include_paths)
|
|
|
|
if include_path:
|
|
|
|
# found public header
|
|
|
|
expanded = False
|
|
|
|
if include_path in excluded:
|
|
|
|
source_out.write(text)
|
|
|
|
else:
|
2022-09-21 00:47:52 +00:00
|
|
|
expand_include(
|
|
|
|
include_path,
|
|
|
|
f,
|
|
|
|
abs_path,
|
|
|
|
header_out,
|
|
|
|
None,
|
|
|
|
public_include_paths,
|
|
|
|
[],
|
|
|
|
)
|
2015-09-24 22:29:05 +00:00
|
|
|
else:
|
2022-09-21 00:47:52 +00:00
|
|
|
sys.exit(
|
|
|
|
"unable to find {}, included in {} on line {}".format(
|
|
|
|
filename, abs_path, line
|
|
|
|
)
|
|
|
|
)
|
2015-09-24 22:29:05 +00:00
|
|
|
|
|
|
|
if expanded:
|
2024-10-14 10:01:20 +00:00
|
|
|
print(f'#line {line + 1} "{abs_path}"', file=source_out)
|
2015-09-24 22:29:05 +00:00
|
|
|
elif text != "#pragma once\n":
|
|
|
|
source_out.write(text)
|
|
|
|
|
2022-09-21 00:47:52 +00:00
|
|
|
|
2015-09-24 22:29:05 +00:00
|
|
|
def main():
|
2022-09-21 00:47:52 +00:00
|
|
|
parser = argparse.ArgumentParser(
|
|
|
|
description="Transform a unity build into an amalgamation"
|
|
|
|
)
|
2015-09-24 22:29:05 +00:00
|
|
|
parser.add_argument("source", help="source file")
|
2022-09-21 00:47:52 +00:00
|
|
|
parser.add_argument(
|
|
|
|
"-I",
|
|
|
|
action="append",
|
|
|
|
dest="include_paths",
|
|
|
|
help="include paths for private headers",
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"-i",
|
|
|
|
action="append",
|
|
|
|
dest="public_include_paths",
|
|
|
|
help="include paths for public headers",
|
|
|
|
)
|
|
|
|
parser.add_argument(
|
|
|
|
"-x", action="append", dest="excluded", help="excluded header files"
|
|
|
|
)
|
2015-09-24 22:29:05 +00:00
|
|
|
parser.add_argument("-o", dest="source_out", help="output C++ file", required=True)
|
2022-09-21 00:47:52 +00:00
|
|
|
parser.add_argument(
|
|
|
|
"-H", dest="header_out", help="output C++ header file", required=True
|
|
|
|
)
|
2015-09-24 22:29:05 +00:00
|
|
|
args = parser.parse_args()
|
|
|
|
|
|
|
|
include_paths = list(map(path.abspath, args.include_paths or []))
|
|
|
|
public_include_paths = list(map(path.abspath, args.public_include_paths or []))
|
|
|
|
excluded.update(map(path.abspath, args.excluded or []))
|
|
|
|
filename = args.source
|
|
|
|
abs_path = path.abspath(filename)
|
2022-09-21 00:47:52 +00:00
|
|
|
with open(filename) as f, open(args.source_out, "w") as source_out, open(
|
|
|
|
args.header_out, "w"
|
|
|
|
) as header_out:
|
2024-10-14 10:01:20 +00:00
|
|
|
print(f'#line 1 "{filename}"', file=source_out)
|
|
|
|
print(f'#include "{header_out.name}"', file=source_out)
|
2022-09-21 00:47:52 +00:00
|
|
|
process_file(
|
|
|
|
f, abs_path, source_out, header_out, include_paths, public_include_paths
|
|
|
|
)
|
|
|
|
|
2015-09-24 22:29:05 +00:00
|
|
|
|
|
|
|
if __name__ == "__main__":
|
|
|
|
main()
|