Index: pylib/gyp/generator/ninja.py |
diff --git a/pylib/gyp/generator/ninja.py b/pylib/gyp/generator/ninja.py |
index 6823f4ffbdaf25ad8676650f65e55cd3e58b2a30..602f9fdb36d5a1406303974736d0376b4b21acd5 100644 |
--- a/pylib/gyp/generator/ninja.py |
+++ b/pylib/gyp/generator/ninja.py |
@@ -140,6 +140,10 @@ class Target(object): |
# On Windows, incremental linking requires linking against all the .objs |
# that compose a .lib (rather than the .lib itself). That list is stored |
# here. |
+ |
+ # Path to the file representing completion of building module, if any. |
+ self.module_stamp = None |
+ |
self.component_objs = None |
# Windows only. The import .lib is the output of a build step, but |
# because dependents only link against the lib (not both the lib and the |
@@ -376,6 +380,9 @@ class NinjaWriter(object): |
# should be used for linking. |
self.uses_cpp = False |
+ # Track if there are any swift sources |
+ self.uses_swift = False |
+ |
self.is_mac_bundle = gyp.xcode_emulation.IsMacBundle(self.flavor, spec) |
self.xcode_settings = self.msvs_settings = None |
if self.flavor == 'mac': |
@@ -408,6 +415,7 @@ class NinjaWriter(object): |
# any of its compile steps. |
actions_depends = [] |
compile_depends = [] |
+ module_depends = [] |
# TODO(evan): it is rather confusing which things are lists and which |
# are strings. Fix these. |
if 'dependencies' in spec: |
@@ -416,8 +424,14 @@ class NinjaWriter(object): |
target = self.target_outputs[dep] |
actions_depends.append(target.PreActionInput(self.flavor)) |
compile_depends.append(target.PreCompileInput()) |
+ module_depends.append(target.module_stamp) |
actions_depends = filter(None, actions_depends) |
compile_depends = filter(None, compile_depends) |
+ module_depends = filter(None, module_depends) |
+ if (self.flavor == 'mac' and |
+ self.xcode_settings.AreModulesEnabled(config_name)): |
+ compile_depends += module_depends |
+ |
actions_depends = self.WriteCollapsedDependencies('actions_depends', |
actions_depends) |
compile_depends = self.WriteCollapsedDependencies('compile_depends', |
@@ -439,6 +453,14 @@ class NinjaWriter(object): |
# because no compile ever depends on them. |
compile_depends_stamp = (self.target.actions_stamp or compile_depends) |
+ if self.flavor == 'mac': |
+ # Module should be written before compilaton, because Swift compiler |
+ # use it |
+ module_stamp = self.WriteModule(self.ninja, compile_depends_stamp, |
+ config_name, spec) |
+ if module_stamp: |
+ compile_depends_stamp = module_stamp |
+ |
# Write out the compilation steps, if any. |
link_deps = [] |
sources = extra_sources + spec.get('sources', []) |
@@ -855,6 +877,236 @@ class NinjaWriter(object): |
('binary', isBinary)]) |
bundle_depends.append(out) |
+ def _AddSwiftModuleExt(self, module): |
+ return module + '.swiftmodule' |
+ |
+ def _GetSwiftModulePath(self, config_name, spec, arch=None): |
+ module_path = self._AddSwiftModuleExt( |
+ self.xcode_settings.GetProductModuleName(config_name)) |
+ if self._IsFramework(spec): |
+ module_path = os.path.join( |
+ self.xcode_settings.GetBundleModulesFolderPath(), module_path) |
+ if arch: |
+ # Fixing path for armv7, since Xcode expects it to have 'arm' name instead |
+ if arch == 'armv7': |
+ arch = 'arm' |
+ module_path = os.path.join(module_path, self._AddSwiftModuleExt(arch)) |
+ return module_path |
+ |
+ def WriteFrameworkHeaders(self, ninja_file, headers, headers_dir): |
+ outputs = [] |
+ for header in headers: |
+ out_path = os.path.join(headers_dir, os.path.basename(header)) |
+ outputs.append(out_path) |
+ self.ninja.build(out_path, 'mac_tool', header, |
+ variables=[('mactool_cmd', 'copy-bundle-resource'), \ |
+ ('binary', False)]) |
+ return outputs |
+ |
+ def _GetModuleMapFilePath(self): |
+ return os.path.join(self.xcode_settings.GetBundleModulesFolderPath(), |
+ 'module.modulemap') |
+ |
+ def _IsFramework(self, spec): |
+ is_framework = (spec['type'] == 'shared_library') and self.is_mac_bundle |
+ return is_framework |
+ |
+ def WriteModule(self, ninja_file, predepends, config_name, spec): |
+ """Write build rules to build Clang module.""" |
+ if not self.xcode_settings.IsModuleDefined(config_name): |
+ return None |
+ if not self._IsFramework(spec): |
+ # There are no any actions by Xcode for non-framework targets |
+ return None |
+ |
+ assert(self.xcode_settings.AreModulesEnabled(config_name)) |
+ |
+ ninja_file.newline() |
+ |
+ module_inputs = [] |
+ # Copying headers to framework |
+ public_headers = map(self.GypPathToNinja, |
+ list(spec.get('mac_framework_headers', []))) |
+ module_inputs += self.WriteFrameworkHeaders( |
+ ninja_file, public_headers, |
+ self.xcode_settings.GetBundlePublicHeadersFolderPath()) |
+ |
+ private_headers = map(self.GypPathToNinja, |
+ list(spec.get('mac_framework_private_headers', []))) |
+ module_inputs += self.WriteFrameworkHeaders( |
+ ninja_file, private_headers, |
+ self.xcode_settings.GetBundlePrivateHeadersFolderPath()) |
+ |
+ module_name = self.xcode_settings.GetProductModuleName(config_name) |
+ umbrella_header = None |
+ for header in public_headers: |
+ filename, _ = os.path.splitext(os.path.basename(header)) |
+ if filename == module_name: |
+ umbrella_header = header |
+ break |
+ |
+ assert(umbrella_header) |
+ |
+ # Building module map file in the /Modules folder |
+ module_map_path = self._GetModuleMapFilePath() |
+ variables = { |
+ 'module_name': module_name, |
+ 'umbrella_header': os.path.basename(umbrella_header), |
+ 'map_file': module_map_path } |
+ module_map_stamp = self.GypPathToUniqueOutput('modulemap.stamp') |
+ self.ninja.build(module_map_stamp, 'modulemap', [umbrella_header], |
+ variables=variables) |
+ module_inputs.append(module_map_stamp) |
+ |
+ # Resulting module stamp |
+ module_stamp = self.GypPathToUniqueOutput('module.stamp') |
+ self.ninja.build(module_stamp, 'stamp', module_inputs, |
+ order_only=predepends) |
+ self.target.module_stamp = module_stamp |
+ ninja_file.newline() |
+ return module_stamp |
+ |
+ def WriteSwiftSourcesForArch(self, ninja_file, config_name, sources, |
+ predepends, spec, arch=None): |
+ assert(self.flavor == 'mac') |
+ |
+ # Collecting all Swift sources |
+ swift_sources = [] |
+ for source in sources: |
+ filename, ext = os.path.splitext(source) |
+ ext = ext[1:] |
+ if ext == 'swift': |
+ swift_sources.append(self.GypPathToNinja(source)) |
+ if not swift_sources: |
+ return ([], None) |
+ |
+ self.uses_swift = True |
+ |
+ if not arch: |
+ arch = self.archs[0] |
+ |
+ # Writing compile flags |
+ swift_compile_flags = self.xcode_settings.GetSwiftCompileFlags( |
+ config_name, self.GypPathToNinja, arch) |
+ self.WriteVariableList(ninja_file, 'swift_compile_flags', |
+ map(self.ExpandSpecial, swift_compile_flags)) |
+ ninja_file.newline() |
+ |
+ # Writing per-file rules |
+ link_deps = [] |
+ partial_modules = [] |
+ for source in swift_sources: |
+ filename, ext = os.path.splitext(os.path.basename(source)) |
+ obj_path = self.GypPathToUniqueOutput(filename + self.obj_ext) |
+ obj_path = AddArch(obj_path, arch) |
+ src_module_path = self.GypPathToUniqueOutput( |
+ self._AddSwiftModuleExt(filename)) |
+ src_module_path = AddArch(src_module_path, arch) |
+ |
+ compile_input = [source] |
+ for another_source in swift_sources: |
+ if source != another_source: |
+ compile_input.append(another_source) |
+ |
+ variables = {'out_obj': obj_path, 'out_module': src_module_path} |
+ ninja_file.build([obj_path, src_module_path], 'swift', compile_input, |
+ variables=variables, |
+ order_only=predepends) |
+ |
+ link_deps.append(obj_path) |
+ partial_modules.append(src_module_path) |
+ |
+ ninja_file.newline() |
+ |
+ # Writing Swift merge flags |
+ swift_merge_flags = self.xcode_settings.GetSwiftMergeFlags( |
+ config_name, self.GypPathToNinja, arch) |
+ self.WriteVariableList(ninja_file, 'swift_merge_flags', |
+ map(self.ExpandSpecial, swift_merge_flags)) |
+ ninja_file.newline() |
+ |
+ |
+ # Writing header for first arch without arch extension, |
+ # for it to be used by Objc sources |
+ header_arch = None if arch == self.archs[0] else arch |
+ header_path = self.xcode_settings.GetSwiftHeaderPath( |
+ config_name, self.GypPathToNinja, header_arch) |
+ module_path = self._GetSwiftModulePath(config_name, spec, arch) |
+ variables = {'out_module': module_path, 'out_header': header_path} |
+ ninja_file.build([module_path, header_path], 'swiftmerge', |
+ partial_modules, variables=variables) |
+ ninja_file.newline() |
+ |
+ return (link_deps, module_path) |
+ |
+ def WriteSwiftModule(self, ninja_file, config_name, spec): |
+ """Write build rules to build Swift module data""" |
+ if not self.uses_swift: |
+ return |
+ |
+ assert(self.xcode_settings.AreModulesEnabled(config_name)) |
+ |
+ ninja_file.newline() |
+ |
+ module_inputs = [] |
+ map_depends = [] |
+ for arch in self.archs: |
+ swift_module_path = self._GetSwiftModulePath(config_name, spec, arch) |
+ module_inputs.append(swift_module_path) |
+ map_depends.append(swift_module_path) |
+ |
+ if self._IsFramework(spec): |
+ assert(self.xcode_settings.IsModuleDefined(config_name)) |
+ |
+ # Copying Swift header to framework |
+ swift_header = self.xcode_settings.GetSwiftHeaderPath( |
+ config_name, self.GypPathToNinja) |
+ module_inputs += self.WriteFrameworkHeaders( |
+ ninja_file, [swift_header], |
+ self.xcode_settings.GetBundlePublicHeadersFolderPath()) |
+ |
+ # Appending Swift info to the module map file in the /Modules folder |
+ module_name = self.xcode_settings.GetProductModuleName(config_name) |
+ module_map_path = self._GetModuleMapFilePath() |
+ variables = { |
+ 'module_name': module_name, |
+ 'swift_header': os.path.basename(swift_header), |
+ 'map_file': module_map_path } |
+ module_map_stamp = self.GypPathToUniqueOutput('swiftmodulemap.stamp') |
+ self.ninja.build(module_map_stamp, 'swiftmodulemap', [swift_header], |
+ order_only=map_depends, variables=variables) |
+ module_inputs.append(module_map_stamp) |
+ |
+ # Resulting Swift actions stamp |
+ module_stamp = self.GypPathToUniqueOutput('swift_module.stamp') |
+ self.ninja.build(module_stamp, 'stamp', module_inputs) |
+ self.target.module_stamp = module_stamp |
+ ninja_file.newline() |
+ |
+ def AppendSwiftLinkerOptions(self, ldflags, implicit_deps, config_name, spec, |
+ arch): |
+ if not self.uses_swift: |
+ return |
+ if arch is None: |
+ arch = self.archs[0] |
+ module_path = self._GetSwiftModulePath(config_name, spec, arch) |
+ ldflags += self.xcode_settings.GetSwiftLdflags(config_name, module_path) |
+ implicit_deps.add(module_path) |
+ |
+ def CopySwiftLibsToBundle(self, spec): |
+ is_final = spec['type'] in ('executable', 'loadable_module') |
+ if not gyp.xcode_emulation.IsSwiftSupported() or not is_final: |
+ return None |
+ |
+ # Copying all needed Swift dylibs right into the bundle |
+ app_frameworks_path = ( |
+ self.xcode_settings.GetBundleFrameworksFolderPath()) |
+ swift_libs_path = self.xcode_settings.GetSwiftLibsPath(self.config_name) |
+ variables = {'swift_libs_path': swift_libs_path} |
+ self.ninja.build(app_frameworks_path, 'copyswiftlibs', self.target.binary, |
+ variables=variables) |
+ return app_frameworks_path |
+ |
def WriteSources(self, ninja_file, config_name, config, sources, predepends, |
precompiled_header, spec): |
"""Write build rules to compile all of |sources|.""" |
@@ -976,6 +1228,13 @@ class NinjaWriter(object): |
map(self.ExpandSpecial, arflags)) |
ninja_file.newline() |
outputs = [] |
+ |
+ if self.flavor == 'mac': |
+ swift_outputs, swift_module = self.WriteSwiftSourcesForArch( |
+ ninja_file, config_name, sources, predepends, spec, arch) |
+ outputs += swift_outputs |
+ predepends = swift_module or predepends |
+ |
has_rc_source = False |
for source in sources: |
filename, ext = os.path.splitext(source) |
@@ -1139,6 +1398,8 @@ class NinjaWriter(object): |
self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']), |
self.GypPathToNinja, arch) |
ldflags = env_ldflags + ldflags |
+ self.AppendSwiftLinkerOptions( |
+ ldflags, implicit_deps, config_name, spec, arch) |
elif self.flavor == 'win': |
manifest_base_name = self.GypPathToUniqueOutput( |
self.ComputeOutputFileName(spec)) |
@@ -1248,6 +1509,7 @@ class NinjaWriter(object): |
return linked_binary |
def WriteTarget(self, spec, config_name, config, link_deps, compile_deps): |
+ self.WriteSwiftModule(self.ninja, config_name, spec) |
extra_link_deps = any(self.target_outputs.get(dep).Linkable() |
for dep in spec.get('dependencies', []) |
if dep in self.target_outputs) |
@@ -1300,6 +1562,14 @@ class NinjaWriter(object): |
def WriteMacBundle(self, spec, mac_bundle_depends, is_empty): |
assert self.is_mac_bundle |
+ |
+ swift_frameworks = self.CopySwiftLibsToBundle(spec) |
+ if swift_frameworks: |
+ mac_bundle_depends.append(swift_frameworks) |
+ |
+ if self.target.module_stamp: |
+ mac_bundle_depends.append(self.target.module_stamp) |
+ |
package_framework = spec['type'] in ('shared_library', 'loadable_module') |
output = self.ComputeMacBundleOutput() |
if is_empty: |
@@ -1922,6 +2192,10 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params, |
master_ninja.variable( |
'readelf', GetEnvironFallback(['READELF_target', 'READELF'], readelf)) |
+ if flavor == 'mac' and gyp.xcode_emulation.IsSwiftSupported(): |
+ master_ninja.variable( |
+ 'swift', subprocess.check_output(['xcrun', '-f', 'swift'])) |
+ |
if generator_supports_multiple_toolsets: |
if not cc_host: |
cc_host = cc |
@@ -2106,6 +2380,32 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params, |
depfile='$out.d', |
deps=deps) |
master_ninja.rule( |
+ 'swift', |
+ description='SWIFT $out_obj', |
+ command=('$swift -frontend -c -primary-file $in $swift_compile_flags ' |
+ '-emit-module-path $out_module ' |
+ '-o $out_obj')) |
+ master_ninja.rule( |
+ 'swiftmerge', |
+ description='SWIFT MERGE $out_module', |
+ command=('$swift -frontend -emit-module $in $swift_merge_flags ' |
+ '-emit-objc-header-path $out_header ' |
+ '-o $out_module')) |
+ master_ninja.rule( |
+ 'copyswiftlibs', |
+ description='COPY SWIFT LIBS $out', |
+ command=('./gyp-mac-tool copy-swift-libs $swift_libs_path $out $in')) |
+ master_ninja.rule( |
+ 'modulemap', |
+ description='MODULE MAP $map_file', |
+ command=('./gyp-mac-tool build-module-map-file ' |
+ '$module_name $umbrella_header $map_file $out')) |
+ master_ninja.rule( |
+ 'swiftmodulemap', |
+ description='SWIFT MODULE MAP $map_file', |
+ command=('./gyp-mac-tool append-swift-to-module-map-file ' |
+ '$module_name $swift_header $map_file $out')) |
+ master_ninja.rule( |
'alink', |
description='LIBTOOL-STATIC $out, POSTBUILDS', |
command='rm -f $out && ' |