mirror of
https://github.com/apple/swift.git
synced 2026-06-20 15:42:51 +02:00
8d1e4c4988
Improve bridging header chaining when prefix mapping is used so it matches the behavior of non-prefix-map and non-caching builds. This also fixes a corner case where the same bridging header is used by different modules in the dependency chain, preventing it from being imported twice. The improvements are: * Fully utilize the clang scanner prefix mapping option, which can restore prefix-mapped paths during scanning to find the real file on the file system. This allows the generated bridging header to always reference the header path without worrying about introducing path dependencies. * Move the header existence check from a file system access in the Swift scanner to a `__has_include` check in the clang scanner. This allows direct header import even when the header path is previously prefix mapped. * Use `#import` to chain bridging headers so the same header will not be imported twice. rdar://172870182
119 lines
5.5 KiB
Python
Executable File
119 lines
5.5 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
|
|
# This tool helps building swift explicit module from the JSON output of the scan-dependencies command. It will build all the module dependencies from JSON and construct a response file for the common arguments for main module build.
|
|
# Usage:
|
|
# /path/to/bin/dir/swift-build-modules.py /path/to/swift-frontend /path/to/depscan.json /path/to/output-resp
|
|
#
|
|
|
|
import argparse
|
|
import json
|
|
import os
|
|
import subprocess
|
|
import sys
|
|
|
|
|
|
def writeOutputResponseFile(filename, cmd):
|
|
with open(filename, 'w') as output:
|
|
for c in cmd:
|
|
output.write('"{}"\n'.format(c))
|
|
|
|
|
|
def apply_prefix_maps(path, prefix_maps):
|
|
for old, new in prefix_maps:
|
|
if path.startswith(old):
|
|
return new + path[len(old):]
|
|
return path
|
|
|
|
|
|
def build_module(swift_frontend, mode, detail):
|
|
cmd = [swift_frontend] + detail['details'][mode]['commandLine']
|
|
subprocess.check_call(cmd)
|
|
|
|
|
|
def main():
|
|
parser = argparse.ArgumentParser()
|
|
parser.add_argument('swift_frontend', help="path to swift-frontend")
|
|
parser.add_argument('input', help="path to json output from scan-dependencies")
|
|
parser.add_argument('-c', '--cas', metavar='<CAS directory>')
|
|
parser.add_argument('--llvm-cas-tool', metavar='<path>', default="llvm-cas")
|
|
parser.add_argument('-o', '--output', metavar="<output>",
|
|
help="output response file for building main module")
|
|
parser.add_argument('-b', '--bridging-header-resp', metavar="<response file>",
|
|
help="output response file for building bridging header")
|
|
parser.add_argument('--prefix-map', metavar=("<old_path>", "<new_path>"), nargs=2,
|
|
action='append', default=[],
|
|
help="remap path prefix when writing chainedBridgingHeaderPath "
|
|
"(may be specified multiple times)")
|
|
args = parser.parse_args()
|
|
|
|
with open(args.input, 'r') as file:
|
|
# Read input json file.
|
|
deps = json.load(file)
|
|
modules = []
|
|
# Traverse the module name and detail pair in reverse order assuming that is the order of dependencies.
|
|
# Skip the first module since that is the main module.
|
|
module_names = reversed(deps['modules'][2::2])
|
|
module_details = reversed(deps['modules'][3::2])
|
|
for name, detail in zip(module_names, module_details):
|
|
module = {}
|
|
module["isFramework"] = False
|
|
if 'clang' in name:
|
|
build_module(args.swift_frontend, 'clang', detail)
|
|
module["moduleName"] = name['clang']
|
|
if "moduleCacheKey" in detail["details"]['clang']:
|
|
module["clangModuleCacheKey"] = detail["details"]['clang']["moduleCacheKey"]
|
|
module["clangModulePath"] = os.path.basename(detail["modulePath"])
|
|
else:
|
|
module["clangModulePath"] = detail["modulePath"]
|
|
if 'swift' in name:
|
|
build_module(args.swift_frontend, 'swift', detail)
|
|
module["moduleName"] = name['swift']
|
|
if "moduleCacheKey" in detail["details"]['swift']:
|
|
module["moduleCacheKey"] = detail["details"]['swift']["moduleCacheKey"]
|
|
module["modulePath"] = os.path.basename(detail["modulePath"])
|
|
else:
|
|
module["modulePath"] = detail["modulePath"]
|
|
if 'swiftPrebuiltExternal' in name:
|
|
module["moduleName"] = name['swiftPrebuiltExternal']
|
|
if "moduleCacheKey" in detail["details"]['swiftPrebuiltExternal']:
|
|
module["moduleCacheKey"] = detail["details"]['swiftPrebuiltExternal']["moduleCacheKey"]
|
|
module["modulePath"] = os.path.basename(detail["modulePath"])
|
|
else:
|
|
module["modulePath"] = detail["modulePath"]
|
|
if "libraryLevel" in detail:
|
|
module["libraryLevel"] = detail["libraryLevel"]
|
|
modules.append(module)
|
|
|
|
# Write output response file if requested.
|
|
if args.output:
|
|
cmd = deps['modules'][1]['details']['swift']['commandLine']
|
|
# Add some helpful flags for explicit module build.
|
|
cmd.extend(['-disable-implicit-swift-modules'])
|
|
# Write explicit module map.
|
|
module_map_out = args.output + ".map"
|
|
with open(module_map_out, 'w') as mapfile:
|
|
json.dump(modules, mapfile, indent=2)
|
|
# If using caching, create the map in CAS.
|
|
if args.cas:
|
|
casid = subprocess.check_output(
|
|
[args.llvm_cas_tool, '--cas', args.cas, '--make-blob', '--data', module_map_out], text=True).strip()
|
|
cmd.extend(['-explicit-swift-module-map-file', casid])
|
|
else:
|
|
cmd.extend(['-explicit-swift-module-map-file', module_map_out])
|
|
writeOutputResponseFile(args.output, cmd)
|
|
|
|
# Write bridging header response file if request.
|
|
if args.bridging_header_resp:
|
|
info = deps['modules'][1]['details']['swift']
|
|
# the first argument is `-frontend`
|
|
cmd = info['bridgingHeader']['commandLine'][1:]
|
|
# print input file name if using chained bridging header.
|
|
if "chainedBridgingHeaderPath" in info:
|
|
path = apply_prefix_maps(info['chainedBridgingHeaderPath'], args.prefix_map)
|
|
cmd.append(path)
|
|
writeOutputResponseFile(args.bridging_header_resp, cmd)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|