diff options
-rw-r--r-- | pylib/gyp/common.py | 7 | ||||
-rw-r--r-- | pylib/gyp/generator/ninja.py | 269 | ||||
-rw-r--r-- | pylib/gyp/generator/ninja_test.py | 48 | ||||
-rw-r--r-- | pylib/gyp/msvs_emulation.py | 5 | ||||
-rw-r--r-- | pylib/gyp/xcode_emulation.py | 63 | ||||
-rwxr-xr-x | test/cflags/gyptest-cflags.py | 10 | ||||
-rw-r--r-- | test/mac/archs/empty_main.cc | 1 | ||||
-rw-r--r-- | test/mac/archs/file.mm | 1 | ||||
-rw-r--r-- | test/mac/archs/header.h | 1 | ||||
-rw-r--r-- | test/mac/archs/test-archs-multiarch.gyp | 60 | ||||
-rw-r--r-- | test/mac/cflags/test.gyp | 13 | ||||
-rw-r--r-- | test/mac/gyptest-archs.py | 36 | ||||
-rw-r--r-- | test/win/generator-output-different-drive/gyptest-generator-output-different-drive.py | 44 | ||||
-rw-r--r-- | test/win/generator-output-different-drive/prog.c | 10 | ||||
-rw-r--r-- | test/win/generator-output-different-drive/prog.gyp | 15 |
15 files changed, 452 insertions, 131 deletions
diff --git a/pylib/gyp/common.py b/pylib/gyp/common.py index e50f51c3..19f1cf4a 100644 --- a/pylib/gyp/common.py +++ b/pylib/gyp/common.py @@ -131,6 +131,13 @@ def RelativePath(path, relative_to): path = os.path.realpath(path) relative_to = os.path.realpath(relative_to) + # On Windows, we can't create a relative path to a different drive, so just + # use the absolute path. + if sys.platform == 'win32': + if (os.path.splitdrive(path)[0].lower() != + os.path.splitdrive(relative_to)[0].lower()): + return path + # Split the paths into components. path_split = path.split(os.path.sep) relative_to_split = relative_to.split(os.path.sep) diff --git a/pylib/gyp/generator/ninja.py b/pylib/gyp/generator/ninja.py index ccb48717..19cecd2e 100644 --- a/pylib/gyp/generator/ninja.py +++ b/pylib/gyp/generator/ninja.py @@ -97,6 +97,12 @@ def Define(d, flavor): return QuoteShellArgument(ninja_syntax.escape('-D' + d), flavor) +def AddArch(output, arch): + """Adds an arch string to an output path.""" + output, extension = os.path.splitext(output) + return '%s.%s%s' % (output, arch, extension) + + class Target: """Target represents the paths used within a single gyp target. @@ -203,7 +209,8 @@ class Target: class NinjaWriter: def __init__(self, qualified_target, target_outputs, base_dir, build_dir, - output_file, flavor, toplevel_dir=None): + output_file, toplevel_build, output_file_name, flavor, + toplevel_dir=None): """ base_dir: path from source root to directory containing this gyp file, by gyp semantics, all input paths are relative to this @@ -216,6 +223,9 @@ class NinjaWriter: self.base_dir = base_dir self.build_dir = build_dir self.ninja = ninja_syntax.Writer(output_file) + self.toplevel_build = toplevel_build + self.output_file_name = output_file_name + self.flavor = flavor self.abs_build_dir = None if toplevel_dir is not None: @@ -345,6 +355,10 @@ class NinjaWriter: self.ninja.newline() return targets[0] + def _SubninjaNameForArch(self, arch): + output_file_base = os.path.splitext(self.output_file_name)[0] + return '%s.%s.ninja' % (output_file_base, arch) + def WriteSpec(self, spec, config_name, generator_flags, case_sensitive_filesystem): """The main entry point for NinjaWriter: write the build rules for a spec. @@ -373,6 +387,16 @@ class NinjaWriter: self.ninja.variable('cc', '$cl_' + arch) self.ninja.variable('cxx', '$cl_' + arch) + if self.flavor == 'mac': + self.archs = self.xcode_settings.GetActiveArchs(config_name) + if len(self.archs) > 1: + self.arch_subninjas = dict( + (arch, ninja_syntax.Writer( + open(os.path.join(self.toplevel_build, + self._SubninjaNameForArch(arch)), + 'w'))) + for arch in self.archs) + # Compute predepends for all rules. # actions_depends is the dependencies this target depends on before running # any of its action/rule/copy steps. @@ -415,6 +439,12 @@ class NinjaWriter: link_deps = [] sources = spec.get('sources', []) + extra_sources if sources: + if self.flavor == 'mac' and len(self.archs) > 1: + # Write subninja file containing compile and link commands scoped to + # a single arch if a fat binary is being built. + for arch in self.archs: + self.ninja.subninja(self._SubninjaNameForArch(arch)) + pch = None if self.flavor == 'win': gyp.msvs_emulation.VerifyMissingSources( @@ -427,11 +457,17 @@ class NinjaWriter: self.xcode_settings, self.GypPathToNinja, lambda path, lang: self.GypPathToUniqueOutput(path + '-' + lang)) link_deps = self.WriteSources( - config_name, config, sources, compile_depends_stamp, pch, + self.ninja, config_name, config, sources, compile_depends_stamp, pch, case_sensitive_filesystem, spec) # Some actions/rules output 'sources' that are already object files. - link_deps += [self.GypPathToNinja(f) - for f in sources if f.endswith(self.obj_ext)] + obj_outputs = [f for f in sources if f.endswith(self.obj_ext)] + if obj_outputs: + if self.flavor != 'mac' or len(self.archs) == 1: + link_deps += [self.GypPathToNinja(o) for o in obj_outputs] + else: + print "Warning: Actions/rules writing object files don't work with " \ + "multiarch targets, dropping. (target %s)" % spec['target_name'] + if self.flavor == 'win' and self.target.type == 'static_library': self.target.component_objs = link_deps @@ -720,7 +756,7 @@ class NinjaWriter: ('env', env)]) bundle_depends.append(out) - def WriteSources(self, config_name, config, sources, predepends, + def WriteSources(self, ninja_file, config_name, config, sources, predepends, precompiled_header, case_sensitive_filesystem, spec): """Write build rules to compile all of |sources|.""" if self.toolset == 'host': @@ -729,9 +765,24 @@ class NinjaWriter: self.ninja.variable('cxx', '$cxx_host') self.ninja.variable('ld', '$ld_host') + if self.flavor != 'mac' or len(self.archs) == 1: + return self.WriteSourcesForArch( + self.ninja, config_name, config, sources, predepends, + precompiled_header, case_sensitive_filesystem, spec) + else: + return dict((arch, self.WriteSourcesForArch( + self.arch_subninjas[arch], config_name, config, sources, predepends, + precompiled_header, case_sensitive_filesystem, spec, arch=arch)) + for arch in self.archs) + + def WriteSourcesForArch(self, ninja_file, config_name, config, sources, + predepends, precompiled_header, + case_sensitive_filesystem, spec, arch=None): + """Write build rules to compile all of |sources|.""" + extra_defines = [] if self.flavor == 'mac': - cflags = self.xcode_settings.GetCflags(config_name) + cflags = self.xcode_settings.GetCflags(config_name, arch=arch) cflags_c = self.xcode_settings.GetCflagsC(config_name) cflags_cc = self.xcode_settings.GetCflagsCC(config_name) cflags_objc = ['$cflags_c'] + \ @@ -751,17 +802,25 @@ class NinjaWriter: obj += '.' + self.toolset pdbpath = os.path.normpath(os.path.join(obj, self.base_dir, self.name + '.pdb')) - self.WriteVariableList('pdbname', [pdbpath]) - self.WriteVariableList('pchprefix', [self.name]) + self.WriteVariableList(ninja_file, 'pdbname', [pdbpath]) + self.WriteVariableList(ninja_file, 'pchprefix', [self.name]) else: cflags = config.get('cflags', []) - cflags_c = config.get('cflags_c', []) - cflags_cc = config.get('cflags_cc', []) + + # Respect environment variables related to build, but target-specific + # flags can still override them. + cflags_c = (os.environ.get('CPPFLAGS', '').split() + + os.environ.get('CFLAGS', '').split() + + config.get('cflags_c', [])) + cflags_cc = (os.environ.get('CPPFLAGS', '').split() + + os.environ.get('CXXFLAGS', '').split() + + config.get('cflags_cc', [])) defines = config.get('defines', []) + extra_defines - self.WriteVariableList('defines', [Define(d, self.flavor) for d in defines]) + self.WriteVariableList(ninja_file, 'defines', + [Define(d, self.flavor) for d in defines]) if self.flavor == 'win': - self.WriteVariableList('rcflags', + self.WriteVariableList(ninja_file, 'rcflags', [QuoteShellArgument(self.ExpandSpecial(f), self.flavor) for f in self.msvs_settings.GetRcflags(config_name, self.GypPathToNinja)]) @@ -771,27 +830,30 @@ class NinjaWriter: include_dirs = self.msvs_settings.AdjustIncludeDirs(include_dirs, config_name) env = self.GetSortedXcodeEnv() - self.WriteVariableList('includes', + self.WriteVariableList(ninja_file, 'includes', [QuoteShellArgument('-I' + self.GypPathToNinja(i, env), self.flavor) for i in include_dirs]) - pch_commands = precompiled_header.GetPchBuildCommands() + pch_commands = precompiled_header.GetPchBuildCommands(arch) if self.flavor == 'mac': # Most targets use no precompiled headers, so only write these if needed. for ext, var in [('c', 'cflags_pch_c'), ('cc', 'cflags_pch_cc'), ('m', 'cflags_pch_objc'), ('mm', 'cflags_pch_objcc')]: - include = precompiled_header.GetInclude(ext) - if include: self.ninja.variable(var, include) - - self.WriteVariableList('cflags', map(self.ExpandSpecial, cflags)) - self.WriteVariableList('cflags_c', map(self.ExpandSpecial, cflags_c)) - self.WriteVariableList('cflags_cc', map(self.ExpandSpecial, cflags_cc)) + include = precompiled_header.GetInclude(ext, arch) + if include: ninja_file.variable(var, include) + + self.WriteVariableList(ninja_file, 'cflags', + map(self.ExpandSpecial, cflags)) + self.WriteVariableList(ninja_file, 'cflags_c', + map(self.ExpandSpecial, cflags_c)) + self.WriteVariableList(ninja_file, 'cflags_cc', + map(self.ExpandSpecial, cflags_cc)) if self.flavor == 'mac': - self.WriteVariableList('cflags_objc', map(self.ExpandSpecial, - cflags_objc)) - self.WriteVariableList('cflags_objcc', map(self.ExpandSpecial, - cflags_objcc)) - self.ninja.newline() + self.WriteVariableList(ninja_file, 'cflags_objc', + map(self.ExpandSpecial, cflags_objc)) + self.WriteVariableList(ninja_file, 'cflags_objcc', + map(self.ExpandSpecial, cflags_objcc)) + ninja_file.newline() outputs = [] for source in sources: filename, ext = os.path.splitext(source) @@ -823,29 +885,31 @@ class NinjaWriter: continue input = self.GypPathToNinja(source) output = self.GypPathToUniqueOutput(filename + obj_ext) + if arch is not None: + output = AddArch(output, arch) # Ninja's depfile handling gets confused when the case of a filename # changes on a case-insensitive file system. To work around that, always # convert .o filenames to lowercase on such file systems. See # https://github.com/martine/ninja/issues/402 for details. if not case_sensitive_filesystem: output = output.lower() - implicit = precompiled_header.GetObjDependencies([input], [output]) + implicit = precompiled_header.GetObjDependencies([input], [output], arch) variables = [] if self.flavor == 'win': variables, output, implicit = precompiled_header.GetFlagsModifications( input, output, implicit, command, cflags_c, cflags_cc, self.ExpandSpecial) - self.ninja.build(output, command, input, + ninja_file.build(output, command, input, implicit=[gch for _, _, gch in implicit], order_only=predepends, variables=variables) outputs.append(output) - self.WritePchTargets(pch_commands) + self.WritePchTargets(ninja_file, pch_commands) - self.ninja.newline() + ninja_file.newline() return outputs - def WritePchTargets(self, pch_commands): + def WritePchTargets(self, ninja_file, pch_commands): """Writes ninja rules to compile prefix headers.""" if not pch_commands: return @@ -860,11 +924,28 @@ class NinjaWriter: map = { 'c': 'cc', 'cc': 'cxx', 'm': 'objc', 'mm': 'objcxx', } cmd = map.get(lang) - self.ninja.build(gch, cmd, input, variables=[(var_name, lang_flag)]) + ninja_file.build(gch, cmd, input, variables=[(var_name, lang_flag)]) def WriteLink(self, spec, config_name, config, link_deps): """Write out a link step. Fills out target.binary. """ - + if self.flavor != 'mac' or len(self.archs) == 1: + return self.WriteLinkForArch( + self.ninja, spec, config_name, config, link_deps) + else: + output = self.ComputeOutput(spec) + inputs = [self.WriteLinkForArch(self.arch_subninjas[arch], spec, + config_name, config, link_deps[arch], + arch=arch) + for arch in self.archs] + extra_bindings = [] + if not self.is_mac_bundle: + self.AppendPostbuildVariable(extra_bindings, spec, output, output) + self.ninja.build(output, 'lipo', inputs, variables=extra_bindings) + return output + + def WriteLinkForArch(self, ninja_file, spec, config_name, config, + link_deps, arch=None): + """Write out a link step. Fills out target.binary. """ command = { 'executable': 'link', 'loadable_module': 'solink_module', @@ -906,24 +987,21 @@ class NinjaWriter: link_deps.extend(list(extra_link_deps)) extra_bindings = [] - if self.is_mac_bundle: - output = self.ComputeMacBundleBinaryOutput() - else: - output = self.ComputeOutput(spec) - extra_bindings.append(('postbuilds', - self.GetPostbuildCommand(spec, output, output))) + output = self.ComputeOutput(spec, arch) + if arch is None and not self.is_mac_bundle: + self.AppendPostbuildVariable(extra_bindings, spec, output, output) is_executable = spec['type'] == 'executable' if self.flavor == 'mac': ldflags = self.xcode_settings.GetLdflags(config_name, self.ExpandSpecial(generator_default_variables['PRODUCT_DIR']), - self.GypPathToNinja) + self.GypPathToNinja, arch) elif self.flavor == 'win': manifest_name = self.GypPathToUniqueOutput( self.ComputeOutputFileName(spec)) ldflags, manifest_files = self.msvs_settings.GetLdflags(config_name, self.GypPathToNinja, self.ExpandSpecial, manifest_name, is_executable) - self.WriteVariableList('manifests', manifest_files) + self.WriteVariableList(ninja_file, 'manifests', manifest_files) command_suffix = _GetWinLinkRuleNameSuffix( self.msvs_settings.IsEmbedManifest(config_name), self.msvs_settings.IsLinkIncremental(config_name)) @@ -931,16 +1009,18 @@ class NinjaWriter: if def_file: implicit_deps.add(def_file) else: - ldflags = config.get('ldflags', []) + # Respect environment variables related to build, but target-specific + # flags can still override them. + ldflags = (os.environ.get('LDFLAGS', '').split() + + config.get('ldflags', [])) if is_executable and len(solibs): rpath = 'lib/' if self.toolset != 'target': rpath += self.toolset ldflags.append('-Wl,-rpath=\$$ORIGIN/%s' % rpath) ldflags.append('-Wl,-rpath-link=%s' % rpath) - self.WriteVariableList('ldflags', - gyp.common.uniquer(map(self.ExpandSpecial, - ldflags))) + self.WriteVariableList(ninja_file, 'ldflags', + gyp.common.uniquer(map(self.ExpandSpecial, ldflags))) library_dirs = config.get('library_dirs', []) if self.flavor == 'win': @@ -961,9 +1041,9 @@ class NinjaWriter: elif self.flavor == 'win': libraries = self.msvs_settings.AdjustLibraries(libraries) - self.WriteVariableList('libs', library_dirs + libraries) + self.WriteVariableList(ninja_file, 'libs', library_dirs + libraries) - self.target.binary = output + linked_binary = output if command in ('solink', 'solink_module'): extra_bindings.append(('soname', os.path.split(output)[1])) @@ -982,9 +1062,10 @@ class NinjaWriter: if len(solibs): extra_bindings.append(('solibs', gyp.common.EncodePOSIXShellList(solibs))) - self.ninja.build(output, command + command_suffix, link_deps, + ninja_file.build(output, command + command_suffix, link_deps, implicit=list(implicit_deps), variables=extra_bindings) + return linked_binary def WriteTarget(self, spec, config_name, config, link_deps, compile_deps): if spec['type'] == 'none': @@ -993,39 +1074,53 @@ class NinjaWriter: self.target.binary = compile_deps elif spec['type'] == 'static_library': self.target.binary = self.ComputeOutput(spec) - variables = [] - postbuild = self.GetPostbuildCommand( - spec, self.target.binary, self.target.binary) - if postbuild: - variables.append(('postbuilds', postbuild)) - if self.xcode_settings: - libtool_flags = self.xcode_settings.GetLibtoolflags(config_name) - if libtool_flags: - variables.append(('libtool_flags', libtool_flags)) if (self.flavor not in ('mac', 'openbsd', 'win') and not self.is_standalone_static_library): self.ninja.build(self.target.binary, 'alink_thin', link_deps, - order_only=compile_deps, variables=variables) + order_only=compile_deps) else: + variables = [] + if self.xcode_settings: + libtool_flags = self.xcode_settings.GetLibtoolflags(config_name) + if libtool_flags: + variables.append(('libtool_flags', libtool_flags)) if self.msvs_settings: libflags = self.msvs_settings.GetLibFlags(config_name, self.GypPathToNinja) variables.append(('libflags', libflags)) - self.ninja.build(self.target.binary, 'alink', link_deps, - order_only=compile_deps, variables=variables) + + if self.flavor != 'mac' or len(self.archs) == 1: + self.AppendPostbuildVariable(variables, spec, + self.target.binary, self.target.binary) + self.ninja.build(self.target.binary, 'alink', link_deps, + order_only=compile_deps, variables=variables) + else: + inputs = [] + for arch in self.archs: + output = self.ComputeOutput(spec, arch) + self.arch_subninjas[arch].build(output, 'alink', link_deps[arch], + order_only=compile_deps, + variables=variables) + inputs.append(output) + # TODO: It's not clear if libtool_flags should be passed to the alink + # call that combines single-arch .a files into a fat .a file. + self.AppendPostbuildVariable(variables, spec, + self.target.binary, self.target.binary) + self.ninja.build(self.target.binary, 'alink', inputs, + # FIXME: test proving order_only=compile_deps isn't + # needed. + variables=variables) else: - self.WriteLink(spec, config_name, config, link_deps) + self.target.binary = self.WriteLink(spec, config_name, config, link_deps) return self.target.binary def WriteMacBundle(self, spec, mac_bundle_depends): assert self.is_mac_bundle package_framework = spec['type'] in ('shared_library', 'loadable_module') output = self.ComputeMacBundleOutput() - postbuild = self.GetPostbuildCommand(spec, output, self.target.binary, - is_command_start=not package_framework) variables = [] - if postbuild: - variables.append(('postbuilds', postbuild)) + self.AppendPostbuildVariable(variables, spec, output, self.target.binary, + is_command_start=not package_framework) if package_framework: variables.append(('version', self.xcode_settings.GetFrameworkVersion())) self.ninja.build(output, 'package_framework', mac_bundle_depends, @@ -1056,8 +1151,14 @@ class NinjaWriter: postbuild_settings['CHROMIUM_STRIP_SAVE_FILE'] = strip_save_file return self.GetSortedXcodeEnv(additional_settings=postbuild_settings) - def GetPostbuildCommand(self, spec, output, output_binary, - is_command_start=False): + def AppendPostbuildVariable(self, variables, spec, output, binary, + is_command_start=False): + """Adds a 'postbuild' variable if there is a postbuild for |output|.""" + postbuild = self.GetPostbuildCommand(spec, output, binary, is_command_start) + if postbuild: + variables.append(('postbuilds', postbuild)) + + def GetPostbuildCommand(self, spec, output, output_binary, is_command_start): """Returns a shell command that runs all the postbuilds, and removes |output| if any of them fails. If |is_command_start| is False, then the returned string will start with ' && '.""" @@ -1111,13 +1212,6 @@ class NinjaWriter: return self.ExpandSpecial( os.path.join(path, self.xcode_settings.GetWrapperName())) - def ComputeMacBundleBinaryOutput(self): - """Return the 'output' (full output path) to the binary in a bundle.""" - assert self.is_mac_bundle - path = generator_default_variables['PRODUCT_DIR'] - return self.ExpandSpecial( - os.path.join(path, self.xcode_settings.GetExecutablePath())) - def ComputeOutputFileName(self, spec, type=None): """Compute the filename of the final output for the current target.""" if not type: @@ -1168,12 +1262,9 @@ class NinjaWriter: else: raise Exception('Unhandled output type %s' % type) - def ComputeOutput(self, spec, type=None): + def ComputeOutput(self, spec, arch=None): """Compute the path for the final output of the spec.""" - assert not self.is_mac_bundle or type - - if not type: - type = spec['type'] + type = spec['type'] if self.flavor == 'win': override = self.msvs_settings.GetOutputName(self.config_name, @@ -1181,13 +1272,13 @@ class NinjaWriter: if override: return override - if self.flavor == 'mac' and type in ( + if arch is None and self.flavor == 'mac' and type in ( 'static_library', 'executable', 'shared_library', 'loadable_module'): filename = self.xcode_settings.GetExecutablePath() else: filename = self.ComputeOutputFileName(spec, type) - if 'product_dir' in spec: + if arch is None and 'product_dir' in spec: path = os.path.join(spec['product_dir'], filename) return self.ExpandSpecial(path) @@ -1199,7 +1290,14 @@ class NinjaWriter: elif self.flavor == 'win' and self.toolset == 'target': type_in_output_root += ['shared_library'] - if type in type_in_output_root or self.is_standalone_static_library: + if arch is not None: + # Make sure partial executables don't end up in a bundle or the regular + # output directory. + archdir = 'arch' + if self.toolset != 'target': + archdir = os.path.join('arch', '%s' % self.toolset) + return os.path.join(archdir, AddArch(filename, arch)) + elif type in type_in_output_root or self.is_standalone_static_library: return filename elif type == 'shared_library': libdir = 'lib' @@ -1209,11 +1307,11 @@ class NinjaWriter: else: return self.GypPathToUniqueOutput(filename, qualified=False) - def WriteVariableList(self, var, values): + def WriteVariableList(self, ninja_file, var, values): assert not isinstance(values, str) if values is None: values = [] - self.ninja.variable(var, ' '.join(values)) + ninja_file.variable(var, ' '.join(values)) def WriteNewNinjaRule(self, name, args, description, is_cygwin, env): """Write out a new ninja "rule" statement for a given command. @@ -1759,6 +1857,10 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params, './gyp-mac-tool filter-libtool libtool $libtool_flags ' '-static -o $out $in' '$postbuilds') + master_ninja.rule( + 'lipo', + description='LIPO $out, POSTBUILDS', + command='rm -f $out && lipo -create $in -output $out$postbuilds') # Record the public interface of $lib in $lib.TOC. See the corresponding # comment in the posix section above for details. @@ -1873,6 +1975,7 @@ def GenerateOutputForConfig(target_list, target_dicts, data, params, writer = NinjaWriter(qualified_target, target_outputs, base_path, build_dir, OpenOutput(os.path.join(toplevel_build, output_file)), + toplevel_build, output_file, flavor, toplevel_dir=options.toplevel_dir) master_ninja.subninja(output_file) diff --git a/pylib/gyp/generator/ninja_test.py b/pylib/gyp/generator/ninja_test.py index 90dd1537..52661bcd 100644 --- a/pylib/gyp/generator/ninja_test.py +++ b/pylib/gyp/generator/ninja_test.py @@ -14,31 +14,31 @@ import TestCommon class TestPrefixesAndSuffixes(unittest.TestCase): - if sys.platform in ('win32', 'cygwin'): - def test_BinaryNamesWindows(self): - writer = ninja.NinjaWriter('foo', 'wee', '.', '.', 'ninja.build', 'win') - spec = { 'target_name': 'wee' } - self.assertTrue(writer.ComputeOutputFileName(spec, 'executable'). - endswith('.exe')) - self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library'). - endswith('.dll')) - self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library'). - endswith('.lib')) + def test_BinaryNamesWindows(self): + writer = ninja.NinjaWriter('foo', 'wee', '.', '.', 'build.ninja', '.', + 'build.ninja', 'win') + spec = { 'target_name': 'wee' } + self.assertTrue(writer.ComputeOutputFileName(spec, 'executable'). + endswith('.exe')) + self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library'). + endswith('.dll')) + self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library'). + endswith('.lib')) - if sys.platform == 'linux2': - def test_BinaryNamesLinux(self): - writer = ninja.NinjaWriter('foo', 'wee', '.', '.', 'ninja.build', 'linux') - spec = { 'target_name': 'wee' } - self.assertTrue('.' not in writer.ComputeOutputFileName(spec, - 'executable')) - self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library'). - startswith('lib')) - self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library'). - startswith('lib')) - self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library'). - endswith('.so')) - self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library'). - endswith('.a')) + def test_BinaryNamesLinux(self): + writer = ninja.NinjaWriter('foo', 'wee', '.', '.', 'build.ninja', '.', + 'build.ninja', 'linux') + spec = { 'target_name': 'wee' } + self.assertTrue('.' not in writer.ComputeOutputFileName(spec, + 'executable')) + self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library'). + startswith('lib')) + self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library'). + startswith('lib')) + self.assertTrue(writer.ComputeOutputFileName(spec, 'shared_library'). + endswith('.so')) + self.assertTrue(writer.ComputeOutputFileName(spec, 'static_library'). + endswith('.a')) if __name__ == '__main__': unittest.main() diff --git a/pylib/gyp/msvs_emulation.py b/pylib/gyp/msvs_emulation.py index ae65e859..0e16ed6f 100644 --- a/pylib/gyp/msvs_emulation.py +++ b/pylib/gyp/msvs_emulation.py @@ -677,11 +677,12 @@ class PrecompiledHeader(object): files.""" return os.path.split(self.settings.msvs_precompiled_header[self.config])[1] - def GetObjDependencies(self, sources, objs): + def GetObjDependencies(self, sources, objs, arch): """Given a list of sources files and the corresponding object files, returns a list of the pch files that should be depended upon. The additional wrapping in the return value is for interface compatability with make.py on Mac, and xcode_emulation.py.""" + assert arch is None if not self._PchHeader(): return [] pch_ext = os.path.splitext(self.pch_source)[1] @@ -690,7 +691,7 @@ class PrecompiledHeader(object): return [(None, None, self.output_obj)] return [] - def GetPchBuildCommands(self): + def GetPchBuildCommands(self, arch): """Not used on Windows as there are no additional build steps required (instead, existing steps are modified in GetFlagsModifications below).""" return [] diff --git a/pylib/gyp/xcode_emulation.py b/pylib/gyp/xcode_emulation.py index 3ef1cc7a..5e8f2b78 100644 --- a/pylib/gyp/xcode_emulation.py +++ b/pylib/gyp/xcode_emulation.py @@ -39,6 +39,15 @@ class XcodeSettings(object): None): self.isIOS = True + # If you need this, speak up at http://crbug.com/122592 + conditional_keys = [key for key in self.xcode_settings[configname] + if key.endswith(']')] + if conditional_keys: + print 'Warning: Conditional keys not implemented, ignoring:', \ + ' '.join(conditional_keys) + for key in conditional_keys: + del self.xcode_settings[configname][key] + # This is only non-None temporarily during the execution of some methods. self.configname = None @@ -231,6 +240,12 @@ class XcodeSettings(object): else: return self._GetStandaloneBinaryPath() + def GetActiveArchs(self, configname): + """Returns the architectures this target should be built for.""" + # TODO: Look at VALID_ARCHS, ONLY_ACTIVE_ARCH; possibly set + # CURRENT_ARCH / NATIVE_ARCH env vars? + return self.xcode_settings[configname].get('ARCHS', ['i386']) + def _GetSdkVersionInfoItem(self, sdk, infoitem): job = subprocess.Popen(['xcodebuild', '-version', '-sdk', sdk, infoitem], stdout=subprocess.PIPE) @@ -261,7 +276,7 @@ class XcodeSettings(object): self._Appendf(lst, 'IPHONEOS_DEPLOYMENT_TARGET', '-miphoneos-version-min=%s') - def GetCflags(self, configname): + def GetCflags(self, configname, arch=None): """Returns flags that need to be added to .c, .cc, .m, and .mm compilations.""" # This functions (and the similar ones below) do not offer complete @@ -334,7 +349,10 @@ class XcodeSettings(object): self._WarnUnimplemented('MACH_O_TYPE') self._WarnUnimplemented('PRODUCT_TYPE') - archs = self._Settings().get('ARCHS', ['i386']) + if arch is not None: + archs = [arch] + else: + archs = self._Settings().get('ARCHS', ['i386']) if len(archs) != 1: # TODO: Supporting fat binaries will be annoying. self._WarnUnimplemented('ARCHS') @@ -366,7 +384,10 @@ class XcodeSettings(object): """Returns flags that need to be added to .c, and .m compilations.""" self.configname = configname cflags_c = [] - self._Appendf(cflags_c, 'GCC_C_LANGUAGE_STANDARD', '-std=%s') + if self._Settings().get('GCC_C_LANGUAGE_STANDARD', '') == 'ansi': + cflags_c.append('-ansi') + else: + self._Appendf(cflags_c, 'GCC_C_LANGUAGE_STANDARD', '-std=%s') cflags_c += self._Settings().get('OTHER_CFLAGS', []) self.configname = None return cflags_c @@ -532,7 +553,7 @@ class XcodeSettings(object): ldflag = '-L' + gyp_to_build_path(ldflag[len('-L'):]) return ldflag - def GetLdflags(self, configname, product_dir, gyp_to_build_path): + def GetLdflags(self, configname, product_dir, gyp_to_build_path, arch=None): """Returns flags that need to be passed to the linker. Args: @@ -574,7 +595,10 @@ class XcodeSettings(object): '-Wl,' + gyp_to_build_path( self._Settings()['ORDER_FILE'])) - archs = self._Settings().get('ARCHS', ['i386']) + if arch is not None: + archs = [arch] + else: + archs = self._Settings().get('ARCHS', ['i386']) if len(archs) != 1: # TODO: Supporting fat binaries will be annoying. self._WarnUnimplemented('ARCHS') @@ -779,21 +803,28 @@ class MacPrefixHeader(object): self.header, lang) self.header = gyp_path_to_build_path(self.header) - def GetInclude(self, lang): + def _CompiledHeader(self, lang, arch): + assert self.compile_headers + h = self.compiled_headers[lang] + if arch: + h += '.' + arch + return h + + def GetInclude(self, lang, arch=None): """Gets the cflags to include the prefix header for language |lang|.""" if self.compile_headers and lang in self.compiled_headers: - return '-include %s' % self.compiled_headers[lang] + return '-include %s' % self._CompiledHeader(lang, arch) elif self.header: return '-include %s' % self.header else: return '' - def _Gch(self, lang): + def _Gch(self, lang, arch): """Returns the actual file name of the prefix header for language |lang|.""" assert self.compile_headers - return self.compiled_headers[lang] + '.gch' + return self._CompiledHeader(lang, arch) + '.gch' - def GetObjDependencies(self, sources, objs): + def GetObjDependencies(self, sources, objs, arch=None): """Given a list of source files and the corresponding object files, returns a list of (source, object, gch) tuples, where |gch| is the build-directory relative path to the gch file each object file depends on. |compilable[i]| @@ -811,20 +842,20 @@ class MacPrefixHeader(object): '.mm': 'mm', }.get(ext, None) if lang: - result.append((source, obj, self._Gch(lang))) + result.append((source, obj, self._Gch(lang, arch))) return result - def GetPchBuildCommands(self): + def GetPchBuildCommands(self, arch=None): """Returns [(path_to_gch, language_flag, language, header)]. |path_to_gch| and |header| are relative to the build directory. """ if not self.header or not self.compile_headers: return [] return [ - (self._Gch('c'), '-x c-header', 'c', self.header), - (self._Gch('cc'), '-x c++-header', 'cc', self.header), - (self._Gch('m'), '-x objective-c-header', 'm', self.header), - (self._Gch('mm'), '-x objective-c++-header', 'mm', self.header), + (self._Gch('c', arch), '-x c-header', 'c', self.header), + (self._Gch('cc', arch), '-x c++-header', 'cc', self.header), + (self._Gch('m', arch), '-x objective-c-header', 'm', self.header), + (self._Gch('mm', arch), '-x objective-c++-header', 'mm', self.header), ] diff --git a/test/cflags/gyptest-cflags.py b/test/cflags/gyptest-cflags.py index a0076238..5de8c32c 100755 --- a/test/cflags/gyptest-cflags.py +++ b/test/cflags/gyptest-cflags.py @@ -10,6 +10,7 @@ the use of the environment during regeneration when the gyp file changes. """ import os +import sys import TestGyp env_stack = [] @@ -22,9 +23,12 @@ def PushEnv(): def PopEnv(): os.eniron=env_stack.pop() -# Regenerating build files when a gyp file changes is currently only supported -# by the make generator. -test = TestGyp.TestGyp(formats=['make']) +formats = ['make'] +if sys.platform.startswith('linux'): + # Only Linux ninja generator supports CFLAGS. + formats.append('ninja') + +test = TestGyp.TestGyp(formats=formats) try: PushEnv() diff --git a/test/mac/archs/empty_main.cc b/test/mac/archs/empty_main.cc new file mode 100644 index 00000000..237c8ce1 --- /dev/null +++ b/test/mac/archs/empty_main.cc @@ -0,0 +1 @@ +int main() {} diff --git a/test/mac/archs/file.mm b/test/mac/archs/file.mm new file mode 100644 index 00000000..d0b39d1f --- /dev/null +++ b/test/mac/archs/file.mm @@ -0,0 +1 @@ +MyInt f() { return 0; } diff --git a/test/mac/archs/header.h b/test/mac/archs/header.h new file mode 100644 index 00000000..0716e500 --- /dev/null +++ b/test/mac/archs/header.h @@ -0,0 +1 @@ +typedef int MyInt; diff --git a/test/mac/archs/test-archs-multiarch.gyp b/test/mac/archs/test-archs-multiarch.gyp new file mode 100644 index 00000000..a187ca5c --- /dev/null +++ b/test/mac/archs/test-archs-multiarch.gyp @@ -0,0 +1,60 @@ +# Copyright (c) 2012 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'static_32_64', + 'type': 'static_library', + 'sources': [ 'my_file.cc' ], + 'xcode_settings': { + 'ARCHS': [ 'i386', 'x86_64' ], + }, + }, + { + 'target_name': 'shared_32_64', + 'type': 'shared_library', + 'sources': [ 'my_file.cc' ], + 'xcode_settings': { + 'ARCHS': [ 'i386', 'x86_64' ], + }, + }, + { + 'target_name': 'module_32_64', + 'type': 'loadable_module', + 'sources': [ 'my_file.cc' ], + 'xcode_settings': { + 'ARCHS': [ 'i386', 'x86_64' ], + }, + }, + { + 'target_name': 'exe_32_64', + 'type': 'executable', + 'sources': [ 'empty_main.cc' ], + 'xcode_settings': { + 'ARCHS': [ 'i386', 'x86_64' ], + }, + }, + { + 'target_name': 'exe_32_64_bundle', + 'product_name': 'Test App', + 'type': 'executable', + 'mac_bundle': 1, + 'sources': [ 'empty_main.cc' ], + 'xcode_settings': { + 'ARCHS': [ 'i386', 'x86_64' ], + }, + }, + # This only needs to compile. + { + 'target_name': 'precompiled_prefix_header_mm_32_64', + 'type': 'shared_library', + 'sources': [ 'file.mm', ], + 'xcode_settings': { + 'GCC_PREFIX_HEADER': 'header.h', + 'GCC_PRECOMPILE_PREFIX_HEADER': 'YES', + }, + }, + ] +} diff --git a/test/mac/cflags/test.gyp b/test/mac/cflags/test.gyp index 6b04b5f2..d330a548 100644 --- a/test/mac/cflags/test.gyp +++ b/test/mac/cflags/test.gyp @@ -115,5 +115,18 @@ 'GCC_C_LANGUAGE_STANDARD': 'c99', }, }, + { + 'target_name': 'ansi_standard', + 'type': 'shared_library', + 'sources': [ + 'cfile.c', + ], + 'xcode_settings': { + 'OTHER_CFLAGS': [ + '-DCFLAG', + ], + 'GCC_C_LANGUAGE_STANDARD': 'ansi', + }, + }, ], } diff --git a/test/mac/gyptest-archs.py b/test/mac/gyptest-archs.py index 781e9ef1..70bb6a3d 100644 --- a/test/mac/gyptest-archs.py +++ b/test/mac/gyptest-archs.py @@ -10,6 +10,7 @@ Tests things related to ARCHS. import TestGyp +import re import subprocess import sys @@ -20,7 +21,7 @@ if sys.platform == 'darwin': proc = subprocess.Popen(['file', '-b', file], stdout=subprocess.PIPE) o = proc.communicate()[0].strip() assert not proc.returncode - if o != expected: + if not re.match(expected, o, re.DOTALL): print 'File: Expected %s, got %s' % (expected, o) test.fail_test() @@ -28,10 +29,39 @@ if sys.platform == 'darwin': test.build('test-no-archs.gyp', test.ALL, chdir='archs') result_file = test.built_file_path('Test', chdir='archs') test.must_exist(result_file) - CheckFileType(result_file, 'Mach-O executable i386') + # FIXME: The default setting changed from i386 to x86_64 in Xcode 5. + #CheckFileType(result_file, '^Mach-O executable i386') test.run_gyp('test-archs-x86_64.gyp', chdir='archs') test.build('test-archs-x86_64.gyp', test.ALL, chdir='archs') result_file = test.built_file_path('Test64', chdir='archs') test.must_exist(result_file) - CheckFileType(result_file, 'Mach-O 64-bit executable x86_64') + CheckFileType(result_file, '^Mach-O 64-bit executable x86_64$') + + if test.format != 'make': + test.run_gyp('test-archs-multiarch.gyp', chdir='archs') + test.build('test-archs-multiarch.gyp', test.ALL, chdir='archs') + + result_file = test.built_file_path( + 'static_32_64', chdir='archs', type=test.STATIC_LIB) + test.must_exist(result_file) + CheckFileType(result_file, 'Mach-O universal binary with 2 architectures' + '.*architecture i386.*architecture x86_64') + + result_file = test.built_file_path( + 'shared_32_64', chdir='archs', type=test.SHARED_LIB) + test.must_exist(result_file) + CheckFileType(result_file, 'Mach-O universal binary with 2 architectures' + '.*architecture i386.*architecture x86_64') + + result_file = test.built_file_path( + 'exe_32_64', chdir='archs', type=test.EXECUTABLE) + test.must_exist(result_file) + CheckFileType(result_file, 'Mach-O universal binary with 2 architectures' + '.*architecture i386.*architecture x86_64') + + result_file = test.built_file_path('Test App.app/Contents/MacOS/Test App', + chdir='archs') + test.must_exist(result_file) + CheckFileType(result_file, 'Mach-O universal binary with 2 architectures' + '.*architecture i386.*architecture x86_64') diff --git a/test/win/generator-output-different-drive/gyptest-generator-output-different-drive.py b/test/win/generator-output-different-drive/gyptest-generator-output-different-drive.py new file mode 100644 index 00000000..8c8c365d --- /dev/null +++ b/test/win/generator-output-different-drive/gyptest-generator-output-different-drive.py @@ -0,0 +1,44 @@ +#!/usr/bin/env python + +# Copyright (c) 2013 Google Inc. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +""" +Test that the generator output can be written to a different drive on Windows. +""" + +import os +import TestGyp +import string +import subprocess +import sys + + +if sys.platform == 'win32': + import win32api + + test = TestGyp.TestGyp(formats=['msvs', 'ninja']) + + def GetFirstFreeDriveLetter(): + """ Returns the first unused Windows drive letter in [A, Z] """ + all_letters = [c for c in string.uppercase] + in_use = win32api.GetLogicalDriveStrings() + free = list(set(all_letters) - set(in_use)) + return free[0] + + output_dir = os.path.join('different-drive', 'output') + if not os.path.isdir(os.path.abspath(output_dir)): + os.makedirs(os.path.abspath(output_dir)) + output_drive = GetFirstFreeDriveLetter() + subprocess.call(['subst', '%c:' % output_drive, os.path.abspath(output_dir)]) + try: + test.run_gyp('prog.gyp', '--generator-output=%s' % ( + os.path.join(output_drive, 'output'))) + test.build('prog.gyp', test.ALL, chdir=os.path.join(output_drive, 'output')) + test.built_file_must_exist('program', chdir=os.path.join(output_drive, + 'output'), + type=test.EXECUTABLE) + test.pass_test() + finally: + subprocess.call(['subst', '%c:' % output_drive, '/D']) diff --git a/test/win/generator-output-different-drive/prog.c b/test/win/generator-output-different-drive/prog.c new file mode 100644 index 00000000..2f855c43 --- /dev/null +++ b/test/win/generator-output-different-drive/prog.c @@ -0,0 +1,10 @@ +// Copyright 2013 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include <stdio.h> + +int main(int argc, char *argv[]) { + printf("Hello from prog.c\n"); + return 0; +} diff --git a/test/win/generator-output-different-drive/prog.gyp b/test/win/generator-output-different-drive/prog.gyp new file mode 100644 index 00000000..92f53e5d --- /dev/null +++ b/test/win/generator-output-different-drive/prog.gyp @@ -0,0 +1,15 @@ +# Copyright 2013 The Chromium Authors. All rights reserved. +# Use of this source code is governed by a BSD-style license that can be +# found in the LICENSE file. + +{ + 'targets': [ + { + 'target_name': 'program', + 'type': 'executable', + 'sources': [ + 'prog.c', + ], + }, + ], +} |