• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1#!/usr/bin/python3
2
3
4import argparse
5import collections
6import json
7import os
8import subprocess
9import sys
10import tempfile
11
12
13class OrderedSet(collections.OrderedDict):
14
15  def add(self, value):
16    self[value] = True
17
18
19def compile_module(module, sources, settings, extras, tmpdir):
20  output_file_map = {}
21  if settings.whole_module_optimization:
22    output_file_map[''] = {
23        'object': os.path.join(settings.object_dir, module + '.o'),
24        'dependencies': os.path.join(tmpdir, module + '.d'),
25    }
26  else:
27    for source in sources:
28      name, _ = os.path.splitext(os.path.basename(source))
29      output_file_map[source] = {
30          'object': os.path.join(settings.object_dir, name + '.o'),
31          'dependencies': os.path.join(tmpdir, name + '.d'),
32      }
33
34  for key in ('module_path', 'header_path', 'depfile'):
35    path = getattr(settings, key)
36    if os.path.exists(path):
37      os.unlink(path)
38    if key == 'module_path':
39      for ext in '.swiftdoc', '.swiftsourceinfo':
40        path = os.path.splitext(getattr(settings, key))[0] + ext
41        if os.path.exists(path):
42          os.unlink(path)
43    directory = os.path.dirname(path)
44    if not os.path.exists(directory):
45      os.makedirs(directory)
46
47  if not os.path.exists(settings.object_dir):
48    os.makedirs(settings.object_dir)
49
50  for key in output_file_map:
51    path = output_file_map[key]['object']
52    if os.path.exists(path):
53      os.unlink(path)
54
55  output_file_map_path = os.path.join(tmpdir, module + '.json')
56  with open(output_file_map_path, 'w') as output_file_map_file:
57    output_file_map_file.write(json.dumps(output_file_map))
58    output_file_map_file.flush()
59
60  extra_args = []
61  if settings.bridge_header:
62    extra_args.extend([
63        '-import-objc-header',
64        os.path.abspath(settings.bridge_header),
65    ])
66
67  if settings.whole_module_optimization:
68    extra_args.append('-whole-module-optimization')
69
70  if settings.target:
71    extra_args.extend([
72        '-target',
73        settings.target,
74    ])
75
76  if settings.sdk:
77    extra_args.extend([
78        '-sdk',
79        os.path.abspath(settings.sdk),
80    ])
81
82  if settings.include_dirs:
83    for include_dir in settings.include_dirs:
84      extra_args.append('-I' + include_dir)
85
86  process = subprocess.Popen(
87      ['swiftc',
88       '-parse-as-library',
89       '-module-name',
90       module,
91       '-emit-object',
92       '-emit-dependencies',
93       '-emit-module',
94       '-emit-module-path',
95       settings.module_path,
96       '-emit-objc-header',
97       '-emit-objc-header-path',
98       settings.header_path,
99       '-output-file-map',
100       output_file_map_path,
101      ] + extra_args + extras + sources,
102      stdout=subprocess.PIPE, stderr=subprocess.PIPE,
103      universal_newlines=True)
104
105  stdout, stderr = process.communicate()
106  if process.returncode:
107    sys.stdout.write(stdout)
108    sys.stderr.write(stderr)
109    sys.exit(process.returncode)
110
111
112  depfile_content = collections.OrderedDict()
113  for key in output_file_map:
114    for line in open(output_file_map[key]['dependencies']):
115      output, inputs = line.split(' : ', 2)
116      _, ext = os.path.splitext(output)
117      if ext == '.o':
118        key = output
119      else:
120        key = os.path.splitext(settings.module_path)[0] + ext
121      if key not in depfile_content:
122        depfile_content[key] = OrderedSet()
123      for path in inputs.split():
124        depfile_content[key].add(path)
125
126  with open(settings.depfile, 'w') as depfile:
127    for key in depfile_content:
128      if not settings.depfile_filter or key in settings.depfile_filter:
129        inputs = depfile_content[key]
130        depfile.write('%s : %s\n' % (key, ' '.join(inputs)))
131
132
133def main(args):
134  parser = argparse.ArgumentParser(add_help=False)
135  parser.add_argument(
136      '--module-name',
137      help='name of the Swift module')
138  parser.add_argument(
139      '--include', '-I', action='append', dest='include_dirs',
140      help='add directory to header search path')
141  parser.add_argument(
142      'sources', nargs='+',
143      help='Swift source file to compile')
144  parser.add_argument(
145      '--whole-module-optimization', action='store_true',
146      help='enable whole module optimization')
147  parser.add_argument(
148      '--object-dir', '-o',
149      help='path to the generated object files directory')
150  parser.add_argument(
151      '--module-path', '-m',
152      help='path to the generated module file')
153  parser.add_argument(
154      '--header-path', '-h',
155      help='path to the generated header file')
156  parser.add_argument(
157      '--bridge-header', '-b',
158      help='path to the Objective-C bridge header')
159  parser.add_argument(
160      '--depfile', '-d',
161      help='path to the generated depfile')
162  parser.add_argument(
163      '--depfile-filter', action='append',
164      help='limit depfile to those files')
165  parser.add_argument(
166      '--target', action='store',
167      help='generate code for the given target <triple>')
168  parser.add_argument(
169      '--sdk', action='store',
170      help='compile against sdk')
171
172  parsed, extras = parser.parse_known_args(args)
173  with tempfile.TemporaryDirectory() as tmpdir:
174    compile_module(
175        parsed.module_name,
176        parsed.sources,
177        parsed,
178        extras,
179        tmpdir)
180
181
182if __name__ == '__main__':
183  sys.exit(main(sys.argv[1:]))
184