Index: pylib/gyp/generator/ninja.py |
diff --git a/pylib/gyp/generator/ninja.py b/pylib/gyp/generator/ninja.py |
index c2437822a7f85b1aab6adeb899d29279d191fa0b..386235a0f0381bcf1107092a629f2edc635b7c3b 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 |
@@ -302,6 +306,9 @@ class NinjaWriter(object): |
assert '$' not in path, path |
return os.path.normpath(os.path.join(self.build_to_base, path)) |
+ def GypPathToNinjaWithToolchain(self, path): |
+ return self.GypPathToNinja(path, self.GetToolchainEnv()) |
+ |
def GypPathToUniqueOutput(self, path, qualified=True): |
"""Translate a gyp path to a ninja path for writing output. |
@@ -376,6 +383,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 +418,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 +427,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 +456,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 +880,290 @@ 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': QuoteShellArgument(module_map_path, self.flavor) |
+ } |
+ 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 _as_list(self, input): |
+ if input is None: |
+ return [] |
+ if isinstance(input, list): |
+ return input |
+ return [input] |
+ |
+ 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) |
+ |
+ if not gyp.xcode_emulation.IsSwiftSupported(): |
+ raise Exception('You are trying to build Swift files ' |
+ 'with unsupported Xcode version.') |
+ self.uses_swift = True |
+ |
+ if not arch: |
+ arch = self.archs[0] |
+ |
+ # Writing compile flags |
+ swift_compile_flags = self.xcode_settings.GetSwiftCompileFlags( |
+ config_name, self.GypPathToNinjaWithToolchain, arch) |
+ self.WriteVariableList(ninja_file, 'swift_compile_flags', |
+ map(self.ExpandSpecial, swift_compile_flags)) |
+ ninja_file.newline() |
+ |
+ single_rule = self.xcode_settings.IsSwiftWMOEnabled(config_name) |
+ |
+ # 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) |
+ |
+ link_deps.append(obj_path) |
+ partial_modules.append(src_module_path) |
+ |
+ if not single_rule: |
+ compile_input = [source] |
+ for another_source in swift_sources: |
+ if source != another_source: |
+ compile_input.append(another_source) |
+ |
+ variables = { |
+ 'out_obj': QuoteShellArgument(obj_path, self.flavor), |
+ 'out_module': QuoteShellArgument(src_module_path, self.flavor) |
+ } |
+ ninja_file.build([obj_path, src_module_path], 'swift_perfile', |
+ compile_input, variables=variables, |
+ order_only=predepends) |
+ |
+ module_predepends = self._as_list(predepends) |
+ |
+ # Writing header for first arch without arch extension. |
+ # Obj sources will use it when referencing to <Module>-Swift.h file |
+ common_header_path = self.xcode_settings.GetSwiftHeaderPath( |
+ config_name, self.GypPathToNinja) |
+ if arch == self.archs[0]: |
+ header_path = common_header_path |
+ else: |
+ header_path = self.xcode_settings.GetSwiftHeaderPath( |
+ config_name, self.GypPathToNinja, arch) |
+ module_predepends.append(common_header_path) |
+ |
+ module_path = self._GetSwiftModulePath(config_name, spec, arch) |
+ |
+ if single_rule: |
+ out_objects = '' |
+ for obj in link_deps: |
+ out_objects += ' -o ' + obj |
+ variables = { |
+ 'out_module': QuoteShellArgument(module_path, self.flavor), |
+ 'out_header': QuoteShellArgument(header_path, self.flavor), |
+ 'out_objects': QuoteShellArgument(out_objects, self.flavor), |
+ 'num_threads': str(multiprocessing.cpu_count()) |
+ } |
+ ninja_file.build(link_deps + [module_path, header_path], 'swift', |
+ swift_sources, variables=variables, |
+ order_only=module_predepends) |
+ else: |
+ # Writing Swift merge flags |
+ ninja_file.newline() |
+ swift_merge_flags = self.xcode_settings.GetSwiftMergeFlags( |
+ config_name, self.GypPathToNinjaWithToolchain, arch) |
+ self.WriteVariableList(ninja_file, 'swift_merge_flags', |
+ map(self.ExpandSpecial, swift_merge_flags)) |
+ # Writing merge rule |
+ ninja_file.newline() |
+ variables = { |
+ 'out_module': QuoteShellArgument(module_path, self.flavor), |
+ 'out_header': QuoteShellArgument(header_path, self.flavor), |
+ } |
+ ninja_file.build([module_path, header_path], 'swift_merge', |
+ partial_modules, variables=variables, |
+ order_only=module_predepends) |
+ 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': QuoteShellArgument(module_map_path, self.flavor) |
+ } |
+ 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') |
+ # We should try to copy swift libs even if the target does not have |
+ # swift files itself, because some of the dependent libs can have it. |
+ if not gyp.xcode_emulation.IsSwiftSupported() or not is_final: |
+ return None |
+ |
+ if not self.target.binary: |
+ return None |
+ |
+ # Copying all needed Swift dylibs right into the bundle |
+ app_frameworks_path = ( |
+ self.xcode_settings.GetBundleFrameworksFolderPath()) |
+ codesign_key = self.xcode_settings.GetCodeSignIdentityKey( |
+ self.config_name) or '' |
+ variables = { |
+ 'platform': self.xcode_settings.GetPlatform(self.config_name), |
+ 'dst_path': QuoteShellArgument(app_frameworks_path, self.flavor), |
+ 'codesign_key': codesign_key} |
+ swift_libs_stamp = QuoteShellArgument( |
+ self.GypPathToUniqueOutput('copyswiftlibs.stamp'), self.flavor) |
+ self.ninja.build(swift_libs_stamp, 'copyswiftlibs', self.target.binary, |
+ variables=variables) |
+ return swift_libs_stamp |
+ |
def WriteSources(self, ninja_file, config_name, config, sources, predepends, |
precompiled_header, spec): |
"""Write build rules to compile all of |sources|.""" |
@@ -883,7 +1192,8 @@ class NinjaWriter(object): |
extra_defines = [] |
if self.flavor == 'mac': |
- cflags = self.xcode_settings.GetCflags(config_name, arch=arch) |
+ cflags = self.xcode_settings.GetCflags(config_name, |
+ self.GypPathToNinjaWithToolchain, arch=arch) |
cflags_c = self.xcode_settings.GetCflagsC(config_name) |
cflags_cc = self.xcode_settings.GetCflagsCC(config_name) |
cflags_objc = ['$cflags_c'] + \ |
@@ -976,6 +1286,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) |
@@ -1137,8 +1454,10 @@ class NinjaWriter(object): |
if self.flavor == 'mac': |
ldflags = self.xcode_settings.GetLdflags(config_name, |
self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']), |
- self.GypPathToNinja, arch) |
+ self.GypPathToNinjaWithToolchain, 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 +1567,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 +1620,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 +2250,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 +2438,41 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params, |
depfile='$out.d', |
deps=deps) |
master_ninja.rule( |
+ 'swift', |
+ description='SWIFT $out_module', |
+ command=('$swift -frontend -c $in $swift_compile_flags ' |
+ '-emit-module-path $out_module ' |
+ '-emit-objc-header-path $out_header ' |
+ '-num-threads $num_threads ' |
+ '$out_objects')) |
+ master_ninja.rule( |
+ 'swift_perfile', |
+ 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( |
+ 'swift_merge', |
+ 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 $in $platform $dst_path ' |
+ '"$codesign_key" $out')) |
+ 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 && ' |